Subject: Open-FCoE update for Beta3 From: John Fastabend Date: Thu Nov 6 13:01:32 2008 +0100: Git: 2d8ac46f55c5f06ac3fe9830c899386789aa8900 References: bnc#438954 Incremental Open-FCoE update for Beta3. Signed-off-by: John Fastabend Acked-by: Hannes Reinecke diff --git a/drivers/scsi/fcoe/fcoe_if.c b/drivers/scsi/fcoe/fcoe_if.c index 7f983e2..73b83ce 100644 --- a/drivers/scsi/fcoe/fcoe_if.c +++ b/drivers/scsi/fcoe/fcoe_if.c @@ -155,18 +155,18 @@ int fcoe_destroy_interface(const char *ifname) list_del(&fc->list); write_unlock_bh(&fci->fcoe_hostlist_lock); - /* Cleanup the fc_lport */ - fc_lport_destroy(lp); - fc_fcp_destroy(lp); - if (lp->emp) - fc_exch_mgr_free(lp->emp); + /* Don't listen for Ethernet packets anymore */ + dev_remove_pack(&fc->fcoe_packet_type); /* Detach from the scsi-ml */ fc_remove_host(lp->host); scsi_remove_host(lp->host); - /* Don't listen for Ethernet packets anymore */ - dev_remove_pack(&fc->fcoe_packet_type); + /* Cleanup the fc_lport */ + fc_lport_destroy(lp); + fc_fcp_destroy(lp); + if (lp->emp) + fc_exch_mgr_free(lp->emp); /* Delete secondary MAC addresses */ rtnl_lock(); @@ -388,8 +388,7 @@ static int libfc_config(struct fc_lport *lp) fc_exch_init(lp); fc_lport_init(lp); fc_rport_init(lp); - fc_ns_init(lp); - fc_attr_init(lp); + fc_disc_init(lp); return 0; } diff --git a/drivers/scsi/fcoe/fcoeinit.c b/drivers/scsi/fcoe/fcoeinit.c index e069835..7d52ed5 100644 --- a/drivers/scsi/fcoe/fcoeinit.c +++ b/drivers/scsi/fcoe/fcoeinit.c @@ -53,8 +53,8 @@ struct scsi_transport_template *fcoe_transport_template; static int fcoe_reset(struct Scsi_Host *shost) { - struct fc_lport *lp = shost_priv(shost); - fc_lport_enter_reset(lp); + struct fc_lport *lport = shost_priv(shost); + fc_lport_reset(lport); return 0; } @@ -66,11 +66,10 @@ struct fc_function_template fcoe_transport_function = { .show_host_active_fc4s = 1, .show_host_maxframe_size = 1, - .get_host_port_id = fc_get_host_port_id, .show_host_port_id = 1, + .show_host_supported_speeds = 1, .get_host_speed = fc_get_host_speed, .show_host_speed = 1, - .get_host_port_type = fc_get_host_port_type, .show_host_port_type = 1, .get_host_port_state = fc_get_host_port_state, .show_host_port_state = 1, @@ -80,7 +79,6 @@ struct fc_function_template fcoe_transport_function = { .show_rport_maxframe_size = 1, .show_rport_supported_classes = 1, - .get_host_fabric_name = fc_get_host_fabric_name, .show_host_fabric_name = 1, .show_starget_node_name = 1, .show_starget_port_name = 1, @@ -89,6 +87,8 @@ struct fc_function_template fcoe_transport_function = { .show_rport_dev_loss_tmo = 1, .get_fc_host_stats = fc_get_host_stats, .issue_fc_host_lip = fcoe_reset, + + .terminate_rport_io = fc_rport_terminate_io, }; struct fcoe_percpu_s *fcoe_percpu[NR_CPUS]; diff --git a/drivers/scsi/libfc/Makefile b/drivers/scsi/libfc/Makefile index 0a31ca2..e6d4086 100644 --- a/drivers/scsi/libfc/Makefile +++ b/drivers/scsi/libfc/Makefile @@ -3,10 +3,9 @@ obj-$(CONFIG_LIBFC) += libfc.o libfc-objs := \ - fc_ns.o \ + fc_disc.o \ fc_exch.o \ fc_frame.o \ fc_lport.o \ fc_rport.o \ - fc_attr.o \ fc_fcp.o diff --git a/drivers/scsi/libfc/fc_attr.c b/drivers/scsi/libfc/fc_attr.c deleted file mode 100644 index d73f39e..0000000 --- a/drivers/scsi/libfc/fc_attr.c +++ /dev/null @@ -1,129 +0,0 @@ -/* - * Copyright(c) 2007 Intel Corporation. All rights reserved. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms and conditions of the GNU General Public License, - * version 2, as published by the Free Software Foundation. - * - * This program is distributed in the hope it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. - * - * Maintained at www.Open-FCoE.org - */ - -#include -#include - -#include - -#include - -MODULE_AUTHOR("Open-FCoE.org"); -MODULE_DESCRIPTION("libfc"); -MODULE_LICENSE("GPL"); - -void fc_get_host_port_id(struct Scsi_Host *shost) -{ - struct fc_lport *lp = shost_priv(shost); - - fc_host_port_id(shost) = fc_lport_get_fid(lp); -} -EXPORT_SYMBOL(fc_get_host_port_id); - -void fc_get_host_speed(struct Scsi_Host *shost) -{ - /* - * should be obtain from DEC or Enet Driver - */ - fc_host_speed(shost) = 1; /* for now it is 1g */ -} -EXPORT_SYMBOL(fc_get_host_speed); - -void fc_get_host_port_type(struct Scsi_Host *shost) -{ - fc_host_port_type(shost) = FC_PORTTYPE_NPORT; -} -EXPORT_SYMBOL(fc_get_host_port_type); - -void fc_get_host_fabric_name(struct Scsi_Host *shost) -{ - struct fc_lport *lp = shost_priv(shost); - - fc_host_fabric_name(shost) = lp->wwnn; -} -EXPORT_SYMBOL(fc_get_host_fabric_name); - -void fc_attr_init(struct fc_lport *lp) -{ - fc_host_node_name(lp->host) = lp->wwnn; - fc_host_port_name(lp->host) = lp->wwpn; - fc_host_supported_classes(lp->host) = FC_COS_CLASS3; - memset(fc_host_supported_fc4s(lp->host), 0, - sizeof(fc_host_supported_fc4s(lp->host))); - fc_host_supported_fc4s(lp->host)[2] = 1; - fc_host_supported_fc4s(lp->host)[7] = 1; - /* This value is also unchanging */ - memset(fc_host_active_fc4s(lp->host), 0, - sizeof(fc_host_active_fc4s(lp->host))); - fc_host_active_fc4s(lp->host)[2] = 1; - fc_host_active_fc4s(lp->host)[7] = 1; - fc_host_maxframe_size(lp->host) = lp->mfs; -} -EXPORT_SYMBOL(fc_attr_init); - -void fc_set_rport_loss_tmo(struct fc_rport *rport, u32 timeout) -{ - if (timeout) - rport->dev_loss_tmo = timeout + 5; - else - rport->dev_loss_tmo = 30; - -} -EXPORT_SYMBOL(fc_set_rport_loss_tmo); - -struct fc_host_statistics *fc_get_host_stats(struct Scsi_Host *shost) -{ - int i; - struct fc_host_statistics *fcoe_stats; - struct fc_lport *lp = shost_priv(shost); - struct timespec v0, v1; - - fcoe_stats = &lp->host_stats; - memset(fcoe_stats, 0, sizeof(struct fc_host_statistics)); - - jiffies_to_timespec(jiffies, &v0); - jiffies_to_timespec(lp->boot_time, &v1); - fcoe_stats->seconds_since_last_reset = (v0.tv_sec - v1.tv_sec); - - for_each_online_cpu(i) { - struct fcoe_dev_stats *stats = lp->dev_stats[i]; - if (stats == NULL) - continue; - fcoe_stats->tx_frames += stats->TxFrames; - fcoe_stats->tx_words += stats->TxWords; - fcoe_stats->rx_frames += stats->RxFrames; - fcoe_stats->rx_words += stats->RxWords; - fcoe_stats->error_frames += stats->ErrorFrames; - fcoe_stats->invalid_crc_count += stats->InvalidCRCCount; - fcoe_stats->fcp_input_requests += stats->InputRequests; - fcoe_stats->fcp_output_requests += stats->OutputRequests; - fcoe_stats->fcp_control_requests += stats->ControlRequests; - fcoe_stats->fcp_input_megabytes += stats->InputMegabytes; - fcoe_stats->fcp_output_megabytes += stats->OutputMegabytes; - fcoe_stats->link_failure_count += stats->LinkFailureCount; - } - fcoe_stats->lip_count = -1; - fcoe_stats->nos_count = -1; - fcoe_stats->loss_of_sync_count = -1; - fcoe_stats->loss_of_signal_count = -1; - fcoe_stats->prim_seq_protocol_err_count = -1; - fcoe_stats->dumped_frames = -1; - return fcoe_stats; -} -EXPORT_SYMBOL(fc_get_host_stats); diff --git a/drivers/scsi/libfc/fc_disc.c b/drivers/scsi/libfc/fc_disc.c new file mode 100644 index 0000000..30403aa --- /dev/null +++ b/drivers/scsi/libfc/fc_disc.c @@ -0,0 +1,599 @@ +/* + * Copyright(c) 2007 - 2008 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. + * + * Maintained at www.Open-FCoE.org + */ + +/* + * Target Discovery + * Actually, this discovers all FC-4 remote ports, including FCP initiators. + */ + +#include +#include +#include + +#include + +#include + +#define FC_DISC_RETRY_LIMIT 3 /* max retries */ +#define FC_DISC_RETRY_DELAY 500UL /* (msecs) delay */ + +int fc_disc_debug; + +static void fc_disc_gpn_ft_req(struct fc_lport *); +static void fc_disc_gpn_ft_resp(struct fc_seq *, struct fc_frame *, void *); +static int fc_disc_new_target(struct fc_lport *, struct fc_rport *, + struct fc_rport_identifiers *); +static void fc_disc_del_target(struct fc_lport *, struct fc_rport *); +static void fc_disc_done(struct fc_lport *); +static void fc_disc_error(struct fc_lport *, struct fc_frame *); +static void fc_disc_timeout(struct work_struct *); +static void fc_disc_single(struct fc_lport *, struct fc_disc_port *); +static int fc_disc_restart(struct fc_lport *); + +/** + * fc_disc_rscn_req - Handle Registered State Change Notification (RSCN) + * @sp: Current sequence of the RSCN exchange + * @fp: RSCN Frame + * @lp: Fibre Channel host port instance + */ +static void fc_disc_rscn_req(struct fc_seq *sp, struct fc_frame *fp, + struct fc_lport *lp) +{ + struct fc_els_rscn *rp; + struct fc_els_rscn_page *pp; + struct fc_seq_els_data rjt_data; + unsigned int len; + int redisc = 0; + enum fc_els_rscn_ev_qual ev_qual; + enum fc_els_rscn_addr_fmt fmt; + LIST_HEAD(disc_list); + struct fc_disc_port *dp, *next; + + rp = fc_frame_payload_get(fp, sizeof(*rp)); + + if (!rp || rp->rscn_page_len != sizeof(*pp)) + goto reject; + + len = ntohs(rp->rscn_plen); + if (len < sizeof(*rp)) + goto reject; + len -= sizeof(*rp); + + for (pp = (void *)(rp + 1); len; len -= sizeof(*pp), pp++) { + ev_qual = pp->rscn_page_flags >> ELS_RSCN_EV_QUAL_BIT; + ev_qual &= ELS_RSCN_EV_QUAL_MASK; + fmt = pp->rscn_page_flags >> ELS_RSCN_ADDR_FMT_BIT; + fmt &= ELS_RSCN_ADDR_FMT_MASK; + /* + * if we get an address format other than port + * (area, domain, fabric), then do a full discovery + */ + switch (fmt) { + case ELS_ADDR_FMT_PORT: + dp = kzalloc(sizeof(*dp), GFP_KERNEL); + if (!dp) { + redisc = 1; + break; + } + dp->lp = lp; + 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; + list_add_tail(&dp->peers, &disc_list); + break; + case ELS_ADDR_FMT_AREA: + case ELS_ADDR_FMT_DOM: + case ELS_ADDR_FMT_FAB: + default: + redisc = 1; + break; + } + } + lp->tt.seq_els_rsp_send(sp, ELS_LS_ACC, NULL); + if (redisc) { + if (fc_disc_debug) + FC_DBG("RSCN received: rediscovering\n"); + list_for_each_entry_safe(dp, next, &disc_list, peers) { + list_del(&dp->peers); + kfree(dp); + } + fc_disc_restart(lp); + } else { + if (fc_disc_debug) + FC_DBG("RSCN received: not rediscovering. " + "redisc %d state %d in_prog %d\n", + redisc, lp->state, lp->disc_pending); + list_for_each_entry_safe(dp, next, &disc_list, peers) { + list_del(&dp->peers); + fc_disc_single(lp, dp); + } + } + fc_frame_free(fp); + return; +reject: + rjt_data.fp = NULL; + rjt_data.reason = ELS_RJT_LOGIC; + rjt_data.explan = ELS_EXPL_NONE; + lp->tt.seq_els_rsp_send(sp, ELS_LS_RJT, &rjt_data); + fc_frame_free(fp); +} + +static void fc_disc_recv_req(struct fc_seq *sp, struct fc_frame *fp, + struct fc_lport *lp) +{ + switch (fc_frame_payload_op(fp)) { + case ELS_RSCN: + fc_disc_rscn_req(sp, fp, lp); + break; + default: + FC_DBG("fc_disc recieved an unexpected request\n"); + break; + } +} + +/* + * Refresh target discovery, perhaps due to an RSCN. + * A configurable delay is introduced to collect any subsequent RSCNs. + */ +static int fc_disc_restart(struct fc_lport *lp) +{ + if (!lp->disc_requested && !lp->disc_pending) { + schedule_delayed_work(&lp->disc_work, + msecs_to_jiffies(lp->disc_delay * 1000)); + } + lp->disc_requested = 1; + return 0; +} + +/* + * Fibre Channel Target discovery. + * + * Returns non-zero if discovery cannot be started. + * + * Callback is called for each target remote port found in discovery. + * When discovery is complete, the callback is called with a NULL remote port. + * Discovery may be restarted after an RSCN is received, causing the + * callback to be called after discovery complete is indicated. + */ +int fc_disc_start(struct fc_lport *lp) +{ + struct fc_rport *rport; + int error; + struct fc_rport_identifiers ids; + + /* + * If not ready, or already running discovery, just set request flag. + */ + if (!fc_lport_test_ready(lp) || lp->disc_pending) { + lp->disc_requested = 1; + + return 0; + } + lp->disc_pending = 1; + lp->disc_requested = 0; + lp->disc_retry_count = 0; + + /* + * Handle point-to-point mode as a simple discovery + * of the remote port. + */ + rport = lp->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); + + error = fc_disc_new_target(lp, rport, &ids); + put_device(&rport->dev); + if (!error) + fc_disc_done(lp); + } else { + fc_block_rports(lp); + fc_disc_gpn_ft_req(lp); /* get ports by FC-4 type */ + error = 0; + } + return error; +} + +/* + * Restart discovery after a delay due to resource shortages. + * If the error persists, the discovery will be abandoned. + */ +static void fc_disc_retry(struct fc_lport *lp) +{ + unsigned long delay = FC_DISC_RETRY_DELAY; + + if (!lp->disc_retry_count) + delay /= 4; /* timeout faster first time */ + if (lp->disc_retry_count++ < FC_DISC_RETRY_LIMIT) + schedule_delayed_work(&lp->disc_work, + msecs_to_jiffies(delay)); + else + fc_disc_done(lp); +} + +/* + * Handle new target found by discovery. + * Create remote port and session if needed. + * Ignore returns of our own FID & WWPN. + * + * If a non-NULL rp is passed in, it is held for the caller, but not for us. + * + * Events delivered are: + * FC_EV_READY, when remote port is rediscovered. + */ +static int fc_disc_new_target(struct fc_lport *lp, + struct fc_rport *rport, + struct fc_rport_identifiers *ids) +{ + struct fc_rport_libfc_priv *rp; + 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(lp, rport); + rport = NULL; + } + } + if (((ids->port_name != -1) || (ids->port_id != -1)) && + ids->port_id != lp->fid && ids->port_name != lp->wwpn) { + if (!rport) { + rport = lp->tt.rport_lookup(lp, ids->port_id); + if (!rport) { + struct fc_disc_port dp; + dp.lp = lp; + 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 = fc_rport_dummy_create(&dp); + } + if (!rport) + error = ENOMEM; + } + if (rport) { + rp = rport->dd_data; + rp->event_callback = lp->tt.event_callback; + rp->rp_state = RPORT_ST_INIT; + lp->tt.rport_login(rport); + } + } + return error; +} + +/* + * Delete the remote port. + */ +static void fc_disc_del_target(struct fc_lport *lp, struct fc_rport *rport) +{ + lp->tt.rport_reset(rport); + fc_remote_port_delete(rport); /* release hold from create */ +} + +/* + * Done with discovery + */ +static void fc_disc_done(struct fc_lport *lp) +{ + lp->disc_done = 1; + lp->disc_pending = 0; + if (lp->disc_requested) + lp->tt.disc_start(lp); +} + +/** + * fc_disc_gpn_ft_req - Send Get Port Names by FC-4 type (GPN_FT) request + * @lp: Fibre Channel host port instance + */ +static void fc_disc_gpn_ft_req(struct fc_lport *lp) +{ + struct fc_frame *fp; + struct fc_seq *sp = NULL; + struct req { + struct fc_ct_hdr ct; + struct fc_ns_gid_ft gid; + } *rp; + int error = 0; + + lp->disc_buf_len = 0; + lp->disc_seq_count = 0; + fp = fc_frame_alloc(lp, sizeof(*rp)); + if (fp == NULL) { + error = ENOMEM; + } else { + rp = fc_frame_payload_get(fp, sizeof(*rp)); + fc_fill_dns_hdr(lp, &rp->ct, FC_NS_GPN_FT, sizeof(rp->gid)); + rp->gid.fn_fc4_type = FC_TYPE_FCP; + + WARN_ON(!fc_lport_test_ready(lp)); + + fc_frame_setup(fp, FC_RCTL_DD_UNSOL_CTL, FC_TYPE_CT); + sp = lp->tt.exch_seq_send(lp, fp, + fc_disc_gpn_ft_resp, NULL, + lp, lp->e_d_tov, + lp->fid, + FC_FID_DIR_SERV, + FC_FC_SEQ_INIT | FC_FC_END_SEQ); + } + if (error || sp == NULL) + fc_disc_retry(lp); +} + +/* + * Handle error on dNS request. + */ +static void fc_disc_error(struct fc_lport *lp, struct fc_frame *fp) +{ + int err = PTR_ERR(fp); + + switch (err) { + case -FC_EX_TIMEOUT: + if (lp->disc_retry_count++ < FC_DISC_RETRY_LIMIT) { + fc_disc_gpn_ft_req(lp); + } else { + FC_DBG("err %d - ending\n", err); + fc_disc_done(lp); + } + break; + default: + FC_DBG("err %d - ending\n", err); + fc_disc_done(lp); + break; + } +} + +/** + * fc_disc_gpn_ft_parse - Parse the list of IDs and names resulting from a request + * @lp: Fibre Channel host port instance + * @buf: GPN_FT response buffer + * @len: size of response buffer + */ +static int fc_disc_gpn_ft_parse(struct fc_lport *lp, void *buf, size_t len) +{ + struct fc_gpn_ft_resp *np; + char *bp; + size_t plen; + size_t tlen; + int error = 0; + struct fc_disc_port dp; + struct fc_rport *rp; + struct fc_rport_libfc_priv *rpp; + + /* + * Handle partial name record left over from previous call. + */ + bp = buf; + plen = len; + np = (struct fc_gpn_ft_resp *)bp; + tlen = lp->disc_buf_len; + if (tlen) { + WARN_ON(tlen >= sizeof(*np)); + plen = sizeof(*np) - tlen; + WARN_ON(plen <= 0); + WARN_ON(plen >= sizeof(*np)); + if (plen > len) + plen = len; + np = &lp->disc_buf; + memcpy((char *)np + tlen, bp, plen); + + /* + * Set bp so that the loop below will advance it to the + * first valid full name element. + */ + bp -= tlen; + len += tlen; + plen += tlen; + lp->disc_buf_len = (unsigned char) plen; + if (plen == sizeof(*np)) + lp->disc_buf_len = 0; + } + + /* + * Handle full name records, including the one filled from above. + * Normally, np == bp and plen == len, but from the partial case above, + * bp, len describe the overall buffer, and np, plen describe the + * partial buffer, which if would usually be full now. + * After the first time through the loop, things return to "normal". + */ + while (plen >= sizeof(*np)) { + dp.lp = lp; + 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 != lp->fid) && + (dp.ids.port_name != lp->wwpn)) { + rp = fc_rport_dummy_create(&dp); + if (rp) { + rpp = rp->dd_data; + rpp->local_port = lp; + lp->tt.rport_login(rp); + } else + FC_DBG("Failed to allocate memory for " + "the newly discovered port (%6x)\n", + dp.ids.port_id); + } + + if (np->fp_flags & FC_NS_FID_LAST) { + fc_disc_done(lp); + len = 0; + break; + } + len -= sizeof(*np); + bp += sizeof(*np); + np = (struct fc_gpn_ft_resp *)bp; + plen = len; + } + + /* + * Save any partial record at the end of the buffer for next time. + */ + if (error == 0 && len > 0 && len < sizeof(*np)) { + if (np != &lp->disc_buf) + memcpy(&lp->disc_buf, np, len); + lp->disc_buf_len = (unsigned char) len; + } else { + lp->disc_buf_len = 0; + } + return error; +} + +/* + * Handle retry of memory allocation for remote ports. + */ +static void fc_disc_timeout(struct work_struct *work) +{ + struct fc_lport *lp; + + lp = container_of(work, struct fc_lport, disc_work.work); + + if (lp->disc_pending) + fc_disc_gpn_ft_req(lp); + else + lp->tt.disc_start(lp); +} + +/** + * fc_disc_gpn_ft_resp - Handle a response frame from Get Port Names (GPN_FT) + * @sp: Current sequence of GPN_FT exchange + * @fp: response frame + * @lp_arg: Fibre Channel host port instance + * + * The response may be in multiple frames + */ +static void fc_disc_gpn_ft_resp(struct fc_seq *sp, struct fc_frame *fp, + void *lp_arg) +{ + struct fc_lport *lp = lp_arg; + struct fc_ct_hdr *cp; + struct fc_frame_header *fh; + unsigned int seq_cnt; + void *buf = NULL; + unsigned int len; + int error; + + if (IS_ERR(fp)) { + fc_disc_error(lp, fp); + return; + } + + WARN_ON(!fc_frame_is_linear(fp)); /* buffer must be contiguous */ + 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 && + lp->disc_seq_count == 0) { + cp = fc_frame_payload_get(fp, sizeof(*cp)); + if (cp == NULL) { + FC_DBG("GPN_FT response too short, len %d\n", + fr_len(fp)); + } else if (ntohs(cp->ct_cmd) == FC_FS_ACC) { + + /* + * Accepted. Parse response. + */ + buf = cp + 1; + len -= sizeof(*cp); + } else if (ntohs(cp->ct_cmd) == FC_FS_RJT) { + FC_DBG("GPN_FT rejected reason %x exp %x " + "(check zoning)\n", cp->ct_reason, + cp->ct_explan); + fc_disc_done(lp); + } else { + FC_DBG("GPN_FT unexpected response code %x\n", + ntohs(cp->ct_cmd)); + } + } else if (fr_sof(fp) == FC_SOF_N3 && + seq_cnt == lp->disc_seq_count) { + buf = fh + 1; + } else { + FC_DBG("GPN_FT unexpected frame - out of sequence? " + "seq_cnt %x expected %x sof %x eof %x\n", + seq_cnt, lp->disc_seq_count, fr_sof(fp), fr_eof(fp)); + } + if (buf) { + error = fc_disc_gpn_ft_parse(lp, buf, len); + if (error) + fc_disc_retry(lp); + else + lp->disc_seq_count++; + } + fc_frame_free(fp); +} + +/* + * Discover the directory information for a single target. + * This could be from an RSCN that reported a change for the target. + */ +static void fc_disc_single(struct fc_lport *lp, struct fc_disc_port *dp) +{ + struct fc_rport *rport; + struct fc_rport *rp; + struct fc_rport_libfc_priv *rpp; + + if (dp->ids.port_id == lp->fid) + goto out; + + rport = lp->tt.rport_lookup(lp, dp->ids.port_id); + if (rport) { + fc_disc_del_target(lp, rport); + put_device(&rport->dev); /* hold from lookup */ + } + + rp = fc_rport_dummy_create(dp); + if (rp) { + rpp = rp->dd_data; + kfree(dp); + lp->tt.rport_login(rp); + } + return; +out: + kfree(dp); +} + +int fc_disc_init(struct fc_lport *lp) +{ + INIT_DELAYED_WORK(&lp->disc_work, fc_disc_timeout); + + if (!lp->tt.disc_start) + lp->tt.disc_start = fc_disc_start; + + if (!lp->tt.disc_recv_req) + lp->tt.disc_recv_req = fc_disc_recv_req; + + return 0; +} +EXPORT_SYMBOL(fc_disc_init); diff --git a/drivers/scsi/libfc/fc_exch.c b/drivers/scsi/libfc/fc_exch.c index 11a03bd..ed74d95 100644 --- a/drivers/scsi/libfc/fc_exch.c +++ b/drivers/scsi/libfc/fc_exch.c @@ -1,5 +1,7 @@ /* * Copyright(c) 2007 Intel Corporation. All rights reserved. + * Copyright(c) 2008 Red Hat, Inc. All rights reserved. + * Copyright(c) 2008 Mike Christie * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, @@ -73,6 +75,9 @@ struct fc_exch; * esb_stat, f_ctl, seq.ssb_stat, seq.f_ctl. * seq_id * sequence allocation + * + * If the em_lock and ex_lock must be taken at the same time, the + * em_lock must be taken before the ex_lock. */ struct fc_exch { struct fc_exch_mgr *em; /* exchange manager */ @@ -95,13 +100,16 @@ struct fc_exch { u8 fh_type; /* frame type */ enum fc_class class; /* class of service */ struct fc_seq seq; /* single sequence */ - struct fc_exch *aborted_ep; /* ref to ep rrq is cleaning up */ - /* * Handler for responses to this current exchange. */ void (*resp)(struct fc_seq *, struct fc_frame *, void *); - void *resp_arg; /* 3rd arg for exchange resp handler */ + void (*destructor)(struct fc_seq *, void *); + /* + * arg is passed as void pointer to exchange + * resp and destructor handlers + */ + void *arg; }; /* @@ -297,6 +305,8 @@ static void fc_exch_release(struct fc_exch *ep) if (atomic_dec_and_test(&ep->ex_refcnt)) { 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); @@ -324,7 +334,6 @@ static int fc_exch_done_locked(struct fc_exch *ep) ep->state |= FC_EX_DONE; if (del_timer(&ep->ex_timer)) atomic_dec(&ep->ex_refcnt); /* drop hold for timer */ - atomic_dec(&ep->ex_refcnt); /* drop hold from alloc */ rc = 0; } return rc; @@ -341,6 +350,7 @@ static void fc_exch_mgr_delete_ep(struct fc_exch *ep) mp->exches[ep->xid - mp->min_xid] = NULL; list_del(&ep->ex_list); spin_unlock_bh(&mp->em_lock); + fc_exch_release(ep); /* drop hold for exch in mp */ } /* @@ -441,13 +451,13 @@ static void fc_exch_timeout(unsigned long ep_arg) e_stat = ep->esb_stat; if (e_stat & ESB_ST_COMPLETE) { ep->esb_stat = e_stat & ~ESB_ST_REC_QUAL; - spin_unlock_bh(&ep->ex_lock); if (e_stat & ESB_ST_REC_QUAL) fc_exch_rrq(ep); + spin_unlock_bh(&ep->ex_lock); goto done; } else { resp = ep->resp; - arg = ep->resp_arg; + arg = ep->arg; ep->resp = NULL; if (e_stat & ESB_ST_ABNORMAL) rc = fc_exch_done_locked(ep); @@ -492,6 +502,7 @@ static struct fc_seq *fc_seq_alloc(struct fc_exch *ep, u8 seq_id) * * 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, u16 xid) { @@ -540,16 +551,24 @@ struct fc_exch *fc_exch_alloc(struct fc_exch_mgr *mp, u16 xid) xid = fc_exch_next_xid(mp, xid); } - if (likely(mp->exches[xid - min_xid] == NULL)) { - mp->last_xid = xid; - } else { - spin_unlock_bh(&mp->em_lock); - atomic_inc(&mp->stats.no_free_exch_xid); - mempool_free(ep, mp->ep_pool); - goto out; - } + if (unlikely(mp->exches[xid - min_xid] != NULL)) + goto err; + mp->last_xid = xid; } + /* lport lock ? */ + if (mp->lp->state == LPORT_ST_RESET) + goto err; /* don't add new ep during local port reset */ + + fc_exch_hold(ep); /* hold for exch in mp */ + spin_lock_init(&ep->ex_lock); + /* + * Hold exch lock for caller to prevent fc_exch_reset() + * from releasing exch while fc_exch_alloc() caller is + * still working on exch. + */ + spin_lock_bh(&ep->ex_lock); + mp->exches[xid - min_xid] = ep; list_add_tail(&ep->ex_list, &mp->ex_list); fc_seq_alloc(ep, ep->seq_id++); @@ -565,13 +584,14 @@ struct fc_exch *fc_exch_alloc(struct fc_exch_mgr *mp, u16 xid) ep->f_ctl = FC_FC_FIRST_SEQ; /* next seq is first seq */ ep->rxid = FC_XID_UNKNOWN; ep->class = mp->class; - - spin_lock_init(&ep->ex_lock); setup_timer(&ep->ex_timer, fc_exch_timeout, (unsigned long)ep); - - fc_exch_hold(ep); /* hold for caller */ out: return ep; +err: + spin_unlock_bh(&mp->em_lock); + atomic_inc(&mp->stats.no_free_exch_xid); + mempool_free(ep, mp->ep_pool); + return NULL; } EXPORT_SYMBOL(fc_exch_alloc); @@ -654,6 +674,8 @@ static struct fc_exch *fc_exch_resp(struct fc_exch_mgr *mp, struct fc_frame *fp) WARN_ON(rxid != FC_XID_UNKNOWN); fh->fh_rx_id = htons(ep->rxid); } + fc_exch_hold(ep); /* hold for caller */ + spin_unlock_bh(&ep->ex_lock); /* lock from exch_get */ } return ep; } @@ -667,7 +689,7 @@ static enum fc_pf_rjt_reason fc_seq_lookup_recip(struct fc_exch_mgr *mp, struct fc_frame *fp) { struct fc_frame_header *fh = fc_frame_header_get(fp); - struct fc_exch *ep = NULL, *new_ep = NULL; + struct fc_exch *ep = NULL; struct fc_seq *sp = NULL; enum fc_pf_rjt_reason reject = FC_RJT_NONE; u32 f_ctl; @@ -717,12 +739,11 @@ fc_seq_lookup_recip(struct fc_exch_mgr *mp, struct fc_frame *fp) reject = FC_RJT_RX_ID; goto rel; } - new_ep = ep = fc_exch_resp(mp, fp); + ep = fc_exch_resp(mp, fp); if (!ep) { reject = FC_RJT_EXCH_EST; /* XXX */ goto out; } - fc_exch_hold(ep); /* Additional hold for caller */ xid = ep->xid; /* get our XID */ } else if (!ep) { atomic_inc(&mp->stats.xid_not_found); @@ -760,9 +781,8 @@ fc_seq_lookup_recip(struct fc_exch_mgr *mp, struct fc_frame *fp) out: return reject; rel: - fc_exch_release(ep); - if (new_ep) - fc_exch_release(new_ep); + fc_exch_done(&ep->seq); + fc_exch_release(ep); /* hold from fc_exch_find/fc_exch_resp */ return reject; } @@ -1160,7 +1180,7 @@ static void fc_exch_recv_req(struct fc_lport *lp, struct fc_exch_mgr *mp, * first. */ if (ep->resp) - ep->resp(sp, fp, ep->resp_arg); + ep->resp(sp, fp, ep->arg); else lp->tt.lport_recv(lp, sp, fp); fc_exch_release(ep); /* release from lookup */ @@ -1222,7 +1242,7 @@ static void fc_exch_recv_seq_resp(struct fc_exch_mgr *mp, struct fc_frame *fp) if (fc_sof_needs_ack(sof)) fc_seq_send_ack(sp, fp); resp = ep->resp; - ex_resp_arg = ep->resp_arg; + ex_resp_arg = ep->arg; if (fh->fh_type != FC_TYPE_FCP && fr_eof(fp) == FC_EOF_T && (f_ctl & (FC_FC_LAST_SEQ | FC_FC_END_SEQ)) == @@ -1332,7 +1352,7 @@ static void fc_exch_abts_resp(struct fc_exch *ep, struct fc_frame *fp) } resp = ep->resp; - ex_resp_arg = ep->resp_arg; + ex_resp_arg = ep->arg; /* do we need to do some other checks here. Can we reuse more of * fc_exch_recv_seq_resp @@ -1341,7 +1361,7 @@ static void fc_exch_abts_resp(struct fc_exch *ep, struct fc_frame *fp) /* * do we want to check END_SEQ as well as LAST_SEQ here? */ - if (fh->fh_type != FC_TYPE_FCP && + if (ep->fh_type != FC_TYPE_FCP && ntoh24(fh->fh_f_ctl) & FC_FC_LAST_SEQ) rc = fc_exch_done_locked(ep); spin_unlock_bh(&ep->ex_lock); @@ -1485,11 +1505,9 @@ static void fc_exch_reset(struct fc_exch *ep) if (ep->esb_stat & ESB_ST_REC_QUAL) atomic_dec(&ep->ex_refcnt); /* drop hold for rec_qual */ ep->esb_stat &= ~ESB_ST_REC_QUAL; - arg = ep->resp_arg; + arg = ep->arg; sp = &ep->seq; - - if (ep->fh_type != FC_TYPE_FCP) - rc = fc_exch_done_locked(ep); + rc = fc_exch_done_locked(ep); spin_unlock_bh(&ep->ex_lock); if (!rc) fc_exch_mgr_delete_ep(ep); @@ -1640,9 +1658,7 @@ reject: */ static void fc_exch_rrq_resp(struct fc_seq *sp, struct fc_frame *fp, void *arg) { - struct fc_exch *ep = fc_seq_exch(sp); - struct fc_exch *aborted_ep; - + struct fc_exch *aborted_ep = arg; unsigned int op; if (IS_ERR(fp)) { @@ -1669,16 +1685,9 @@ static void fc_exch_rrq_resp(struct fc_seq *sp, struct fc_frame *fp, void *arg) } cleanup: - spin_lock_bh(&ep->ex_lock); - aborted_ep = ep->aborted_ep; - ep->aborted_ep = NULL; - spin_unlock_bh(&ep->ex_lock); - - if (aborted_ep) { - fc_exch_done(&aborted_ep->seq); - /* drop hold for rec qual */ - fc_exch_release(aborted_ep); - } + fc_exch_done(&aborted_ep->seq); + /* drop hold for rec qual */ + fc_exch_release(aborted_ep); } /* @@ -1692,7 +1701,6 @@ static void fc_exch_rrq(struct fc_exch *ep) struct fc_els_rrq *rrq; struct fc_frame *fp; struct fc_seq *rrq_sp; - struct fc_exch *rrq_ep; u32 did; lp = ep->lp; @@ -1711,18 +1719,14 @@ static void fc_exch_rrq(struct fc_exch *ep) did = ep->did; if (ep->esb_stat & ESB_ST_RESP) did = ep->sid; - rrq_sp = fc_exch_seq_send(lp, fp, fc_exch_rrq_resp, ep, lp->e_d_tov, - lp->fid, did, FC_FC_SEQ_INIT | FC_FC_END_SEQ); + rrq_sp = fc_exch_seq_send(lp, fp, fc_exch_rrq_resp, NULL, ep, + lp->e_d_tov, lp->fid, did, + FC_FC_SEQ_INIT | FC_FC_END_SEQ); if (!rrq_sp) { - spin_lock_bh(&ep->ex_lock); ep->esb_stat |= ESB_ST_REC_QUAL; fc_exch_timer_set_locked(ep, ep->r_a_tov); - spin_unlock_bh(&ep->ex_lock); return; } - - rrq_ep = fc_seq_exch(rrq_sp); - rrq_ep->aborted_ep = ep; } @@ -1860,13 +1864,15 @@ struct fc_seq *fc_exch_seq_send(struct fc_lport *lp, void (*resp)(struct fc_seq *, struct fc_frame *fp, void *arg), - void *resp_arg, u32 timer_msec, + void (*destructor)(struct fc_seq *, void *), + void *arg, u32 timer_msec, u32 sid, u32 did, u32 f_ctl) { struct fc_exch *ep; struct fc_seq *sp = NULL; struct fc_frame_header *fh; u16 fill; + int rc = 1; ep = lp->tt.exch_get(lp, fp); if (!ep) { @@ -1876,7 +1882,8 @@ struct fc_seq *fc_exch_seq_send(struct fc_lport *lp, ep->esb_stat |= ESB_ST_SEQ_INIT; fc_exch_set_addr(ep, sid, did); ep->resp = resp; - ep->resp_arg = resp_arg; + ep->destructor = destructor; + ep->arg = arg; ep->r_a_tov = FC_DEF_R_A_TOV; ep->lp = lp; sp = &ep->seq; @@ -1912,7 +1919,6 @@ struct fc_seq *fc_exch_seq_send(struct fc_lport *lp, if (unlikely(lp->tt.frame_send(lp, fp))) goto err; - spin_lock_bh(&ep->ex_lock); if (timer_msec) fc_exch_timer_set_locked(ep, timer_msec); sp->f_ctl = f_ctl; /* save for possible abort */ @@ -1924,7 +1930,10 @@ struct fc_seq *fc_exch_seq_send(struct fc_lport *lp, spin_unlock_bh(&ep->ex_lock); return sp; err: - fc_exch_done(sp); + rc = fc_exch_done_locked(ep); + spin_unlock_bh(&ep->ex_lock); + if (!rc) + fc_exch_mgr_delete_ep(ep); return NULL; } EXPORT_SYMBOL(fc_exch_seq_send); @@ -1938,6 +1947,7 @@ void fc_exch_recv(struct fc_lport *lp, struct fc_exch_mgr *mp, struct fc_frame_header *fh = fc_frame_header_get(fp); u32 f_ctl; + /* lport lock ? */ if (!lp || !mp || (lp->state == LPORT_ST_NONE)) { FC_DBG("fc_lport or EM is not allocated and configured"); fc_frame_free(fp); diff --git a/drivers/scsi/libfc/fc_fcp.c b/drivers/scsi/libfc/fc_fcp.c index 97da731..2566eed 100644 --- a/drivers/scsi/libfc/fc_fcp.c +++ b/drivers/scsi/libfc/fc_fcp.c @@ -38,6 +38,10 @@ #include +MODULE_AUTHOR("Open-FCoE.org"); +MODULE_DESCRIPTION("libfc"); +MODULE_LICENSE("GPL"); + int fc_fcp_debug; static struct kmem_cache *scsi_pkt_cachep; @@ -206,6 +210,7 @@ static struct fc_fcp_pkt *fc_fcp_pkt_alloc(struct fc_lport *lp, gfp_t gfp) atomic_set(&sp->ref_cnt, 1); init_timer(&sp->timer); INIT_LIST_HEAD(&sp->list); + spin_lock_init(&sp->scsi_pkt_lock); } return sp; } @@ -233,6 +238,22 @@ static void fc_fcp_pkt_hold(struct fc_fcp_pkt *sp) } /** + * fc_fcp_pkt_destory - release hold on scsi_pkt packet + * + * @sp: exchange sequence + * @fsp: fcp packet struct + * + * Release hold on scsi_pkt packet set to keep scsi_pkt + * till EM layer exch resource is not freed. + * Context : called from from EM layer. + * no locking required + */ +static void fc_fcp_pkt_destroy(struct fc_seq *sp, void *arg) +{ + fc_fcp_pkt_release(arg); +} + +/** * fc_fcp_lock_pkt - lock a packet and get a ref to it. * @fsp: fcp packet * @@ -604,13 +625,31 @@ static int fc_fcp_send_data(struct fc_fcp_pkt *fsp, struct fc_seq *sp, return 0; } -static void fc_fcp_abts_resp(struct fc_fcp_pkt *fsp, struct fc_frame_header *fh) +static void fc_fcp_abts_resp(struct fc_fcp_pkt *fsp, struct fc_frame *fp) { - /* - * we will let the command timeout and scsi-ml escalate if - * the abort was rejected - */ - if (fh->fh_r_ctl == FC_RCTL_BA_ACC) { + int ba_done = 1; + struct fc_ba_rjt *brp; + struct fc_frame_header *fh; + + fh = fc_frame_header_get(fp); + switch (fh->fh_r_ctl) { + case FC_RCTL_BA_ACC: + break; + case FC_RCTL_BA_RJT: + brp = fc_frame_payload_get(fp, sizeof(*brp)); + if (brp && brp->br_reason == FC_BA_RJT_LOG_ERR) + break; + /* fall thru */ + default: + /* + * we will let the command timeout + * and scsi-ml recover in this case, + * therefore cleared the ba_done flag. + */ + ba_done = 0; + } + + if (ba_done) { fsp->state |= FC_SRB_ABORTED; fsp->state &= ~FC_SRB_ABORT_PENDING; @@ -666,7 +705,7 @@ static void fc_fcp_recv(struct fc_seq *sp, struct fc_frame *fp, void *arg) struct fc_fcp_pkt *fsp = (struct fc_fcp_pkt *)arg; struct fc_lport *lp; struct fc_frame_header *fh; - struct fc_data_desc *dd; + struct fcp_txrdy *dd; u8 r_ctl; int rc = 0; @@ -684,7 +723,7 @@ static void fc_fcp_recv(struct fc_seq *sp, struct fc_frame *fp, void *arg) fsp->last_pkt_time = jiffies; if (fh->fh_type == FC_TYPE_BLS) { - fc_fcp_abts_resp(fsp, fh); + fc_fcp_abts_resp(fsp, fp); goto unlock; } @@ -701,8 +740,8 @@ static void fc_fcp_recv(struct fc_seq *sp, struct fc_frame *fp, void *arg) WARN_ON(!dd); rc = fc_fcp_send_data(fsp, sp, - (size_t) ntohl(dd->dd_offset), - (size_t) ntohl(dd->dd_len), fp, + (size_t) ntohl(dd->ft_data_ro), + (size_t) ntohl(dd->ft_burst_len), fp, lp->capabilities & TRANS_C_SG); if (!rc) lp->tt.seq_set_rec_data(sp, fsp->xfer_len); @@ -1025,6 +1064,7 @@ static int fc_fcp_send_cmd(struct fc_fcp_pkt *fsp) rp = rport->dd_data; sp = lp->tt.exch_seq_send(lp, fp, fc_fcp_recv, + fc_fcp_pkt_destroy, fsp, 0, rp->local_port->fid, rport->port_id, @@ -1034,7 +1074,9 @@ static int fc_fcp_send_cmd(struct fc_fcp_pkt *fsp) rc = -1; goto unlock; } + fsp->last_pkt_time = jiffies; fsp->seq_ptr = sp; + fc_fcp_pkt_hold(fsp); /* hold for fc_fcp_pkt_destroy */ setup_timer(&fsp->timer, fc_fcp_timeout, (unsigned long)fsp); fc_fcp_timer_set(fsp, @@ -1131,6 +1173,7 @@ static void fc_lun_reset_send(unsigned long data) rp = rport->dd_data; sp = lp->tt.exch_seq_send(lp, fp, fc_tm_done, + fc_fcp_pkt_destroy, fsp, 0, rp->local_port->fid, rport->port_id, @@ -1138,6 +1181,7 @@ static void fc_lun_reset_send(unsigned long data) if (sp) { fsp->seq_ptr = sp; + fc_fcp_pkt_hold(fsp); /* hold for fc_fcp_pkt_destroy */ goto unlock; } /* @@ -1183,12 +1227,6 @@ static int fc_lun_reset(struct fc_lport *lp, struct fc_fcp_pkt *fsp, spin_lock_bh(&fsp->scsi_pkt_lock); if (fsp->seq_ptr) { - /* TODO: - * if the exch resp function is running and trying to grab - * the scsi_pkt_lock, this could free the exch from under - * it and it could allow the fsp to be freed from under - * fc_tm_done. - */ lp->tt.exch_done(fsp->seq_ptr); fsp->seq_ptr = NULL; } @@ -1231,9 +1269,6 @@ static void fc_tm_done(struct fc_seq *sp, struct fc_frame *fp, void *arg) /* * raced with eh timeout handler. - * - * TODO: If this happens we could be freeing the fsp right now and - * would oops. Next patches will fix this race. */ if ((fsp->state & FC_SRB_COMPL) || !fsp->seq_ptr || !fsp->wait_for_comp) { @@ -1288,14 +1323,13 @@ static void fc_fcp_timeout(unsigned long data) if (rp->flags & FC_RP_FLAGS_REC_SUPPORTED) fc_fcp_rec(fsp); - /* TODO: change this to time_before/after */ - else if (jiffies - fsp->last_pkt_time < FC_SCSI_ER_TIMEOUT / 2) + else if (time_after_eq(fsp->last_pkt_time + (FC_SCSI_ER_TIMEOUT / 2), + jiffies)) fc_fcp_timer_set(fsp, FC_SCSI_ER_TIMEOUT); else if (fsp->state & FC_SRB_RCV_STATUS) fc_fcp_complete(fsp); else fc_timeout_error(fsp); - fsp->state &= ~FC_SRB_FCP_PROCESSING_TMO; unlock: fc_fcp_unlock_pkt(fsp); @@ -1340,7 +1374,7 @@ static void fc_fcp_rec(struct fc_fcp_pkt *fsp) fc_frame_setup(fp, FC_RCTL_ELS_REQ, FC_TYPE_ELS); fc_frame_set_offset(fp, 0); sp = lp->tt.exch_seq_send(lp, fp, - fc_fcp_rec_resp, + fc_fcp_rec_resp, NULL, fsp, jiffies_to_msecs(FC_SCSI_REC_TOV), rp->local_port->fid, rport->port_id, @@ -1402,9 +1436,14 @@ static void fc_fcp_rec_resp(struct fc_seq *sp, struct fc_frame *fp, void *arg) if (fc_fcp_debug) FC_DBG("device does not support REC\n"); rp = fsp->rport->dd_data; + /* + * if we do not spport RECs or got some bogus + * reason then resetup timer so we check for + * making progress. + */ rp->flags &= ~FC_RP_FLAGS_REC_SUPPORTED; - /* fall through */ - + fc_fcp_timer_set(fsp, FC_SCSI_ER_TIMEOUT); + break; case ELS_RJT_LOGIC: case ELS_RJT_UNAB: /* @@ -1595,7 +1634,7 @@ static void fc_fcp_srr(struct fc_fcp_pkt *fsp, enum fc_rctl r_ctl, u32 offset) fc_frame_setup(fp, FC_RCTL_ELS4_REQ, FC_TYPE_FCP); fc_frame_set_offset(fp, 0); sp = lp->tt.exch_seq_send(lp, fp, - fc_fcp_srr_resp, + fc_fcp_srr_resp, NULL, fsp, jiffies_to_msecs(FC_SCSI_REC_TOV), rp->local_port->fid, rport->port_id, @@ -2048,7 +2087,7 @@ int fc_eh_host_reset(struct scsi_cmnd *sc_cmd) shost_printk(KERN_INFO, shost, "Host reset succeeded.\n"); return SUCCESS; } else { - shost_printk(KERN_INFO, shost, "Host reset succeeded failed." + shost_printk(KERN_INFO, shost, "Host reset failed. " "lport not ready.\n"); return FAILED; } diff --git a/drivers/scsi/libfc/fc_lport.c b/drivers/scsi/libfc/fc_lport.c index b390a32..b1854b9 100644 --- a/drivers/scsi/libfc/fc_lport.c +++ b/drivers/scsi/libfc/fc_lport.c @@ -36,76 +36,130 @@ static int fc_lport_debug; +static void fc_lport_error(struct fc_lport *, struct fc_frame *); + +static void fc_lport_enter_reset(struct fc_lport *); static void fc_lport_enter_flogi(struct fc_lport *); +static void fc_lport_enter_dns(struct fc_lport *); +static void fc_lport_enter_rpn_id(struct fc_lport *); +static void fc_lport_enter_rft_id(struct fc_lport *); +static void fc_lport_enter_scr(struct fc_lport *); +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_FLOGI] = "FLOGI", [LPORT_ST_DNS] = "dNS", - [LPORT_ST_REG_PN] = "REG_PN", - [LPORT_ST_REG_FT] = "REG_FT", + [LPORT_ST_RPN_ID] = "RPN_ID", + [LPORT_ST_RFT_ID] = "RFT_ID", [LPORT_ST_SCR] = "SCR", - [LPORT_ST_READY] = "ready", - [LPORT_ST_DNS_STOP] = "stop", + [LPORT_ST_READY] = "Ready", [LPORT_ST_LOGO] = "LOGO", [LPORT_ST_RESET] = "reset", }; -static int fc_frame_drop(struct fc_lport *lp, struct fc_frame *fp) +static int fc_frame_drop(struct fc_lport *lport, struct fc_frame *fp) { fc_frame_free(fp); return 0; } -static const char *fc_lport_state(struct fc_lport *lp) +/** + * fc_lport_rport_event - Event handler for rport events + * @lport: The lport which is receiving the event + * @port_id: The FID of 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_lport_rport_event(struct fc_lport *lport, u32 port_id, + enum fc_lport_event event) +{ + struct fc_rport *rport = lport->tt.rport_lookup(lport, port_id); + + if (fc_lport_debug) + FC_DBG("Received a %d event for port (%6x)\n", event, port_id); + + if (port_id == FC_FID_DIR_SERV) { + mutex_lock(&lport->lp_mutex); + switch (event) { + case LPORT_EV_RPORT_CREATED: + if (rport) { + lport->dns_rp = rport; + fc_lport_enter_rpn_id(lport); + } + break; + case LPORT_EV_RPORT_LOGO: + case LPORT_EV_RPORT_FAILED: + lport->dns_rp = NULL; + fc_lport_enter_dns(lport); + break; + case LPORT_EV_RPORT_NONE: + break; + } + mutex_unlock(&lport->lp_mutex); + } +} + +/** + * fc_lport_state - Return a string which represents the lport's state + * @lport: The lport whose state is to converted to a string + */ +static const char *fc_lport_state(struct fc_lport *lport) { const char *cp; - cp = fc_lport_state_names[lp->state]; + cp = fc_lport_state_names[lport->state]; if (!cp) cp = "unknown"; return cp; } -static void fc_lport_ptp_setup(struct fc_lport *lp, - u32 remote_fid, u64 remote_wwpn, - u64 remote_wwnn) +/** + * fc_lport_ptp_clear - Delete the ptp rport + * @lport: The lport whose ptp rport should be removed + */ +static void fc_lport_ptp_clear(struct fc_lport *lport) { - struct fc_rport *rport; - struct fc_rport_identifiers ids = { - .port_id = remote_fid, - .port_name = remote_wwpn, - .node_name = remote_wwnn, - }; - - /* - * if we have to create a rport the fc class can sleep so we must - * drop the lock here - */ - fc_lport_unlock(lp); - rport = lp->tt.rport_lookup(lp, ids.port_id); /* lookup and hold */ - if (rport == NULL) - rport = lp->tt.rport_create(lp, &ids); /* create and hold */ - fc_lport_lock(lp); - if (rport) { - if (lp->ptp_rp) - fc_remote_port_delete(lp->ptp_rp); - lp->ptp_rp = rport; - fc_lport_state_enter(lp, LPORT_ST_READY); + if (lport->ptp_rp) { + fc_remote_port_delete(lport->ptp_rp); + lport->ptp_rp = NULL; } } -static void fc_lport_ptp_clear(struct fc_lport *lp) +/** + * fc_lport_ptp_setup - Create an rport for point-to-point mode + * @lport: The lport to attach the ptp rport to + * @fid: The FID of the ptp rport + * @remote_wwpn: The WWPN of the ptp rport + * @remote_wwnn: The WWNN of the ptp rport + */ +static void fc_lport_ptp_setup(struct fc_lport *lport, + u32 remote_fid, u64 remote_wwpn, + u64 remote_wwnn) { - if (lp->ptp_rp) { - fc_remote_port_delete(lp->ptp_rp); - lp->ptp_rp = NULL; - } + 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; + + fc_lport_ptp_clear(lport); + + lport->ptp_rp = fc_rport_dummy_create(&dp); + + lport->tt.rport_login(lport->ptp_rp); + + fc_lport_enter_ready(lport); } -/* - * Routines to support struct fc_function_template +/** + * fc_get_host_port_state - supports fc_function_template + * @shost: The host whose port state should be returned */ void fc_get_host_port_state(struct Scsi_Host *shost) { @@ -118,82 +172,130 @@ void fc_get_host_port_state(struct Scsi_Host *shost) } EXPORT_SYMBOL(fc_get_host_port_state); +void fc_get_host_speed(struct Scsi_Host *shost) +{ + struct fc_lport *lport = shost_priv(shost); + + fc_host_speed(shost) = lport->link_speed; +} +EXPORT_SYMBOL(fc_get_host_speed); + +struct fc_host_statistics *fc_get_host_stats(struct Scsi_Host *shost) +{ + int i; + struct fc_host_statistics *fcoe_stats; + struct fc_lport *lp = shost_priv(shost); + struct timespec v0, v1; + + fcoe_stats = &lp->host_stats; + memset(fcoe_stats, 0, sizeof(struct fc_host_statistics)); + + jiffies_to_timespec(jiffies, &v0); + jiffies_to_timespec(lp->boot_time, &v1); + fcoe_stats->seconds_since_last_reset = (v0.tv_sec - v1.tv_sec); + + for_each_online_cpu(i) { + struct fcoe_dev_stats *stats = lp->dev_stats[i]; + if (stats == NULL) + continue; + fcoe_stats->tx_frames += stats->TxFrames; + fcoe_stats->tx_words += stats->TxWords; + fcoe_stats->rx_frames += stats->RxFrames; + fcoe_stats->rx_words += stats->RxWords; + fcoe_stats->error_frames += stats->ErrorFrames; + fcoe_stats->invalid_crc_count += stats->InvalidCRCCount; + fcoe_stats->fcp_input_requests += stats->InputRequests; + fcoe_stats->fcp_output_requests += stats->OutputRequests; + fcoe_stats->fcp_control_requests += stats->ControlRequests; + fcoe_stats->fcp_input_megabytes += stats->InputMegabytes; + fcoe_stats->fcp_output_megabytes += stats->OutputMegabytes; + fcoe_stats->link_failure_count += stats->LinkFailureCount; + } + fcoe_stats->lip_count = -1; + fcoe_stats->nos_count = -1; + fcoe_stats->loss_of_sync_count = -1; + fcoe_stats->loss_of_signal_count = -1; + fcoe_stats->prim_seq_protocol_err_count = -1; + fcoe_stats->dumped_frames = -1; + return fcoe_stats; +} +EXPORT_SYMBOL(fc_get_host_stats); + /* * Fill in FLOGI command for request. */ static void -fc_lport_flogi_fill(struct fc_lport *lp, - struct fc_els_flogi *flogi, unsigned int op) +fc_lport_flogi_fill(struct fc_lport *lport, struct fc_els_flogi *flogi, + unsigned int op) { struct fc_els_csp *sp; struct fc_els_cssp *cp; memset(flogi, 0, sizeof(*flogi)); flogi->fl_cmd = (u8) op; - put_unaligned_be64(lp->wwpn, &flogi->fl_wwpn); - put_unaligned_be64(lp->wwnn, &flogi->fl_wwnn); + put_unaligned_be64(lport->wwpn, &flogi->fl_wwpn); + put_unaligned_be64(lport->wwnn, &flogi->fl_wwnn); sp = &flogi->fl_csp; sp->sp_hi_ver = 0x20; sp->sp_lo_ver = 0x20; sp->sp_bb_cred = htons(10); /* this gets set by gateway */ - sp->sp_bb_data = htons((u16) lp->mfs); + sp->sp_bb_data = htons((u16) lport->mfs); cp = &flogi->fl_cssp[3 - 1]; /* class 3 parameters */ cp->cp_class = htons(FC_CPC_VALID | FC_CPC_SEQ); if (op != ELS_FLOGI) { sp->sp_features = htons(FC_SP_FT_CIRO); sp->sp_tot_seq = htons(255); /* seq. we accept */ sp->sp_rel_off = htons(0x1f); - sp->sp_e_d_tov = htonl(lp->e_d_tov); + sp->sp_e_d_tov = htonl(lport->e_d_tov); - cp->cp_rdfs = htons((u16) lp->mfs); + cp->cp_rdfs = htons((u16) lport->mfs); cp->cp_con_seq = htons(255); cp->cp_open_seq = 1; } } /* - * Set the fid. This indicates that we have a new connection to the - * fabric so we should reset our list of fc_rports. Passing a fid of - * 0 will also reset the rport list regardless of the previous fid. - */ -static void fc_lport_set_fid(struct fc_lport *lp, u32 fid) -{ - if (fid != 0 && lp->fid == fid) - return; - - if (fc_lport_debug) - FC_DBG("changing local port fid from %x to %x\n", - lp->fid, fid); - lp->fid = fid; - lp->tt.rport_reset_list(lp); -} - -/* * Add a supported FC-4 type. */ -static void fc_lport_add_fc4_type(struct fc_lport *lp, enum fc_fh_type type) +static void fc_lport_add_fc4_type(struct fc_lport *lport, enum fc_fh_type type) { __be32 *mp; - mp = &lp->fcts.ff_type_map[type / FC_NS_BPW]; + mp = &lport->fcts.ff_type_map[type / FC_NS_BPW]; *mp = htonl(ntohl(*mp) | 1UL << (type % FC_NS_BPW)); } -/* - * Handle received RLIR - registered link incident report. +/** + * fc_lport_recv_rlir_req - Handle received Registered Link Incident Report. + * @lport: Fibre Channel local port recieving the RLIR + * @sp: current sequence in the RLIR exchange + * @fp: RLIR request frame + * + * Locking Note: The lport lock is exected to be held before calling + * this function. */ -static void fc_lport_rlir_req(struct fc_seq *sp, struct fc_frame *fp, - struct fc_lport *lp) +static void fc_lport_recv_rlir_req(struct fc_seq *sp, struct fc_frame *fp, + struct fc_lport *lport) { - lp->tt.seq_els_rsp_send(sp, ELS_LS_ACC, NULL); + if (fc_lport_debug) + FC_DBG("Received RLIR request while in state %s\n", + fc_lport_state(lport)); + + lport->tt.seq_els_rsp_send(sp, ELS_LS_ACC, NULL); fc_frame_free(fp); } -/* - * Handle received ECHO. +/** + * fc_lport_recv_echo_req - Handle received ECHO request + * @lport: Fibre Channel local port recieving the ECHO + * @sp: current sequence in the ECHO exchange + * @fp: ECHO request frame + * + * Locking Note: The lport lock is exected to be held before calling + * this function. */ -static void fc_lport_echo_req(struct fc_seq *sp, struct fc_frame *in_fp, - struct fc_lport *lp) +static void fc_lport_recv_echo_req(struct fc_seq *sp, struct fc_frame *in_fp, + struct fc_lport *lport) { struct fc_frame *fp; unsigned int len; @@ -201,29 +303,40 @@ static void fc_lport_echo_req(struct fc_seq *sp, struct fc_frame *in_fp, void *dp; u32 f_ctl; + if (fc_lport_debug) + FC_DBG("Received RLIR request while in state %s\n", + fc_lport_state(lport)); + len = fr_len(in_fp) - sizeof(struct fc_frame_header); pp = fc_frame_payload_get(in_fp, len); if (len < sizeof(__be32)) len = sizeof(__be32); - fp = fc_frame_alloc(lp, len); + + fp = fc_frame_alloc(lport, len); if (fp) { dp = fc_frame_payload_get(fp, len); memcpy(dp, pp, len); *((u32 *)dp) = htonl(ELS_LS_ACC << 24); - sp = lp->tt.seq_start_next(sp); + sp = lport->tt.seq_start_next(sp); f_ctl = FC_FC_LAST_SEQ | FC_FC_END_SEQ; fc_frame_setup(fp, FC_RCTL_ELS_REP, FC_TYPE_ELS); - lp->tt.seq_send(lp, sp, fp, f_ctl); + lport->tt.seq_send(lport, sp, fp, f_ctl); } fc_frame_free(in_fp); } -/* - * Handle received RNID. +/** + * fc_lport_recv_echo_req - Handle received Request Node ID data request + * @lport: Fibre Channel local port recieving the RNID + * @sp: current sequence in the RNID exchange + * @fp: RNID request frame + * + * Locking Note: The lport lock is exected to be held before calling + * this function. */ -static void fc_lport_rnid_req(struct fc_seq *sp, struct fc_frame *in_fp, - struct fc_lport *lp) +static void fc_lport_recv_rnid_req(struct fc_seq *sp, struct fc_frame *in_fp, + struct fc_lport *lport) { struct fc_frame *fp; struct fc_els_rnid *req; @@ -237,146 +350,165 @@ static void fc_lport_rnid_req(struct fc_seq *sp, struct fc_frame *in_fp, size_t len; u32 f_ctl; + if (fc_lport_debug) + FC_DBG("Received RNID 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; - lp->tt.seq_els_rsp_send(sp, ELS_LS_RJT, &rjt_data); + lport->tt.seq_els_rsp_send(sp, ELS_LS_RJT, &rjt_data); } else { fmt = req->rnid_fmt; len = sizeof(*rp); if (fmt != ELS_RNIDF_GEN || - ntohl(lp->rnid_gen.rnid_atype) == 0) { + ntohl(lport->rnid_gen.rnid_atype) == 0) { fmt = ELS_RNIDF_NONE; /* nothing to provide */ len -= sizeof(rp->gen); } - fp = fc_frame_alloc(lp, len); + fp = fc_frame_alloc(lport, len); if (fp) { rp = fc_frame_payload_get(fp, len); memset(rp, 0, len); rp->rnid.rnid_cmd = ELS_LS_ACC; rp->rnid.rnid_fmt = fmt; rp->rnid.rnid_cid_len = sizeof(rp->cid); - rp->cid.rnid_wwpn = htonll(lp->wwpn); - rp->cid.rnid_wwnn = htonll(lp->wwnn); + rp->cid.rnid_wwpn = htonll(lport->wwpn); + rp->cid.rnid_wwnn = htonll(lport->wwnn); if (fmt == ELS_RNIDF_GEN) { rp->rnid.rnid_sid_len = sizeof(rp->gen); - memcpy(&rp->gen, &lp->rnid_gen, + memcpy(&rp->gen, &lport->rnid_gen, sizeof(rp->gen)); } - sp = lp->tt.seq_start_next(sp); + sp = lport->tt.seq_start_next(sp); f_ctl = FC_FC_SEQ_INIT | FC_FC_LAST_SEQ | FC_FC_END_SEQ; fc_frame_setup(fp, FC_RCTL_ELS_REP, FC_TYPE_ELS); - lp->tt.seq_send(lp, sp, fp, f_ctl); + lport->tt.seq_send(lport, sp, fp, f_ctl); } } fc_frame_free(in_fp); } -/* - * Handle received fabric logout request. +/** + * 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 + * @fp: LOGO request frame + * + * Locking Note: The lport lock is exected to be held before calling + * this function. */ static void fc_lport_recv_logo_req(struct fc_seq *sp, struct fc_frame *fp, - struct fc_lport *lp) + struct fc_lport *lport) { - lp->tt.seq_els_rsp_send(sp, ELS_LS_ACC, NULL); - fc_lport_enter_reset(lp); + lport->tt.seq_els_rsp_send(sp, ELS_LS_ACC, NULL); + fc_lport_enter_reset(lport); fc_frame_free(fp); } -/* - * Receive request frame +/** + * fc_fabric_login - Start the lport state machine + * @lport: The lport that should log into the fabric + * + * Locking Note: This function should not be called + * with the lport lock held. */ - -int fc_fabric_login(struct fc_lport *lp) +int fc_fabric_login(struct fc_lport *lport) { int rc = -1; - if (lp->state == LPORT_ST_NONE) { - fc_lport_lock(lp); - fc_lport_enter_reset(lp); - fc_lport_unlock(lp); + mutex_lock(&lport->lp_mutex); + if (lport->state == LPORT_ST_NONE) { + fc_lport_enter_reset(lport); rc = 0; } + mutex_unlock(&lport->lp_mutex); + return rc; } EXPORT_SYMBOL(fc_fabric_login); /** - * fc_linkup - link up notification - * @dev: Pointer to fc_lport . - **/ -void fc_linkup(struct fc_lport *lp) + * fc_linkup - Handler for transport linkup events + * @lport: The lport whose link is up + */ +void fc_linkup(struct fc_lport *lport) { - if ((lp->link_status & FC_LINK_UP) != FC_LINK_UP) { - lp->link_status |= FC_LINK_UP; - fc_lport_lock(lp); - if (lp->state == LPORT_ST_RESET) - lp->tt.lport_login(lp); - fc_lport_unlock(lp); + mutex_lock(&lport->lp_mutex); + if ((lport->link_status & FC_LINK_UP) != FC_LINK_UP) { + lport->link_status |= FC_LINK_UP; + + if (lport->state == LPORT_ST_RESET) + fc_lport_enter_flogi(lport); } + mutex_unlock(&lport->lp_mutex); } EXPORT_SYMBOL(fc_linkup); /** - * fc_linkdown - link down notification - * @dev: Pointer to fc_lport . - **/ -void fc_linkdown(struct fc_lport *lp) + * fc_linkdown - Handler for transport linkdown events + * @lport: The lport whose link is down + */ +void fc_linkdown(struct fc_lport *lport) { - if ((lp->link_status & FC_LINK_UP) == FC_LINK_UP) { - lp->link_status &= ~(FC_LINK_UP); - fc_lport_enter_reset(lp); - lp->tt.scsi_cleanup(lp); + mutex_lock(&lport->lp_mutex); + + if ((lport->link_status & FC_LINK_UP) == FC_LINK_UP) { + lport->link_status &= ~(FC_LINK_UP); + fc_lport_enter_reset(lport); + lport->tt.scsi_cleanup(lport); } + + mutex_unlock(&lport->lp_mutex); } EXPORT_SYMBOL(fc_linkdown); -void fc_pause(struct fc_lport *lp) +/** + * fc_pause - Pause the flow of frames + * @lport: The lport to be paused + */ +void fc_pause(struct fc_lport *lport) { - lp->link_status |= FC_PAUSE; + mutex_lock(&lport->lp_mutex); + lport->link_status |= FC_PAUSE; + mutex_unlock(&lport->lp_mutex); } EXPORT_SYMBOL(fc_pause); -void fc_unpause(struct fc_lport *lp) +/** + * fc_unpause - Unpause the flow of frames + * @lport: The lport to be unpaused + */ +void fc_unpause(struct fc_lport *lport) { - lp->link_status &= ~(FC_PAUSE); + mutex_lock(&lport->lp_mutex); + lport->link_status &= ~(FC_PAUSE); + mutex_unlock(&lport->lp_mutex); } EXPORT_SYMBOL(fc_unpause); -int fc_fabric_logoff(struct fc_lport *lp) +/** + * fc_fabric_logoff - Logout of the fabric + * @lport: fc_lport pointer to logoff the fabric + * + * Return value: + * 0 for success, -1 for failure + **/ +int fc_fabric_logoff(struct fc_lport *lport) { - fc_lport_lock(lp); - switch (lp->state) { - case LPORT_ST_NONE: - break; - case LPORT_ST_FLOGI: - case LPORT_ST_LOGO: - case LPORT_ST_RESET: - fc_lport_enter_reset(lp); - break; - case LPORT_ST_DNS: - case LPORT_ST_DNS_STOP: - fc_lport_enter_logo(lp); - break; - case LPORT_ST_REG_PN: - case LPORT_ST_REG_FT: - case LPORT_ST_SCR: - case LPORT_ST_READY: - lp->tt.disc_stop(lp); - break; - } - fc_lport_unlock(lp); - lp->tt.scsi_cleanup(lp); - + mutex_lock(&lport->lp_mutex); + fc_lport_enter_logo(lport); + lport->tt.scsi_cleanup(lport); + mutex_unlock(&lport->lp_mutex); return 0; } EXPORT_SYMBOL(fc_fabric_logoff); /** * fc_lport_destroy - unregister a fc_lport - * @lp: fc_lport pointer to unregister + * @lport: fc_lport pointer to unregister * * Return value: * None @@ -386,30 +518,26 @@ EXPORT_SYMBOL(fc_fabric_logoff); * and free up other system resources. * **/ -int fc_lport_destroy(struct fc_lport *lp) +int fc_lport_destroy(struct fc_lport *lport) { - fc_lport_lock(lp); - fc_lport_state_enter(lp, LPORT_ST_LOGO); - fc_lport_unlock(lp); - - cancel_delayed_work_sync(&lp->ns_disc_work); - - lp->tt.scsi_abort_io(lp); - - lp->tt.frame_send = fc_frame_drop; - - lp->tt.exch_mgr_reset(lp->emp, 0, 0); - + mutex_lock(&lport->lp_mutex); + cancel_delayed_work_sync(&lport->disc_work); + lport->tt.scsi_abort_io(lport); + lport->tt.frame_send = fc_frame_drop; + lport->tt.exch_mgr_reset(lport->emp, 0, 0); + mutex_unlock(&lport->lp_mutex); return 0; } EXPORT_SYMBOL(fc_lport_destroy); -int fc_set_mfs(struct fc_lport *lp, u32 mfs) +int fc_set_mfs(struct fc_lport *lport, u32 mfs) { unsigned int old_mfs; int rc = -1; - old_mfs = lp->mfs; + mutex_lock(&lport->lp_mutex); + + old_mfs = lport->mfs; if (mfs >= FC_MIN_MAX_FRAME) { mfs &= ~3; @@ -417,97 +545,55 @@ int fc_set_mfs(struct fc_lport *lp, u32 mfs) if (mfs > FC_MAX_FRAME) mfs = FC_MAX_FRAME; mfs -= sizeof(struct fc_frame_header); - lp->mfs = mfs; + lport->mfs = mfs; rc = 0; } if (!rc && mfs < old_mfs) { - lp->ns_disc_done = 0; - fc_lport_enter_reset(lp); + lport->disc_done = 0; + fc_lport_enter_reset(lport); } + + mutex_unlock(&lport->lp_mutex); + return rc; } EXPORT_SYMBOL(fc_set_mfs); -/* - * re-enter state for retrying a request after a timeout or alloc failure. +/** + * fc_rport_enter_ready - Enter the ready state and start discovery + * @lport: Fibre Channel local port that is ready + * + * Locking Note: The lport lock is expected to be held before calling + * this routine. */ -static void fc_lport_enter_retry(struct fc_lport *lp) +static void fc_lport_enter_ready(struct fc_lport *lport) { - switch (lp->state) { - case LPORT_ST_NONE: - case LPORT_ST_READY: - case LPORT_ST_RESET: - case LPORT_ST_DNS: - case LPORT_ST_DNS_STOP: - case LPORT_ST_REG_PN: - case LPORT_ST_REG_FT: - case LPORT_ST_SCR: - WARN_ON(1); - break; - case LPORT_ST_FLOGI: - fc_lport_enter_flogi(lp); - break; - case LPORT_ST_LOGO: - fc_lport_enter_logo(lp); - break; - } -} + if (fc_lport_debug) + FC_DBG("Port (%6x) entered Ready from state %s\n", + lport->fid, fc_lport_state(lport)); -/* - * enter next state for handling an exchange reject or retry exhaustion - * in the current state. - */ -static void fc_lport_enter_reject(struct fc_lport *lp) -{ - switch (lp->state) { - case LPORT_ST_NONE: - case LPORT_ST_READY: - case LPORT_ST_RESET: - case LPORT_ST_REG_PN: - case LPORT_ST_REG_FT: - case LPORT_ST_SCR: - case LPORT_ST_DNS_STOP: - case LPORT_ST_DNS: - WARN_ON(1); - break; - case LPORT_ST_FLOGI: - fc_lport_enter_flogi(lp); - break; - case LPORT_ST_LOGO: - fc_lport_enter_reset(lp); - break; - } -} + fc_lport_state_enter(lport, LPORT_ST_READY); -/* - * Handle resource allocation problem by retrying in a bit. - */ -static void fc_lport_retry(struct fc_lport *lp) -{ - if (lp->retry_count == 0) - FC_DBG("local port %6x alloc failure in state %s " - "- will retry\n", lp->fid, fc_lport_state(lp)); - if (lp->retry_count < lp->max_retry_count) { - lp->retry_count++; - mod_timer(&lp->state_timer, - jiffies + msecs_to_jiffies(lp->e_d_tov)); - } else { - FC_DBG("local port %6x alloc failure in state %s " - "- retries exhausted\n", lp->fid, - fc_lport_state(lp)); - fc_lport_enter_reject(lp); - } + lport->tt.disc_start(lport); } -/* +/** + * fc_lport_recv_flogi_req - Receive a FLOGI request + * @sp_in: The sequence the FLOGI is on + * @rx_fp: The frame the FLOGI is in + * @lport: The lport that recieved the request + * * A received FLOGI request indicates a point-to-point connection. * Accept it with the common service parameters indicating our N port. * Set up to do a PLOGI if we have the higher-number WWPN. + * + * Locking Note: The lport lock is exected to be held before calling + * this function. */ static void fc_lport_recv_flogi_req(struct fc_seq *sp_in, struct fc_frame *rx_fp, - struct fc_lport *lp) + struct fc_lport *lport) { struct fc_frame *fp; struct fc_frame_header *fh; @@ -519,19 +605,22 @@ static void fc_lport_recv_flogi_req(struct fc_seq *sp_in, u32 local_fid; u32 f_ctl; + if (fc_lport_debug) + FC_DBG("Received FLOGI request while in state %s\n", + fc_lport_state(lport)); + fh = fc_frame_header_get(rx_fp); remote_fid = ntoh24(fh->fh_s_id); flp = fc_frame_payload_get(rx_fp, sizeof(*flp)); if (!flp) goto out; remote_wwpn = get_unaligned_be64(&flp->fl_wwpn); - if (remote_wwpn == lp->wwpn) { + if (remote_wwpn == lport->wwpn) { FC_DBG("FLOGI from port with same WWPN %llx " "possible configuration error\n", remote_wwpn); goto out; } FC_DBG("FLOGI from port WWPN %llx\n", remote_wwpn); - fc_lport_lock(lp); /* * XXX what is the right thing to do for FIDs? @@ -539,20 +628,21 @@ static void fc_lport_recv_flogi_req(struct fc_seq *sp_in, * But if so, both of us could end up with the same FID. */ local_fid = FC_LOCAL_PTP_FID_LO; - if (remote_wwpn < lp->wwpn) { + if (remote_wwpn < lport->wwpn) { local_fid = FC_LOCAL_PTP_FID_HI; if (!remote_fid || remote_fid == local_fid) remote_fid = FC_LOCAL_PTP_FID_LO; } else if (!remote_fid) { remote_fid = FC_LOCAL_PTP_FID_HI; } - fc_lport_set_fid(lp, local_fid); - fp = fc_frame_alloc(lp, sizeof(*flp)); + lport->fid = local_fid; + + fp = fc_frame_alloc(lport, sizeof(*flp)); if (fp) { - sp = lp->tt.seq_start_next(fr_seq(rx_fp)); + sp = lport->tt.seq_start_next(fr_seq(rx_fp)); new_flp = fc_frame_payload_get(fp, sizeof(*flp)); - fc_lport_flogi_fill(lp, new_flp, ELS_FLOGI); + fc_lport_flogi_fill(lport, new_flp, ELS_FLOGI); new_flp->fl_cmd = (u8) ELS_LS_ACC; /* @@ -561,23 +651,35 @@ static void fc_lport_recv_flogi_req(struct fc_seq *sp_in, */ f_ctl = FC_FC_LAST_SEQ | FC_FC_END_SEQ; fc_frame_setup(fp, FC_RCTL_ELS_REP, FC_TYPE_ELS); - lp->tt.seq_send(lp, sp, fp, f_ctl); + lport->tt.seq_send(lport, sp, fp, f_ctl); } else { - fc_lport_retry(lp); + fc_lport_error(lport, fp); } - fc_lport_ptp_setup(lp, remote_fid, remote_wwpn, + fc_lport_ptp_setup(lport, remote_fid, remote_wwpn, get_unaligned_be64(&flp->fl_wwnn)); - fc_lport_unlock(lp); - if (lp->tt.disc_start(lp)) + + if (lport->tt.disc_start(lport)) FC_DBG("target discovery start error\n"); out: sp = fr_seq(rx_fp); fc_frame_free(rx_fp); } -static void fc_lport_recv(struct fc_lport *lp, struct fc_seq *sp, - struct fc_frame *fp) +/** + * fc_lport_recv_req - The generic lport request handler + * @lport: The lport that received the request + * @sp: The sequence the request is on + * @fp: The frame the request is in + * + * This function will see if the lport handles the request or + * if an rport should handle the request. + * + * Locking Note: This function should not be called with the lport + * lock held becuase it will grab the lock. + */ +static void fc_lport_recv_req(struct fc_lport *lport, struct fc_seq *sp, + struct fc_frame *fp) { struct fc_frame_header *fh = fc_frame_header_get(fp); void (*recv) (struct fc_seq *, struct fc_frame *, struct fc_lport *); @@ -586,6 +688,8 @@ static void fc_lport_recv(struct fc_lport *lp, struct fc_seq *sp, u32 d_id; struct fc_seq_els_data rjt_data; + mutex_lock(&lport->lp_mutex); + /* * Handle special ELS cases like FLOGI, LOGO, and * RSCN here. These don't require a session. @@ -606,21 +710,21 @@ static void fc_lport_recv(struct fc_lport *lp, struct fc_seq *sp, recv = fc_lport_recv_logo_req; break; case ELS_RSCN: - recv = lp->tt.disc_recv_req; + recv = lport->tt.disc_recv_req; break; case ELS_ECHO: - recv = fc_lport_echo_req; + recv = fc_lport_recv_echo_req; break; case ELS_RLIR: - recv = fc_lport_rlir_req; + recv = fc_lport_recv_rlir_req; break; case ELS_RNID: - recv = fc_lport_rnid_req; + recv = fc_lport_recv_rnid_req; break; } if (recv) - recv(sp, fp, lp); + recv(sp, fp, lport); else { /* * Find session. @@ -629,16 +733,17 @@ static void fc_lport_recv(struct fc_lport *lp, struct fc_seq *sp, s_id = ntoh24(fh->fh_s_id); d_id = ntoh24(fh->fh_d_id); - rport = lp->tt.rport_lookup(lp, s_id); + rport = lport->tt.rport_lookup(lport, s_id); if (rport) { - lp->tt.rport_recv_req(sp, fp, rport); + lport->tt.rport_recv_req(sp, fp, rport); put_device(&rport->dev); /* hold from lookup */ } else { rjt_data.fp = NULL; rjt_data.reason = ELS_RJT_UNAB; rjt_data.explan = ELS_EXPL_NONE; - lp->tt.seq_els_rsp_send(sp, - ELS_LS_RJT, &rjt_data); + lport->tt.seq_els_rsp_send(sp, + ELS_LS_RJT, + &rjt_data); fc_frame_free(fp); } } @@ -646,151 +751,572 @@ static void fc_lport_recv(struct fc_lport *lp, struct fc_seq *sp, FC_DBG("dropping invalid frame (eof %x)\n", fr_eof(fp)); fc_frame_free(fp); } + mutex_unlock(&lport->lp_mutex); +} - /* - * The common exch_done for all request may not be good - * if any request requires longer hold on exhange. XXX - */ - lp->tt.exch_done(sp); +/** + * fc_lport_reset - Reset an lport + * @lport: The lport which should be reset + * + * Locking Note: This functions should not be called with the + * lport lock held. + */ +int fc_lport_reset(struct fc_lport *lport) +{ + mutex_lock(&lport->lp_mutex); + fc_lport_enter_reset(lport); + mutex_unlock(&lport->lp_mutex); + return 0; } +EXPORT_SYMBOL(fc_lport_reset); -/* - * Put the local port back into the initial state. Reset all sessions. - * This is called after a SCSI reset or the driver is unloading - * or the program is exiting. +/** + * fc_rport_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. */ -int fc_lport_enter_reset(struct fc_lport *lp) +static void fc_lport_enter_reset(struct fc_lport *lport) { if (fc_lport_debug) - FC_DBG("Processing RESET state\n"); + FC_DBG("Port (%6x) entered RESET state from %s state\n", + lport->fid, fc_lport_state(lport)); + + fc_lport_state_enter(lport, LPORT_ST_RESET); - if (lp->dns_rp) { - fc_remote_port_delete(lp->dns_rp); - lp->dns_rp = NULL; + if (lport->dns_rp) { + fc_remote_port_delete(lport->dns_rp); + lport->dns_rp = NULL; } - fc_lport_ptp_clear(lp); + fc_lport_ptp_clear(lport); - /* - * Setting state RESET keeps fc_lport_error() callbacks - * by exch_mgr_reset() from recursing on the lock. - * It also causes fc_lport_sess_event() to ignore events. - * The lock is held for the duration of the time in RESET state. - */ - fc_lport_state_enter(lp, LPORT_ST_RESET); - lp->tt.exch_mgr_reset(lp->emp, 0, 0); - fc_lport_set_fid(lp, 0); - if ((lp->link_status & FC_LINK_UP) == FC_LINK_UP) - fc_lport_enter_flogi(lp); - return 0; + fc_block_rports(lport); + + lport->tt.rport_reset_list(lport); + lport->tt.exch_mgr_reset(lport->emp, 0, 0); + fc_host_fabric_name(lport->host) = 0; + lport->fid = 0; + + if ((lport->link_status & FC_LINK_UP) == FC_LINK_UP) + fc_lport_enter_flogi(lport); } -EXPORT_SYMBOL(fc_lport_enter_reset); -/* - * Handle errors on local port requests. - * Don't get locks if in RESET state. - * The only possible errors so far are exchange TIMEOUT and CLOSED (reset). +/** + * fc_lport_error - Handler for any errors + * @lport: The fc_lport object + * @fp: The frame pointer + * + * If the error was caused by a resource allocation failure + * then wait for half a second and retry, otherwise retry + * after the e_d_tov time. + */ +static void fc_lport_error(struct fc_lport *lport, struct fc_frame *fp) +{ + unsigned long delay = 0; + if (fc_lport_debug) + FC_DBG("Error %ld in state %s, retries %d\n", + PTR_ERR(fp), fc_lport_state(lport), + lport->retry_count); + + if (lport->retry_count < lport->max_retry_count) { + lport->retry_count++; + if (!fp) + delay = msecs_to_jiffies(500); + else + delay = jiffies + + msecs_to_jiffies(lport->e_d_tov); + + schedule_delayed_work(&lport->retry_work, delay); + } else { + switch (lport->state) { + case LPORT_ST_NONE: + case LPORT_ST_READY: + case LPORT_ST_RESET: + case LPORT_ST_RPN_ID: + case LPORT_ST_RFT_ID: + case LPORT_ST_SCR: + case LPORT_ST_DNS: + case LPORT_ST_FLOGI: + case LPORT_ST_LOGO: + fc_lport_enter_reset(lport); + break; + } + } +} + +/** + * fc_lport_rft_id_resp - Handle response to Register Fibre + * Channel Types by ID (RPN_ID) request + * @sp: current sequence in RPN_ID exchange + * @fp: response frame + * @lp_arg: Fibre Channel host port instance + * + * Locking Note: This function will be called without the lport lock + * held, but it will lock, call an _enter_* function or fc_lport_error + * and then unlock the lport. + */ +static void fc_lport_rft_id_resp(struct fc_seq *sp, struct fc_frame *fp, + void *lp_arg) +{ + struct fc_lport *lport = lp_arg; + struct fc_frame_header *fh; + struct fc_ct_hdr *ct; + + mutex_lock(&lport->lp_mutex); + + if (fc_lport_debug) + FC_DBG("Received a RFT_ID response\n"); + + if (lport->state != LPORT_ST_RFT_ID) { + FC_DBG("Received a RFT_ID response, but in state %s\n", + fc_lport_state(lport)); + goto out; + } + + if (IS_ERR(fp)) { + fc_lport_error(lport, fp); + goto out; + } + + fh = fc_frame_header_get(fp); + ct = fc_frame_payload_get(fp, sizeof(*ct)); + + if (fh && ct && fh->fh_type == FC_TYPE_CT && + ct->ct_fs_type == FC_FST_DIR && + ct->ct_fs_subtype == FC_NS_SUBTYPE && + ntohs(ct->ct_cmd) == FC_FS_ACC) + fc_lport_enter_scr(lport); + else + fc_lport_error(lport, fp); +out: + mutex_unlock(&lport->lp_mutex); + fc_frame_free(fp); +} + +/** + * fc_lport_rpn_id_resp - Handle response to Register Port + * Name by ID (RPN_ID) request + * @sp: current sequence in RPN_ID exchange + * @fp: response frame + * @lp_arg: Fibre Channel host port instance + * + * Locking Note: This function will be called without the lport lock + * held, but it will lock, call an _enter_* function or fc_lport_error + * and then unlock the lport. + */ +static void fc_lport_rpn_id_resp(struct fc_seq *sp, struct fc_frame *fp, + void *lp_arg) +{ + struct fc_lport *lport = lp_arg; + struct fc_frame_header *fh; + struct fc_ct_hdr *ct; + + mutex_lock(&lport->lp_mutex); + + if (fc_lport_debug) + FC_DBG("Received a RPN_ID response\n"); + + if (lport->state != LPORT_ST_RPN_ID) { + FC_DBG("Received a RPN_ID response, but in state %s\n", + fc_lport_state(lport)); + goto out; + } + + if (IS_ERR(fp)) { + fc_lport_error(lport, fp); + goto out; + } + + fh = fc_frame_header_get(fp); + ct = fc_frame_payload_get(fp, sizeof(*ct)); + if (fh && ct && fh->fh_type == FC_TYPE_CT && + ct->ct_fs_type == FC_FST_DIR && + ct->ct_fs_subtype == FC_NS_SUBTYPE && + ntohs(ct->ct_cmd) == FC_FS_ACC) + fc_lport_enter_rft_id(lport); + else + fc_lport_error(lport, fp); + +out: + mutex_unlock(&lport->lp_mutex); + fc_frame_free(fp); +} + +/** + * fc_lport_scr_resp - Handle response to State Change Register (SCR) request + * @sp: current sequence in SCR exchange + * @fp: response frame + * @lp_arg: Fibre Channel lport port instance that sent the registration request + * + * Locking Note: This function will be called without the lport lock + * held, but it will lock, call an _enter_* function or fc_lport_error + * and then unlock the lport. */ -static void fc_lport_error(struct fc_lport *lp, struct fc_frame *fp) +static void fc_lport_scr_resp(struct fc_seq *sp, struct fc_frame *fp, + void *lp_arg) { - if (lp->state == LPORT_ST_RESET) + struct fc_lport *lport = lp_arg; + u8 op; + + mutex_lock(&lport->lp_mutex); + + if (fc_lport_debug) + FC_DBG("Received a SCR response\n"); + + if (lport->state != LPORT_ST_SCR) { + FC_DBG("Received a SCR response, but in state %s\n", + fc_lport_state(lport)); + goto out; + } + + if (IS_ERR(fp)) { + fc_lport_error(lport, fp); + goto out; + } + + op = fc_frame_payload_op(fp); + if (op == ELS_LS_ACC) + fc_lport_enter_ready(lport); + else + fc_lport_error(lport, fp); + +out: + mutex_unlock(&lport->lp_mutex); + fc_frame_free(fp); +} + +/** + * fc_lport_enter_scr - Send a State Change Register (SCR) request + * @lport: Fibre Channel local port to register for state changes + * + * Locking Note: The lport lock is expected to be held before calling + * this routine. + */ +static void fc_lport_enter_scr(struct fc_lport *lport) +{ + struct fc_frame *fp; + struct fc_els_scr *scr; + + if (fc_lport_debug) + FC_DBG("Port (%6x) entered SCR state from %s state\n", + lport->fid, fc_lport_state(lport)); + + fc_lport_state_enter(lport, LPORT_ST_SCR); + + fp = fc_frame_alloc(lport, sizeof(*scr)); + if (!fp) { + fc_lport_error(lport, fp); return; + } - fc_lport_lock(lp); - if (PTR_ERR(fp) == -FC_EX_TIMEOUT) { - if (lp->retry_count < lp->max_retry_count) { - lp->retry_count++; - fc_lport_enter_retry(lp); - } else { - fc_lport_enter_reject(lp); + scr = fc_frame_payload_get(fp, sizeof(*scr)); + memset(scr, 0, sizeof(*scr)); + scr->scr_cmd = ELS_SCR; + scr->scr_reg_func = ELS_SCRF_FULL; + fc_frame_setup(fp, FC_RCTL_ELS_REQ, FC_TYPE_ELS); + fc_frame_set_offset(fp, 0); + + if (!lport->tt.exch_seq_send(lport, fp, + fc_lport_scr_resp, NULL, + lport, lport->e_d_tov, + lport->fid, FC_FID_FCTRL, + FC_FC_SEQ_INIT | FC_FC_END_SEQ)) + fc_lport_error(lport, fp); +} + +/** + * fc_lport_enter_rft_id - Register FC4-types with the name server + * @lport: Fibre Channel local port to register + * + * Locking Note: The lport lock is expected to be held before calling + * this routine. + */ +static void fc_lport_enter_rft_id(struct fc_lport *lport) +{ + struct fc_frame *fp; + struct req { + struct fc_ct_hdr ct; + struct fc_ns_fid fid; /* port ID object */ + struct fc_ns_fts fts; /* FC4-types object */ + } *req; + struct fc_ns_fts *lps; + int i; + + if (fc_lport_debug) + FC_DBG("Port (%6x) entered RFT_ID state from %s state\n", + lport->fid, fc_lport_state(lport)); + fc_lport_state_enter(lport, LPORT_ST_RFT_ID); + + lps = &lport->fcts; + i = sizeof(lps->ff_type_map) / sizeof(lps->ff_type_map[0]); + while (--i >= 0) + if (ntohl(lps->ff_type_map[i]) != 0) + break; + if (i < 0) { + /* nothing to register, move on to SCR */ + fc_lport_enter_scr(lport); + } else { + fp = fc_frame_alloc(lport, sizeof(*req)); + if (!fp) { + fc_lport_error(lport, fp); + return; } + + req = fc_frame_payload_get(fp, sizeof(*req)); + fc_fill_dns_hdr(lport, &req->ct, + FC_NS_RFT_ID, + sizeof(*req) - + sizeof(struct fc_ct_hdr)); + hton24(req->fid.fp_fid, lport->fid); + req->fts = *lps; + fc_frame_setup(fp, FC_RCTL_DD_UNSOL_CTL, FC_TYPE_CT); + + if (!lport->tt.exch_seq_send(lport, fp, + fc_lport_rft_id_resp, NULL, + lport, lport->e_d_tov, + lport->fid, + FC_FID_DIR_SERV, + FC_FC_SEQ_INIT | + FC_FC_END_SEQ)) + fc_lport_error(lport, fp); + } +} + +/** + * fc_rport_enter_rft_id - Register port name with the name server + * @lport: Fibre Channel local port to register + * + * Locking Note: The lport lock is expected to be held before calling + * this routine. + */ +static void fc_lport_enter_rpn_id(struct fc_lport *lport) +{ + struct fc_frame *fp; + struct req { + struct fc_ct_hdr ct; + struct fc_ns_rn_id rn; + } *req; + + if (fc_lport_debug) + FC_DBG("Port (%6x) entered RPN_ID state from %s state\n", + lport->fid, fc_lport_state(lport)); + + fc_lport_state_enter(lport, LPORT_ST_RPN_ID); + + fp = fc_frame_alloc(lport, sizeof(*req)); + if (!fp) { + fc_lport_error(lport, fp); + return; } + + req = fc_frame_payload_get(fp, sizeof(*req)); + memset(req, 0, sizeof(*req)); + fc_fill_dns_hdr(lport, &req->ct, FC_NS_RPN_ID, sizeof(req->rn)); + hton24(req->rn.fr_fid.fp_fid, lport->fid); + put_unaligned_be64(lport->wwpn, &req->rn.fr_wwn); + fc_frame_setup(fp, FC_RCTL_DD_UNSOL_CTL, FC_TYPE_CT); + + if (!lport->tt.exch_seq_send(lport, fp, + fc_lport_rpn_id_resp, NULL, + lport, lport->e_d_tov, + lport->fid, + FC_FID_DIR_SERV, + FC_FC_SEQ_INIT | FC_FC_END_SEQ)) + fc_lport_error(lport, fp); + +} + +/** + * fc_rport_enter_dns - Create a rport to the name server + * @lport: Fibre Channel local port requesting a rport for the name server + * + * Locking Note: The lport lock is expected to be held before calling + * this routine. + */ +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; + if (fc_lport_debug) - FC_DBG("error %ld retries %d limit %d\n", - PTR_ERR(fp), lp->retry_count, lp->max_retry_count); - fc_lport_unlock(lp); + FC_DBG("Port (%6x) entered DNS state from %s state\n", + lport->fid, fc_lport_state(lport)); + + fc_lport_state_enter(lport, LPORT_ST_DNS); + + if (!lport->dns_rp) { + /* Set up a dummy rport to directory server */ + rport = fc_rport_dummy_create(&dp); + + if (!rport) + goto err; + lport->dns_rp = rport; + FC_DBG("created an rport for the NS\n"); + } + + rport = lport->dns_rp; + rdata = rport->dd_data; + rdata->event_callback = fc_lport_rport_event; + lport->tt.rport_login(rport); + return; + +err: + fc_lport_error(lport, NULL); } -static void fc_lport_timeout(unsigned long lp_arg) +/** + * fc_lport_timeout - Handler for the retry_work timer. + * @work: The work struct of the fc_lport + */ +static void fc_lport_timeout(struct work_struct *work) { - struct fc_lport *lp = (struct fc_lport *)lp_arg; + struct fc_lport *lport = + container_of(work, struct fc_lport, + retry_work.work); - fc_lport_lock(lp); - fc_lport_enter_retry(lp); - fc_lport_unlock(lp); + mutex_lock(&lport->lp_mutex); + + switch (lport->state) { + case LPORT_ST_NONE: + case LPORT_ST_READY: + case LPORT_ST_RESET: + WARN_ON(1); + break; + case LPORT_ST_FLOGI: + fc_lport_enter_flogi(lport); + break; + case LPORT_ST_DNS: + fc_lport_enter_dns(lport); + break; + case LPORT_ST_RPN_ID: + fc_lport_enter_rpn_id(lport); + break; + case LPORT_ST_RFT_ID: + fc_lport_enter_rft_id(lport); + break; + case LPORT_ST_SCR: + fc_lport_enter_scr(lport); + break; + case LPORT_ST_LOGO: + fc_lport_enter_logo(lport); + break; + } + + mutex_unlock(&lport->lp_mutex); } +/** + * fc_lport_logo_resp - Handle response to LOGO request + * @sp: current sequence in LOGO exchange + * @fp: response frame + * @lp_arg: Fibre Channel lport port instance that sent the LOGO request + * + * Locking Note: This function will be called without the lport lock + * held, but it will lock, call an _enter_* function or fc_lport_error + * and then unlock the lport. + */ static void fc_lport_logo_resp(struct fc_seq *sp, struct fc_frame *fp, void *lp_arg) { - struct fc_lport *lp = lp_arg; + struct fc_lport *lport = lp_arg; + u8 op; - if (IS_ERR(fp)) - fc_lport_error(lp, fp); - else { - fc_frame_free(fp); - fc_lport_lock(lp); - fc_lport_enter_reset(lp); - fc_lport_unlock(lp); + mutex_lock(&lport->lp_mutex); + + if (fc_lport_debug) + FC_DBG("Received a LOGO response\n"); + + if (lport->state != LPORT_ST_LOGO) { + FC_DBG("Received a LOGO response, but in state %s\n", + fc_lport_state(lport)); + goto out; } + + if (IS_ERR(fp)) { + fc_lport_error(lport, fp); + goto out; + } + + op = fc_frame_payload_op(fp); + if (op == ELS_LS_ACC) + fc_lport_enter_reset(lport); + else + fc_lport_error(lport, fp); + +out: + mutex_unlock(&lport->lp_mutex); + fc_frame_free(fp); } -/* Logout of the FC fabric */ -static void fc_lport_enter_logo(struct fc_lport *lp) +/** + * fc_rport_enter_logo - Logout of the fabric + * @lport: Fibre Channel local port to be logged out + * + * Locking Note: The lport lock is expected to be held before calling + * this routine. + */ +static void fc_lport_enter_logo(struct fc_lport *lport) { struct fc_frame *fp; struct fc_els_logo *logo; if (fc_lport_debug) - FC_DBG("Processing LOGO state\n"); + FC_DBG("Port (%6x) entered LOGO state from %s state\n", + lport->fid, fc_lport_state(lport)); - fc_lport_state_enter(lp, LPORT_ST_LOGO); + fc_lport_state_enter(lport, LPORT_ST_LOGO); /* DNS session should be closed so we can release it here */ - if (lp->dns_rp) { - fc_remote_port_delete(lp->dns_rp); - lp->dns_rp = NULL; + if (lport->dns_rp) { + fc_remote_port_delete(lport->dns_rp); + lport->dns_rp = NULL; } - fp = fc_frame_alloc(lp, sizeof(*logo)); + fp = fc_frame_alloc(lport, sizeof(*logo)); if (!fp) { - FC_DBG("failed to allocate frame\n"); + fc_lport_error(lport, fp); return; } logo = fc_frame_payload_get(fp, sizeof(*logo)); memset(logo, 0, sizeof(*logo)); logo->fl_cmd = ELS_LOGO; - hton24(logo->fl_n_port_id, lp->fid); - logo->fl_n_port_wwn = htonll(lp->wwpn); - + hton24(logo->fl_n_port_id, lport->fid); + logo->fl_n_port_wwn = htonll(lport->wwpn); fc_frame_setup(fp, FC_RCTL_ELS_REQ, FC_TYPE_ELS); fc_frame_set_offset(fp, 0); - lp->tt.exch_seq_send(lp, fp, - fc_lport_logo_resp, - lp, lp->e_d_tov, - lp->fid, FC_FID_FLOGI, - FC_FC_SEQ_INIT | FC_FC_END_SEQ); -} - -static int fc_lport_logout(struct fc_lport *lp) -{ - fc_lport_lock(lp); - if (lp->state != LPORT_ST_LOGO) - fc_lport_enter_logo(lp); - fc_lport_unlock(lp); - return 0; + if (!lport->tt.exch_seq_send(lport, fp, + fc_lport_logo_resp, NULL, + lport, lport->e_d_tov, + lport->fid, FC_FID_FLOGI, + FC_FC_SEQ_INIT | FC_FC_END_SEQ)) + fc_lport_error(lport, fp); } -/* - * Handle incoming ELS FLOGI response. - * Save parameters of remote switch. Finish exchange. +/** + * fc_lport_flogi_resp - Handle response to FLOGI request + * @sp: current sequence in FLOGI exchange + * @fp: response frame + * @lp_arg: Fibre Channel lport port instance that sent the FLOGI request + * + * Locking Note: This function will be called without the lport lock + * held, but it will lock, call an _enter_* function or fc_lport_error + * and then unlock the lport. */ -static void -fc_lport_flogi_resp(struct fc_seq *sp, struct fc_frame *fp, void *lp_arg) +static void fc_lport_flogi_resp(struct fc_seq *sp, struct fc_frame *fp, + void *lp_arg) { - struct fc_lport *lp = lp_arg; + struct fc_lport *lport = lp_arg; struct fc_frame_header *fh; struct fc_els_flogi *flp; u32 did; @@ -799,127 +1325,160 @@ fc_lport_flogi_resp(struct fc_seq *sp, struct fc_frame *fp, void *lp_arg) unsigned int e_d_tov; u16 mfs; + mutex_lock(&lport->lp_mutex); + + if (fc_lport_debug) + FC_DBG("Received a FLOGI response\n"); + + if (lport->state != LPORT_ST_FLOGI) { + FC_DBG("Received a FLOGI response, but in state %s\n", + fc_lport_state(lport)); + goto out; + } + if (IS_ERR(fp)) { - fc_lport_error(lp, fp); - return; + fc_lport_error(lport, fp); + goto out; } fh = fc_frame_header_get(fp); did = ntoh24(fh->fh_d_id); if (fc_frame_payload_op(fp) == ELS_LS_ACC && did != 0) { if (fc_lport_debug) - FC_DBG("assigned fid %x\n", did); - fc_lport_lock(lp); - fc_lport_set_fid(lp, did); + FC_DBG("Assigned fid %x\n", did); + + lport->fid = did; flp = fc_frame_payload_get(fp, sizeof(*flp)); if (flp) { mfs = ntohs(flp->fl_csp.sp_bb_data) & FC_SP_BB_DATA_MASK; if (mfs >= FC_SP_MIN_MAX_PAYLOAD && - mfs < lp->mfs) - lp->mfs = mfs; + mfs < lport->mfs) + lport->mfs = mfs; csp_flags = ntohs(flp->fl_csp.sp_features); r_a_tov = ntohl(flp->fl_csp.sp_r_a_tov); e_d_tov = ntohl(flp->fl_csp.sp_e_d_tov); if (csp_flags & FC_SP_FT_EDTR) e_d_tov /= 1000000; if ((csp_flags & FC_SP_FT_FPORT) == 0) { - if (e_d_tov > lp->e_d_tov) - lp->e_d_tov = e_d_tov; - lp->r_a_tov = 2 * e_d_tov; - FC_DBG("point-to-point mode\n"); - fc_lport_ptp_setup(lp, ntoh24(fh->fh_s_id), + if (e_d_tov > lport->e_d_tov) + lport->e_d_tov = e_d_tov; + lport->r_a_tov = 2 * e_d_tov; + FC_DBG("Point-to-Point mode\n"); + fc_lport_ptp_setup(lport, ntoh24(fh->fh_s_id), get_unaligned_be64( &flp->fl_wwpn), get_unaligned_be64( &flp->fl_wwnn)); } else { - lp->e_d_tov = e_d_tov; - lp->r_a_tov = r_a_tov; - lp->tt.dns_register(lp); + lport->e_d_tov = e_d_tov; + lport->r_a_tov = r_a_tov; + fc_host_fabric_name(lport->host) = + get_unaligned_be64(&flp->fl_wwnn); + fc_lport_enter_dns(lport); } } - fc_lport_unlock(lp); + if (flp) { csp_flags = ntohs(flp->fl_csp.sp_features); if ((csp_flags & FC_SP_FT_FPORT) == 0) { - if (lp->tt.disc_start(lp)) - FC_DBG("target disc start error\n"); + if (lport->tt.disc_start(lport)) + FC_DBG("Target disc start error\n"); } } } else { FC_DBG("bad FLOGI response\n"); } + +out: + mutex_unlock(&lport->lp_mutex); fc_frame_free(fp); } -/* - * Send ELS (extended link service) FLOGI request to peer. +/** + * fc_rport_enter_flogi - Send a FLOGI request to the fabric manager + * @lport: Fibre Channel local port to be logged in to the fabric + * + * Locking Note: The lport lock is expected to be held before calling + * this routine. */ -static void fc_lport_flogi_send(struct fc_lport *lp) +void fc_lport_enter_flogi(struct fc_lport *lport) { struct fc_frame *fp; struct fc_els_flogi *flp; - fp = fc_frame_alloc(lp, sizeof(*flp)); + if (fc_lport_debug) + FC_DBG("Processing FLOGI state\n"); + + fc_lport_state_enter(lport, LPORT_ST_FLOGI); + + fp = fc_frame_alloc(lport, sizeof(*flp)); if (!fp) - return fc_lport_retry(lp); + return fc_lport_error(lport, fp); flp = fc_frame_payload_get(fp, sizeof(*flp)); - fc_lport_flogi_fill(lp, flp, ELS_FLOGI); + fc_lport_flogi_fill(lport, flp, ELS_FLOGI); fc_frame_setup(fp, FC_RCTL_ELS_REQ, FC_TYPE_ELS); fc_frame_set_offset(fp, 0); - if (!lp->tt.exch_seq_send(lp, fp, - fc_lport_flogi_resp, - lp, lp->e_d_tov, - 0, FC_FID_FLOGI, - FC_FC_SEQ_INIT | FC_FC_END_SEQ)) - fc_lport_retry(lp); - -} + if (!lport->tt.exch_seq_send(lport, fp, + fc_lport_flogi_resp, NULL, + lport, lport->e_d_tov, + 0, FC_FID_FLOGI, + FC_FC_SEQ_INIT | FC_FC_END_SEQ)) + fc_lport_error(lport, fp); -void fc_lport_enter_flogi(struct fc_lport *lp) -{ - if (fc_lport_debug) - FC_DBG("Processing FLOGI state\n"); - fc_lport_state_enter(lp, LPORT_ST_FLOGI); - fc_lport_flogi_send(lp); } /* Configure a fc_lport */ -int fc_lport_config(struct fc_lport *lp) +int fc_lport_config(struct fc_lport *lport) { - setup_timer(&lp->state_timer, fc_lport_timeout, (unsigned long)lp); - spin_lock_init(&lp->state_lock); + INIT_DELAYED_WORK(&lport->retry_work, fc_lport_timeout); + mutex_init(&lport->lp_mutex); - fc_lport_lock(lp); - fc_lport_state_enter(lp, LPORT_ST_NONE); - fc_lport_unlock(lp); + fc_lport_state_enter(lport, LPORT_ST_NONE); - lp->ns_disc_delay = DNS_DELAY; + lport->disc_delay = DNS_DELAY; - fc_lport_add_fc4_type(lp, FC_TYPE_FCP); - fc_lport_add_fc4_type(lp, FC_TYPE_CT); + fc_lport_add_fc4_type(lport, FC_TYPE_FCP); + fc_lport_add_fc4_type(lport, FC_TYPE_CT); return 0; } EXPORT_SYMBOL(fc_lport_config); -int fc_lport_init(struct fc_lport *lp) +int fc_lport_init(struct fc_lport *lport) { - if (!lp->tt.lport_recv) - lp->tt.lport_recv = fc_lport_recv; - - if (!lp->tt.lport_login) - lp->tt.lport_login = fc_lport_enter_reset; - - if (!lp->tt.lport_reset) - lp->tt.lport_reset = fc_lport_enter_reset; - - if (!lp->tt.lport_logout) - lp->tt.lport_logout = fc_lport_logout; + if (!lport->tt.lport_recv) + lport->tt.lport_recv = fc_lport_recv_req; + + if (!lport->tt.lport_reset) + lport->tt.lport_reset = fc_lport_reset; + + if (!lport->tt.event_callback) + lport->tt.event_callback = fc_lport_rport_event; + + fc_host_port_type(lport->host) = FC_PORTTYPE_NPORT; + fc_host_node_name(lport->host) = lport->wwnn; + fc_host_port_name(lport->host) = lport->wwpn; + fc_host_supported_classes(lport->host) = FC_COS_CLASS3; + memset(fc_host_supported_fc4s(lport->host), 0, + sizeof(fc_host_supported_fc4s(lport->host))); + fc_host_supported_fc4s(lport->host)[2] = 1; + fc_host_supported_fc4s(lport->host)[7] = 1; + + /* This value is also unchanging */ + memset(fc_host_active_fc4s(lport->host), 0, + sizeof(fc_host_active_fc4s(lport->host))); + fc_host_active_fc4s(lport->host)[2] = 1; + fc_host_active_fc4s(lport->host)[7] = 1; + fc_host_maxframe_size(lport->host) = lport->mfs; + fc_host_supported_speeds(lport->host) = 0; + if (lport->link_supported_speeds & FC_PORTSPEED_1GBIT) + fc_host_supported_speeds(lport->host) |= FC_PORTSPEED_1GBIT; + if (lport->link_supported_speeds & FC_PORTSPEED_10GBIT) + fc_host_supported_speeds(lport->host) |= FC_PORTSPEED_10GBIT; return 0; } diff --git a/drivers/scsi/libfc/fc_ns.c b/drivers/scsi/libfc/fc_ns.c deleted file mode 100644 index 5c9272c..0000000 --- a/drivers/scsi/libfc/fc_ns.c +++ /dev/null @@ -1,1283 +0,0 @@ -/* - * Copyright(c) 2007 - 2008 Intel Corporation. All rights reserved. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms and conditions of the GNU General Public License, - * version 2, as published by the Free Software Foundation. - * - * This program is distributed in the hope it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. - * - * Maintained at www.Open-FCoE.org - */ - -/* - * Target Discovery - * Actually, this discovers all FC-4 remote ports, including FCP initiators. - */ - -#include -#include -#include - -#include - -#include - -#define FC_NS_RETRY_LIMIT 3 /* max retries */ -#define FC_NS_RETRY_DELAY 500UL /* (msecs) delay */ - -int fc_ns_debug; - -static void fc_ns_gpn_ft_req(struct fc_lport *); -static void fc_ns_gpn_ft_resp(struct fc_seq *, struct fc_frame *, void *); -static int fc_ns_new_target(struct fc_lport *, struct fc_rport *, - struct fc_rport_identifiers *); -static void fc_ns_del_target(struct fc_lport *, struct fc_rport *); -static void fc_ns_disc_done(struct fc_lport *); -static void fcdt_ns_error(struct fc_lport *, struct fc_frame *); -static void fc_ns_timeout(struct work_struct *); - -/** - * struct fc_ns_port - temporary discovery port to hold rport identifiers - * @lp: Fibre Channel host port instance - * @peers: node for list management during discovery and RSCN processing - * @ids: identifiers structure to pass to fc_remote_port_add() - */ -struct fc_ns_port { - struct fc_lport *lp; - struct list_head peers; - struct fc_rport_identifiers ids; -}; - -static int fc_ns_gpn_id_req(struct fc_lport *, struct fc_ns_port *); -static void fc_ns_gpn_id_resp(struct fc_seq *, struct fc_frame *, void *); -static void fc_ns_gpn_id_error(struct fc_ns_port *rp, struct fc_frame *fp); - -static int fc_ns_gnn_id_req(struct fc_lport *, struct fc_ns_port *); -static void fc_ns_gnn_id_resp(struct fc_seq *, struct fc_frame *, void *); -static void fc_ns_gnn_id_error(struct fc_ns_port *, struct fc_frame *); -static void fc_ns_enter_reg_pn(struct fc_lport *lp); -static void fc_ns_error(struct fc_lport *lp, struct fc_frame *fp); -static void fc_lport_fill_dns_hdr(struct fc_lport *lp, struct fc_ct_hdr *ct, - unsigned int op, unsigned int req_size); -static void fc_ns_resp(struct fc_seq *sp, struct fc_frame *fp, - void *lp_arg); -static void fc_ns_retry(struct fc_lport *lp); -static void fc_ns_single(struct fc_lport *, struct fc_ns_port *); -static int fc_ns_restart(struct fc_lport *); - - -/** - * fc_ns_rscn_req - Handle Registered State Change Notification (RSCN) - * @sp: Current sequence of the RSCN exchange - * @fp: RSCN Frame - * @lp: Fibre Channel host port instance - */ -static void fc_ns_rscn_req(struct fc_seq *sp, struct fc_frame *fp, - struct fc_lport *lp) -{ - struct fc_els_rscn *rp; - struct fc_els_rscn_page *pp; - struct fc_seq_els_data rjt_data; - unsigned int len; - int redisc = 0; - enum fc_els_rscn_ev_qual ev_qual; - enum fc_els_rscn_addr_fmt fmt; - LIST_HEAD(disc_list); - struct fc_ns_port *dp, *next; - - rp = fc_frame_payload_get(fp, sizeof(*rp)); - - if (!rp || rp->rscn_page_len != sizeof(*pp)) - goto reject; - - len = ntohs(rp->rscn_plen); - if (len < sizeof(*rp)) - goto reject; - len -= sizeof(*rp); - - for (pp = (void *)(rp + 1); len; len -= sizeof(*pp), pp++) { - ev_qual = pp->rscn_page_flags >> ELS_RSCN_EV_QUAL_BIT; - ev_qual &= ELS_RSCN_EV_QUAL_MASK; - fmt = pp->rscn_page_flags >> ELS_RSCN_ADDR_FMT_BIT; - fmt &= ELS_RSCN_ADDR_FMT_MASK; - /* - * if we get an address format other than port - * (area, domain, fabric), then do a full discovery - */ - switch (fmt) { - case ELS_ADDR_FMT_PORT: - dp = kzalloc(sizeof(*dp), GFP_KERNEL); - if (!dp) { - redisc = 1; - break; - } - dp->lp = lp; - 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; - list_add_tail(&dp->peers, &disc_list); - break; - case ELS_ADDR_FMT_AREA: - case ELS_ADDR_FMT_DOM: - case ELS_ADDR_FMT_FAB: - default: - redisc = 1; - break; - } - } - lp->tt.seq_els_rsp_send(sp, ELS_LS_ACC, NULL); - if (redisc) { - if (fc_ns_debug) - FC_DBG("RSCN received: rediscovering\n"); - list_for_each_entry_safe(dp, next, &disc_list, peers) { - list_del(&dp->peers); - kfree(dp); - } - fc_ns_restart(lp); - } else { - if (fc_ns_debug) - FC_DBG("RSCN received: not rediscovering. " - "redisc %d state %d in_prog %d\n", - redisc, lp->state, lp->ns_disc_pending); - list_for_each_entry_safe(dp, next, &disc_list, peers) { - list_del(&dp->peers); - fc_ns_single(lp, dp); - } - } - fc_frame_free(fp); - return; -reject: - rjt_data.fp = NULL; - rjt_data.reason = ELS_RJT_LOGIC; - rjt_data.explan = ELS_EXPL_NONE; - lp->tt.seq_els_rsp_send(sp, ELS_LS_RJT, &rjt_data); - fc_frame_free(fp); -} - -static void fc_ns_recv_req(struct fc_seq *sp, struct fc_frame *fp, - struct fc_lport *lp) -{ - switch (fc_frame_payload_op(fp)) { - case ELS_RSCN: - fc_ns_rscn_req(sp, fp, lp); - break; - default: - FC_DBG("fc_ns recieved an unexpected request\n"); - break; - } -} - -/** - * fc_ns_scr_resp - Handle response to State Change Register (SCR) request - * @sp: current sequence in SCR exchange - * @fp: response frame - * @lp_arg: Fibre Channel host port instance - */ -static void fc_ns_scr_resp(struct fc_seq *sp, struct fc_frame *fp, - void *lp_arg) -{ - struct fc_lport *lp = lp_arg; - int err; - - if (IS_ERR(fp)) - fc_ns_error(lp, fp); - else { - fc_lport_lock(lp); - fc_lport_state_enter(lp, LPORT_ST_READY); - fc_lport_unlock(lp); - err = lp->tt.disc_start(lp); - if (err) - FC_DBG("target discovery start error\n"); - fc_frame_free(fp); - } -} - -/** - * fc_ns_enter scr - Send a State Change Register (SCR) request - * @lp: Fibre Channel host port instance - */ -static void fc_ns_enter_scr(struct fc_lport *lp) -{ - struct fc_frame *fp; - struct fc_els_scr *scr; - - if (fc_ns_debug) - FC_DBG("Processing SCR state\n"); - - fc_lport_state_enter(lp, LPORT_ST_SCR); - - fp = fc_frame_alloc(lp, sizeof(*scr)); - if (fp) { - scr = fc_frame_payload_get(fp, sizeof(*scr)); - memset(scr, 0, sizeof(*scr)); - scr->scr_cmd = ELS_SCR; - scr->scr_reg_func = ELS_SCRF_FULL; - } - fc_frame_setup(fp, FC_RCTL_ELS_REQ, FC_TYPE_ELS); - fc_frame_set_offset(fp, 0); - - lp->tt.exch_seq_send(lp, fp, - fc_ns_scr_resp, - lp, lp->e_d_tov, - lp->fid, FC_FID_FCTRL, - FC_FC_SEQ_INIT | FC_FC_END_SEQ); -} - -/** - * fc_ns_enter_reg_ft - Register FC4-types with the name server - * @lp: Fibre Channel host port instance - */ -static void fc_ns_enter_reg_ft(struct fc_lport *lp) -{ - struct fc_frame *fp; - struct req { - struct fc_ct_hdr ct; - struct fc_ns_fid fid; /* port ID object */ - struct fc_ns_fts fts; /* FC4-types object */ - } *req; - struct fc_ns_fts *lps; - int i; - - if (fc_ns_debug) - FC_DBG("Processing REG_FT state\n"); - - fc_lport_state_enter(lp, LPORT_ST_REG_FT); - - lps = &lp->fcts; - i = sizeof(lps->ff_type_map) / sizeof(lps->ff_type_map[0]); - while (--i >= 0) - if (ntohl(lps->ff_type_map[i]) != 0) - break; - if (i >= 0) { - fp = fc_frame_alloc(lp, sizeof(*req)); - if (fp) { - req = fc_frame_payload_get(fp, sizeof(*req)); - fc_lport_fill_dns_hdr(lp, &req->ct, - FC_NS_RFT_ID, - sizeof(*req) - - sizeof(struct fc_ct_hdr)); - hton24(req->fid.fp_fid, lp->fid); - req->fts = *lps; - fc_frame_setup(fp, FC_RCTL_DD_UNSOL_CTL, FC_TYPE_CT); - if (!lp->tt.exch_seq_send(lp, fp, - fc_ns_resp, lp, - lp->e_d_tov, - lp->fid, - lp->dns_rp->port_id, - FC_FC_SEQ_INIT | - FC_FC_END_SEQ)) - fc_ns_retry(lp); - } else { - fc_ns_retry(lp); - } - } else { - fc_ns_enter_scr(lp); - } -} - -/* - * enter next state for handling an exchange reject or retry exhaustion - * in the current state. - */ -static void fc_ns_enter_reject(struct fc_lport *lp) -{ - switch (lp->state) { - case LPORT_ST_NONE: - case LPORT_ST_READY: - case LPORT_ST_RESET: - case LPORT_ST_FLOGI: - case LPORT_ST_LOGO: - WARN_ON(1); - break; - case LPORT_ST_REG_PN: - fc_ns_enter_reg_ft(lp); - break; - case LPORT_ST_REG_FT: - fc_ns_enter_scr(lp); - break; - case LPORT_ST_SCR: - case LPORT_ST_DNS_STOP: - lp->tt.disc_stop(lp); - break; - case LPORT_ST_DNS: - lp->tt.lport_reset(lp); - break; - } -} - -static void fc_ns_enter_retry(struct fc_lport *lp) -{ - switch (lp->state) { - case LPORT_ST_NONE: - case LPORT_ST_RESET: - case LPORT_ST_READY: - case LPORT_ST_FLOGI: - case LPORT_ST_LOGO: - WARN_ON(1); - break; - case LPORT_ST_DNS: - lp->tt.dns_register(lp); - break; - case LPORT_ST_DNS_STOP: - lp->tt.disc_stop(lp); - break; - case LPORT_ST_REG_PN: - fc_ns_enter_reg_pn(lp); - break; - case LPORT_ST_REG_FT: - fc_ns_enter_reg_ft(lp); - break; - case LPORT_ST_SCR: - fc_ns_enter_scr(lp); - break; - } -} - -/* - * Refresh target discovery, perhaps due to an RSCN. - * A configurable delay is introduced to collect any subsequent RSCNs. - */ -static int fc_ns_restart(struct fc_lport *lp) -{ - fc_lport_lock(lp); - if (!lp->ns_disc_requested && !lp->ns_disc_pending) { - schedule_delayed_work(&lp->ns_disc_work, - msecs_to_jiffies(lp->ns_disc_delay * 1000)); - } - lp->ns_disc_requested = 1; - fc_lport_unlock(lp); - return 0; -} - -/* unlocked varient of scsi_target_block from scsi_lib.c */ -#include "../scsi_priv.h" - -static void __device_block(struct scsi_device *sdev, void *data) -{ - scsi_internal_device_block(sdev); -} - -static int __target_block(struct device *dev, void *data) -{ - if (scsi_is_target_device(dev)) - __starget_for_each_device(to_scsi_target(dev), - NULL, __device_block); - return 0; -} - -static void __scsi_target_block(struct device *dev) -{ - if (scsi_is_target_device(dev)) - __starget_for_each_device(to_scsi_target(dev), - NULL, __device_block); - else - device_for_each_child(dev, NULL, __target_block); -} - -static void fc_block_rports(struct fc_lport *lp) -{ - struct Scsi_Host *shost = lp->host; - struct fc_rport *rport; - unsigned long flags; - - spin_lock_irqsave(shost->host_lock, flags); - list_for_each_entry(rport, &fc_host_rports(shost), peers) { - /* protect the name service remote port */ - if (rport == lp->dns_rp) - continue; - if (rport->port_state != FC_PORTSTATE_ONLINE) - continue; - rport->port_state = FC_PORTSTATE_BLOCKED; - rport->flags |= FC_RPORT_DEVLOSS_PENDING; - __scsi_target_block(&rport->dev); - } - spin_unlock_irqrestore(shost->host_lock, flags); -} - -/* - * Fibre Channel Target discovery. - * - * Returns non-zero if discovery cannot be started. - * - * Callback is called for each target remote port found in discovery. - * When discovery is complete, the callback is called with a NULL remote port. - * Discovery may be restarted after an RSCN is received, causing the - * callback to be called after discovery complete is indicated. - */ -int fc_ns_disc_start(struct fc_lport *lp) -{ - struct fc_rport *rport; - int error; - struct fc_rport_identifiers ids; - - fc_lport_lock(lp); - - /* - * If not ready, or already running discovery, just set request flag. - */ - if (!fc_lport_test_ready(lp) || lp->ns_disc_pending) { - lp->ns_disc_requested = 1; - fc_lport_unlock(lp); - return 0; - } - lp->ns_disc_pending = 1; - lp->ns_disc_requested = 0; - lp->ns_disc_retry_count = 0; - - /* - * Handle point-to-point mode as a simple discovery - * of the remote port. - */ - rport = lp->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); - fc_lport_unlock(lp); - error = fc_ns_new_target(lp, rport, &ids); - put_device(&rport->dev); - if (!error) - fc_ns_disc_done(lp); - } else { - fc_lport_unlock(lp); - fc_block_rports(lp); - fc_ns_gpn_ft_req(lp); /* get ports by FC-4 type */ - error = 0; - } - return error; -} - -/* - * Handle resource allocation problem by retrying in a bit. - */ -static void fc_ns_retry(struct fc_lport *lp) -{ - if (lp->retry_count == 0) - FC_DBG("local port %6x alloc failure " - "- will retry\n", lp->fid); - if (lp->retry_count < lp->max_retry_count) { - lp->retry_count++; - mod_timer(&lp->state_timer, - jiffies + msecs_to_jiffies(lp->e_d_tov)); - } else { - FC_DBG("local port %6x alloc failure " - "- retries exhausted\n", lp->fid); - fc_ns_enter_reject(lp); - } -} - -/* - * Handle errors on local port requests. - * Don't get locks if in RESET state. - * The only possible errors so far are exchange TIMEOUT and CLOSED (reset). - */ -static void fc_ns_error(struct fc_lport *lp, struct fc_frame *fp) -{ - if (lp->state == LPORT_ST_RESET) - return; - - fc_lport_lock(lp); - if (PTR_ERR(fp) == -FC_EX_TIMEOUT) { - if (lp->retry_count < lp->max_retry_count) { - lp->retry_count++; - fc_ns_enter_retry(lp); - } else { - fc_ns_enter_reject(lp); - } - } - if (fc_ns_debug) - FC_DBG("error %ld retries %d limit %d\n", - PTR_ERR(fp), lp->retry_count, lp->max_retry_count); - fc_lport_unlock(lp); -} - -/* - * Restart discovery after a delay due to resource shortages. - * If the error persists, the discovery will be abandoned. - */ -static void fcdt_ns_retry(struct fc_lport *lp) -{ - unsigned long delay = FC_NS_RETRY_DELAY; - - if (!lp->ns_disc_retry_count) - delay /= 4; /* timeout faster first time */ - if (lp->ns_disc_retry_count++ < FC_NS_RETRY_LIMIT) - schedule_delayed_work(&lp->ns_disc_work, - msecs_to_jiffies(delay)); - else - fc_ns_disc_done(lp); -} - -/* - * Test for dNS accept in response payload. - */ -static int fc_lport_dns_acc(struct fc_frame *fp) -{ - struct fc_frame_header *fh; - struct fc_ct_hdr *ct; - int rc = 0; - - fh = fc_frame_header_get(fp); - ct = fc_frame_payload_get(fp, sizeof(*ct)); - if (fh && ct && fh->fh_type == FC_TYPE_CT && - ct->ct_fs_type == FC_FST_DIR && - ct->ct_fs_subtype == FC_NS_SUBTYPE && - ntohs(ct->ct_cmd) == FC_FS_ACC) { - rc = 1; - } - return rc; -} - -/* - * Handle response from name server. - */ -static void -fc_ns_resp(struct fc_seq *sp, struct fc_frame *fp, void *lp_arg) -{ - struct fc_lport *lp = lp_arg; - - if (!IS_ERR(fp)) { - fc_lport_lock(lp); - del_timer(&lp->state_timer); - if (fc_lport_dns_acc(fp)) { - if (lp->state == LPORT_ST_REG_PN) - fc_ns_enter_reg_ft(lp); - else - fc_ns_enter_scr(lp); - - } else { - fc_ns_retry(lp); - } - fc_lport_unlock(lp); - fc_frame_free(fp); - } else - fc_ns_error(lp, fp); -} - -/* - * Handle new target found by discovery. - * Create remote port and session if needed. - * Ignore returns of our own FID & WWPN. - * - * If a non-NULL rp is passed in, it is held for the caller, but not for us. - * - * Events delivered are: - * FC_EV_READY, when remote port is rediscovered. - */ -static int fc_ns_new_target(struct fc_lport *lp, - struct fc_rport *rport, - struct fc_rport_identifiers *ids) -{ - struct fc_rport_libfc_priv *rp; - 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_ns_del_target(lp, rport); - rport = NULL; - } - } - if (((ids->port_name != -1) || (ids->port_id != -1)) && - ids->port_id != lp->fid && ids->port_name != lp->wwpn) { - if (!rport) { - rport = lp->tt.rport_lookup(lp, ids->port_id); - if (rport == NULL) - rport = lp->tt.rport_create(lp, ids); - if (!rport) - error = ENOMEM; - } - if (rport) { - rp = rport->dd_data; - rp->rp_state = RPORT_ST_INIT; - lp->tt.rport_login(rport); - } - } - return error; -} - -/* - * Delete the remote port. - */ -static void fc_ns_del_target(struct fc_lport *lp, struct fc_rport *rport) -{ - lp->tt.rport_reset(rport); - fc_remote_port_delete(rport); /* release hold from create */ -} - -/* - * Done with discovery - */ -static void fc_ns_disc_done(struct fc_lport *lp) -{ - lp->ns_disc_done = 1; - lp->ns_disc_pending = 0; - if (lp->ns_disc_requested) - lp->tt.disc_start(lp); -} - -/** - * fc_ns_fill_dns_hdr - Fill in a name service request header - * @lp: Fibre Channel host port instance - * @ct: Common Transport (CT) header structure - * @op: Name Service request code - * @req_size: Full size of Name Service request - */ -static void fc_ns_fill_dns_hdr(struct fc_lport *lp, struct fc_ct_hdr *ct, - unsigned int op, unsigned int req_size) -{ - memset(ct, 0, sizeof(*ct) + req_size); - ct->ct_rev = FC_CT_REV; - ct->ct_fs_type = FC_FST_DIR; - ct->ct_fs_subtype = FC_NS_SUBTYPE; - ct->ct_cmd = htons((u16) op); -} - -/** - * fc_ns_gpn_ft_req - Send Get Port Names by FC-4 type (GPN_FT) request - * @lp: Fibre Channel host port instance - */ -static void fc_ns_gpn_ft_req(struct fc_lport *lp) -{ - struct fc_frame *fp; - struct fc_seq *sp = NULL; - struct req { - struct fc_ct_hdr ct; - struct fc_ns_gid_ft gid; - } *rp; - int error = 0; - - lp->ns_disc_buf_len = 0; - lp->ns_disc_seq_count = 0; - fp = fc_frame_alloc(lp, sizeof(*rp)); - if (fp == NULL) { - error = ENOMEM; - } else { - rp = fc_frame_payload_get(fp, sizeof(*rp)); - fc_ns_fill_dns_hdr(lp, &rp->ct, FC_NS_GPN_FT, sizeof(rp->gid)); - rp->gid.fn_fc4_type = FC_TYPE_FCP; - - WARN_ON(!fc_lport_test_ready(lp)); - - fc_frame_setup(fp, FC_RCTL_DD_UNSOL_CTL, FC_TYPE_CT); - sp = lp->tt.exch_seq_send(lp, fp, - fc_ns_gpn_ft_resp, - lp, lp->e_d_tov, - lp->fid, - lp->dns_rp->port_id, - FC_FC_SEQ_INIT | FC_FC_END_SEQ); - } - if (error || sp == NULL) - fcdt_ns_retry(lp); -} - -/* - * Handle error on dNS request. - */ -static void fcdt_ns_error(struct fc_lport *lp, struct fc_frame *fp) -{ - int err = PTR_ERR(fp); - - switch (err) { - case -FC_EX_TIMEOUT: - if (lp->ns_disc_retry_count++ < FC_NS_RETRY_LIMIT) { - fc_ns_gpn_ft_req(lp); - } else { - FC_DBG("err %d - ending\n", err); - fc_ns_disc_done(lp); - } - break; - default: - FC_DBG("err %d - ending\n", err); - fc_ns_disc_done(lp); - break; - } -} - -/** - * fc_ns_gpn_ft_parse - Parse the list of IDs and names resulting from a request - * @lp: Fibre Channel host port instance - * @buf: GPN_FT response buffer - * @len: size of response buffer - */ -static int fc_ns_gpn_ft_parse(struct fc_lport *lp, void *buf, size_t len) -{ - struct fc_gpn_ft_resp *np; - char *bp; - size_t plen; - size_t tlen; - int error = 0; - struct fc_ns_port *dp; - - /* - * Handle partial name record left over from previous call. - */ - bp = buf; - plen = len; - np = (struct fc_gpn_ft_resp *)bp; - tlen = lp->ns_disc_buf_len; - if (tlen) { - WARN_ON(tlen >= sizeof(*np)); - plen = sizeof(*np) - tlen; - WARN_ON(plen <= 0); - WARN_ON(plen >= sizeof(*np)); - if (plen > len) - plen = len; - np = &lp->ns_disc_buf; - memcpy((char *)np + tlen, bp, plen); - - /* - * Set bp so that the loop below will advance it to the - * first valid full name element. - */ - bp -= tlen; - len += tlen; - plen += tlen; - lp->ns_disc_buf_len = (unsigned char) plen; - if (plen == sizeof(*np)) - lp->ns_disc_buf_len = 0; - } - - /* - * Handle full name records, including the one filled from above. - * Normally, np == bp and plen == len, but from the partial case above, - * bp, len describe the overall buffer, and np, plen describe the - * partial buffer, which if would usually be full now. - * After the first time through the loop, things return to "normal". - */ - while (plen >= sizeof(*np)) { - dp = kzalloc(sizeof(*dp), GFP_KERNEL); - if (!dp) - break; - dp->lp = lp; - 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; - error = fc_ns_gnn_id_req(lp, dp); - if (error) - break; - if (np->fp_flags & FC_NS_FID_LAST) { - fc_ns_disc_done(lp); - len = 0; - break; - } - len -= sizeof(*np); - bp += sizeof(*np); - np = (struct fc_gpn_ft_resp *)bp; - plen = len; - } - - /* - * Save any partial record at the end of the buffer for next time. - */ - if (error == 0 && len > 0 && len < sizeof(*np)) { - if (np != &lp->ns_disc_buf) - memcpy(&lp->ns_disc_buf, np, len); - lp->ns_disc_buf_len = (unsigned char) len; - } else { - lp->ns_disc_buf_len = 0; - } - return error; -} - -/* - * Handle retry of memory allocation for remote ports. - */ -static void fc_ns_timeout(struct work_struct *work) -{ - struct fc_lport *lp; - - lp = container_of(work, struct fc_lport, ns_disc_work.work); - - if (lp->ns_disc_pending) - fc_ns_gpn_ft_req(lp); - else - lp->tt.disc_start(lp); -} - -/** - * fc_ns_gpn_ft_resp - Handle a response frame from Get Port Names (GPN_FT) - * @sp: Current sequence of GPN_FT exchange - * @fp: response frame - * @lp_arg: Fibre Channel host port instance - * - * The response may be in multiple frames - */ -static void fc_ns_gpn_ft_resp(struct fc_seq *sp, struct fc_frame *fp, - void *lp_arg) -{ - struct fc_lport *lp = lp_arg; - struct fc_ct_hdr *cp; - struct fc_frame_header *fh; - unsigned int seq_cnt; - void *buf = NULL; - unsigned int len; - int error; - - if (IS_ERR(fp)) { - fcdt_ns_error(lp, fp); - return; - } - - WARN_ON(!fc_frame_is_linear(fp)); /* buffer must be contiguous */ - 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 && - lp->ns_disc_seq_count == 0) { - cp = fc_frame_payload_get(fp, sizeof(*cp)); - if (cp == NULL) { - FC_DBG("GPN_FT response too short, len %d\n", - fr_len(fp)); - } else if (ntohs(cp->ct_cmd) == FC_FS_ACC) { - - /* - * Accepted. Parse response. - */ - buf = cp + 1; - len -= sizeof(*cp); - } else if (ntohs(cp->ct_cmd) == FC_FS_RJT) { - FC_DBG("GPN_FT rejected reason %x exp %x " - "(check zoning)\n", cp->ct_reason, - cp->ct_explan); - fc_ns_disc_done(lp); - } else { - FC_DBG("GPN_FT unexpected response code %x\n", - ntohs(cp->ct_cmd)); - } - } else if (fr_sof(fp) == FC_SOF_N3 && - seq_cnt == lp->ns_disc_seq_count) { - buf = fh + 1; - } else { - FC_DBG("GPN_FT unexpected frame - out of sequence? " - "seq_cnt %x expected %x sof %x eof %x\n", - seq_cnt, lp->ns_disc_seq_count, fr_sof(fp), fr_eof(fp)); - } - if (buf) { - error = fc_ns_gpn_ft_parse(lp, buf, len); - if (error) - fcdt_ns_retry(lp); - else - lp->ns_disc_seq_count++; - } - fc_frame_free(fp); -} - -/* - * Discover the directory information for a single target. - * This could be from an RSCN that reported a change for the target. - */ -static void fc_ns_single(struct fc_lport *lp, struct fc_ns_port *dp) -{ - struct fc_rport *rport; - - if (dp->ids.port_id == lp->fid) - goto out; - - rport = lp->tt.rport_lookup(lp, dp->ids.port_id); - if (rport) { - fc_ns_del_target(lp, rport); - put_device(&rport->dev); /* hold from lookup */ - } - - if (fc_ns_gpn_id_req(lp, dp) != 0) - goto error; - return; -error: - fc_ns_restart(lp); -out: - kfree(dp); -} - -/** - * fc_ns_gpn_id_req - Send Get Port Name by ID (GPN_ID) request - * @lp: Fibre Channel host port instance - * @dp: Temporary discovery port for holding IDs and world wide names - * - * The remote port is held by the caller for us. - */ -static int fc_ns_gpn_id_req(struct fc_lport *lp, struct fc_ns_port *dp) -{ - struct fc_frame *fp; - struct req { - struct fc_ct_hdr ct; - struct fc_ns_fid fid; - } *cp; - int error = 0; - - fp = fc_frame_alloc(lp, sizeof(*cp)); - if (fp == NULL) - return -ENOMEM; - - cp = fc_frame_payload_get(fp, sizeof(*cp)); - fc_ns_fill_dns_hdr(lp, &cp->ct, FC_NS_GPN_ID, sizeof(cp->fid)); - hton24(cp->fid.fp_fid, dp->ids.port_id); - - WARN_ON(!fc_lport_test_ready(lp)); - - fc_frame_setup(fp, FC_RCTL_DD_UNSOL_CTL, FC_TYPE_CT); - if (!lp->tt.exch_seq_send(lp, fp, - fc_ns_gpn_id_resp, - dp, lp->e_d_tov, - lp->fid, - lp->dns_rp->port_id, - FC_FC_SEQ_INIT | FC_FC_END_SEQ)) - error = -ENOMEM; - - return error; -} - -/** - * fc_ns_gpn_id_resp - Handle response to GPN_ID - * @sp: Current sequence of GPN_ID exchange - * @fp: response frame - * @dp_arg: Temporary discovery port for holding IDs and world wide names - */ -static void fc_ns_gpn_id_resp(struct fc_seq *sp, struct fc_frame *fp, - void *dp_arg) -{ - struct fc_ns_port *dp = dp_arg; - struct fc_lport *lp; - struct resp { - struct fc_ct_hdr ct; - __be64 wwn; - } *cp; - unsigned int cmd; - - if (IS_ERR(fp)) { - fc_ns_gpn_id_error(dp, fp); - return; - } - - lp = dp->lp; - WARN_ON(!fc_frame_is_linear(fp)); /* buffer must be contiguous */ - - cp = fc_frame_payload_get(fp, sizeof(cp->ct)); - if (cp == NULL) { - FC_DBG("GPN_ID response too short, len %d\n", fr_len(fp)); - return; - } - cmd = ntohs(cp->ct.ct_cmd); - switch (cmd) { - case FC_FS_ACC: - cp = fc_frame_payload_get(fp, sizeof(*cp)); - if (cp == NULL) { - FC_DBG("GPN_ID response payload too short, len %d\n", - fr_len(fp)); - break; - } - dp->ids.port_name = ntohll(cp->wwn); - fc_ns_gnn_id_req(lp, dp); - break; - case FC_FS_RJT: - fc_ns_restart(lp); - break; - default: - FC_DBG("GPN_ID unexpected CT response cmd %x\n", cmd); - break; - } - fc_frame_free(fp); -} - -/** - * fc_ns_gpn_id_error - Handle error from GPN_ID - * @dp: Temporary discovery port for holding IDs and world wide names - * @fp: response frame - */ -static void fc_ns_gpn_id_error(struct fc_ns_port *dp, struct fc_frame *fp) -{ - struct fc_lport *lp = dp->lp; - - switch (PTR_ERR(fp)) { - case -FC_EX_TIMEOUT: - fc_ns_restart(lp); - break; - case -FC_EX_CLOSED: - default: - break; - } - kfree(dp); -} - -/* - * Setup session to dNS if not already set up. - */ -static void fc_ns_enter_dns(struct fc_lport *lp) -{ - struct fc_rport *rport; - struct fc_rport_libfc_priv *rp; - struct fc_rport_identifiers ids = { - .port_id = FC_FID_DIR_SERV, - .port_name = -1, - .node_name = -1, - .roles = FC_RPORT_ROLE_UNKNOWN, - }; - - if (fc_ns_debug) - FC_DBG("Processing DNS state\n"); - - fc_lport_state_enter(lp, LPORT_ST_DNS); - - if (!lp->dns_rp) { - /* - * Set up remote port to directory server. - */ - - /* - * we are called with the state_lock, but if rport_lookup_create - * needs to create a rport then it will sleep. - */ - fc_lport_unlock(lp); - rport = lp->tt.rport_lookup(lp, ids.port_id); - if (rport == NULL) - rport = lp->tt.rport_create(lp, &ids); - fc_lport_lock(lp); - if (!rport) - goto err; - lp->dns_rp = rport; - } - - rport = lp->dns_rp; - rp = rport->dd_data; - - /* - * If dNS session isn't ready, start its logon. - */ - if (rp->rp_state != RPORT_ST_READY) { - lp->tt.rport_login(rport); - } else { - del_timer(&lp->state_timer); - fc_ns_enter_reg_pn(lp); - } - return; - - /* - * Resource allocation problem (malloc). Try again in 500 mS. - */ -err: - fc_ns_retry(lp); -} - -/* - * Logoff DNS session. - * We should get an event call when the session has been logged out. - */ -static void fc_ns_enter_dns_stop(struct fc_lport *lp) -{ - struct fc_rport *rport = lp->dns_rp; - - if (fc_ns_debug) - FC_DBG("Processing DNS_STOP state\n"); - - fc_lport_state_enter(lp, LPORT_ST_DNS_STOP); - - if (rport) - lp->tt.rport_logout(rport); - else - lp->tt.lport_logout(lp); -} - -/* - * Fill in dNS request header. - */ -static void -fc_lport_fill_dns_hdr(struct fc_lport *lp, struct fc_ct_hdr *ct, - unsigned int op, unsigned int req_size) -{ - memset(ct, 0, sizeof(*ct) + req_size); - ct->ct_rev = FC_CT_REV; - ct->ct_fs_type = FC_FST_DIR; - ct->ct_fs_subtype = FC_NS_SUBTYPE; - ct->ct_cmd = htons(op); -} - -/* - * Register port name with name server. - */ -static void fc_ns_enter_reg_pn(struct fc_lport *lp) -{ - struct fc_frame *fp; - struct req { - struct fc_ct_hdr ct; - struct fc_ns_rn_id rn; - } *req; - - if (fc_ns_debug) - FC_DBG("Processing REG_PN state\n"); - - fc_lport_state_enter(lp, LPORT_ST_REG_PN); - fp = fc_frame_alloc(lp, sizeof(*req)); - if (!fp) { - fc_ns_retry(lp); - return; - } - req = fc_frame_payload_get(fp, sizeof(*req)); - memset(req, 0, sizeof(*req)); - fc_lport_fill_dns_hdr(lp, &req->ct, FC_NS_RPN_ID, sizeof(req->rn)); - hton24(req->rn.fr_fid.fp_fid, lp->fid); - put_unaligned_be64(lp->wwpn, &req->rn.fr_wwn); - fc_frame_setup(fp, FC_RCTL_DD_UNSOL_CTL, FC_TYPE_CT); - if (!lp->tt.exch_seq_send(lp, fp, - fc_ns_resp, lp, - lp->e_d_tov, - lp->fid, - lp->dns_rp->port_id, - FC_FC_SEQ_INIT | FC_FC_END_SEQ)) - fc_ns_retry(lp); -} - -int fc_ns_init(struct fc_lport *lp) -{ - INIT_DELAYED_WORK(&lp->ns_disc_work, fc_ns_timeout); - - if (!lp->tt.disc_start) - lp->tt.disc_start = fc_ns_disc_start; - - if (!lp->tt.disc_recv_req) - lp->tt.disc_recv_req = fc_ns_recv_req; - - if (!lp->tt.dns_register) - lp->tt.dns_register = fc_ns_enter_dns; - - if (!lp->tt.disc_stop) - lp->tt.disc_stop = fc_ns_enter_dns_stop; - - return 0; -} -EXPORT_SYMBOL(fc_ns_init); - -/** - * fc_ns_gnn_id_req - Send Get Node Name by ID (GNN_ID) request - * @lp: Fibre Channel host port instance - * @dp: Temporary discovery port for holding IDs and world wide names - * - * The remote port is held by the caller for us. - */ -static int fc_ns_gnn_id_req(struct fc_lport *lp, struct fc_ns_port *dp) -{ - struct fc_frame *fp; - struct req { - struct fc_ct_hdr ct; - struct fc_ns_fid fid; - } *cp; - int error = 0; - - fp = fc_frame_alloc(lp, sizeof(*cp)); - if (fp == NULL) - return -ENOMEM; - - cp = fc_frame_payload_get(fp, sizeof(*cp)); - fc_ns_fill_dns_hdr(lp, &cp->ct, FC_NS_GNN_ID, sizeof(cp->fid)); - hton24(cp->fid.fp_fid, dp->ids.port_id); - - WARN_ON(!fc_lport_test_ready(lp)); - - fc_frame_setup(fp, FC_RCTL_DD_UNSOL_CTL, FC_TYPE_CT); - if (!lp->tt.exch_seq_send(lp, fp, - fc_ns_gnn_id_resp, - dp, lp->e_d_tov, - lp->fid, - lp->dns_rp->port_id, - FC_FC_SEQ_INIT | FC_FC_END_SEQ)) - error = -ENOMEM; - - return error; -} - -/** - * fc_ns_gnn_id_resp - Handle response to GNN_ID - * @sp: Current sequence of GNN_ID exchange - * @fp: response frame - * @dp_arg: Temporary discovery port for holding IDs and world wide names - */ -static void fc_ns_gnn_id_resp(struct fc_seq *sp, struct fc_frame *fp, - void *dp_arg) -{ - struct fc_ns_port *dp = dp_arg; - struct fc_lport *lp; - struct resp { - struct fc_ct_hdr ct; - __be64 wwn; - } *cp; - unsigned int cmd; - - if (IS_ERR(fp)) { - fc_ns_gnn_id_error(dp, fp); - return; - } - - lp = dp->lp; - WARN_ON(!fc_frame_is_linear(fp)); /* buffer must be contiguous */ - - cp = fc_frame_payload_get(fp, sizeof(cp->ct)); - if (cp == NULL) { - FC_DBG("GNN_ID response too short, len %d\n", fr_len(fp)); - return; - } - cmd = ntohs(cp->ct.ct_cmd); - switch (cmd) { - case FC_FS_ACC: - cp = fc_frame_payload_get(fp, sizeof(*cp)); - if (cp == NULL) { - FC_DBG("GNN_ID response payload too short, len %d\n", - fr_len(fp)); - break; - } - dp->ids.node_name = ntohll(cp->wwn); - fc_ns_new_target(lp, NULL, &dp->ids); - break; - case FC_FS_RJT: - fc_ns_restart(lp); - break; - default: - FC_DBG("GNN_ID unexpected CT response cmd %x\n", cmd); - break; - } - kfree(dp); - fc_frame_free(fp); -} - -/** - * fc_ns_gnn_id_error - Handle error from GNN_ID - * @dp: Temporary discovery port for holding IDs and world wide names - * @fp: response frame - */ -static void fc_ns_gnn_id_error(struct fc_ns_port *dp, struct fc_frame *fp) -{ - struct fc_lport *lp = dp->lp; - - switch (PTR_ERR(fp)) { - case -FC_EX_TIMEOUT: - fc_ns_restart(lp); - break; - case -FC_EX_CLOSED: - default: - break; - } - kfree(dp); -} - diff --git a/drivers/scsi/libfc/fc_rport.c b/drivers/scsi/libfc/fc_rport.c index 6d0c970..107b304 100644 --- a/drivers/scsi/libfc/fc_rport.c +++ b/drivers/scsi/libfc/fc_rport.c @@ -36,14 +36,12 @@ static int fc_rp_debug; -/* - * static functions. - */ -static void fc_rport_enter_start(struct fc_rport *); 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 *, struct fc_seq *, struct fc_frame *); static void fc_rport_recv_prli_req(struct fc_rport *, @@ -53,9 +51,69 @@ static void fc_rport_recv_prlo_req(struct fc_rport *, static void fc_rport_recv_logo_req(struct fc_rport *, 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 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", +}; + +struct fc_rport *fc_rport_dummy_create(struct fc_disc_port *dp) +{ + struct fc_rport *rport; + struct fc_rport_libfc_priv *rdata; + rport = kzalloc(sizeof(*rport) + sizeof(*rdata), GFP_KERNEL); -static struct fc_rport *fc_remote_port_create(struct fc_lport *, - struct fc_rport_identifiers *); + if (!rport) + return NULL; + + rdata = RPORT_TO_PRIV(rport); + + 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; + + mutex_init(&rdata->rp_mutex); + rdata->local_port = dp->lp; + rdata->trans_state = FC_PORTSTATE_ROGUE; + rdata->rp_state = RPORT_ST_INIT; + rdata->event = LPORT_EV_RPORT_NONE; + rdata->flags = FC_RP_FLAGS_REC_SUPPORTED; + rdata->event_callback = NULL; + rdata->e_d_tov = dp->lp->e_d_tov; + rdata->r_a_tov = dp->lp->r_a_tov; + INIT_DELAYED_WORK(&rdata->retry_work, fc_rport_timeout); + + return rport; +} + +void fc_rport_dummy_destroy(struct fc_rport *rport) +{ + kfree(rport); +} + +/** + * 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 + */ +static const char *fc_rport_state(struct fc_rport *rport) +{ + const char *cp; + struct fc_rport_libfc_priv *rdata = rport->dd_data; + + cp = fc_rport_state_names[rdata->rp_state]; + if (!cp) + cp = "Unknown"; + return cp; +} /** * fc_rport_lookup - lookup a remote port by port_id @@ -82,48 +140,18 @@ struct fc_rport *fc_rport_lookup(const struct fc_lport *lp, u32 fid) } /** - * fc_remote_port_create - create a remote port - * @lp: Fibre Channel host port instance - * @ids: remote port identifiers (port_id, port_name, and node_name must be set) + * fc_set_rport_loss_tmo - Set the remote port loss timeout in seconds. + * @rport: Pointer to Fibre Channel remote port structure + * @timeout: timeout in seconds */ -static struct fc_rport *fc_remote_port_create(struct fc_lport *lp, - struct fc_rport_identifiers *ids) +void fc_set_rport_loss_tmo(struct fc_rport *rport, u32 timeout) { - struct fc_rport_libfc_priv *rp; - struct fc_rport *rport; - - rport = fc_remote_port_add(lp->host, 0, ids); - if (!rport) - return NULL; - - rp = rport->dd_data; - rp->local_port = lp; - - /* default value until service parameters are exchanged in PLOGI */ - rport->maxframe_size = FC_MIN_MAX_PAYLOAD; - - spin_lock_init(&rp->rp_lock); - rp->rp_state = RPORT_ST_INIT; - rp->local_port = lp; - rp->e_d_tov = lp->e_d_tov; - rp->r_a_tov = lp->r_a_tov; - rp->flags = FC_RP_FLAGS_REC_SUPPORTED; - INIT_DELAYED_WORK(&rp->retry_work, fc_rport_timeout); - - return rport; -} - -static inline void fc_rport_lock(struct fc_rport *rport) -{ - struct fc_rport_libfc_priv *rp = rport->dd_data; - spin_lock_bh(&rp->rp_lock); -} - -static inline void fc_rport_unlock(struct fc_rport *rport) -{ - struct fc_rport_libfc_priv *rp = rport->dd_data; - spin_unlock_bh(&rp->rp_lock); + if (timeout) + rport->dev_loss_tmo = timeout + 5; + else + rport->dev_loss_tmo = 30; } +EXPORT_SYMBOL(fc_set_rport_loss_tmo); /** * fc_plogi_get_maxframe - Get max payload from the common service parameters @@ -150,12 +178,12 @@ fc_plogi_get_maxframe(struct fc_els_flogi *flp, unsigned int maxval) /** * fc_lport_plogi_fill - Fill in PLOGI command for request - * @lp: Fibre Channel host port instance + * @lport: Fibre Channel host port instance * @plogi: PLOGI command structure to fill (same structure as FLOGI) * @op: either ELS_PLOGI for a localy generated request, or ELS_LS_ACC */ static void -fc_lport_plogi_fill(struct fc_lport *lp, +fc_lport_plogi_fill(struct fc_lport *lport, struct fc_els_flogi *plogi, unsigned int op) { struct fc_els_csp *sp; @@ -163,266 +191,241 @@ fc_lport_plogi_fill(struct fc_lport *lp, memset(plogi, 0, sizeof(*plogi)); plogi->fl_cmd = (u8) op; - put_unaligned_be64(lp->wwpn, &plogi->fl_wwpn); - put_unaligned_be64(lp->wwnn, &plogi->fl_wwnn); + put_unaligned_be64(lport->wwpn, &plogi->fl_wwpn); + put_unaligned_be64(lport->wwnn, &plogi->fl_wwnn); sp = &plogi->fl_csp; sp->sp_hi_ver = 0x20; sp->sp_lo_ver = 0x20; sp->sp_bb_cred = htons(10); /* this gets set by gateway */ - sp->sp_bb_data = htons((u16) lp->mfs); + sp->sp_bb_data = htons((u16) lport->mfs); cp = &plogi->fl_cssp[3 - 1]; /* class 3 parameters */ cp->cp_class = htons(FC_CPC_VALID | FC_CPC_SEQ); if (op != ELS_FLOGI) { sp->sp_features = htons(FC_SP_FT_CIRO); sp->sp_tot_seq = htons(255); /* seq. we accept */ sp->sp_rel_off = htons(0x1f); - sp->sp_e_d_tov = htonl(lp->e_d_tov); + sp->sp_e_d_tov = htonl(lport->e_d_tov); - cp->cp_rdfs = htons((u16) lp->mfs); + cp->cp_rdfs = htons((u16) lport->mfs); cp->cp_con_seq = htons(255); cp->cp_open_seq = 1; } } +/** + * fc_rport_state_enter - Change the rport's state + * @rport: 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, enum fc_rport_state new) { - struct fc_rport_libfc_priv *rp = rport->dd_data; - if (rp->rp_state != new) - rp->retries = 0; - rp->rp_state = new; + struct fc_rport_libfc_priv *rdata = rport->dd_data; + if (rdata->rp_state != new) + rdata->retries = 0; + rdata->rp_state = new; +} + +static void fc_rport_unlock(struct fc_rport *rport) +{ + struct fc_rport_libfc_priv *rdata = rport->dd_data; + enum fc_lport_event event = rdata->event; + struct fc_lport *lport = rdata->local_port; + u32 fid = rport->port_id; + void (*event_callback)(struct fc_lport *, u32, + enum fc_lport_event) = + rdata->event_callback; + + if (event == LPORT_EV_RPORT_CREATED) { + struct fc_rport *new_rport; + struct fc_rport_libfc_priv *new_rdata; + struct fc_rport_identifiers ids; + + ids.port_id = rport->port_id; + ids.roles = rport->roles; + ids.port_name = rport->port_name; + ids.node_name = rport->node_name; + + new_rport = fc_remote_port_add(lport->host, 0, &ids); + if (new_rport) { + /* + * Switch from the dummy 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->event_callback = rdata->event_callback; + 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); + + fc_rport_state_enter(new_rport, RPORT_ST_READY); + fc_remote_port_rolechg(new_rport, rdata->roles); + } else { + FC_DBG("Failed to create the rport for port " + "(%6x).\n", ids.port_id); + event = LPORT_EV_RPORT_FAILED; + } + + mutex_unlock(&rdata->rp_mutex); + fc_rport_dummy_destroy(rport); + rport = new_rport; + rdata = new_rport->dd_data; + } else if ((event == LPORT_EV_RPORT_FAILED) || + (event == LPORT_EV_RPORT_LOGO)) { + if (rdata->trans_state == FC_PORTSTATE_ROGUE) { + mutex_unlock(&rdata->rp_mutex); + fc_rport_dummy_destroy(rport); + } else { + mutex_unlock(&rdata->rp_mutex); + fc_remote_port_delete(rport); + } + } else { + mutex_unlock(&rdata->rp_mutex); + } + + if (event != LPORT_EV_RPORT_NONE && event_callback) { + event_callback(lport, fid, event); + rdata->event = LPORT_EV_RPORT_NONE; + } } /** * fc_rport_login - Start the remote port login state machine * @rport: Fibre Channel 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_login(struct fc_rport *rport) { - struct fc_rport_libfc_priv *rp = rport->dd_data; - struct fc_lport *lp = rp->local_port; + struct fc_rport_libfc_priv *rdata = rport->dd_data; - fc_rport_lock(rport); - if (rp->rp_state == RPORT_ST_INIT) { - fc_rport_unlock(rport); - fc_rport_enter_start(rport); - } else if (rp->rp_state == RPORT_ST_ERROR) { - fc_rport_state_enter(rport, RPORT_ST_INIT); - fc_rport_unlock(rport); - if (fc_rp_debug) - FC_DBG("remote %6x closed\n", rport->port_id); - - if (rport == lp->dns_rp && - lp->state != LPORT_ST_RESET) { - fc_lport_lock(lp); - del_timer(&lp->state_timer); - lp->dns_rp = NULL; - - if (lp->state == LPORT_ST_DNS_STOP) { - fc_lport_unlock(lp); - lp->tt.lport_logout(lp); - } else { - lp->tt.lport_login(lp); - fc_lport_unlock(lp); - } - fc_remote_port_delete(rport); - } - } else - fc_rport_unlock(rport); + mutex_lock(&rdata->rp_mutex); + + if (fc_rp_debug) + FC_DBG("Login to port (%6x)\n", rport->port_id); + + fc_rport_enter_plogi(rport); + + fc_rport_unlock(rport); return 0; } -/* - * Stop the session - log it off. +/** + * fc_rport_logout - Logout of the remote port and delete it + * @rport: Fibre Channel 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_logout(struct fc_rport *rport) { - struct fc_rport_libfc_priv *rp = rport->dd_data; - struct fc_lport *lp = rp->local_port; + struct fc_rport_libfc_priv *rdata = rport->dd_data; - fc_rport_lock(rport); - switch (rp->rp_state) { - case RPORT_ST_PRLI: - case RPORT_ST_RTV: - case RPORT_ST_READY: - fc_rport_enter_logo(rport); - fc_rport_unlock(rport); - break; - default: - fc_rport_state_enter(rport, RPORT_ST_INIT); - fc_rport_unlock(rport); - if (fc_rp_debug) - FC_DBG("remote %6x closed\n", rport->port_id); - if (rport == lp->dns_rp && - lp->state != LPORT_ST_RESET) { - fc_lport_lock(lp); - del_timer(&lp->state_timer); - lp->dns_rp = NULL; - - if (lp->state == LPORT_ST_DNS_STOP) { - fc_lport_unlock(lp); - lp->tt.lport_logout(lp); - } else { - lp->tt.lport_login(lp); - fc_lport_unlock(lp); - } + mutex_lock(&rdata->rp_mutex); - fc_remote_port_delete(rport); - } - break; - } + if (fc_rp_debug) + FC_DBG("Logout of port (%6x)\n", rport->port_id); + + fc_rport_enter_logo(rport); + fc_rport_unlock(rport); return 0; } -/* - * Reset the session - assume it is logged off. Used after fabric logoff. - * The local port code takes care of resetting the exchange manager. +/** + * fc_rport_reset - Reset the remote port + * @rport: Fibre Channel remote port + * + * XXX - This functionality is currently broken + * + * Locking Note: Called without the rport lock held. This + * function will hold the rport lock, call an _enter_* + * function and then unlock the rport. */ void fc_rport_reset(struct fc_rport *rport) { - struct fc_rport_libfc_priv *rp = rport->dd_data; - struct fc_lport *lp; + struct fc_rport_libfc_priv *rdata = rport->dd_data; + + mutex_lock(&rdata->rp_mutex); if (fc_rp_debug) - FC_DBG("sess to %6x reset\n", rport->port_id); - fc_rport_lock(rport); + FC_DBG("Reset port (%6x)\n", rport->port_id); - lp = rp->local_port; - fc_rport_state_enter(rport, RPORT_ST_INIT); - fc_rport_unlock(rport); + fc_rport_enter_plogi(rport); - if (fc_rp_debug) - FC_DBG("remote %6x closed\n", rport->port_id); - if (rport == lp->dns_rp && - lp->state != LPORT_ST_RESET) { - fc_lport_lock(lp); - del_timer(&lp->state_timer); - lp->dns_rp = NULL; - if (lp->state == LPORT_ST_DNS_STOP) { - fc_lport_unlock(lp); - lp->tt.lport_logout(lp); - } else { - lp->tt.lport_login(lp); - fc_lport_unlock(lp); - } - fc_remote_port_delete(rport); - } + fc_rport_unlock(rport); } -/* - * Reset all sessions for a local port session list. +/** + * fc_rport_reset_list - Reset all sessions for a local port session list. + * @lport: The lport whose rports should be reset + * + * Locking Note: TBD */ -void fc_rport_reset_list(struct fc_lport *lp) +void fc_rport_reset_list(struct fc_lport *lport) { - struct Scsi_Host *shost = lp->host; + struct Scsi_Host *shost = lport->host; struct fc_rport *rport; struct fc_rport *next; unsigned long flags; spin_lock_irqsave(shost->host_lock, flags); list_for_each_entry_safe(rport, next, &fc_host_rports(shost), peers) { - lp->tt.rport_reset(rport); + lport->tt.rport_reset(rport); } spin_unlock_irqrestore(shost->host_lock, flags); } -static void fc_rport_enter_start(struct fc_rport *rport) +/** + * fc_rport_enter_ready - The rport is ready + * @rport: Fibre Channel remote port that is ready + * + * Locking Note: The rport lock is expected to be held before calling + * this routine. + */ +static void fc_rport_enter_ready(struct fc_rport *rport) { - struct fc_rport_libfc_priv *rp = rport->dd_data; - struct fc_lport *lp = rp->local_port; + struct fc_rport_libfc_priv *rdata = rport->dd_data; - /* - * If the local port is already logged on, advance to next state. - * Otherwise the local port will be logged on by fc_rport_unlock(). - */ - fc_rport_state_enter(rport, RPORT_ST_STARTED); + fc_rport_state_enter(rport, RPORT_ST_READY); - if (rport == lp->dns_rp || fc_lport_test_ready(lp)) - fc_rport_enter_plogi(rport); -} + if (fc_rp_debug) + FC_DBG("Port (%6x) is Ready\n", rport->port_id); -/* - * Handle exchange reject or retry exhaustion in various states. - */ -static void fc_rport_reject(struct fc_rport *rport) -{ - struct fc_rport_libfc_priv *rp = rport->dd_data; - struct fc_lport *lp = rp->local_port; - switch (rp->rp_state) { - case RPORT_ST_PLOGI: - case RPORT_ST_PRLI: - fc_rport_state_enter(rport, RPORT_ST_ERROR); - if (rport == lp->dns_rp && - lp->state != LPORT_ST_RESET) { - fc_lport_lock(lp); - del_timer(&lp->state_timer); - lp->dns_rp = NULL; - if (lp->state == LPORT_ST_DNS_STOP) { - fc_lport_unlock(lp); - lp->tt.lport_logout(lp); - } else { - lp->tt.lport_login(lp); - fc_lport_unlock(lp); - } - fc_remote_port_delete(rport); - } - break; - case RPORT_ST_RTV: - fc_rport_state_enter(rport, RPORT_ST_READY); - if (fc_rp_debug) - FC_DBG("remote %6x ready\n", rport->port_id); - if (rport == lp->dns_rp && - lp->state == LPORT_ST_DNS) { - fc_lport_lock(lp); - del_timer(&lp->state_timer); - lp->tt.dns_register(lp); - fc_lport_unlock(lp); - } - break; - case RPORT_ST_LOGO: - fc_rport_state_enter(rport, RPORT_ST_INIT); - if (fc_rp_debug) - FC_DBG("remote %6x closed\n", rport->port_id); - if (rport == lp->dns_rp && - lp->state != LPORT_ST_RESET) { - fc_lport_lock(lp); - del_timer(&lp->state_timer); - lp->dns_rp = NULL; - if (lp->state == LPORT_ST_DNS_STOP) { - fc_lport_unlock(lp); - lp->tt.lport_logout(lp); - } else { - lp->tt.lport_login(lp); - fc_lport_unlock(lp); - } - fc_remote_port_delete(rport); - } - break; - case RPORT_ST_NONE: - case RPORT_ST_READY: - case RPORT_ST_ERROR: - case RPORT_ST_PLOGI_RECV: - case RPORT_ST_STARTED: - case RPORT_ST_INIT: - BUG(); - break; - } - return; + rdata->event = LPORT_EV_RPORT_CREATED; } -/* - * Timeout handler for retrying after allocation failures or exchange timeout. +/** + * fc_rport_timeout - Handler for the retry_work timer. + * @work: The work struct of the fc_rport_libfc_priv + * + * Locking Note: Called without the rport lock held. This + * function will hold the rport lock, call an _enter_* + * function and then unlock the rport. */ static void fc_rport_timeout(struct work_struct *work) { - struct fc_rport_libfc_priv *rp = + struct fc_rport_libfc_priv *rdata = container_of(work, struct fc_rport_libfc_priv, retry_work.work); - struct fc_rport *rport = (((void *)rp) - sizeof(struct fc_rport)); + struct fc_rport *rport = PRIV_TO_RPORT(rdata); - switch (rp->rp_state) { + mutex_lock(&rdata->rp_mutex); + + switch (rdata->rp_state) { case RPORT_ST_PLOGI: fc_rport_enter_plogi(rport); break; @@ -436,149 +439,178 @@ static void fc_rport_timeout(struct work_struct *work) fc_rport_enter_logo(rport); break; case RPORT_ST_READY: - case RPORT_ST_ERROR: case RPORT_ST_INIT: break; case RPORT_ST_NONE: - case RPORT_ST_PLOGI_RECV: - case RPORT_ST_STARTED: BUG(); break; } put_device(&rport->dev); -} - -/* - * Handle retry for allocation failure via timeout. - */ -static void fc_rport_retry(struct fc_rport *rport) -{ - struct fc_rport_libfc_priv *rp = rport->dd_data; - struct fc_lport *lp = rp->local_port; - if (rp->retries < lp->max_retry_count) { - rp->retries++; - get_device(&rport->dev); - schedule_delayed_work(&rp->retry_work, - msecs_to_jiffies(rp->e_d_tov)); - } else { - FC_DBG("sess %6x alloc failure in state %d, " - "retries exhausted\n", - rport->port_id, rp->rp_state); - fc_rport_reject(rport); - } + fc_rport_unlock(rport); } -/* - * Handle error from a sequence issued by the rport state machine. +/** + * fc_rport_error - Handler for any errors + * @rport: The fc_rport object + * @fp: The frame pointer + * + * If the error was caused by a resource allocation failure + * then wait for half a second and retry, otherwise retry + * immediately. + * + * 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) { - struct fc_rport_libfc_priv *rp = rport->dd_data; - fc_rport_lock(rport); + struct fc_rport_libfc_priv *rdata = rport->dd_data; + unsigned long delay = 0; + if (fc_rp_debug) - FC_DBG("state %d error %ld retries %d\n", - rp->rp_state, PTR_ERR(fp), rp->retries); + FC_DBG("Error %ld in state %s, retries %d\n", + PTR_ERR(fp), fc_rport_state(rport), rdata->retries); - if (PTR_ERR(fp) == -FC_EX_TIMEOUT && - rp->retries++ >= rp->local_port->max_retry_count) { + if (rdata->retries < rdata->local_port->max_retry_count) { + rdata->retries++; + if (!fp) + delay = msecs_to_jiffies(500); get_device(&rport->dev); - schedule_delayed_work(&rp->retry_work, 0); - } else - fc_rport_reject(rport); + schedule_delayed_work(&rdata->retry_work, delay); + } else { + switch (rdata->rp_state) { + case RPORT_ST_PLOGI: + case RPORT_ST_PRLI: + case RPORT_ST_LOGO: + if (fc_rp_debug) + FC_DBG("Remote port (%6x) closed.\n", + rport->port_id); - fc_rport_unlock(rport); + fc_remote_port_delete(rport); + + rdata->event = LPORT_EV_RPORT_FAILED; + break; + case RPORT_ST_RTV: + fc_rport_enter_ready(rport); + break; + case RPORT_ST_NONE: + case RPORT_ST_READY: + case RPORT_ST_INIT: + BUG(); + break; + } + } } /** - * fc_rport_plpogi_recv_resp - Handle incoming ELS PLOGI response + * 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 + * + * 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) { - struct fc_els_ls_rjt *rjp; + struct fc_rport *rport = rp_arg; + struct fc_rport_libfc_priv *rdata = rport->dd_data; + struct fc_lport *lport = rdata->local_port; struct fc_els_flogi *plp; - u64 wwpn, wwnn; unsigned int tov; u16 csp_seq; u16 cssp_seq; u8 op; - struct fc_rport *rport = rp_arg; - struct fc_rport_libfc_priv *rp = rport->dd_data; - if (!IS_ERR(fp)) { - op = fc_frame_payload_op(fp); - fc_rport_lock(rport); - if (op == ELS_LS_ACC && - (plp = fc_frame_payload_get(fp, sizeof(*plp))) != NULL) { - wwpn = get_unaligned_be64(&plp->fl_wwpn); - wwnn = get_unaligned_be64(&plp->fl_wwnn); + mutex_lock(&rdata->rp_mutex); - fc_rport_set_name(rport, wwpn, wwnn); - tov = ntohl(plp->fl_csp.sp_e_d_tov); - if (ntohs(plp->fl_csp.sp_features) & FC_SP_FT_EDTR) - tov /= 1000; - if (tov > rp->e_d_tov) - rp->e_d_tov = tov; - csp_seq = ntohs(plp->fl_csp.sp_tot_seq); - cssp_seq = ntohs(plp->fl_cssp[3 - 1].cp_con_seq); - if (cssp_seq < csp_seq) - csp_seq = cssp_seq; - rp->max_seq = csp_seq; - rport->maxframe_size = - fc_plogi_get_maxframe(plp, rp->local_port->mfs); - if (rp->rp_state == RPORT_ST_PLOGI) - fc_rport_enter_prli(rport); - } else { - if (fc_rp_debug) - FC_DBG("bad PLOGI response\n"); - - rjp = fc_frame_payload_get(fp, sizeof(*rjp)); - if (op == ELS_LS_RJT && rjp != NULL && - rjp->er_reason == ELS_RJT_INPROG) - fc_rport_retry(rport); /* try again */ - else - fc_rport_reject(rport); /* error */ - } - fc_rport_unlock(rport); - fc_frame_free(fp); - } else { + if (fc_rp_debug) + FC_DBG("Received a PLOGI response\n"); + + if (rdata->rp_state != RPORT_ST_PLOGI) { + FC_DBG("Received a PLOGI response, but in state %s\n", + fc_rport_state(rport)); + goto out; + } + + if (IS_ERR(fp)) { fc_rport_error(rport, fp); + goto out; } + + op = fc_frame_payload_op(fp); + if (op == ELS_LS_ACC && + (plp = fc_frame_payload_get(fp, sizeof(*plp))) != NULL) { + tov = ntohl(plp->fl_csp.sp_e_d_tov); + if (ntohs(plp->fl_csp.sp_features) & FC_SP_FT_EDTR) + tov /= 1000; + if (tov > rdata->e_d_tov) + rdata->e_d_tov = tov; + csp_seq = ntohs(plp->fl_csp.sp_tot_seq); + cssp_seq = ntohs(plp->fl_cssp[3 - 1].cp_con_seq); + 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); + } else + fc_rport_error(rport, fp); + +out: + fc_rport_unlock(rport); + fc_frame_free(fp); } /** * fc_rport_enter_plogi - Send Port Login (PLOGI) request to peer * @rport: Fibre Channel remote port to send PLOGI to + * + * Locking Note: The rport lock is expected to be held before calling + * this routine. */ static void fc_rport_enter_plogi(struct fc_rport *rport) { + struct fc_rport_libfc_priv *rdata = rport->dd_data; + struct fc_lport *lport = rdata->local_port; struct fc_frame *fp; struct fc_els_flogi *plogi; - struct fc_lport *lp; - struct fc_rport_libfc_priv *rp = rport->dd_data; - lp = rp->local_port; + if (fc_rp_debug) + FC_DBG("Port (%6x) entered PLOGI state from %s state\n", + rport->port_id, fc_rport_state(rport)); + fc_rport_state_enter(rport, RPORT_ST_PLOGI); + rport->maxframe_size = FC_MIN_MAX_PAYLOAD; - fp = fc_frame_alloc(lp, sizeof(*plogi)); - if (!fp) - return fc_rport_retry(rport); + fp = fc_frame_alloc(lport, sizeof(*plogi)); + if (!fp) { + fc_rport_error(rport, fp); + return; + } + plogi = fc_frame_payload_get(fp, sizeof(*plogi)); - WARN_ON(!plogi); - fc_lport_plogi_fill(rp->local_port, plogi, ELS_PLOGI); - rp->e_d_tov = lp->e_d_tov; + fc_lport_plogi_fill(rdata->local_port, plogi, ELS_PLOGI); + rdata->e_d_tov = lport->e_d_tov; fc_frame_setup(fp, FC_RCTL_ELS_REQ, FC_TYPE_ELS); - if (!lp->tt.exch_seq_send(lp, fp, - fc_rport_plogi_resp, - rport, lp->e_d_tov, - rp->local_port->fid, - rport->port_id, - FC_FC_SEQ_INIT | FC_FC_END_SEQ)) - fc_rport_retry(rport); + + if (!lport->tt.exch_seq_send(lport, fp, + fc_rport_plogi_resp, NULL, + rport, lport->e_d_tov, + rdata->local_port->fid, + rport->port_id, + FC_FC_SEQ_INIT | FC_FC_END_SEQ)) + fc_rport_error(rport, fp); } /** @@ -586,13 +618,16 @@ static void fc_rport_enter_plogi(struct fc_rport *rport) * @sp: current sequence in the PRLI exchange * @fp: response frame * @rp_arg: Fibre Channel remote port + * + * 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) { struct fc_rport *rport = rp_arg; - struct fc_rport_libfc_priv *rp = rport->dd_data; - struct fc_lport *lp = rp->local_port; + struct fc_rport_libfc_priv *rdata = rport->dd_data; struct { struct fc_els_prli prli; struct fc_els_spp spp; @@ -601,19 +636,29 @@ static void fc_rport_prli_resp(struct fc_seq *sp, struct fc_frame *fp, u32 fcp_parm = 0; u8 op; + mutex_lock(&rdata->rp_mutex); + + if (fc_rp_debug) + FC_DBG("Received a PRLI response\n"); + + if (rdata->rp_state != RPORT_ST_PRLI) { + FC_DBG("Received a PRLI response, but in state %s\n", + fc_rport_state(rport)); + goto out; + } + if (IS_ERR(fp)) { fc_rport_error(rport, fp); - return; + goto out; } - fc_rport_lock(rport); op = fc_frame_payload_op(fp); if (op == ELS_LS_ACC) { pp = fc_frame_payload_get(fp, sizeof(*pp)); if (pp && pp->prli.prli_spp_len >= sizeof(pp->spp)) { fcp_parm = ntohl(pp->spp.spp_params); if (fcp_parm & FCP_SPPF_RETRY) - rp->flags |= FC_RP_FLAGS_RETRY; + rdata->flags |= FC_RP_FLAGS_RETRY; } rport->supported_classes = FC_COS_CLASS3; @@ -622,28 +667,17 @@ static void fc_rport_prli_resp(struct fc_seq *sp, struct fc_frame *fp, if (fcp_parm & FCP_SPPF_TARG_FCN) roles |= FC_RPORT_ROLE_FCP_TARGET; + rdata->roles = roles; fc_rport_enter_rtv(rport); - fc_rport_unlock(rport); - fc_remote_port_rolechg(rport, roles); + } else { - FC_DBG("bad ELS response\n"); - fc_rport_state_enter(rport, RPORT_ST_ERROR); - fc_rport_unlock(rport); - if (rport == lp->dns_rp && lp->state != LPORT_ST_RESET) { - fc_lport_lock(lp); - del_timer(&lp->state_timer); - lp->dns_rp = NULL; - if (lp->state == LPORT_ST_DNS_STOP) { - fc_lport_unlock(lp); - lp->tt.lport_logout(lp); - } else { - lp->tt.lport_login(lp); - fc_lport_unlock(lp); - } - fc_remote_port_delete(rport); - } + FC_DBG("Bad ELS response\n"); + rdata->event = LPORT_EV_RPORT_FAILED; + fc_remote_port_delete(rport); } +out: + fc_rport_unlock(rport); fc_frame_free(fp); } @@ -652,101 +686,94 @@ static void fc_rport_prli_resp(struct fc_seq *sp, struct fc_frame *fp, * @sp: current sequence in the LOGO exchange * @fp: response frame * @rp_arg: Fibre Channel remote port + * + * 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) { struct fc_rport *rport = rp_arg; - struct fc_rport_libfc_priv *rp = rport->dd_data; - struct fc_lport *lp = rp->local_port; + struct fc_rport_libfc_priv *rdata = rport->dd_data; u8 op; + mutex_lock(&rdata->rp_mutex); + + if (fc_rp_debug) + FC_DBG("Received a LOGO response\n"); + + if (rdata->rp_state != RPORT_ST_LOGO) { + FC_DBG("Received a LOGO response, but in state %s\n", + fc_rport_state(rport)); + goto out; + } + if (IS_ERR(fp)) { fc_rport_error(rport, fp); - return; + goto out; } - fc_rport_lock(rport); op = fc_frame_payload_op(fp); if (op == ELS_LS_ACC) { fc_rport_enter_rtv(rport); - fc_rport_unlock(rport); + } else { - FC_DBG("bad ELS response\n"); - fc_rport_state_enter(rport, RPORT_ST_ERROR); - fc_rport_unlock(rport); - if (rport == lp->dns_rp && lp->state != LPORT_ST_RESET) { - fc_lport_lock(lp); - del_timer(&lp->state_timer); - lp->dns_rp = NULL; - if (lp->state == LPORT_ST_DNS_STOP) { - fc_lport_unlock(lp); - lp->tt.lport_logout(lp); - } else { - lp->tt.lport_login(lp); - fc_lport_unlock(lp); - } - fc_remote_port_delete(rport); - } + FC_DBG("Bad ELS response\n"); + rdata->event = LPORT_EV_RPORT_LOGO; + fc_remote_port_delete(rport); } +out: + fc_rport_unlock(rport); fc_frame_free(fp); } /** * fc_rport_enter_prli - Send Process Login (PRLI) request to peer * @rport: Fibre Channel remote port to send PRLI to + * + * Locking Note: The rport lock is expected to be held before calling + * this routine. */ static void fc_rport_enter_prli(struct fc_rport *rport) { + struct fc_rport_libfc_priv *rdata = rport->dd_data; + struct fc_lport *lport = rdata->local_port; struct { struct fc_els_prli prli; struct fc_els_spp spp; } *pp; struct fc_frame *fp; - struct fc_rport_libfc_priv *rp = rport->dd_data; - struct fc_lport *lp = rp->local_port; + + if (fc_rp_debug) + FC_DBG("Port (%6x) entered PRLI state from %s state\n", + rport->port_id, fc_rport_state(rport)); fc_rport_state_enter(rport, RPORT_ST_PRLI); - /* - * Special case if session is for name server or any other - * well-known address: Skip the PRLI step. - * This should be made more general, possibly moved to the FCP layer. - */ - if (rport->port_id >= FC_FID_DOM_MGR) { - fc_rport_state_enter(rport, RPORT_ST_READY); - if (fc_rp_debug) - FC_DBG("remote %6x ready\n", rport->port_id); - if (rport == lp->dns_rp && - lp->state == LPORT_ST_DNS) { - fc_lport_lock(lp); - del_timer(&lp->state_timer); - lp->tt.dns_register(lp); - fc_lport_unlock(lp); - } + fp = fc_frame_alloc(lport, sizeof(*pp)); + if (!fp) { + fc_rport_error(rport, fp); return; } - fp = fc_frame_alloc(lp, sizeof(*pp)); - if (!fp) - return fc_rport_retry(rport); + pp = fc_frame_payload_get(fp, sizeof(*pp)); - WARN_ON(!pp); memset(pp, 0, sizeof(*pp)); pp->prli.prli_cmd = ELS_PRLI; pp->prli.prli_spp_len = sizeof(struct fc_els_spp); pp->prli.prli_len = htons(sizeof(*pp)); pp->spp.spp_type = FC_TYPE_FCP; pp->spp.spp_flags = FC_SPP_EST_IMG_PAIR; - pp->spp.spp_params = htonl(rp->local_port->service_params); + pp->spp.spp_params = htonl(lport->service_params); fc_frame_setup(fp, FC_RCTL_ELS_REQ, FC_TYPE_ELS); - if (!lp->tt.exch_seq_send(lp, fp, - fc_rport_prli_resp, - rport, lp->e_d_tov, - rp->local_port->fid, - rport->port_id, - FC_FC_SEQ_INIT | FC_FC_END_SEQ)) - fc_rport_retry(rport); + + if (!lport->tt.exch_seq_send(lport, fp, + fc_rport_prli_resp, NULL, + rport, lport->e_d_tov, + lport->fid, rport->port_id, + FC_FC_SEQ_INIT | FC_FC_END_SEQ)) + fc_rport_error(rport, fp); } /** @@ -756,21 +783,34 @@ static void fc_rport_enter_prli(struct fc_rport *rport) * @rp_arg: Fibre Channel remote port * * Many targets don't seem to support this. + * + * 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_rtv_resp(struct fc_seq *sp, struct fc_frame *fp, void *rp_arg) { struct fc_rport *rport = rp_arg; - struct fc_rport_libfc_priv *rp = rport->dd_data; - struct fc_lport *lp = rp->local_port; + struct fc_rport_libfc_priv *rdata = rport->dd_data; u8 op; + mutex_lock(&rdata->rp_mutex); + + if (fc_rp_debug) + FC_DBG("Received a RTV response\n"); + + if (rdata->rp_state != RPORT_ST_RTV) { + FC_DBG("Received a RTV response, but in state %s\n", + fc_rport_state(rport)); + goto out; + } + if (IS_ERR(fp)) { fc_rport_error(rport, fp); - return; + goto out; } - fc_rport_lock(rport); op = fc_frame_payload_op(fp); if (op == ELS_LS_ACC) { struct fc_els_rtv_acc *rtv; @@ -783,107 +823,126 @@ static void fc_rport_rtv_resp(struct fc_seq *sp, struct fc_frame *fp, tov = ntohl(rtv->rtv_r_a_tov); if (tov == 0) tov = 1; - rp->r_a_tov = tov; + rdata->r_a_tov = tov; tov = ntohl(rtv->rtv_e_d_tov); if (toq & FC_ELS_RTV_EDRES) tov /= 1000000; if (tov == 0) tov = 1; - rp->e_d_tov = tov; + rdata->e_d_tov = tov; } } - fc_rport_state_enter(rport, RPORT_ST_READY); + + fc_rport_enter_ready(rport); + +out: fc_rport_unlock(rport); - if (fc_rp_debug) - FC_DBG("remote %6x ready\n", rport->port_id); - if (rport == lp->dns_rp && - lp->state == LPORT_ST_DNS) { - fc_lport_lock(lp); - del_timer(&lp->state_timer); - lp->tt.dns_register(lp); - fc_lport_unlock(lp); - } fc_frame_free(fp); } /** * fc_rport_enter_rtv - Send Request Timeout Value (RTV) request to peer * @rport: Fibre Channel remote port to send RTV to + * + * Locking Note: The rport lock is expected to be held before calling + * this routine. */ static void fc_rport_enter_rtv(struct fc_rport *rport) { struct fc_els_rtv *rtv; struct fc_frame *fp; - struct fc_lport *lp; - struct fc_rport_libfc_priv *rp = rport->dd_data; + struct fc_rport_libfc_priv *rdata = rport->dd_data; + struct fc_lport *lport = rdata->local_port; + + if (fc_rp_debug) + FC_DBG("Port (%6x) entered RTV state from %s state\n", + rport->port_id, fc_rport_state(rport)); - lp = rp->local_port; fc_rport_state_enter(rport, RPORT_ST_RTV); - fp = fc_frame_alloc(lp, sizeof(*rtv)); - if (!fp) - return fc_rport_retry(rport); + fp = fc_frame_alloc(lport, sizeof(*rtv)); + if (!fp) { + fc_rport_error(rport, fp); + return; + } + rtv = fc_frame_payload_get(fp, sizeof(*rtv)); - WARN_ON(!rtv); memset(rtv, 0, sizeof(*rtv)); rtv->rtv_cmd = ELS_RTV; fc_frame_setup(fp, FC_RCTL_ELS_REQ, FC_TYPE_ELS); - if (!lp->tt.exch_seq_send(lp, fp, - fc_rport_rtv_resp, - rport, lp->e_d_tov, - rp->local_port->fid, - rport->port_id, - FC_FC_SEQ_INIT | FC_FC_END_SEQ)) - fc_rport_retry(rport); + + if (!lport->tt.exch_seq_send(lport, fp, + fc_rport_rtv_resp, NULL, + rport, lport->e_d_tov, + lport->fid, rport->port_id, + FC_FC_SEQ_INIT | FC_FC_END_SEQ)) + fc_rport_error(rport, fp); } /** * fc_rport_enter_logo - Send Logout (LOGO) request to peer * @rport: Fibre Channel remote port to send LOGO to + * + * Locking Note: The rport lock is expected to be held before calling + * this routine. */ static void fc_rport_enter_logo(struct fc_rport *rport) { + struct fc_rport_libfc_priv *rdata = rport->dd_data; + struct fc_lport *lport = rdata->local_port; struct fc_frame *fp; struct fc_els_logo *logo; - struct fc_lport *lp; - struct fc_rport_libfc_priv *rp = rport->dd_data; + + if (fc_rp_debug) + FC_DBG("Port (%6x) entered LOGO state from %s state\n", + rport->port_id, fc_rport_state(rport)); fc_rport_state_enter(rport, RPORT_ST_LOGO); - lp = rp->local_port; - fp = fc_frame_alloc(lp, sizeof(*logo)); - if (!fp) - return fc_rport_retry(rport); + fp = fc_frame_alloc(lport, sizeof(*logo)); + if (!fp) { + fc_rport_error(rport, fp); + return; + } + logo = fc_frame_payload_get(fp, sizeof(*logo)); memset(logo, 0, sizeof(*logo)); logo->fl_cmd = ELS_LOGO; - hton24(logo->fl_n_port_id, lp->fid); - logo->fl_n_port_wwn = htonll(lp->wwpn); - + hton24(logo->fl_n_port_id, lport->fid); + logo->fl_n_port_wwn = htonll(lport->wwpn); fc_frame_setup(fp, FC_RCTL_ELS_REQ, FC_TYPE_ELS); - if (!lp->tt.exch_seq_send(lp, fp, - fc_rport_logo_resp, - rport, lp->e_d_tov, - rp->local_port->fid, - rport->port_id, - FC_FC_SEQ_INIT | FC_FC_END_SEQ)) - fc_rport_retry(rport); + + if (!lport->tt.exch_seq_send(lport, fp, + fc_rport_logo_resp, NULL, + rport, lport->e_d_tov, + lport->fid, rport->port_id, + FC_FC_SEQ_INIT | FC_FC_END_SEQ)) + fc_rport_error(rport, fp); } -/* - * Handle a request received by the exchange manager for the session. - * This may be an entirely new session, or a PLOGI or LOGO for an existing one. - * This will free the frame. + +/** + * fc_rport_recv_req - Receive a request from a rport + * @sp: current sequence in the PLOGI exchange + * @fp: response frame + * @rp_arg: Fibre Channel 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. */ void fc_rport_recv_req(struct fc_seq *sp, struct fc_frame *fp, struct fc_rport *rport) { - struct fc_rport_libfc_priv *rp = rport->dd_data; + struct fc_rport_libfc_priv *rdata = rport->dd_data; + struct fc_lport *lport = rdata->local_port; + struct fc_frame_header *fh; - struct fc_lport *lp = rp->local_port; 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; @@ -907,21 +966,21 @@ void fc_rport_recv_req(struct fc_seq *sp, struct fc_frame *fp, break; case ELS_RRQ: els_data.fp = fp; - lp->tt.seq_els_rsp_send(sp, ELS_RRQ, &els_data); + lport->tt.seq_els_rsp_send(sp, ELS_RRQ, &els_data); break; case ELS_REC: els_data.fp = fp; - lp->tt.seq_els_rsp_send(sp, ELS_REC, &els_data); + lport->tt.seq_els_rsp_send(sp, ELS_REC, &els_data); break; default: els_data.reason = ELS_RJT_UNSUP; - lp->tt.seq_els_rsp_send(sp, ELS_LS_RJT, &els_data); - fc_frame_free(fp); + lport->tt.seq_els_rsp_send(sp, ELS_LS_RJT, &els_data); break; } - } else { - fc_frame_free(fp); } + + fc_rport_unlock(rport); + fc_frame_free(fp); } /** @@ -929,14 +988,18 @@ void fc_rport_recv_req(struct fc_seq *sp, struct fc_frame *fp, * @rport: Fibre Channel remote port that initiated PLOGI * @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. */ static void fc_rport_recv_plogi_req(struct fc_rport *rport, struct fc_seq *sp, struct fc_frame *rx_fp) { - struct fc_rport_libfc_priv *rp = rport->dd_data; + struct fc_rport_libfc_priv *rdata = rport->dd_data; + struct fc_lport *lport = rdata->local_port; struct fc_frame *fp = rx_fp; + struct fc_frame_header *fh; - struct fc_lport *lp; struct fc_els_flogi *pl; struct fc_seq_els_data rjt_data; u32 sid; @@ -944,9 +1007,15 @@ static void fc_rport_recv_plogi_req(struct fc_rport *rport, u64 wwnn; enum fc_els_rjt_reason reject = 0; u32 f_ctl; - rjt_data.fp = NULL; + fh = fc_frame_header_get(fp); + + if (fc_rp_debug) + FC_DBG("Received PLOGI request from port (%6x) " + "while in state %s\n", ntoh24(fh->fh_s_id), + fc_rport_state(rport)); + sid = ntoh24(fh->fh_s_id); pl = fc_frame_payload_get(fp, sizeof(*pl)); if (!pl) { @@ -958,8 +1027,6 @@ static void fc_rport_recv_plogi_req(struct fc_rport *rport, } wwpn = get_unaligned_be64(&pl->fl_wwpn); wwnn = get_unaligned_be64(&pl->fl_wwnn); - fc_rport_lock(rport); - lp = rp->local_port; /* * If the session was just created, possibly due to the incoming PLOGI, @@ -972,63 +1039,50 @@ static void fc_rport_recv_plogi_req(struct fc_rport *rport, * XXX TBD: If the session was ready before, the PLOGI should result in * all outstanding exchanges being reset. */ - switch (rp->rp_state) { + switch (rdata->rp_state) { case RPORT_ST_INIT: if (fc_rp_debug) FC_DBG("incoming PLOGI from %6x wwpn %llx state INIT " "- reject\n", sid, wwpn); reject = ELS_RJT_UNSUP; break; - case RPORT_ST_STARTED: - /* - * we'll only accept a login if the port name - * matches or was unknown. - */ - if (rport->port_name != -1 && - rport->port_name != wwpn) { - FC_DBG("incoming PLOGI from name %llx expected %llx\n", - wwpn, rport->port_name); - reject = ELS_RJT_UNAB; - } - break; case RPORT_ST_PLOGI: if (fc_rp_debug) FC_DBG("incoming PLOGI from %x in PLOGI state %d\n", - sid, rp->rp_state); - if (wwpn < lp->wwpn) + sid, rdata->rp_state); + if (wwpn < lport->wwpn) reject = ELS_RJT_INPROG; break; case RPORT_ST_PRLI: - case RPORT_ST_ERROR: case RPORT_ST_READY: if (fc_rp_debug) FC_DBG("incoming PLOGI from %x in logged-in state %d " - "- ignored for now\n", sid, rp->rp_state); + "- ignored for now\n", sid, rdata->rp_state); /* XXX TBD - should reset */ break; case RPORT_ST_NONE: default: if (fc_rp_debug) FC_DBG("incoming PLOGI from %x in unexpected " - "state %d\n", sid, rp->rp_state); + "state %d\n", sid, rdata->rp_state); break; } if (reject) { rjt_data.reason = reject; rjt_data.explan = ELS_EXPL_NONE; - lp->tt.seq_els_rsp_send(sp, ELS_LS_RJT, &rjt_data); + lport->tt.seq_els_rsp_send(sp, ELS_LS_RJT, &rjt_data); fc_frame_free(fp); } else { - fp = fc_frame_alloc(lp, sizeof(*pl)); + 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; - lp->tt.seq_els_rsp_send(sp, ELS_LS_RJT, &rjt_data); + lport->tt.seq_els_rsp_send(sp, ELS_LS_RJT, &rjt_data); fc_frame_free(fp); } else { - sp = lp->tt.seq_start_next(sp); + sp = lport->tt.seq_start_next(sp); WARN_ON(!sp); fc_rport_set_name(rport, wwpn, wwnn); @@ -1036,11 +1090,11 @@ static void fc_rport_recv_plogi_req(struct fc_rport *rport, * Get session payload size from incoming PLOGI. */ rport->maxframe_size = - fc_plogi_get_maxframe(pl, lp->mfs); + fc_plogi_get_maxframe(pl, lport->mfs); fc_frame_free(rx_fp); pl = fc_frame_payload_get(fp, sizeof(*pl)); WARN_ON(!pl); - fc_lport_plogi_fill(lp, pl, ELS_LS_ACC); + fc_lport_plogi_fill(lport, pl, ELS_LS_ACC); /* * Send LS_ACC. If this fails, @@ -1048,15 +1102,11 @@ static void fc_rport_recv_plogi_req(struct fc_rport *rport, */ f_ctl = FC_FC_SEQ_INIT | FC_FC_LAST_SEQ | FC_FC_END_SEQ; fc_frame_setup(fp, FC_RCTL_ELS_REP, FC_TYPE_ELS); - lp->tt.seq_send(lp, sp, fp, f_ctl); - if (rp->rp_state == RPORT_ST_PLOGI) + lport->tt.seq_send(lport, sp, fp, f_ctl); + if (rdata->rp_state == RPORT_ST_PLOGI) fc_rport_enter_prli(rport); - else - fc_rport_state_enter(rport, - RPORT_ST_PLOGI_RECV); } } - fc_rport_unlock(rport); } /** @@ -1064,14 +1114,18 @@ static void fc_rport_recv_plogi_req(struct fc_rport *rport, * @rport: Fibre Channel remote port that initiated PRLI * @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, struct fc_seq *sp, struct fc_frame *rx_fp) { - struct fc_rport_libfc_priv *rp = rport->dd_data; + struct fc_rport_libfc_priv *rdata = rport->dd_data; + struct fc_lport *lport = rdata->local_port; + struct fc_frame *fp; struct fc_frame_header *fh; - struct fc_lport *lp; struct { struct fc_els_prli prli; struct fc_els_spp spp; @@ -1087,12 +1141,16 @@ static void fc_rport_recv_prli_req(struct fc_rport *rport, u32 f_ctl; u32 fcp_parm; u32 roles = FC_RPORT_ROLE_UNKNOWN; - rjt_data.fp = NULL; + fh = fc_frame_header_get(rx_fp); - lp = rp->local_port; - switch (rp->rp_state) { - case RPORT_ST_PLOGI_RECV: + + if (fc_rp_debug) + FC_DBG("Received PRLI request from port (%6x) " + "while in state %s\n", ntoh24(fh->fh_s_id), + fc_rport_state(rport)); + + switch (rdata->rp_state) { case RPORT_ST_PRLI: case RPORT_ST_READY: reason = ELS_RJT_NONE; @@ -1122,12 +1180,12 @@ static void fc_rport_recv_prli_req(struct fc_rport *rport, rspp = &pp->spp; } if (reason != ELS_RJT_NONE || - (fp = fc_frame_alloc(lp, len)) == NULL) { + (fp = fc_frame_alloc(lport, len)) == NULL) { rjt_data.reason = reason; rjt_data.explan = explan; - lp->tt.seq_els_rsp_send(sp, ELS_LS_RJT, &rjt_data); + lport->tt.seq_els_rsp_send(sp, ELS_LS_RJT, &rjt_data); } else { - sp = lp->tt.seq_start_next(sp); + sp = lport->tt.seq_start_next(sp); WARN_ON(!sp); pp = fc_frame_payload_get(fp, len); WARN_ON(!pp); @@ -1156,15 +1214,16 @@ static void fc_rport_recv_prli_req(struct fc_rport *rport, case FC_TYPE_FCP: fcp_parm = ntohl(rspp->spp_params); if (fcp_parm * FCP_SPPF_RETRY) - rp->flags |= FC_RP_FLAGS_RETRY; + rdata->flags |= FC_RP_FLAGS_RETRY; rport->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; - fc_remote_port_rolechg(rport, roles); + rdata->roles = roles; + spp->spp_params = - htonl(rp->local_port->service_params); + htonl(lport->service_params); break; default: resp = FC_SPP_RESP_INVL; @@ -1181,32 +1240,20 @@ static void fc_rport_recv_prli_req(struct fc_rport *rport, */ f_ctl = FC_FC_SEQ_INIT | FC_FC_LAST_SEQ | FC_FC_END_SEQ; fc_frame_setup(fp, FC_RCTL_ELS_REP, FC_TYPE_ELS); - lp->tt.seq_send(lp, sp, fp, f_ctl); + lport->tt.seq_send(lport, sp, fp, f_ctl); /* * Get lock and re-check state. */ - fc_rport_lock(rport); - switch (rp->rp_state) { - case RPORT_ST_PLOGI_RECV: + switch (rdata->rp_state) { case RPORT_ST_PRLI: - fc_rport_state_enter(rport, RPORT_ST_READY); - if (fc_rp_debug) - FC_DBG("remote %6x ready\n", rport->port_id); - if (rport == lp->dns_rp && - lp->state == LPORT_ST_DNS) { - fc_lport_lock(lp); - del_timer(&lp->state_timer); - lp->tt.dns_register(lp); - fc_lport_unlock(lp); - } + fc_rport_enter_ready(rport); break; case RPORT_ST_READY: break; default: break; } - fc_rport_unlock(rport); } fc_frame_free(rx_fp); } @@ -1216,22 +1263,30 @@ static void fc_rport_recv_prli_req(struct fc_rport *rport, * @rport: Fibre Channel remote port that initiated PRLO * @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, struct fc_frame *fp) { - struct fc_rport_libfc_priv *rp = rport->dd_data; + struct fc_rport_libfc_priv *rdata = rport->dd_data; + struct fc_lport *lport = rdata->local_port; + struct fc_frame_header *fh; - struct fc_lport *lp = rp->local_port; struct fc_seq_els_data rjt_data; fh = fc_frame_header_get(fp); - FC_DBG("incoming PRLO from %x state %d\n", - ntoh24(fh->fh_s_id), rp->rp_state); + + if (fc_rp_debug) + FC_DBG("Received PRLO request from port (%6x) " + "while in state %s\n", ntoh24(fh->fh_s_id), + fc_rport_state(rport)); + rjt_data.fp = NULL; rjt_data.reason = ELS_RJT_UNAB; rjt_data.explan = ELS_EXPL_NONE; - lp->tt.seq_els_rsp_send(sp, ELS_LS_RJT, &rjt_data); + lport->tt.seq_els_rsp_send(sp, ELS_LS_RJT, &rjt_data); fc_frame_free(fp); } @@ -1240,62 +1295,95 @@ static void fc_rport_recv_prlo_req(struct fc_rport *rport, struct fc_seq *sp, * @rport: Fibre Channel remote port that initiated LOGO * @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, struct fc_frame *fp) { struct fc_frame_header *fh; - struct fc_rport_libfc_priv *rp = rport->dd_data; - struct fc_lport *lp = rp->local_port; + struct fc_rport_libfc_priv *rdata = rport->dd_data; + struct fc_lport *lport = rdata->local_port; fh = fc_frame_header_get(fp); - fc_rport_lock(rport); - fc_rport_state_enter(rport, RPORT_ST_INIT); - fc_rport_unlock(rport); + if (fc_rp_debug) - FC_DBG("remote %6x closed\n", rport->port_id); - if (rport == lp->dns_rp && - lp->state != LPORT_ST_RESET) { - fc_lport_lock(lp); - del_timer(&lp->state_timer); - lp->dns_rp = NULL; - if (lp->state == LPORT_ST_DNS_STOP) { - fc_lport_unlock(lp); - lp->tt.lport_logout(lp); - } else { - lp->tt.lport_login(lp); - fc_lport_unlock(lp); - } - fc_remote_port_delete(rport); - } - lp->tt.seq_els_rsp_send(sp, ELS_LS_ACC, NULL); + FC_DBG("Received LOGO request from port (%6x) " + "while in state %s\n", ntoh24(fh->fh_s_id), + fc_rport_state(rport)); + + rdata->event = LPORT_EV_RPORT_LOGO; + + lport->tt.seq_els_rsp_send(sp, ELS_LS_ACC, NULL); fc_frame_free(fp); } -int fc_rport_init(struct fc_lport *lp) +int fc_rport_init(struct fc_lport *lport) { - if (!lp->tt.rport_login) - lp->tt.rport_login = fc_rport_login; - - if (!lp->tt.rport_logout) - lp->tt.rport_logout = fc_rport_logout; + if (!lport->tt.rport_login) + lport->tt.rport_login = fc_rport_login; - if (!lp->tt.rport_recv_req) - lp->tt.rport_recv_req = fc_rport_recv_req; + if (!lport->tt.rport_logout) + lport->tt.rport_logout = fc_rport_logout; - if (!lp->tt.rport_create) - lp->tt.rport_create = fc_remote_port_create; + if (!lport->tt.rport_recv_req) + lport->tt.rport_recv_req = fc_rport_recv_req; - if (!lp->tt.rport_lookup) - lp->tt.rport_lookup = fc_rport_lookup; + if (!lport->tt.rport_lookup) + lport->tt.rport_lookup = fc_rport_lookup; - if (!lp->tt.rport_reset) - lp->tt.rport_reset = fc_rport_reset; + if (!lport->tt.rport_reset) + lport->tt.rport_reset = fc_rport_reset; - if (!lp->tt.rport_reset_list) - lp->tt.rport_reset_list = fc_rport_reset_list; + if (!lport->tt.rport_reset_list) + lport->tt.rport_reset_list = fc_rport_reset_list; return 0; } EXPORT_SYMBOL(fc_rport_init); +/** + * fc_block_rports - delete all the remote ports, on reset or link down + * @lp: libfc local port instance + * + * This routine temporarily removes any online remote ports from the fc_host + * rport list, then drops the host lock in order to call fc_remote_port_delete() + * on each rport in turn, and finally splices the list back onto the fc_host. + */ +void fc_block_rports(struct fc_lport *lp) +{ + struct Scsi_Host *shost = lp->host; + struct fc_rport *rport, *next; + unsigned long flags; + LIST_HEAD(rports); + + spin_lock_irqsave(shost->host_lock, flags); + list_for_each_entry_safe(rport, next, &fc_host_rports(shost), peers) { + /* protect the name service remote port */ + if (rport->port_id == FC_FID_DIR_SERV) + continue; + if (rport->port_state != FC_PORTSTATE_ONLINE) + continue; + list_move_tail(&rport->peers, &rports); + } + spin_unlock_irqrestore(shost->host_lock, flags); + + list_for_each_entry(rport, &rports, peers) { + fc_remote_port_delete(rport); + } + + spin_lock_irqsave(shost->host_lock, flags); + list_splice(&rports, &fc_host_rports(shost)); + spin_unlock_irqrestore(shost->host_lock, flags); +} + +void fc_rport_terminate_io(struct fc_rport *rport) +{ + struct fc_rport_libfc_priv *rp = rport->dd_data; + struct fc_lport *lp = rp->local_port; + + lp->tt.exch_mgr_reset(lp->emp, 0, rport->port_id); + lp->tt.exch_mgr_reset(lp->emp, rport->port_id, 0); +} +EXPORT_SYMBOL(fc_rport_terminate_io); diff --git a/include/scsi/fc/fc_fcoe.h b/include/scsi/fc/fc_fcoe.h index b2e07ec..59c9d0c 100644 --- a/include/scsi/fc/fc_fcoe.h +++ b/include/scsi/fc/fc_fcoe.h @@ -93,14 +93,6 @@ static inline void fc_fcoe_set_mac(u8 *mac, u8 *did) mac[5] = did[2]; } -/* - * VLAN header. This is also defined in linux/if_vlan.h, but for kernels only. - */ -struct fcoe_vlan_hdr { - __be16 vlan_tag; /* VLAN tag including priority */ - __be16 vlan_ethertype; /* encapsulated ethertype ETH_P_FCOE */ -}; - #ifndef ETH_P_8021Q #define ETH_P_8021Q 0x8100 #endif diff --git a/include/scsi/fc/fc_fs.h b/include/scsi/fc/fc_fs.h index ba6df64..3897c6c 100644 --- a/include/scsi/fc/fc_fs.h +++ b/include/scsi/fc/fc_fs.h @@ -329,16 +329,4 @@ enum fc_pf_rjt_reason { FC_RJT_VENDOR = 0xff, /* vendor specific reject */ }; -/* - * Data descriptor format (R_CTL == FC_RCTL_DD_DATA_DESC). - * This is used for FCP SCSI transfer ready. - */ -struct fc_data_desc { - __be32 dd_offset; /* data relative offset in bytes */ - __be32 dd_len; /* transfer buffer size in bytes */ - __u8 _dd_resvd[4]; -}; - -#define FC_DATA_DESC_LEN 12 /* expected length of structure */ - #endif /* _FC_FS_H_ */ diff --git a/include/scsi/libfc/libfc.h b/include/scsi/libfc/libfc.h index b139aed..24d3fcb 100644 --- a/include/scsi/libfc/libfc.h +++ b/include/scsi/libfc/libfc.h @@ -29,6 +29,7 @@ #include #include #include +#include #include @@ -91,28 +92,50 @@ enum fc_lport_state { LPORT_ST_NONE = 0, LPORT_ST_FLOGI, LPORT_ST_DNS, - LPORT_ST_REG_PN, - LPORT_ST_REG_FT, + LPORT_ST_RPN_ID, + LPORT_ST_RFT_ID, LPORT_ST_SCR, LPORT_ST_READY, - LPORT_ST_DNS_STOP, LPORT_ST_LOGO, LPORT_ST_RESET }; +enum fc_lport_event { + LPORT_EV_RPORT_NONE = 0, + LPORT_EV_RPORT_CREATED, + LPORT_EV_RPORT_FAILED, + LPORT_EV_RPORT_LOGO +}; + enum fc_rport_state { RPORT_ST_NONE = 0, RPORT_ST_INIT, /* initialized */ - RPORT_ST_STARTED, /* started */ RPORT_ST_PLOGI, /* waiting for PLOGI completion */ - RPORT_ST_PLOGI_RECV, /* received PLOGI (as target) */ RPORT_ST_PRLI, /* waiting for PRLI completion */ RPORT_ST_RTV, /* waiting for RTV completion */ - RPORT_ST_ERROR, /* error */ RPORT_ST_READY, /* ready for use */ RPORT_ST_LOGO, /* port logout sent */ }; +enum fc_rport_trans_state { + FC_PORTSTATE_ROGUE, + FC_PORTSTATE_REAL, +}; + +/** + * struct fc_disc_port - temporary discovery port to hold rport identifiers + * @lp: Fibre Channel host port instance + * @peers: node for list management during discovery and RSCN processing + * @ids: identifiers structure to pass to fc_remote_port_add() + * @rport_work: work struct for starting the rport state machine + */ +struct fc_disc_port { + struct fc_lport *lp; + struct list_head peers; + struct fc_rport_identifiers ids; + struct work_struct rport_work; +}; + /** * struct fc_rport_libfc_priv - libfc internal information about a remote port * @local_port: Fibre Channel host port instance @@ -122,8 +145,9 @@ enum fc_rport_state { * @retries: retry count in current state * @e_d_tov: error detect timeout value (in msec) * @r_a_tov: resource allocation timeout value (in msec) - * @rp_lock: lock protects state + * @rp_mutex: mutex protects rport * @retry_work: + * @event_callback: Callback for rport READY, FAILED or LOGO */ struct fc_rport_libfc_priv { struct fc_lport *local_port; @@ -135,10 +159,23 @@ struct fc_rport_libfc_priv { unsigned int retries; unsigned int e_d_tov; unsigned int r_a_tov; - spinlock_t rp_lock; + enum fc_rport_trans_state trans_state; + struct mutex rp_mutex; struct delayed_work retry_work; + enum fc_lport_event event; + void (*event_callback)(struct fc_lport *, u32, + enum fc_lport_event); + u32 roles; }; +#define PRIV_TO_RPORT(x) \ + (struct fc_rport*)((void *)x - sizeof(struct fc_rport)); +#define RPORT_TO_PRIV(x) \ + (struct fc_rport_libfc_priv*)((void *)x + sizeof(struct fc_rport)); + +struct fc_rport *fc_rport_dummy_create(struct fc_disc_port *); +void fc_rport_dummy_destroy(struct fc_rport *); + static inline void fc_rport_set_name(struct fc_rport *rport, u64 wwpn, u64 wwnn) { rport->node_name = wwnn; @@ -219,9 +256,12 @@ struct libfc_function_template { * fc_frame pointer in response handler will also indicate timeout * as error using IS_ERR related macros. * - * The response handler argumemt resp_arg is passed back to resp - * handler when it is invoked by EM layer in above mentioned - * two scenarios. + * The exchange destructor handler is also set in this routine. + * The destructor handler is invoked by EM layer when exchange + * is about to free, this can be used by caller to free its + * resources along with exchange free. + * + * The arg is passed back to resp and destructor handler. * * The timeout value (in msec) for an exchange is set if non zero * timer_msec argument is specified. The timer is canceled when @@ -232,10 +272,12 @@ struct libfc_function_template { */ struct fc_seq *(*exch_seq_send)(struct fc_lport *lp, struct fc_frame *fp, - void (*resp)(struct fc_seq *, + void (*resp)(struct fc_seq *sp, struct fc_frame *fp, void *arg), - void *resp_arg, unsigned int timer_msec, + void (*destructor)(struct fc_seq *sp, + void *arg), + void *arg, unsigned int timer_msec, u32 sid, u32 did, u32 f_ctl); /* @@ -316,9 +358,10 @@ struct libfc_function_template { void (*lport_recv)(struct fc_lport *lp, struct fc_seq *sp, struct fc_frame *fp); - int (*lport_login)(struct fc_lport *); int (*lport_reset)(struct fc_lport *); - int (*lport_logout)(struct fc_lport *); + + void (*event_callback)(struct fc_lport *, u32, + enum fc_lport_event); /** * Remote Port interfaces @@ -346,9 +389,6 @@ struct libfc_function_template { struct fc_rport *(*rport_lookup)(const struct fc_lport *, u32); - struct fc_rport *(*rport_create)(struct fc_lport *, - struct fc_rport_identifiers *); - void (*rport_reset)(struct fc_rport *); void (*rport_reset_list)(struct fc_lport *); @@ -378,9 +418,6 @@ struct libfc_function_template { * Start discovery for a local port. */ int (*disc_start)(struct fc_lport *); - - void (*dns_register)(struct fc_lport *); - void (*disc_stop)(struct fc_lport *); }; struct fc_lport { @@ -396,7 +433,7 @@ struct fc_lport { /* Operational Information */ struct libfc_function_template tt; u16 link_status; - u8 ns_disc_done; + u8 disc_done; enum fc_lport_state state; unsigned long boot_time; @@ -407,12 +444,12 @@ struct fc_lport { u64 wwnn; u32 fid; u8 retry_count; - unsigned char ns_disc_retry_count; - unsigned char ns_disc_delay; - unsigned char ns_disc_pending; - unsigned char ns_disc_requested; - unsigned short ns_disc_seq_count; - unsigned char ns_disc_buf_len; + unsigned char disc_retry_count; + unsigned char disc_delay; + unsigned char disc_pending; + unsigned char disc_requested; + unsigned short disc_seq_count; + unsigned char disc_buf_len; /* Capabilities */ char ifname[IFNAMSIZ]; @@ -427,13 +464,13 @@ struct fc_lport { struct fc_ns_fts fcts; /* FC-4 type masks */ struct fc_els_rnid_gen rnid_gen; /* RNID information */ - /* Locks */ - spinlock_t state_lock; /* serializes state changes */ + /* Semaphores */ + struct mutex lp_mutex; /* Miscellaneous */ - struct fc_gpn_ft_resp ns_disc_buf; /* partial name buffer */ - struct timer_list state_timer; /* timer for state events */ - struct delayed_work ns_disc_work; + struct fc_gpn_ft_resp disc_buf; /* partial name buffer */ + struct delayed_work retry_work; + struct delayed_work disc_work; void *drv_priv; }; @@ -462,33 +499,26 @@ static inline void fc_set_wwpn(struct fc_lport *lp, u64 wwnn) lp->wwpn = wwnn; } -static inline int fc_lport_locked(struct fc_lport *lp) -{ -#if defined(CONFIG_SMP) || defined(CONFIG_DEBUG_SPINLOCK) - return spin_is_locked(&lp->state_lock); -#else - return 1; -#endif /* CONFIG_SMP || CONFIG_DEBUG_SPINLOCK */ -} - -/* - * Locking code. - */ -static inline void fc_lport_lock(struct fc_lport *lp) -{ - spin_lock_bh(&lp->state_lock); -} - -static inline void fc_lport_unlock(struct fc_lport *lp) +/** + * fc_fill_dns_hdr - Fill in a name service request header + * @lp: Fibre Channel host port instance + * @ct: Common Transport (CT) header structure + * @op: Name Service request code + * @req_size: Full size of Name Service request + */ +static inline void fc_fill_dns_hdr(struct fc_lport *lp, struct fc_ct_hdr *ct, + unsigned int op, unsigned int req_size) { - spin_unlock_bh(&lp->state_lock); + memset(ct, 0, sizeof(*ct) + req_size); + ct->ct_rev = FC_CT_REV; + ct->ct_fs_type = FC_FST_DIR; + ct->ct_fs_subtype = FC_NS_SUBTYPE; + ct->ct_cmd = htons((u16) op); } static inline void fc_lport_state_enter(struct fc_lport *lp, enum fc_lport_state state) { - WARN_ON(!fc_lport_locked(lp)); - del_timer(&lp->state_timer); if (state != lp->state) lp->retry_count = 0; lp->state = state; @@ -543,7 +573,7 @@ int fc_lport_config(struct fc_lport *); /* * Reset the local port. */ -int fc_lport_enter_reset(struct fc_lport *); +int fc_lport_reset(struct fc_lport *); /* * Set the mfs or reset @@ -555,12 +585,14 @@ int fc_set_mfs(struct fc_lport *lp, u32 mfs); * REMOTE PORT LAYER *****************************/ int fc_rport_init(struct fc_lport *lp); +void fc_rport_terminate_io(struct fc_rport *rp); +void fc_block_rports(struct fc_lport *lp); /** * DISCOVERY LAYER *****************************/ -int fc_ns_init(struct fc_lport *lp); +int fc_disc_init(struct fc_lport *lp); /** @@ -670,10 +702,12 @@ void fc_exch_recv(struct fc_lport *lp, struct fc_exch_mgr *mp, */ struct fc_seq *fc_exch_seq_send(struct fc_lport *lp, struct fc_frame *fp, - void (*resp)(struct fc_seq *, + void (*resp)(struct fc_seq *sp, struct fc_frame *fp, void *arg), - void *resp_arg, u32 timer_msec, + void (*destructor)(struct fc_seq *sp, + void *arg), + void *arg, u32 timer_msec, u32 sid, u32 did, u32 f_ctl); /* @@ -738,15 +772,11 @@ void fc_seq_get_xids(struct fc_seq *sp, u16 *oxid, u16 *rxid); */ void fc_seq_set_rec_data(struct fc_seq *sp, u32 rec_data); -/** - * fc_functions_template - *****************************/ -void fc_attr_init(struct fc_lport *); -void fc_get_host_port_id(struct Scsi_Host *shost); +/* + * Functions for fc_functions_template + */ void fc_get_host_speed(struct Scsi_Host *shost); -void fc_get_host_port_type(struct Scsi_Host *shost); void fc_get_host_port_state(struct Scsi_Host *shost); -void fc_get_host_fabric_name(struct Scsi_Host *shost); void fc_set_rport_loss_tmo(struct fc_rport *rport, u32 timeout); struct fc_host_statistics *fc_get_host_stats(struct Scsi_Host *);