diff options
author | Stephen Rothwell <sfr@canb.auug.org.au> | 2008-12-16 01:31:13 +1100 |
---|---|---|
committer | Stephen Rothwell <sfr@canb.auug.org.au> | 2008-12-16 01:31:13 +1100 |
commit | 15c07d607bf89914a045c52cc032aafab3fecd41 (patch) | |
tree | 4e3ae21a1f2f978d55adb097729c0047ab123d79 | |
parent | 65f3ebcb1b994992df2dc2afcf1347413f2757aa (diff) |
Revert "[SCSI] libfc: A modular Fibre Channel library"
This reverts commit 389571f347629d879f9044582bbbafb43e4fe00b.
-rw-r--r-- | drivers/scsi/Kconfig | 6 | ||||
-rw-r--r-- | drivers/scsi/Makefile | 1 | ||||
-rw-r--r-- | drivers/scsi/libfc/Makefile | 12 | ||||
-rw-r--r-- | drivers/scsi/libfc/fc_disc.c | 845 | ||||
-rw-r--r-- | drivers/scsi/libfc/fc_elsct.c | 71 | ||||
-rw-r--r-- | drivers/scsi/libfc/fc_exch.c | 1970 | ||||
-rw-r--r-- | drivers/scsi/libfc/fc_fcp.c | 2130 | ||||
-rw-r--r-- | drivers/scsi/libfc/fc_frame.c | 89 | ||||
-rw-r--r-- | drivers/scsi/libfc/fc_lport.c | 1604 | ||||
-rw-r--r-- | drivers/scsi/libfc/fc_rport.c | 1291 | ||||
-rw-r--r-- | include/scsi/fc_encode.h | 309 | ||||
-rw-r--r-- | include/scsi/fc_frame.h | 242 | ||||
-rw-r--r-- | include/scsi/libfc.h | 938 |
13 files changed, 0 insertions, 9508 deletions
diff --git a/drivers/scsi/Kconfig b/drivers/scsi/Kconfig index a181d5f38202..f6c634dc5e7c 100644 --- a/drivers/scsi/Kconfig +++ b/drivers/scsi/Kconfig @@ -603,12 +603,6 @@ config SCSI_FLASHPOINT substantial, so users of MultiMaster Host Adapters may not wish to include it. -config LIBFC - tristate "LibFC module" - depends on SCSI && SCSI_FC_ATTRS - ---help--- - Fibre Channel library module - config SCSI_DMX3191D tristate "DMX3191D SCSI support" depends on PCI && SCSI diff --git a/drivers/scsi/Makefile b/drivers/scsi/Makefile index 43e4384a09be..b8cfdda6c950 100644 --- a/drivers/scsi/Makefile +++ b/drivers/scsi/Makefile @@ -36,7 +36,6 @@ obj-$(CONFIG_SCSI_SAS_LIBSAS) += libsas/ obj-$(CONFIG_SCSI_SRP_ATTRS) += scsi_transport_srp.o obj-$(CONFIG_SCSI_DH) += device_handler/ -obj-$(CONFIG_LIBFC) += libfc/ obj-$(CONFIG_ISCSI_TCP) += libiscsi.o libiscsi_tcp.o iscsi_tcp.o obj-$(CONFIG_INFINIBAND_ISER) += libiscsi.o obj-$(CONFIG_SCSI_A4000T) += 53c700.o a4000t.o diff --git a/drivers/scsi/libfc/Makefile b/drivers/scsi/libfc/Makefile deleted file mode 100644 index 55f982de3a9a..000000000000 --- a/drivers/scsi/libfc/Makefile +++ /dev/null @@ -1,12 +0,0 @@ -# $Id: Makefile - -obj-$(CONFIG_LIBFC) += libfc.o - -libfc-objs := \ - fc_disc.o \ - fc_exch.o \ - fc_elsct.o \ - fc_frame.o \ - fc_lport.o \ - fc_rport.o \ - fc_fcp.o diff --git a/drivers/scsi/libfc/fc_disc.c b/drivers/scsi/libfc/fc_disc.c deleted file mode 100644 index dd1564c9e04a..000000000000 --- a/drivers/scsi/libfc/fc_disc.c +++ /dev/null @@ -1,845 +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 - * - * This block discovers all FC-4 remote ports, including FCP initiators. It - * also handles RSCN events and re-discovery if necessary. - */ - -/* - * DISC LOCKING - * - * The disc mutex is can be locked when acquiring rport locks, but may not - * be held when acquiring the lport lock. Refer to fc_lport.c for more - * details. - */ - -#include <linux/timer.h> -#include <linux/err.h> -#include <asm/unaligned.h> - -#include <scsi/fc/fc_gs.h> - -#include <scsi/libfc.h> - -#define FC_DISC_RETRY_LIMIT 3 /* max retries */ -#define FC_DISC_RETRY_DELAY 500UL /* (msecs) delay */ - -#define FC_DISC_DELAY 3 - -static int fc_disc_debug; - -#define FC_DEBUG_DISC(fmt...) \ - do { \ - if (fc_disc_debug) \ - FC_DBG(fmt); \ - } while (0) - -static void fc_disc_gpn_ft_req(struct fc_disc *); -static void fc_disc_gpn_ft_resp(struct fc_seq *, struct fc_frame *, void *); -static int fc_disc_new_target(struct fc_disc *, struct fc_rport *, - struct fc_rport_identifiers *); -static void fc_disc_del_target(struct fc_disc *, struct fc_rport *); -static void fc_disc_done(struct fc_disc *); -static void fc_disc_timeout(struct work_struct *); -static void fc_disc_single(struct fc_disc *, struct fc_disc_port *); -static void fc_disc_restart(struct fc_disc *); - -/** - * fc_disc_lookup_rport - lookup a remote port by port_id - * @lport: Fibre Channel host port instance - * @port_id: remote port port_id to match - */ -struct fc_rport *fc_disc_lookup_rport(const struct fc_lport *lport, - u32 port_id) -{ - const struct fc_disc *disc = &lport->disc; - struct fc_rport *rport, *found = NULL; - struct fc_rport_libfc_priv *rdata; - int disc_found = 0; - - list_for_each_entry(rdata, &disc->rports, peers) { - rport = PRIV_TO_RPORT(rdata); - if (rport->port_id == port_id) { - disc_found = 1; - found = rport; - break; - } - } - - if (!disc_found) - found = NULL; - - return found; -} - -/** - * fc_disc_stop_rports - delete all the remote ports associated with the lport - * @disc: The discovery job to stop rports on - * - * Locking Note: This function expects that the lport mutex is locked before - * calling it. - */ -void fc_disc_stop_rports(struct fc_disc *disc) -{ - struct fc_lport *lport; - struct fc_rport *rport; - struct fc_rport_libfc_priv *rdata, *next; - - lport = disc->lport; - - mutex_lock(&disc->disc_mutex); - list_for_each_entry_safe(rdata, next, &disc->rports, peers) { - rport = PRIV_TO_RPORT(rdata); - list_del(&rdata->peers); - lport->tt.rport_logoff(rport); - } - - mutex_unlock(&disc->disc_mutex); -} - -/** - * fc_disc_rport_callback - Event handler for rport events - * @lport: The lport which is receiving the event - * @rport: The rport which the event has occured on - * @event: The event that occured - * - * Locking Note: The rport lock should not be held when calling - * this function. - */ -static void fc_disc_rport_callback(struct fc_lport *lport, - struct fc_rport *rport, - enum fc_rport_event event) -{ - struct fc_rport_libfc_priv *rdata = rport->dd_data; - struct fc_disc *disc = &lport->disc; - int found = 0; - - FC_DEBUG_DISC("Received a %d event for port (%6x)\n", event, - rport->port_id); - - if (event == RPORT_EV_CREATED) { - if (disc) { - found = 1; - mutex_lock(&disc->disc_mutex); - list_add_tail(&rdata->peers, &disc->rports); - mutex_unlock(&disc->disc_mutex); - } - } - - if (!found) - FC_DEBUG_DISC("The rport (%6x) is not maintained " - "by the discovery layer\n", rport->port_id); -} - -/** - * fc_disc_recv_rscn_req - Handle Registered State Change Notification (RSCN) - * @sp: Current sequence of the RSCN exchange - * @fp: RSCN Frame - * @lport: Fibre Channel host port instance - * - * Locking Note: This function expects that the disc_mutex is locked - * before it is called. - */ -static void fc_disc_recv_rscn_req(struct fc_seq *sp, struct fc_frame *fp, - struct fc_disc *disc) -{ - struct fc_lport *lport; - struct fc_rport *rport; - struct fc_rport_libfc_priv *rdata; - struct fc_els_rscn *rp; - struct fc_els_rscn_page *pp; - struct fc_seq_els_data rjt_data; - unsigned int len; - int redisc = 0; - enum fc_els_rscn_ev_qual ev_qual; - enum fc_els_rscn_addr_fmt fmt; - LIST_HEAD(disc_ports); - struct fc_disc_port *dp, *next; - - lport = disc->lport; - - FC_DEBUG_DISC("Received an RSCN event on port (%6x)\n", - fc_host_port_id(lport->host)); - - /* make sure the frame contains an RSCN message */ - rp = fc_frame_payload_get(fp, sizeof(*rp)); - if (!rp) - goto reject; - /* make sure the page length is as expected (4 bytes) */ - if (rp->rscn_page_len != sizeof(*pp)) - goto reject; - /* get the RSCN payload length */ - len = ntohs(rp->rscn_plen); - if (len < sizeof(*rp)) - goto reject; - /* make sure the frame contains the expected payload */ - rp = fc_frame_payload_get(fp, len); - if (!rp) - goto reject; - /* payload must be a multiple of the RSCN page size */ - len -= sizeof(*rp); - if (len % sizeof(*pp)) - goto reject; - - for (pp = (void *)(rp + 1); len > 0; 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: - FC_DEBUG_DISC("Port address format for port (%6x)\n", - ntoh24(pp->rscn_fid)); - dp = kzalloc(sizeof(*dp), GFP_KERNEL); - if (!dp) { - redisc = 1; - break; - } - dp->lp = lport; - dp->ids.port_id = ntoh24(pp->rscn_fid); - dp->ids.port_name = -1; - dp->ids.node_name = -1; - dp->ids.roles = FC_RPORT_ROLE_UNKNOWN; - list_add_tail(&dp->peers, &disc_ports); - break; - case ELS_ADDR_FMT_AREA: - case ELS_ADDR_FMT_DOM: - case ELS_ADDR_FMT_FAB: - default: - FC_DEBUG_DISC("Address format is (%d)\n", fmt); - redisc = 1; - break; - } - } - lport->tt.seq_els_rsp_send(sp, ELS_LS_ACC, NULL); - if (redisc) { - FC_DEBUG_DISC("RSCN received: rediscovering\n"); - fc_disc_restart(disc); - } else { - FC_DEBUG_DISC("RSCN received: not rediscovering. " - "redisc %d state %d in_prog %d\n", - redisc, lport->state, disc->pending); - list_for_each_entry_safe(dp, next, &disc_ports, peers) { - list_del(&dp->peers); - rport = lport->tt.rport_lookup(lport, dp->ids.port_id); - if (rport) { - rdata = RPORT_TO_PRIV(rport); - list_del(&rdata->peers); - lport->tt.rport_logoff(rport); - } - fc_disc_single(disc, dp); - } - } - fc_frame_free(fp); - return; -reject: - FC_DEBUG_DISC("Received a bad RSCN frame\n"); - rjt_data.fp = NULL; - rjt_data.reason = ELS_RJT_LOGIC; - rjt_data.explan = ELS_EXPL_NONE; - lport->tt.seq_els_rsp_send(sp, ELS_LS_RJT, &rjt_data); - fc_frame_free(fp); -} - -/** - * fc_disc_recv_req - Handle incoming requests - * @sp: Current sequence of the request exchange - * @fp: The frame - * @lport: The FC local port - * - * Locking Note: This function is called from the EM and will lock - * the disc_mutex before calling the handler for the - * request. - */ -static void fc_disc_recv_req(struct fc_seq *sp, struct fc_frame *fp, - struct fc_lport *lport) -{ - u8 op; - struct fc_disc *disc = &lport->disc; - - op = fc_frame_payload_op(fp); - switch (op) { - case ELS_RSCN: - mutex_lock(&disc->disc_mutex); - fc_disc_recv_rscn_req(sp, fp, disc); - mutex_unlock(&disc->disc_mutex); - break; - default: - FC_DBG("Received an unsupported request. opcode (%x)\n", op); - break; - } -} - -/** - * fc_disc_restart - Restart discovery - * @lport: FC discovery context - * - * Locking Note: This function expects that the disc mutex - * is already locked. - */ -static void fc_disc_restart(struct fc_disc *disc) -{ - struct fc_rport *rport; - struct fc_rport_libfc_priv *rdata, *next; - struct fc_lport *lport = disc->lport; - - FC_DEBUG_DISC("Restarting discovery for port (%6x)\n", - fc_host_port_id(lport->host)); - - list_for_each_entry_safe(rdata, next, &disc->rports, peers) { - rport = PRIV_TO_RPORT(rdata); - FC_DEBUG_DISC("list_del(%6x)\n", rport->port_id); - list_del(&rdata->peers); - lport->tt.rport_logoff(rport); - } - - disc->requested = 1; - if (!disc->pending) - fc_disc_gpn_ft_req(disc); -} - -/** - * fc_disc_start - Fibre Channel Target discovery - * @lport: FC local port - * - * Returns non-zero if discovery cannot be started. - */ -static void fc_disc_start(void (*disc_callback)(struct fc_lport *, - enum fc_disc_event), - struct fc_lport *lport) -{ - struct fc_rport *rport; - struct fc_rport_identifiers ids; - struct fc_disc *disc = &lport->disc; - - /* - * At this point we may have a new disc job or an existing - * one. Either way, let's lock when we make changes to it - * and send the GPN_FT request. - */ - mutex_lock(&disc->disc_mutex); - - disc->disc_callback = disc_callback; - - /* - * If not ready, or already running discovery, just set request flag. - */ - disc->requested = 1; - - if (disc->pending) { - mutex_unlock(&disc->disc_mutex); - return; - } - - /* - * Handle point-to-point mode as a simple discovery - * of the remote port. Yucky, yucky, yuck, yuck! - */ - rport = disc->lport->ptp_rp; - if (rport) { - ids.port_id = rport->port_id; - ids.port_name = rport->port_name; - ids.node_name = rport->node_name; - ids.roles = FC_RPORT_ROLE_UNKNOWN; - get_device(&rport->dev); - - if (!fc_disc_new_target(disc, rport, &ids)) { - disc->event = DISC_EV_SUCCESS; - fc_disc_done(disc); - } - put_device(&rport->dev); - } else { - fc_disc_gpn_ft_req(disc); /* get ports by FC-4 type */ - } - - mutex_unlock(&disc->disc_mutex); -} - -static struct fc_rport_operations fc_disc_rport_ops = { - .event_callback = fc_disc_rport_callback, -}; - -/** - * fc_disc_new_target - Handle new target found by discovery - * @lport: FC local port - * @rport: The previous FC remote port (NULL if new remote port) - * @ids: Identifiers for the new FC remote port - * - * Locking Note: This function expects that the disc_mutex is locked - * before it is called. - */ -static int fc_disc_new_target(struct fc_disc *disc, - struct fc_rport *rport, - struct fc_rport_identifiers *ids) -{ - struct fc_lport *lport = disc->lport; - struct fc_rport_libfc_priv *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(disc, rport); - rport = NULL; - } - } - if (((ids->port_name != -1) || (ids->port_id != -1)) && - ids->port_id != fc_host_port_id(lport->host) && - ids->port_name != lport->wwpn) { - if (!rport) { - rport = lport->tt.rport_lookup(lport, ids->port_id); - if (!rport) { - struct fc_disc_port dp; - dp.lp = lport; - dp.ids.port_id = ids->port_id; - dp.ids.port_name = ids->port_name; - dp.ids.node_name = ids->node_name; - dp.ids.roles = ids->roles; - rport = fc_rport_rogue_create(&dp); - } - if (!rport) - error = -ENOMEM; - } - if (rport) { - rp = rport->dd_data; - rp->ops = &fc_disc_rport_ops; - rp->rp_state = RPORT_ST_INIT; - lport->tt.rport_login(rport); - } - } - return error; -} - -/** - * fc_disc_del_target - Delete a target - * @disc: FC discovery context - * @rport: The remote port to be removed - */ -static void fc_disc_del_target(struct fc_disc *disc, struct fc_rport *rport) -{ - struct fc_lport *lport = disc->lport; - struct fc_rport_libfc_priv *rdata = RPORT_TO_PRIV(rport); - list_del(&rdata->peers); - lport->tt.rport_logoff(rport); -} - -/** - * fc_disc_done - Discovery has been completed - * @disc: FC discovery context - */ -static void fc_disc_done(struct fc_disc *disc) -{ - struct fc_lport *lport = disc->lport; - - FC_DEBUG_DISC("Discovery complete for port (%6x)\n", - fc_host_port_id(lport->host)); - - disc->disc_callback(lport, disc->event); - disc->event = DISC_EV_NONE; - - if (disc->requested) - fc_disc_gpn_ft_req(disc); - else - disc->pending = 0; -} - -/** - * fc_disc_error - Handle error on dNS request - * @disc: FC discovery context - * @fp: The frame pointer - */ -static void fc_disc_error(struct fc_disc *disc, struct fc_frame *fp) -{ - struct fc_lport *lport = disc->lport; - unsigned long delay = 0; - if (fc_disc_debug) - FC_DBG("Error %ld, retries %d/%d\n", - PTR_ERR(fp), disc->retry_count, - FC_DISC_RETRY_LIMIT); - - if (!fp || PTR_ERR(fp) == -FC_EX_TIMEOUT) { - /* - * Memory allocation failure, or the exchange timed out, - * retry after delay. - */ - if (disc->retry_count < FC_DISC_RETRY_LIMIT) { - /* go ahead and retry */ - if (!fp) - delay = msecs_to_jiffies(FC_DISC_RETRY_DELAY); - else { - delay = msecs_to_jiffies(lport->e_d_tov); - - /* timeout faster first time */ - if (!disc->retry_count) - delay /= 4; - } - disc->retry_count++; - schedule_delayed_work(&disc->disc_work, delay); - } else { - /* exceeded retries */ - disc->event = DISC_EV_FAILED; - fc_disc_done(disc); - } - } -} - -/** - * fc_disc_gpn_ft_req - Send Get Port Names by FC-4 type (GPN_FT) request - * @lport: FC discovery context - * - * Locking Note: This function expects that the disc_mutex is locked - * before it is called. - */ -static void fc_disc_gpn_ft_req(struct fc_disc *disc) -{ - struct fc_frame *fp; - struct fc_lport *lport = disc->lport; - - WARN_ON(!fc_lport_test_ready(lport)); - - disc->pending = 1; - disc->requested = 0; - - disc->buf_len = 0; - disc->seq_count = 0; - fp = fc_frame_alloc(lport, - sizeof(struct fc_ct_hdr) + - sizeof(struct fc_ns_gid_ft)); - if (!fp) - goto err; - - if (lport->tt.elsct_send(lport, NULL, fp, - FC_NS_GPN_FT, - fc_disc_gpn_ft_resp, - disc, lport->e_d_tov)) - return; -err: - fc_disc_error(disc, fp); -} - -/** - * fc_disc_gpn_ft_parse - Parse the list of IDs and names resulting from a request - * @lport: Fibre Channel host port instance - * @buf: GPN_FT response buffer - * @len: size of response buffer - */ -static int fc_disc_gpn_ft_parse(struct fc_disc *disc, void *buf, size_t len) -{ - struct fc_lport *lport; - 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 *rport; - struct fc_rport_libfc_priv *rdata; - - lport = disc->lport; - - /* - * Handle partial name record left over from previous call. - */ - bp = buf; - plen = len; - np = (struct fc_gpn_ft_resp *)bp; - tlen = 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 = &disc->partial_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; - disc->buf_len = (unsigned char) plen; - if (plen == sizeof(*np)) - 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 = lport; - dp.ids.port_id = ntoh24(np->fp_fid); - dp.ids.port_name = ntohll(np->fp_wwpn); - dp.ids.node_name = -1; - dp.ids.roles = FC_RPORT_ROLE_UNKNOWN; - - if ((dp.ids.port_id != fc_host_port_id(lport->host)) && - (dp.ids.port_name != lport->wwpn)) { - rport = fc_rport_rogue_create(&dp); - if (rport) { - rdata = rport->dd_data; - rdata->ops = &fc_disc_rport_ops; - rdata->local_port = lport; - lport->tt.rport_login(rport); - } 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) { - disc->event = DISC_EV_SUCCESS; - fc_disc_done(disc); - 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 != &disc->partial_buf) { - FC_DEBUG_DISC("Partial buffer remains " - "for discovery by (%6x)\n", - fc_host_port_id(lport->host)); - memcpy(&disc->partial_buf, np, len); - } - disc->buf_len = (unsigned char) len; - } else { - 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_disc *disc = container_of(work, - struct fc_disc, - disc_work.work); - mutex_lock(&disc->disc_mutex); - if (disc->requested && !disc->pending) - fc_disc_gpn_ft_req(disc); - mutex_unlock(&disc->disc_mutex); -} - -/** - * 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 - * - * Locking Note: This function expects that the disc_mutex is locked - * before it is called. - */ -static void fc_disc_gpn_ft_resp(struct fc_seq *sp, struct fc_frame *fp, - void *disc_arg) -{ - struct fc_disc *disc = disc_arg; - struct fc_ct_hdr *cp; - struct fc_frame_header *fh; - unsigned int seq_cnt; - void *buf = NULL; - unsigned int len; - int error; - - FC_DEBUG_DISC("Received a GPN_FT response on port (%6x)\n", - fc_host_port_id(disc->lport->host)); - - if (IS_ERR(fp)) { - fc_disc_error(disc, 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 && - disc->seq_count == 0) { - cp = fc_frame_payload_get(fp, sizeof(*cp)); - if (!cp) { - 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); - disc->event = DISC_EV_FAILED; - fc_disc_done(disc); - } else { - FC_DBG("GPN_FT unexpected response code %x\n", - ntohs(cp->ct_cmd)); - } - } else if (fr_sof(fp) == FC_SOF_N3 && - seq_cnt == 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, disc->seq_count, fr_sof(fp), fr_eof(fp)); - } - if (buf) { - error = fc_disc_gpn_ft_parse(disc, buf, len); - if (error) - fc_disc_error(disc, fp); - else - disc->seq_count++; - } - fc_frame_free(fp); -} - -/** - * fc_disc_single - Discover the directory information for a single target - * @lport: FC local port - * @dp: The port to rediscover - * - * Locking Note: This function expects that the disc_mutex is locked - * before it is called. - */ -static void fc_disc_single(struct fc_disc *disc, struct fc_disc_port *dp) -{ - struct fc_lport *lport; - struct fc_rport *rport; - struct fc_rport *new_rport; - struct fc_rport_libfc_priv *rdata; - - lport = disc->lport; - - if (dp->ids.port_id == fc_host_port_id(lport->host)) - goto out; - - rport = lport->tt.rport_lookup(lport, dp->ids.port_id); - if (rport) - fc_disc_del_target(disc, rport); - - new_rport = fc_rport_rogue_create(dp); - if (new_rport) { - rdata = new_rport->dd_data; - rdata->ops = &fc_disc_rport_ops; - kfree(dp); - lport->tt.rport_login(new_rport); - } - return; -out: - kfree(dp); -} - -/** - * fc_disc_stop - Stop discovery for a given lport - * @lport: The lport that discovery should stop for - */ -void fc_disc_stop(struct fc_lport *lport) -{ - struct fc_disc *disc = &lport->disc; - - if (disc) { - cancel_delayed_work_sync(&disc->disc_work); - fc_disc_stop_rports(disc); - } -} - -/** - * fc_disc_stop_final - Stop discovery for a given lport - * @lport: The lport that discovery should stop for - * - * This function will block until discovery has been - * completely stopped and all rports have been deleted. - */ -void fc_disc_stop_final(struct fc_lport *lport) -{ - fc_disc_stop(lport); - lport->tt.rport_flush_queue(); -} - -/** - * fc_disc_init - Initialize the discovery block - * @lport: FC local port - */ -int fc_disc_init(struct fc_lport *lport) -{ - struct fc_disc *disc; - - if (!lport->tt.disc_start) - lport->tt.disc_start = fc_disc_start; - - if (!lport->tt.disc_stop) - lport->tt.disc_stop = fc_disc_stop; - - if (!lport->tt.disc_stop_final) - lport->tt.disc_stop_final = fc_disc_stop_final; - - if (!lport->tt.disc_recv_req) - lport->tt.disc_recv_req = fc_disc_recv_req; - - if (!lport->tt.rport_lookup) - lport->tt.rport_lookup = fc_disc_lookup_rport; - - disc = &lport->disc; - INIT_DELAYED_WORK(&disc->disc_work, fc_disc_timeout); - mutex_init(&disc->disc_mutex); - INIT_LIST_HEAD(&disc->rports); - - disc->lport = lport; - disc->delay = FC_DISC_DELAY; - disc->event = DISC_EV_NONE; - - return 0; -} -EXPORT_SYMBOL(fc_disc_init); diff --git a/drivers/scsi/libfc/fc_elsct.c b/drivers/scsi/libfc/fc_elsct.c deleted file mode 100644 index dd47fe619d1e..000000000000 --- a/drivers/scsi/libfc/fc_elsct.c +++ /dev/null @@ -1,71 +0,0 @@ -/* - * Copyright(c) 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 - */ - -/* - * Provide interface to send ELS/CT FC frames - */ - -#include <asm/unaligned.h> -#include <scsi/fc/fc_gs.h> -#include <scsi/fc/fc_ns.h> -#include <scsi/fc/fc_els.h> -#include <scsi/libfc.h> -#include <scsi/fc_encode.h> - -/* - * fc_elsct_send - sends ELS/CT frame - */ -static struct fc_seq *fc_elsct_send(struct fc_lport *lport, - struct fc_rport *rport, - struct fc_frame *fp, - unsigned int op, - void (*resp)(struct fc_seq *, - struct fc_frame *fp, - void *arg), - void *arg, u32 timer_msec) -{ - enum fc_rctl r_ctl; - u32 did; - enum fc_fh_type fh_type; - int rc; - - /* ELS requests */ - if ((op >= ELS_LS_RJT) && (op <= ELS_AUTH_ELS)) - rc = fc_els_fill(lport, rport, fp, op, &r_ctl, &did, &fh_type); - else - /* CT requests */ - rc = fc_ct_fill(lport, fp, op, &r_ctl, &did, &fh_type); - - if (rc) - return NULL; - - fc_fill_fc_hdr(fp, r_ctl, did, fc_host_port_id(lport->host), fh_type, - FC_FC_FIRST_SEQ | FC_FC_END_SEQ | FC_FC_SEQ_INIT, 0); - - return lport->tt.exch_seq_send(lport, fp, resp, NULL, arg, timer_msec); -} - -int fc_elsct_init(struct fc_lport *lport) -{ - if (!lport->tt.elsct_send) - lport->tt.elsct_send = fc_elsct_send; - - return 0; -} -EXPORT_SYMBOL(fc_elsct_init); diff --git a/drivers/scsi/libfc/fc_exch.c b/drivers/scsi/libfc/fc_exch.c deleted file mode 100644 index 66db08a5f27f..000000000000 --- a/drivers/scsi/libfc/fc_exch.c +++ /dev/null @@ -1,1970 +0,0 @@ -/* - * 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, - * 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 - */ - -/* - * Fibre Channel exchange and sequence handling. - */ - -#include <linux/timer.h> -#include <linux/gfp.h> -#include <linux/err.h> - -#include <scsi/fc/fc_fc2.h> - -#include <scsi/libfc.h> -#include <scsi/fc_encode.h> - -#define FC_DEF_R_A_TOV (10 * 1000) /* resource allocation timeout */ - -/* - * fc_exch_debug can be set in debugger or at compile time to get more logs. - */ -static int fc_exch_debug; - -#define FC_DEBUG_EXCH(fmt...) \ - do { \ - if (fc_exch_debug) \ - FC_DBG(fmt); \ - } while (0) - -static struct kmem_cache *fc_em_cachep; /* cache for exchanges */ - -/* - * Structure and function definitions for managing Fibre Channel Exchanges - * and Sequences. - * - * The three primary structures used here are fc_exch_mgr, fc_exch, and fc_seq. - * - * fc_exch_mgr holds the exchange state for an N port - * - * fc_exch holds state for one exchange and links to its active sequence. - * - * fc_seq holds the state for an individual sequence. - */ - -/* - * Exchange manager. - * - * This structure is the center for creating exchanges and sequences. - * It manages the allocation of exchange IDs. - */ -struct fc_exch_mgr { - enum fc_class class; /* default class for sequences */ - spinlock_t em_lock; /* exchange manager lock, - must be taken before ex_lock */ - u16 last_xid; /* last allocated exchange ID */ - u16 min_xid; /* min exchange ID */ - u16 max_xid; /* max exchange ID */ - u16 max_read; /* max exchange ID for read */ - u16 last_read; /* last xid allocated for read */ - u32 total_exches; /* total allocated exchanges */ - struct list_head ex_list; /* allocated exchanges list */ - struct fc_lport *lp; /* fc device instance */ - mempool_t *ep_pool; /* reserve ep's */ - - /* - * currently exchange mgr stats are updated but not used. - * either stats can be expose via sysfs or remove them - * all together if not used XXX - */ - struct { - atomic_t no_free_exch; - atomic_t no_free_exch_xid; - atomic_t xid_not_found; - atomic_t xid_busy; - atomic_t seq_not_found; - atomic_t non_bls_resp; - } stats; - struct fc_exch **exches; /* for exch pointers indexed by xid */ -}; -#define fc_seq_exch(sp) container_of(sp, struct fc_exch, seq) - -static void fc_exch_rrq(struct fc_exch *); -static void fc_seq_ls_acc(struct fc_seq *); -static void fc_seq_ls_rjt(struct fc_seq *, enum fc_els_rjt_reason, - enum fc_els_rjt_explan); -static void fc_exch_els_rec(struct fc_seq *, struct fc_frame *); -static void fc_exch_els_rrq(struct fc_seq *, struct fc_frame *); -static struct fc_seq *fc_seq_start_next_locked(struct fc_seq *sp); - -/* - * Internal implementation notes. - * - * The exchange manager is one by default in libfc but LLD may choose - * to have one per CPU. The sequence manager is one per exchange manager - * and currently never separated. - * - * Section 9.8 in FC-FS-2 specifies: "The SEQ_ID is a one-byte field - * assigned by the Sequence Initiator that shall be unique for a specific - * D_ID and S_ID pair while the Sequence is open." Note that it isn't - * qualified by exchange ID, which one might think it would be. - * In practice this limits the number of open sequences and exchanges to 256 - * per session. For most targets we could treat this limit as per exchange. - * - * The exchange and its sequence are freed when the last sequence is received. - * It's possible for the remote port to leave an exchange open without - * sending any sequences. - * - * Notes on reference counts: - * - * Exchanges are reference counted and exchange gets freed when the reference - * count becomes zero. - * - * Timeouts: - * Sequences are timed out for E_D_TOV and R_A_TOV. - * - * Sequence event handling: - * - * The following events may occur on initiator sequences: - * - * Send. - * For now, the whole thing is sent. - * Receive ACK - * This applies only to class F. - * The sequence is marked complete. - * ULP completion. - * The upper layer calls fc_exch_done() when done - * with exchange and sequence tuple. - * RX-inferred completion. - * When we receive the next sequence on the same exchange, we can - * retire the previous sequence ID. (XXX not implemented). - * Timeout. - * R_A_TOV frees the sequence ID. If we're waiting for ACK, - * E_D_TOV causes abort and calls upper layer response handler - * with FC_EX_TIMEOUT error. - * Receive RJT - * XXX defer. - * Send ABTS - * On timeout. - * - * The following events may occur on recipient sequences: - * - * Receive - * Allocate sequence for first frame received. - * Hold during receive handler. - * Release when final frame received. - * Keep status of last N of these for the ELS RES command. XXX TBD. - * Receive ABTS - * Deallocate sequence - * Send RJT - * Deallocate - * - * For now, we neglect conditions where only part of a sequence was - * received or transmitted, or where out-of-order receipt is detected. - */ - -/* - * Locking notes: - * - * The EM code run in a per-CPU worker thread. - * - * To protect against concurrency between a worker thread code and timers, - * sequence allocation and deallocation must be locked. - * - exchange refcnt can be done atomicly without locks. - * - sequence allocation must be locked by exch lock. - * - If the em_lock and ex_lock must be taken at the same time, then the - * em_lock must be taken before the ex_lock. - */ - -/* - * opcode names for debugging. - */ -static char *fc_exch_rctl_names[] = FC_RCTL_NAMES_INIT; - -#define FC_TABLE_SIZE(x) (sizeof(x) / sizeof(x[0])) - -static inline const char *fc_exch_name_lookup(unsigned int op, char **table, - unsigned int max_index) -{ - const char *name = NULL; - - if (op < max_index) - name = table[op]; - if (!name) - name = "unknown"; - return name; -} - -static const char *fc_exch_rctl_name(unsigned int op) -{ - return fc_exch_name_lookup(op, fc_exch_rctl_names, - FC_TABLE_SIZE(fc_exch_rctl_names)); -} - -/* - * Hold an exchange - keep it from being freed. - */ -static void fc_exch_hold(struct fc_exch *ep) -{ - atomic_inc(&ep->ex_refcnt); -} - -/* - * setup fc hdr by initializing few more FC header fields and sof/eof. - * Initialized fields by this func: - * - fh_ox_id, fh_rx_id, fh_seq_id, fh_seq_cnt - * - sof and eof - */ -static void fc_exch_setup_hdr(struct fc_exch *ep, struct fc_frame *fp, - u32 f_ctl) -{ - struct fc_frame_header *fh = fc_frame_header_get(fp); - u16 fill; - - fr_sof(fp) = ep->class; - if (ep->seq.cnt) - fr_sof(fp) = fc_sof_normal(ep->class); - - if (f_ctl & FC_FC_END_SEQ) { - fr_eof(fp) = FC_EOF_T; - if (fc_sof_needs_ack(ep->class)) - fr_eof(fp) = FC_EOF_N; - /* - * Form f_ctl. - * The number of fill bytes to make the length a 4-byte - * multiple is the low order 2-bits of the f_ctl. - * The fill itself will have been cleared by the frame - * allocation. - * After this, the length will be even, as expected by - * the transport. - */ - fill = fr_len(fp) & 3; - if (fill) { - fill = 4 - fill; - /* TODO, this may be a problem with fragmented skb */ - skb_put(fp_skb(fp), fill); - hton24(fh->fh_f_ctl, f_ctl | fill); - } - } else { - WARN_ON(fr_len(fp) % 4 != 0); /* no pad to non last frame */ - fr_eof(fp) = FC_EOF_N; - } - - /* - * Initialize remainig fh fields - * from fc_fill_fc_hdr - */ - fh->fh_ox_id = htons(ep->oxid); - fh->fh_rx_id = htons(ep->rxid); - fh->fh_seq_id = ep->seq.id; - fh->fh_seq_cnt = htons(ep->seq.cnt); -} - - -/* - * Release a reference to an exchange. - * If the refcnt goes to zero and the exchange is complete, it is freed. - */ -static void fc_exch_release(struct fc_exch *ep) -{ - struct fc_exch_mgr *mp; - - 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); - mempool_free(ep, mp->ep_pool); - } -} - -static int fc_exch_done_locked(struct fc_exch *ep) -{ - int rc = 1; - - /* - * We must check for completion in case there are two threads - * tyring to complete this. But the rrq code will reuse the - * ep, and in that case we only clear the resp and set it as - * complete, so it can be reused by the timer to send the rrq. - */ - ep->resp = NULL; - if (ep->state & FC_EX_DONE) - return rc; - ep->esb_stat |= ESB_ST_COMPLETE; - - if (!(ep->esb_stat & ESB_ST_REC_QUAL)) { - ep->state |= FC_EX_DONE; - if (cancel_delayed_work(&ep->timeout_work)) - atomic_dec(&ep->ex_refcnt); /* drop hold for timer */ - rc = 0; - } - return rc; -} - -static void fc_exch_mgr_delete_ep(struct fc_exch *ep) -{ - struct fc_exch_mgr *mp; - - mp = ep->em; - spin_lock_bh(&mp->em_lock); - WARN_ON(mp->total_exches <= 0); - mp->total_exches--; - mp->exches[ep->xid - mp->min_xid] = NULL; - list_del(&ep->ex_list); - spin_unlock_bh(&mp->em_lock); - fc_exch_release(ep); /* drop hold for exch in mp */ -} - -/* - * Internal version of fc_exch_timer_set - used with lock held. - */ -static inline void fc_exch_timer_set_locked(struct fc_exch *ep, - unsigned int timer_msec) -{ - if (ep->state & (FC_EX_RST_CLEANUP | FC_EX_DONE)) - return; - - FC_DEBUG_EXCH("Exchange (%4x) timed out, notifying the upper layer\n", - ep->xid); - if (schedule_delayed_work(&ep->timeout_work, - msecs_to_jiffies(timer_msec))) - fc_exch_hold(ep); /* hold for timer */ -} - -/* - * Set timer for an exchange. - * The time is a minimum delay in milliseconds until the timer fires. - * Used for upper level protocols to time out the exchange. - * The timer is cancelled when it fires or when the exchange completes. - * Returns non-zero if a timer couldn't be allocated. - */ -static void fc_exch_timer_set(struct fc_exch *ep, unsigned int timer_msec) -{ - spin_lock_bh(&ep->ex_lock); - fc_exch_timer_set_locked(ep, timer_msec); - spin_unlock_bh(&ep->ex_lock); -} - -int fc_seq_exch_abort(const struct fc_seq *req_sp, unsigned int timer_msec) -{ - struct fc_seq *sp; - struct fc_exch *ep; - struct fc_frame *fp; - int error; - - ep = fc_seq_exch(req_sp); - - spin_lock_bh(&ep->ex_lock); - if (ep->esb_stat & (ESB_ST_COMPLETE | ESB_ST_ABNORMAL) || - ep->state & (FC_EX_DONE | FC_EX_RST_CLEANUP)) { - spin_unlock_bh(&ep->ex_lock); - return -ENXIO; - } - - /* - * Send the abort on a new sequence if possible. - */ - sp = fc_seq_start_next_locked(&ep->seq); - if (!sp) { - spin_unlock_bh(&ep->ex_lock); - return -ENOMEM; - } - - ep->esb_stat |= ESB_ST_SEQ_INIT | ESB_ST_ABNORMAL; - if (timer_msec) - fc_exch_timer_set_locked(ep, timer_msec); - spin_unlock_bh(&ep->ex_lock); - - /* - * If not logged into the fabric, don't send ABTS but leave - * sequence active until next timeout. - */ - if (!ep->sid) - return 0; - - /* - * Send an abort for the sequence that timed out. - */ - fp = fc_frame_alloc(ep->lp, 0); - if (fp) { - fc_fill_fc_hdr(fp, FC_RCTL_BA_ABTS, ep->did, ep->sid, - FC_TYPE_BLS, FC_FC_END_SEQ | FC_FC_SEQ_INIT, 0); - error = fc_seq_send(ep->lp, sp, fp); - } else - error = -ENOBUFS; - return error; -} -EXPORT_SYMBOL(fc_seq_exch_abort); - -/* - * Exchange timeout - handle exchange timer expiration. - * The timer will have been cancelled before this is called. - */ -static void fc_exch_timeout(struct work_struct *work) -{ - struct fc_exch *ep = container_of(work, struct fc_exch, - timeout_work.work); - struct fc_seq *sp = &ep->seq; - void (*resp)(struct fc_seq *, struct fc_frame *fp, void *arg); - void *arg; - u32 e_stat; - int rc = 1; - - spin_lock_bh(&ep->ex_lock); - if (ep->state & (FC_EX_RST_CLEANUP | FC_EX_DONE)) - goto unlock; - - e_stat = ep->esb_stat; - if (e_stat & ESB_ST_COMPLETE) { - ep->esb_stat = e_stat & ~ESB_ST_REC_QUAL; - 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->arg; - ep->resp = NULL; - if (e_stat & ESB_ST_ABNORMAL) - rc = fc_exch_done_locked(ep); - spin_unlock_bh(&ep->ex_lock); - if (!rc) - fc_exch_mgr_delete_ep(ep); - if (resp) - resp(sp, ERR_PTR(-FC_EX_TIMEOUT), arg); - fc_seq_exch_abort(sp, 2 * ep->r_a_tov); - goto done; - } -unlock: - spin_unlock_bh(&ep->ex_lock); -done: - /* - * This release matches the hold taken when the timer was set. - */ - fc_exch_release(ep); -} - -/* - * Allocate a sequence. - * - * We don't support multiple originated sequences on the same exchange. - * By implication, any previously originated sequence on this exchange - * is complete, and we reallocate the same sequence. - */ -static struct fc_seq *fc_seq_alloc(struct fc_exch *ep, u8 seq_id) -{ - struct fc_seq *sp; - - sp = &ep->seq; - sp->ssb_stat = 0; - sp->cnt = 0; - sp->id = seq_id; - return sp; -} - -/* - * fc_em_alloc_xid - returns an xid based on request type - * @lp : ptr to associated lport - * @fp : ptr to the assocated frame - * - * check the associated fc_fsp_pkt to get scsi command type and - * command direction to decide from which range this exch id - * will be allocated from. - * - * Returns : 0 or an valid xid - */ -static u16 fc_em_alloc_xid(struct fc_exch_mgr *mp, const struct fc_frame *fp) -{ - u16 xid, min, max; - u16 *plast; - struct fc_exch *ep = NULL; - - if (mp->max_read) { - if (fc_frame_is_read(fp)) { - min = mp->min_xid; - max = mp->max_read; - plast = &mp->last_read; - } else { - min = mp->max_read + 1; - max = mp->max_xid; - plast = &mp->last_xid; - } - } else { - min = mp->min_xid; - max = mp->max_xid; - plast = &mp->last_xid; - } - xid = *plast; - do { - xid = (xid == max) ? min : xid + 1; - ep = mp->exches[xid - mp->min_xid]; - } while ((ep != NULL) && (xid != *plast)); - - if (unlikely(ep)) - xid = 0; - else - *plast = xid; - - return xid; -} - -/* - * fc_exch_alloc - allocate an exchange. - * @mp : ptr to the exchange manager - * @xid: input xid - * - * if xid is supplied zero then assign next free exchange ID - * from exchange manager, otherwise use supplied xid. - * Returns with exch lock held. - */ -struct fc_exch *fc_exch_alloc(struct fc_exch_mgr *mp, - struct fc_frame *fp, u16 xid) -{ - struct fc_exch *ep; - - /* allocate memory for exchange */ - ep = mempool_alloc(mp->ep_pool, GFP_ATOMIC); - if (!ep) { - atomic_inc(&mp->stats.no_free_exch); - goto out; - } - memset(ep, 0, sizeof(*ep)); - - spin_lock_bh(&mp->em_lock); - /* alloc xid if input xid 0 */ - if (!xid) { - /* alloc a new xid */ - xid = fc_em_alloc_xid(mp, fp); - if (!xid) { - printk(KERN_ERR "fc_em_alloc_xid() failed\n"); - goto err; - } - } - - 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 - mp->min_xid] = ep; - list_add_tail(&ep->ex_list, &mp->ex_list); - fc_seq_alloc(ep, ep->seq_id++); - mp->total_exches++; - spin_unlock_bh(&mp->em_lock); - - /* - * update exchange - */ - ep->oxid = ep->xid = xid; - ep->em = mp; - ep->lp = mp->lp; - ep->f_ctl = FC_FC_FIRST_SEQ; /* next seq is first seq */ - ep->rxid = FC_XID_UNKNOWN; - ep->class = mp->class; - INIT_DELAYED_WORK(&ep->timeout_work, fc_exch_timeout); -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); - -/* - * Lookup and hold an exchange. - */ -static struct fc_exch *fc_exch_find(struct fc_exch_mgr *mp, u16 xid) -{ - struct fc_exch *ep = NULL; - - if ((xid >= mp->min_xid) && (xid <= mp->max_xid)) { - spin_lock_bh(&mp->em_lock); - ep = mp->exches[xid - mp->min_xid]; - if (ep) { - fc_exch_hold(ep); - WARN_ON(ep->xid != xid); - } - spin_unlock_bh(&mp->em_lock); - } - return ep; -} - -void fc_exch_done(struct fc_seq *sp) -{ - struct fc_exch *ep = fc_seq_exch(sp); - int rc; - - spin_lock_bh(&ep->ex_lock); - rc = fc_exch_done_locked(ep); - spin_unlock_bh(&ep->ex_lock); - if (!rc) - fc_exch_mgr_delete_ep(ep); -} -EXPORT_SYMBOL(fc_exch_done); - -/* - * Allocate a new exchange as responder. - * Sets the responder ID in the frame header. - */ -static struct fc_exch *fc_exch_resp(struct fc_exch_mgr *mp, struct fc_frame *fp) -{ - struct fc_exch *ep; - struct fc_frame_header *fh; - u16 rxid; - - ep = mp->lp->tt.exch_get(mp->lp, fp); - if (ep) { - ep->class = fc_frame_class(fp); - - /* - * Set EX_CTX indicating we're responding on this exchange. - */ - ep->f_ctl |= FC_FC_EX_CTX; /* we're responding */ - ep->f_ctl &= ~FC_FC_FIRST_SEQ; /* not new */ - fh = fc_frame_header_get(fp); - ep->sid = ntoh24(fh->fh_d_id); - ep->did = ntoh24(fh->fh_s_id); - ep->oid = ep->did; - - /* - * Allocated exchange has placed the XID in the - * originator field. Move it to the responder field, - * and set the originator XID from the frame. - */ - ep->rxid = ep->xid; - ep->oxid = ntohs(fh->fh_ox_id); - ep->esb_stat |= ESB_ST_RESP | ESB_ST_SEQ_INIT; - if ((ntoh24(fh->fh_f_ctl) & FC_FC_SEQ_INIT) == 0) - ep->esb_stat &= ~ESB_ST_SEQ_INIT; - - /* - * Set the responder ID in the frame header. - * The old one should've been 0xffff. - * If it isn't, don't assign one. - * Incoming basic link service frames may specify - * a referenced RX_ID. - */ - if (fh->fh_type != FC_TYPE_BLS) { - rxid = ntohs(fh->fh_rx_id); - 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; -} - -/* - * Find a sequence for receive where the other end is originating the sequence. - * If fc_pf_rjt_reason is FC_RJT_NONE then this function will have a hold - * on the ep that should be released by the caller. - */ -static enum fc_pf_rjt_reason -fc_seq_lookup_recip(struct fc_exch_mgr *mp, struct fc_frame *fp) -{ - struct fc_frame_header *fh = fc_frame_header_get(fp); - struct fc_exch *ep = NULL; - struct fc_seq *sp = NULL; - enum fc_pf_rjt_reason reject = FC_RJT_NONE; - u32 f_ctl; - u16 xid; - - f_ctl = ntoh24(fh->fh_f_ctl); - WARN_ON((f_ctl & FC_FC_SEQ_CTX) != 0); - - /* - * Lookup or create the exchange if we will be creating the sequence. - */ - if (f_ctl & FC_FC_EX_CTX) { - xid = ntohs(fh->fh_ox_id); /* we originated exch */ - ep = fc_exch_find(mp, xid); - if (!ep) { - atomic_inc(&mp->stats.xid_not_found); - reject = FC_RJT_OX_ID; - goto out; - } - if (ep->rxid == FC_XID_UNKNOWN) - ep->rxid = ntohs(fh->fh_rx_id); - else if (ep->rxid != ntohs(fh->fh_rx_id)) { - reject = FC_RJT_OX_ID; - goto rel; - } - } else { - xid = ntohs(fh->fh_rx_id); /* we are the responder */ - - /* - * Special case for MDS issuing an ELS TEST with a - * bad rxid of 0. - * XXX take this out once we do the proper reject. - */ - if (xid == 0 && fh->fh_r_ctl == FC_RCTL_ELS_REQ && - fc_frame_payload_op(fp) == ELS_TEST) { - fh->fh_rx_id = htons(FC_XID_UNKNOWN); - xid = FC_XID_UNKNOWN; - } - - /* - * new sequence - find the exchange - */ - ep = fc_exch_find(mp, xid); - if ((f_ctl & FC_FC_FIRST_SEQ) && fc_sof_is_init(fr_sof(fp))) { - if (ep) { - atomic_inc(&mp->stats.xid_busy); - reject = FC_RJT_RX_ID; - goto rel; - } - ep = fc_exch_resp(mp, fp); - if (!ep) { - reject = FC_RJT_EXCH_EST; /* XXX */ - goto out; - } - xid = ep->xid; /* get our XID */ - } else if (!ep) { - atomic_inc(&mp->stats.xid_not_found); - reject = FC_RJT_RX_ID; /* XID not found */ - goto out; - } - } - - /* - * At this point, we have the exchange held. - * Find or create the sequence. - */ - if (fc_sof_is_init(fr_sof(fp))) { - sp = fc_seq_start_next(&ep->seq); - if (!sp) { - reject = FC_RJT_SEQ_XS; /* exchange shortage */ - goto rel; - } - sp->id = fh->fh_seq_id; - sp->ssb_stat |= SSB_ST_RESP; - } else { - sp = &ep->seq; - if (sp->id != fh->fh_seq_id) { - atomic_inc(&mp->stats.seq_not_found); - reject = FC_RJT_SEQ_ID; /* sequence/exch should exist */ - goto rel; - } - } - WARN_ON(ep != fc_seq_exch(sp)); - - if (f_ctl & FC_FC_SEQ_INIT) - ep->esb_stat |= ESB_ST_SEQ_INIT; - - fr_seq(fp) = sp; -out: - return reject; -rel: - fc_exch_done(&ep->seq); - fc_exch_release(ep); /* hold from fc_exch_find/fc_exch_resp */ - return reject; -} - -/* - * Find the sequence for a frame being received. - * We originated the sequence, so it should be found. - * We may or may not have originated the exchange. - * Does not hold the sequence for the caller. - */ -static struct fc_seq *fc_seq_lookup_orig(struct fc_exch_mgr *mp, - struct fc_frame *fp) -{ - struct fc_frame_header *fh = fc_frame_header_get(fp); - struct fc_exch *ep; - struct fc_seq *sp = NULL; - u32 f_ctl; - u16 xid; - - f_ctl = ntoh24(fh->fh_f_ctl); - WARN_ON((f_ctl & FC_FC_SEQ_CTX) != FC_FC_SEQ_CTX); - xid = ntohs((f_ctl & FC_FC_EX_CTX) ? fh->fh_ox_id : fh->fh_rx_id); - ep = fc_exch_find(mp, xid); - if (!ep) - return NULL; - if (ep->seq.id == fh->fh_seq_id) { - /* - * Save the RX_ID if we didn't previously know it. - */ - sp = &ep->seq; - if ((f_ctl & FC_FC_EX_CTX) != 0 && - ep->rxid == FC_XID_UNKNOWN) { - ep->rxid = ntohs(fh->fh_rx_id); - } - } - fc_exch_release(ep); - return sp; -} - -/* - * Set addresses for an exchange. - * Note this must be done before the first sequence of the exchange is sent. - */ -static void fc_exch_set_addr(struct fc_exch *ep, - u32 orig_id, u32 resp_id) -{ - ep->oid = orig_id; - if (ep->esb_stat & ESB_ST_RESP) { - ep->sid = resp_id; - ep->did = orig_id; - } else { - ep->sid = orig_id; - ep->did = resp_id; - } -} - -static struct fc_seq *fc_seq_start_next_locked(struct fc_seq *sp) -{ - struct fc_exch *ep = fc_seq_exch(sp); - - sp = fc_seq_alloc(ep, ep->seq_id++); - FC_DEBUG_EXCH("exch %4x f_ctl %6x seq %2x\n", - ep->xid, ep->f_ctl, sp->id); - return sp; -} -/* - * Allocate a new sequence on the same exchange as the supplied sequence. - * This will never return NULL. - */ -struct fc_seq *fc_seq_start_next(struct fc_seq *sp) -{ - struct fc_exch *ep = fc_seq_exch(sp); - - spin_lock_bh(&ep->ex_lock); - WARN_ON((ep->esb_stat & ESB_ST_COMPLETE) != 0); - sp = fc_seq_start_next_locked(sp); - spin_unlock_bh(&ep->ex_lock); - - return sp; -} -EXPORT_SYMBOL(fc_seq_start_next); - -int fc_seq_send(struct fc_lport *lp, struct fc_seq *sp, struct fc_frame *fp) -{ - struct fc_exch *ep; - struct fc_frame_header *fh = fc_frame_header_get(fp); - int error; - u32 f_ctl; - - ep = fc_seq_exch(sp); - WARN_ON((ep->esb_stat & ESB_ST_SEQ_INIT) != ESB_ST_SEQ_INIT); - - f_ctl = ntoh24(fh->fh_f_ctl); - fc_exch_setup_hdr(ep, fp, f_ctl); - - /* - * update sequence count if this frame is carrying - * multiple FC frames when sequence offload is enabled - * by LLD. - */ - if (fr_max_payload(fp)) - sp->cnt += DIV_ROUND_UP((fr_len(fp) - sizeof(*fh)), - fr_max_payload(fp)); - else - sp->cnt++; - - /* - * Send the frame. - */ - error = lp->tt.frame_send(lp, fp); - - /* - * Update the exchange and sequence flags, - * assuming all frames for the sequence have been sent. - * We can only be called to send once for each sequence. - */ - spin_lock_bh(&ep->ex_lock); - ep->f_ctl = f_ctl & ~FC_FC_FIRST_SEQ; /* not first seq */ - if (f_ctl & (FC_FC_END_SEQ | FC_FC_SEQ_INIT)) - ep->esb_stat &= ~ESB_ST_SEQ_INIT; - spin_unlock_bh(&ep->ex_lock); - return error; -} -EXPORT_SYMBOL(fc_seq_send); - -void fc_seq_els_rsp_send(struct fc_seq *sp, enum fc_els_cmd els_cmd, - struct fc_seq_els_data *els_data) -{ - switch (els_cmd) { - case ELS_LS_RJT: - fc_seq_ls_rjt(sp, els_data->reason, els_data->explan); - break; - case ELS_LS_ACC: - fc_seq_ls_acc(sp); - break; - case ELS_RRQ: - fc_exch_els_rrq(sp, els_data->fp); - break; - case ELS_REC: - fc_exch_els_rec(sp, els_data->fp); - break; - default: - FC_DBG("Invalid ELS CMD:%x\n", els_cmd); - } -} -EXPORT_SYMBOL(fc_seq_els_rsp_send); - -/* - * Send a sequence, which is also the last sequence in the exchange. - */ -static void fc_seq_send_last(struct fc_seq *sp, struct fc_frame *fp, - enum fc_rctl rctl, enum fc_fh_type fh_type) -{ - u32 f_ctl; - struct fc_exch *ep = fc_seq_exch(sp); - - f_ctl = FC_FC_LAST_SEQ | FC_FC_END_SEQ | FC_FC_SEQ_INIT; - f_ctl |= ep->f_ctl; - fc_fill_fc_hdr(fp, rctl, ep->did, ep->sid, fh_type, f_ctl, 0); - fc_seq_send(ep->lp, sp, fp); -} - -/* - * Send ACK_1 (or equiv.) indicating we received something. - * The frame we're acking is supplied. - */ -static void fc_seq_send_ack(struct fc_seq *sp, const struct fc_frame *rx_fp) -{ - struct fc_frame *fp; - struct fc_frame_header *rx_fh; - struct fc_frame_header *fh; - struct fc_exch *ep = fc_seq_exch(sp); - struct fc_lport *lp = ep->lp; - unsigned int f_ctl; - - /* - * Don't send ACKs for class 3. - */ - if (fc_sof_needs_ack(fr_sof(rx_fp))) { - fp = fc_frame_alloc(lp, 0); - if (!fp) - return; - - fh = fc_frame_header_get(fp); - fh->fh_r_ctl = FC_RCTL_ACK_1; - fh->fh_type = FC_TYPE_BLS; - - /* - * Form f_ctl by inverting EX_CTX and SEQ_CTX (bits 23, 22). - * Echo FIRST_SEQ, LAST_SEQ, END_SEQ, END_CONN, SEQ_INIT. - * Bits 9-8 are meaningful (retransmitted or unidirectional). - * Last ACK uses bits 7-6 (continue sequence), - * bits 5-4 are meaningful (what kind of ACK to use). - */ - rx_fh = fc_frame_header_get(rx_fp); - f_ctl = ntoh24(rx_fh->fh_f_ctl); - f_ctl &= FC_FC_EX_CTX | FC_FC_SEQ_CTX | - FC_FC_FIRST_SEQ | FC_FC_LAST_SEQ | - FC_FC_END_SEQ | FC_FC_END_CONN | FC_FC_SEQ_INIT | - FC_FC_RETX_SEQ | FC_FC_UNI_TX; - f_ctl ^= FC_FC_EX_CTX | FC_FC_SEQ_CTX; - hton24(fh->fh_f_ctl, f_ctl); - - fc_exch_setup_hdr(ep, fp, f_ctl); - fh->fh_seq_id = rx_fh->fh_seq_id; - fh->fh_seq_cnt = rx_fh->fh_seq_cnt; - fh->fh_parm_offset = htonl(1); /* ack single frame */ - - fr_sof(fp) = fr_sof(rx_fp); - if (f_ctl & FC_FC_END_SEQ) - fr_eof(fp) = FC_EOF_T; - else - fr_eof(fp) = FC_EOF_N; - - (void) lp->tt.frame_send(lp, fp); - } -} - -/* - * Send BLS Reject. - * This is for rejecting BA_ABTS only. - */ -static void -fc_exch_send_ba_rjt(struct fc_frame *rx_fp, enum fc_ba_rjt_reason reason, - enum fc_ba_rjt_explan explan) -{ - struct fc_frame *fp; - struct fc_frame_header *rx_fh; - struct fc_frame_header *fh; - struct fc_ba_rjt *rp; - struct fc_lport *lp; - unsigned int f_ctl; - - lp = fr_dev(rx_fp); - fp = fc_frame_alloc(lp, sizeof(*rp)); - if (!fp) - return; - fh = fc_frame_header_get(fp); - rx_fh = fc_frame_header_get(rx_fp); - - memset(fh, 0, sizeof(*fh) + sizeof(*rp)); - - rp = fc_frame_payload_get(fp, sizeof(*rp)); - rp->br_reason = reason; - rp->br_explan = explan; - - /* - * seq_id, cs_ctl, df_ctl and param/offset are zero. - */ - memcpy(fh->fh_s_id, rx_fh->fh_d_id, 3); - memcpy(fh->fh_d_id, rx_fh->fh_s_id, 3); - fh->fh_ox_id = rx_fh->fh_rx_id; - fh->fh_rx_id = rx_fh->fh_ox_id; - fh->fh_seq_cnt = rx_fh->fh_seq_cnt; - fh->fh_r_ctl = FC_RCTL_BA_RJT; - fh->fh_type = FC_TYPE_BLS; - - /* - * Form f_ctl by inverting EX_CTX and SEQ_CTX (bits 23, 22). - * Echo FIRST_SEQ, LAST_SEQ, END_SEQ, END_CONN, SEQ_INIT. - * Bits 9-8 are meaningful (retransmitted or unidirectional). - * Last ACK uses bits 7-6 (continue sequence), - * bits 5-4 are meaningful (what kind of ACK to use). - * Always set LAST_SEQ, END_SEQ. - */ - f_ctl = ntoh24(rx_fh->fh_f_ctl); - f_ctl &= FC_FC_EX_CTX | FC_FC_SEQ_CTX | - FC_FC_END_CONN | FC_FC_SEQ_INIT | - FC_FC_RETX_SEQ | FC_FC_UNI_TX; - f_ctl ^= FC_FC_EX_CTX | FC_FC_SEQ_CTX; - f_ctl |= FC_FC_LAST_SEQ | FC_FC_END_SEQ; - f_ctl &= ~FC_FC_FIRST_SEQ; - hton24(fh->fh_f_ctl, f_ctl); - - fr_sof(fp) = fc_sof_class(fr_sof(rx_fp)); - fr_eof(fp) = FC_EOF_T; - if (fc_sof_needs_ack(fr_sof(fp))) - fr_eof(fp) = FC_EOF_N; - - (void) lp->tt.frame_send(lp, fp); -} - -/* - * Handle an incoming ABTS. This would be for target mode usually, - * but could be due to lost FCP transfer ready, confirm or RRQ. - * We always handle this as an exchange abort, ignoring the parameter. - */ -static void fc_exch_recv_abts(struct fc_exch *ep, struct fc_frame *rx_fp) -{ - struct fc_frame *fp; - struct fc_ba_acc *ap; - struct fc_frame_header *fh; - struct fc_seq *sp; - - if (!ep) - goto reject; - spin_lock_bh(&ep->ex_lock); - if (ep->esb_stat & ESB_ST_COMPLETE) { - spin_unlock_bh(&ep->ex_lock); - goto reject; - } - if (!(ep->esb_stat & ESB_ST_REC_QUAL)) - fc_exch_hold(ep); /* hold for REC_QUAL */ - ep->esb_stat |= ESB_ST_ABNORMAL | ESB_ST_REC_QUAL; - fc_exch_timer_set_locked(ep, ep->r_a_tov); - - fp = fc_frame_alloc(ep->lp, sizeof(*ap)); - if (!fp) { - spin_unlock_bh(&ep->ex_lock); - goto free; - } - fh = fc_frame_header_get(fp); - ap = fc_frame_payload_get(fp, sizeof(*ap)); - memset(ap, 0, sizeof(*ap)); - sp = &ep->seq; - ap->ba_high_seq_cnt = htons(0xffff); - if (sp->ssb_stat & SSB_ST_RESP) { - ap->ba_seq_id = sp->id; - ap->ba_seq_id_val = FC_BA_SEQ_ID_VAL; - ap->ba_high_seq_cnt = fh->fh_seq_cnt; - ap->ba_low_seq_cnt = htons(sp->cnt); - } - sp = fc_seq_start_next(sp); - spin_unlock_bh(&ep->ex_lock); - fc_seq_send_last(sp, fp, FC_RCTL_BA_ACC, FC_TYPE_BLS); - fc_frame_free(rx_fp); - return; - -reject: - fc_exch_send_ba_rjt(rx_fp, FC_BA_RJT_UNABLE, FC_BA_RJT_INV_XID); -free: - fc_frame_free(rx_fp); -} - -/* - * Handle receive where the other end is originating the sequence. - */ -static void fc_exch_recv_req(struct fc_lport *lp, struct fc_exch_mgr *mp, - struct fc_frame *fp) -{ - struct fc_frame_header *fh = fc_frame_header_get(fp); - struct fc_seq *sp = NULL; - struct fc_exch *ep = NULL; - enum fc_sof sof; - enum fc_eof eof; - u32 f_ctl; - enum fc_pf_rjt_reason reject; - - fr_seq(fp) = NULL; - reject = fc_seq_lookup_recip(mp, fp); - if (reject == FC_RJT_NONE) { - sp = fr_seq(fp); /* sequence will be held */ - ep = fc_seq_exch(sp); - sof = fr_sof(fp); - eof = fr_eof(fp); - f_ctl = ntoh24(fh->fh_f_ctl); - fc_seq_send_ack(sp, fp); - - /* - * Call the receive function. - * - * The receive function may allocate a new sequence - * over the old one, so we shouldn't change the - * sequence after this. - * - * The frame will be freed by the receive function. - * If new exch resp handler is valid then call that - * first. - */ - if (ep->resp) - ep->resp(sp, fp, ep->arg); - else - lp->tt.lport_recv(lp, sp, fp); - fc_exch_release(ep); /* release from lookup */ - } else { - FC_DEBUG_EXCH("exch/seq lookup failed: reject %x\n", reject); - fc_frame_free(fp); - } -} - -/* - * Handle receive where the other end is originating the sequence in - * response to our exchange. - */ -static void fc_exch_recv_seq_resp(struct fc_exch_mgr *mp, struct fc_frame *fp) -{ - struct fc_frame_header *fh = fc_frame_header_get(fp); - struct fc_seq *sp; - struct fc_exch *ep; - enum fc_sof sof; - u32 f_ctl; - void (*resp)(struct fc_seq *, struct fc_frame *fp, void *arg); - void *ex_resp_arg; - int rc; - - ep = fc_exch_find(mp, ntohs(fh->fh_ox_id)); - if (!ep) { - atomic_inc(&mp->stats.xid_not_found); - goto out; - } - if (ep->rxid == FC_XID_UNKNOWN) - ep->rxid = ntohs(fh->fh_rx_id); - if (ep->sid != 0 && ep->sid != ntoh24(fh->fh_d_id)) { - atomic_inc(&mp->stats.xid_not_found); - goto rel; - } - if (ep->did != ntoh24(fh->fh_s_id) && - ep->did != FC_FID_FLOGI) { - atomic_inc(&mp->stats.xid_not_found); - goto rel; - } - sof = fr_sof(fp); - if (fc_sof_is_init(sof)) { - sp = fc_seq_start_next(&ep->seq); - sp->id = fh->fh_seq_id; - sp->ssb_stat |= SSB_ST_RESP; - } else { - sp = &ep->seq; - if (sp->id != fh->fh_seq_id) { - atomic_inc(&mp->stats.seq_not_found); - goto rel; - } - } - f_ctl = ntoh24(fh->fh_f_ctl); - fr_seq(fp) = sp; - if (f_ctl & FC_FC_SEQ_INIT) - ep->esb_stat |= ESB_ST_SEQ_INIT; - - if (fc_sof_needs_ack(sof)) - fc_seq_send_ack(sp, fp); - resp = ep->resp; - 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)) == - (FC_FC_LAST_SEQ | FC_FC_END_SEQ)) { - spin_lock_bh(&ep->ex_lock); - rc = fc_exch_done_locked(ep); - WARN_ON(fc_seq_exch(sp) != ep); - spin_unlock_bh(&ep->ex_lock); - if (!rc) - fc_exch_mgr_delete_ep(ep); - } - - /* - * Call the receive function. - * The sequence is held (has a refcnt) for us, - * but not for the receive function. - * - * The receive function may allocate a new sequence - * over the old one, so we shouldn't change the - * sequence after this. - * - * The frame will be freed by the receive function. - * If new exch resp handler is valid then call that - * first. - */ - if (resp) - resp(sp, fp, ex_resp_arg); - else - fc_frame_free(fp); - fc_exch_release(ep); - return; -rel: - fc_exch_release(ep); -out: - fc_frame_free(fp); -} - -/* - * Handle receive for a sequence where other end is responding to our sequence. - */ -static void fc_exch_recv_resp(struct fc_exch_mgr *mp, struct fc_frame *fp) -{ - struct fc_seq *sp; - - sp = fc_seq_lookup_orig(mp, fp); /* doesn't hold sequence */ - if (!sp) { - atomic_inc(&mp->stats.xid_not_found); - FC_DEBUG_EXCH("seq lookup failed\n"); - } else { - atomic_inc(&mp->stats.non_bls_resp); - FC_DEBUG_EXCH("non-BLS response to sequence"); - } - fc_frame_free(fp); -} - -/* - * Handle the response to an ABTS for exchange or sequence. - * This can be BA_ACC or BA_RJT. - */ -static void fc_exch_abts_resp(struct fc_exch *ep, struct fc_frame *fp) -{ - void (*resp)(struct fc_seq *, struct fc_frame *fp, void *arg); - void *ex_resp_arg; - struct fc_frame_header *fh; - struct fc_ba_acc *ap; - struct fc_seq *sp; - u16 low; - u16 high; - int rc = 1, has_rec = 0; - - fh = fc_frame_header_get(fp); - FC_DEBUG_EXCH("exch: BLS rctl %x - %s\n", - fh->fh_r_ctl, fc_exch_rctl_name(fh->fh_r_ctl)); - - if (cancel_delayed_work_sync(&ep->timeout_work)) - fc_exch_release(ep); /* release from pending timer hold */ - - spin_lock_bh(&ep->ex_lock); - switch (fh->fh_r_ctl) { - case FC_RCTL_BA_ACC: - ap = fc_frame_payload_get(fp, sizeof(*ap)); - if (!ap) - break; - - /* - * Decide whether to establish a Recovery Qualifier. - * We do this if there is a non-empty SEQ_CNT range and - * SEQ_ID is the same as the one we aborted. - */ - low = ntohs(ap->ba_low_seq_cnt); - high = ntohs(ap->ba_high_seq_cnt); - if ((ep->esb_stat & ESB_ST_REC_QUAL) == 0 && - (ap->ba_seq_id_val != FC_BA_SEQ_ID_VAL || - ap->ba_seq_id == ep->seq_id) && low != high) { - ep->esb_stat |= ESB_ST_REC_QUAL; - fc_exch_hold(ep); /* hold for recovery qualifier */ - has_rec = 1; - } - break; - case FC_RCTL_BA_RJT: - break; - default: - break; - } - - resp = ep->resp; - ex_resp_arg = ep->arg; - - /* do we need to do some other checks here. Can we reuse more of - * fc_exch_recv_seq_resp - */ - sp = &ep->seq; - /* - * do we want to check END_SEQ as well as LAST_SEQ here? - */ - 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); - if (!rc) - fc_exch_mgr_delete_ep(ep); - - if (resp) - resp(sp, fp, ex_resp_arg); - else - fc_frame_free(fp); - - if (has_rec) - fc_exch_timer_set(ep, ep->r_a_tov); - -} - -/* - * Receive BLS sequence. - * This is always a sequence initiated by the remote side. - * We may be either the originator or recipient of the exchange. - */ -static void fc_exch_recv_bls(struct fc_exch_mgr *mp, struct fc_frame *fp) -{ - struct fc_frame_header *fh; - struct fc_exch *ep; - u32 f_ctl; - - fh = fc_frame_header_get(fp); - f_ctl = ntoh24(fh->fh_f_ctl); - fr_seq(fp) = NULL; - - ep = fc_exch_find(mp, (f_ctl & FC_FC_EX_CTX) ? - ntohs(fh->fh_ox_id) : ntohs(fh->fh_rx_id)); - if (ep && (f_ctl & FC_FC_SEQ_INIT)) { - spin_lock_bh(&ep->ex_lock); - ep->esb_stat |= ESB_ST_SEQ_INIT; - spin_unlock_bh(&ep->ex_lock); - } - if (f_ctl & FC_FC_SEQ_CTX) { - /* - * A response to a sequence we initiated. - * This should only be ACKs for class 2 or F. - */ - switch (fh->fh_r_ctl) { - case FC_RCTL_ACK_1: - case FC_RCTL_ACK_0: - break; - default: - FC_DEBUG_EXCH("BLS rctl %x - %s received", - fh->fh_r_ctl, - fc_exch_rctl_name(fh->fh_r_ctl)); - break; - } - fc_frame_free(fp); - } else { - switch (fh->fh_r_ctl) { - case FC_RCTL_BA_RJT: - case FC_RCTL_BA_ACC: - if (ep) - fc_exch_abts_resp(ep, fp); - else - fc_frame_free(fp); - break; - case FC_RCTL_BA_ABTS: - fc_exch_recv_abts(ep, fp); - break; - default: /* ignore junk */ - fc_frame_free(fp); - break; - } - } - if (ep) - fc_exch_release(ep); /* release hold taken by fc_exch_find */ -} - -/* - * Accept sequence with LS_ACC. - * If this fails due to allocation or transmit congestion, assume the - * originator will repeat the sequence. - */ -static void fc_seq_ls_acc(struct fc_seq *req_sp) -{ - struct fc_seq *sp; - struct fc_els_ls_acc *acc; - struct fc_frame *fp; - - sp = fc_seq_start_next(req_sp); - fp = fc_frame_alloc(fc_seq_exch(sp)->lp, sizeof(*acc)); - if (fp) { - acc = fc_frame_payload_get(fp, sizeof(*acc)); - memset(acc, 0, sizeof(*acc)); - acc->la_cmd = ELS_LS_ACC; - fc_seq_send_last(sp, fp, FC_RCTL_ELS_REP, FC_TYPE_ELS); - } -} - -/* - * Reject sequence with ELS LS_RJT. - * If this fails due to allocation or transmit congestion, assume the - * originator will repeat the sequence. - */ -static void fc_seq_ls_rjt(struct fc_seq *req_sp, enum fc_els_rjt_reason reason, - enum fc_els_rjt_explan explan) -{ - struct fc_seq *sp; - struct fc_els_ls_rjt *rjt; - struct fc_frame *fp; - - sp = fc_seq_start_next(req_sp); - fp = fc_frame_alloc(fc_seq_exch(sp)->lp, sizeof(*rjt)); - if (fp) { - rjt = fc_frame_payload_get(fp, sizeof(*rjt)); - memset(rjt, 0, sizeof(*rjt)); - rjt->er_cmd = ELS_LS_RJT; - rjt->er_reason = reason; - rjt->er_explan = explan; - fc_seq_send_last(sp, fp, FC_RCTL_ELS_REP, FC_TYPE_ELS); - } -} - -static void fc_exch_reset(struct fc_exch *ep) -{ - struct fc_seq *sp; - void (*resp)(struct fc_seq *, struct fc_frame *, void *); - void *arg; - int rc = 1; - - spin_lock_bh(&ep->ex_lock); - ep->state |= FC_EX_RST_CLEANUP; - /* - * we really want to call del_timer_sync, but cannot due - * to the lport calling with the lport lock held (some resp - * functions can also grab the lport lock which could cause - * a deadlock). - */ - if (cancel_delayed_work(&ep->timeout_work)) - atomic_dec(&ep->ex_refcnt); /* drop hold for timer */ - resp = ep->resp; - ep->resp = NULL; - 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->arg; - sp = &ep->seq; - rc = fc_exch_done_locked(ep); - spin_unlock_bh(&ep->ex_lock); - if (!rc) - fc_exch_mgr_delete_ep(ep); - - if (resp) - resp(sp, ERR_PTR(-FC_EX_CLOSED), arg); -} - -/* - * Reset an exchange manager, releasing all sequences and exchanges. - * If sid is non-zero, reset only exchanges we source from that FID. - * If did is non-zero, reset only exchanges destined to that FID. - */ -void fc_exch_mgr_reset(struct fc_exch_mgr *mp, u32 sid, u32 did) -{ - struct fc_exch *ep; - struct fc_exch *next; - - spin_lock_bh(&mp->em_lock); -restart: - list_for_each_entry_safe(ep, next, &mp->ex_list, ex_list) { - if ((sid == 0 || sid == ep->sid) && - (did == 0 || did == ep->did)) { - fc_exch_hold(ep); - spin_unlock_bh(&mp->em_lock); - - fc_exch_reset(ep); - - fc_exch_release(ep); - spin_lock_bh(&mp->em_lock); - - /* - * must restart loop incase while lock was down - * multiple eps were released. - */ - goto restart; - } - } - spin_unlock_bh(&mp->em_lock); -} -EXPORT_SYMBOL(fc_exch_mgr_reset); - -/* - * Handle incoming ELS REC - Read Exchange Concise. - * Note that the requesting port may be different than the S_ID in the request. - */ -static void fc_exch_els_rec(struct fc_seq *sp, struct fc_frame *rfp) -{ - struct fc_frame *fp; - struct fc_exch *ep; - struct fc_exch_mgr *em; - struct fc_els_rec *rp; - struct fc_els_rec_acc *acc; - enum fc_els_rjt_reason reason = ELS_RJT_LOGIC; - enum fc_els_rjt_explan explan; - u32 sid; - u16 rxid; - u16 oxid; - - rp = fc_frame_payload_get(rfp, sizeof(*rp)); - explan = ELS_EXPL_INV_LEN; - if (!rp) - goto reject; - sid = ntoh24(rp->rec_s_id); - rxid = ntohs(rp->rec_rx_id); - oxid = ntohs(rp->rec_ox_id); - - /* - * Currently it's hard to find the local S_ID from the exchange - * manager. This will eventually be fixed, but for now it's easier - * to lookup the subject exchange twice, once as if we were - * the initiator, and then again if we weren't. - */ - em = fc_seq_exch(sp)->em; - ep = fc_exch_find(em, oxid); - explan = ELS_EXPL_OXID_RXID; - if (ep && ep->oid == sid) { - if (ep->rxid != FC_XID_UNKNOWN && - rxid != FC_XID_UNKNOWN && - ep->rxid != rxid) - goto rel; - } else { - if (ep) - fc_exch_release(ep); - ep = NULL; - if (rxid != FC_XID_UNKNOWN) - ep = fc_exch_find(em, rxid); - if (!ep) - goto reject; - } - - fp = fc_frame_alloc(fc_seq_exch(sp)->lp, sizeof(*acc)); - if (!fp) { - fc_exch_done(sp); - goto out; - } - sp = fc_seq_start_next(sp); - acc = fc_frame_payload_get(fp, sizeof(*acc)); - memset(acc, 0, sizeof(*acc)); - acc->reca_cmd = ELS_LS_ACC; - acc->reca_ox_id = rp->rec_ox_id; - memcpy(acc->reca_ofid, rp->rec_s_id, 3); - acc->reca_rx_id = htons(ep->rxid); - if (ep->sid == ep->oid) - hton24(acc->reca_rfid, ep->did); - else - hton24(acc->reca_rfid, ep->sid); - acc->reca_fc4value = htonl(ep->seq.rec_data); - acc->reca_e_stat = htonl(ep->esb_stat & (ESB_ST_RESP | - ESB_ST_SEQ_INIT | - ESB_ST_COMPLETE)); - sp = fc_seq_start_next(sp); - fc_seq_send_last(sp, fp, FC_RCTL_ELS_REP, FC_TYPE_ELS); -out: - fc_exch_release(ep); - fc_frame_free(rfp); - return; - -rel: - fc_exch_release(ep); -reject: - fc_seq_ls_rjt(sp, reason, explan); - fc_frame_free(rfp); -} - -/* - * Handle response from RRQ. - * Not much to do here, really. - * Should report errors. - * - * TODO: fix error handler. - */ -static void fc_exch_rrq_resp(struct fc_seq *sp, struct fc_frame *fp, void *arg) -{ - struct fc_exch *aborted_ep = arg; - unsigned int op; - - if (IS_ERR(fp)) { - int err = PTR_ERR(fp); - - if (err == -FC_EX_CLOSED) - goto cleanup; - FC_DBG("Cannot process RRQ, because of frame error %d\n", err); - return; - } - - op = fc_frame_payload_op(fp); - fc_frame_free(fp); - - switch (op) { - case ELS_LS_RJT: - FC_DBG("LS_RJT for RRQ"); - /* fall through */ - case ELS_LS_ACC: - goto cleanup; - default: - FC_DBG("unexpected response op %x for RRQ", op); - return; - } - -cleanup: - fc_exch_done(&aborted_ep->seq); - /* drop hold for rec qual */ - fc_exch_release(aborted_ep); -} - -/* - * Send ELS RRQ - Reinstate Recovery Qualifier. - * This tells the remote port to stop blocking the use of - * the exchange and the seq_cnt range. - */ -static void fc_exch_rrq(struct fc_exch *ep) -{ - struct fc_lport *lp; - struct fc_els_rrq *rrq; - struct fc_frame *fp; - struct fc_seq *rrq_sp; - u32 did; - - lp = ep->lp; - - fp = fc_frame_alloc(lp, sizeof(*rrq)); - if (!fp) - return; - rrq = fc_frame_payload_get(fp, sizeof(*rrq)); - memset(rrq, 0, sizeof(*rrq)); - rrq->rrq_cmd = ELS_RRQ; - hton24(rrq->rrq_s_id, ep->sid); - rrq->rrq_ox_id = htons(ep->oxid); - rrq->rrq_rx_id = htons(ep->rxid); - - did = ep->did; - if (ep->esb_stat & ESB_ST_RESP) - did = ep->sid; - - fc_fill_fc_hdr(fp, FC_RCTL_ELS_REQ, did, - fc_host_port_id(lp->host), FC_TYPE_ELS, - FC_FC_FIRST_SEQ | FC_FC_END_SEQ | FC_FC_SEQ_INIT, 0); - - rrq_sp = fc_exch_seq_send(lp, fp, fc_exch_rrq_resp, NULL, ep, - lp->e_d_tov); - if (!rrq_sp) { - ep->esb_stat |= ESB_ST_REC_QUAL; - fc_exch_timer_set_locked(ep, ep->r_a_tov); - return; - } -} - - -/* - * Handle incoming ELS RRQ - Reset Recovery Qualifier. - */ -static void fc_exch_els_rrq(struct fc_seq *sp, struct fc_frame *fp) -{ - struct fc_exch *ep; /* request or subject exchange */ - struct fc_els_rrq *rp; - u32 sid; - u16 xid; - enum fc_els_rjt_explan explan; - - rp = fc_frame_payload_get(fp, sizeof(*rp)); - explan = ELS_EXPL_INV_LEN; - if (!rp) - goto reject; - - /* - * lookup subject exchange. - */ - ep = fc_seq_exch(sp); - sid = ntoh24(rp->rrq_s_id); /* subject source */ - xid = ep->did == sid ? ntohs(rp->rrq_ox_id) : ntohs(rp->rrq_rx_id); - ep = fc_exch_find(ep->em, xid); - - explan = ELS_EXPL_OXID_RXID; - if (!ep) - goto reject; - spin_lock_bh(&ep->ex_lock); - if (ep->oxid != ntohs(rp->rrq_ox_id)) - goto unlock_reject; - if (ep->rxid != ntohs(rp->rrq_rx_id) && - ep->rxid != FC_XID_UNKNOWN) - goto unlock_reject; - explan = ELS_EXPL_SID; - if (ep->sid != sid) - goto unlock_reject; - - /* - * Clear Recovery Qualifier state, and cancel timer if complete. - */ - if (ep->esb_stat & ESB_ST_REC_QUAL) { - ep->esb_stat &= ~ESB_ST_REC_QUAL; - atomic_dec(&ep->ex_refcnt); /* drop hold for rec qual */ - } - if (ep->esb_stat & ESB_ST_COMPLETE) { - if (cancel_delayed_work(&ep->timeout_work)) - atomic_dec(&ep->ex_refcnt); /* drop timer hold */ - } - - spin_unlock_bh(&ep->ex_lock); - - /* - * Send LS_ACC. - */ - fc_seq_ls_acc(sp); - fc_frame_free(fp); - return; - -unlock_reject: - spin_unlock_bh(&ep->ex_lock); - fc_exch_release(ep); /* drop hold from fc_exch_find */ -reject: - fc_seq_ls_rjt(sp, ELS_RJT_LOGIC, explan); - fc_frame_free(fp); -} - -struct fc_exch_mgr *fc_exch_mgr_alloc(struct fc_lport *lp, - enum fc_class class, - u16 min_xid, u16 max_xid) -{ - struct fc_exch_mgr *mp; - size_t len; - - if (max_xid <= min_xid || min_xid == 0 || max_xid == FC_XID_UNKNOWN) { - FC_DBG("Invalid min_xid 0x:%x and max_xid 0x:%x\n", - min_xid, max_xid); - return NULL; - } - - /* - * Memory need for EM - */ -#define xid_ok(i, m1, m2) (((i) >= (m1)) && ((i) <= (m2))) - len = (max_xid - min_xid + 1) * (sizeof(struct fc_exch *)); - len += sizeof(struct fc_exch_mgr); - - mp = kzalloc(len, GFP_ATOMIC); - if (!mp) - return NULL; - - mp->class = class; - mp->total_exches = 0; - mp->exches = (struct fc_exch **)(mp + 1); - mp->lp = lp; - /* adjust em exch xid range for offload */ - mp->min_xid = min_xid; - mp->max_xid = max_xid; - mp->last_xid = min_xid - 1; - mp->max_read = 0; - mp->last_read = 0; - if (lp->lro_enabled && xid_ok(lp->lro_xid, min_xid, max_xid)) { - mp->max_read = lp->lro_xid; - mp->last_read = min_xid - 1; - mp->last_xid = mp->max_read; - } else { - /* disable lro if no xid control over read */ - lp->lro_enabled = 0; - } - - INIT_LIST_HEAD(&mp->ex_list); - spin_lock_init(&mp->em_lock); - - mp->ep_pool = mempool_create_slab_pool(2, fc_em_cachep); - if (!mp->ep_pool) - goto free_mp; - - return mp; - -free_mp: - kfree(mp); - return NULL; -} -EXPORT_SYMBOL(fc_exch_mgr_alloc); - -void fc_exch_mgr_free(struct fc_exch_mgr *mp) -{ - WARN_ON(!mp); - /* - * The total exch count must be zero - * before freeing exchange manager. - */ - WARN_ON(mp->total_exches != 0); - mempool_destroy(mp->ep_pool); - kfree(mp); -} -EXPORT_SYMBOL(fc_exch_mgr_free); - -struct fc_exch *fc_exch_get(struct fc_lport *lp, struct fc_frame *fp) -{ - if (!lp || !lp->emp) - return NULL; - - return fc_exch_alloc(lp->emp, fp, 0); -} -EXPORT_SYMBOL(fc_exch_get); - -struct fc_seq *fc_exch_seq_send(struct fc_lport *lp, - struct fc_frame *fp, - void (*resp)(struct fc_seq *, - struct fc_frame *fp, - void *arg), - void (*destructor)(struct fc_seq *, void *), - void *arg, u32 timer_msec) -{ - struct fc_exch *ep; - struct fc_seq *sp = NULL; - struct fc_frame_header *fh; - int rc = 1; - - ep = lp->tt.exch_get(lp, fp); - if (!ep) { - fc_frame_free(fp); - return NULL; - } - ep->esb_stat |= ESB_ST_SEQ_INIT; - fh = fc_frame_header_get(fp); - fc_exch_set_addr(ep, ntoh24(fh->fh_s_id), ntoh24(fh->fh_d_id)); - ep->resp = resp; - ep->destructor = destructor; - ep->arg = arg; - ep->r_a_tov = FC_DEF_R_A_TOV; - ep->lp = lp; - sp = &ep->seq; - - ep->fh_type = fh->fh_type; /* save for possbile timeout handling */ - ep->f_ctl = ntoh24(fh->fh_f_ctl); - fc_exch_setup_hdr(ep, fp, ep->f_ctl); - sp->cnt++; - - if (unlikely(lp->tt.frame_send(lp, fp))) - goto err; - - if (timer_msec) - fc_exch_timer_set_locked(ep, timer_msec); - ep->f_ctl &= ~FC_FC_FIRST_SEQ; /* not first seq */ - - if (ep->f_ctl & FC_FC_SEQ_INIT) - ep->esb_stat &= ~ESB_ST_SEQ_INIT; - spin_unlock_bh(&ep->ex_lock); - return sp; -err: - 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); - -/* - * Receive a frame - */ -void fc_exch_recv(struct fc_lport *lp, struct fc_exch_mgr *mp, - struct fc_frame *fp) -{ - 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); - return; - } - - /* - * If frame is marked invalid, just drop it. - */ - f_ctl = ntoh24(fh->fh_f_ctl); - switch (fr_eof(fp)) { - case FC_EOF_T: - if (f_ctl & FC_FC_END_SEQ) - skb_trim(fp_skb(fp), fr_len(fp) - FC_FC_FILL(f_ctl)); - /* fall through */ - case FC_EOF_N: - if (fh->fh_type == FC_TYPE_BLS) - fc_exch_recv_bls(mp, fp); - else if ((f_ctl & (FC_FC_EX_CTX | FC_FC_SEQ_CTX)) == - FC_FC_EX_CTX) - fc_exch_recv_seq_resp(mp, fp); - else if (f_ctl & FC_FC_SEQ_CTX) - fc_exch_recv_resp(mp, fp); - else - fc_exch_recv_req(lp, mp, fp); - break; - default: - FC_DBG("dropping invalid frame (eof %x)", fr_eof(fp)); - fc_frame_free(fp); - break; - } -} -EXPORT_SYMBOL(fc_exch_recv); - -int fc_exch_init(struct fc_lport *lp) -{ - if (!lp->tt.exch_get) { - /* - * exch_put() should be NULL if - * exch_get() is NULL - */ - WARN_ON(lp->tt.exch_put); - lp->tt.exch_get = fc_exch_get; - } - - if (!lp->tt.seq_start_next) - lp->tt.seq_start_next = fc_seq_start_next; - - if (!lp->tt.exch_seq_send) - lp->tt.exch_seq_send = fc_exch_seq_send; - - if (!lp->tt.seq_send) - lp->tt.seq_send = fc_seq_send; - - if (!lp->tt.seq_els_rsp_send) - lp->tt.seq_els_rsp_send = fc_seq_els_rsp_send; - - if (!lp->tt.exch_done) - lp->tt.exch_done = fc_exch_done; - - if (!lp->tt.exch_mgr_reset) - lp->tt.exch_mgr_reset = fc_exch_mgr_reset; - - if (!lp->tt.seq_exch_abort) - lp->tt.seq_exch_abort = fc_seq_exch_abort; - - return 0; -} -EXPORT_SYMBOL(fc_exch_init); - -int fc_setup_exch_mgr(void) -{ - fc_em_cachep = kmem_cache_create("libfc_em", sizeof(struct fc_exch), - 0, SLAB_HWCACHE_ALIGN, NULL); - if (!fc_em_cachep) - return -ENOMEM; - return 0; -} - -void fc_destroy_exch_mgr(void) -{ - kmem_cache_destroy(fc_em_cachep); -} diff --git a/drivers/scsi/libfc/fc_fcp.c b/drivers/scsi/libfc/fc_fcp.c deleted file mode 100644 index 031bf9e65fbd..000000000000 --- a/drivers/scsi/libfc/fc_fcp.c +++ /dev/null @@ -1,2130 +0,0 @@ -/* - * 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, - * 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 <linux/module.h> -#include <linux/kernel.h> -#include <linux/types.h> -#include <linux/spinlock.h> -#include <linux/scatterlist.h> -#include <linux/err.h> -#include <linux/crc32.h> - -#include <scsi/scsi_tcq.h> -#include <scsi/scsi.h> -#include <scsi/scsi_host.h> -#include <scsi/scsi_device.h> -#include <scsi/scsi_cmnd.h> - -#include <scsi/fc/fc_fc2.h> - -#include <scsi/libfc.h> -#include <scsi/fc_encode.h> - -MODULE_AUTHOR("Open-FCoE.org"); -MODULE_DESCRIPTION("libfc"); -MODULE_LICENSE("GPL"); - -static int fc_fcp_debug; - -#define FC_DEBUG_FCP(fmt...) \ - do { \ - if (fc_fcp_debug) \ - FC_DBG(fmt); \ - } while (0) - -static struct kmem_cache *scsi_pkt_cachep; - -/* SRB state definitions */ -#define FC_SRB_FREE 0 /* cmd is free */ -#define FC_SRB_CMD_SENT (1 << 0) /* cmd has been sent */ -#define FC_SRB_RCV_STATUS (1 << 1) /* response has arrived */ -#define FC_SRB_ABORT_PENDING (1 << 2) /* cmd abort sent to device */ -#define FC_SRB_ABORTED (1 << 3) /* abort acknowleged */ -#define FC_SRB_DISCONTIG (1 << 4) /* non-sequential data recvd */ -#define FC_SRB_COMPL (1 << 5) /* fc_io_compl has been run */ -#define FC_SRB_FCP_PROCESSING_TMO (1 << 6) /* timer function processing */ -#define FC_SRB_NOMEM (1 << 7) /* dropped to out of mem */ - -#define FC_SRB_READ (1 << 1) -#define FC_SRB_WRITE (1 << 0) - -/* - * The SCp.ptr should be tested and set under the host lock. NULL indicates - * that the command has been retruned to the scsi layer. - */ -#define CMD_SP(Cmnd) ((struct fc_fcp_pkt *)(Cmnd)->SCp.ptr) -#define CMD_ENTRY_STATUS(Cmnd) ((Cmnd)->SCp.have_data_in) -#define CMD_COMPL_STATUS(Cmnd) ((Cmnd)->SCp.this_residual) -#define CMD_SCSI_STATUS(Cmnd) ((Cmnd)->SCp.Status) -#define CMD_RESID_LEN(Cmnd) ((Cmnd)->SCp.buffers_residual) - -struct fc_fcp_internal { - mempool_t *scsi_pkt_pool; - struct list_head scsi_pkt_queue; - u8 throttled; -}; - -#define fc_get_scsi_internal(x) ((struct fc_fcp_internal *)(x)->scsi_priv) - -/* - * function prototypes - * FC scsi I/O related functions - */ -static void fc_fcp_recv_data(struct fc_fcp_pkt *, struct fc_frame *); -static void fc_fcp_recv(struct fc_seq *, struct fc_frame *, void *); -static void fc_fcp_resp(struct fc_fcp_pkt *, struct fc_frame *); -static void fc_fcp_complete_locked(struct fc_fcp_pkt *); -static void fc_tm_done(struct fc_seq *, struct fc_frame *, void *); -static void fc_fcp_error(struct fc_fcp_pkt *fsp, struct fc_frame *fp); -static void fc_timeout_error(struct fc_fcp_pkt *); -static void fc_fcp_timeout(unsigned long data); -static void fc_fcp_rec(struct fc_fcp_pkt *); -static void fc_fcp_rec_error(struct fc_fcp_pkt *, struct fc_frame *); -static void fc_fcp_rec_resp(struct fc_seq *, struct fc_frame *, void *); -static void fc_io_compl(struct fc_fcp_pkt *); - -static void fc_fcp_srr(struct fc_fcp_pkt *, enum fc_rctl, u32); -static void fc_fcp_srr_resp(struct fc_seq *, struct fc_frame *, void *); -static void fc_fcp_srr_error(struct fc_fcp_pkt *, struct fc_frame *); - -/* - * command status codes - */ -#define FC_COMPLETE 0 -#define FC_CMD_ABORTED 1 -#define FC_CMD_RESET 2 -#define FC_CMD_PLOGO 3 -#define FC_SNS_RCV 4 -#define FC_TRANS_ERR 5 -#define FC_DATA_OVRRUN 6 -#define FC_DATA_UNDRUN 7 -#define FC_ERROR 8 -#define FC_HRD_ERROR 9 -#define FC_CMD_TIME_OUT 10 - -/* - * Error recovery timeout values. - */ -#define FC_SCSI_ER_TIMEOUT (10 * HZ) -#define FC_SCSI_TM_TOV (10 * HZ) -#define FC_SCSI_REC_TOV (2 * HZ) -#define FC_HOST_RESET_TIMEOUT (30 * HZ) - -#define FC_MAX_ERROR_CNT 5 -#define FC_MAX_RECOV_RETRY 3 - -#define FC_FCP_DFLT_QUEUE_DEPTH 32 - -/** - * fc_fcp_pkt_alloc - allocation routine for scsi_pkt packet - * @lp: fc lport struct - * @gfp: gfp flags for allocation - * - * This is used by upper layer scsi driver. - * Return Value : scsi_pkt structure or null on allocation failure. - * Context : call from process context. no locking required. - */ -static struct fc_fcp_pkt *fc_fcp_pkt_alloc(struct fc_lport *lp, gfp_t gfp) -{ - struct fc_fcp_internal *si = fc_get_scsi_internal(lp); - struct fc_fcp_pkt *fsp; - - fsp = mempool_alloc(si->scsi_pkt_pool, gfp); - if (fsp) { - memset(fsp, 0, sizeof(*fsp)); - fsp->lp = lp; - atomic_set(&fsp->ref_cnt, 1); - init_timer(&fsp->timer); - INIT_LIST_HEAD(&fsp->list); - spin_lock_init(&fsp->scsi_pkt_lock); - } - return fsp; -} - -/** - * fc_fcp_pkt_release - release hold on scsi_pkt packet - * @fsp: fcp packet struct - * - * This is used by upper layer scsi driver. - * Context : call from process and interrupt context. - * no locking required - */ -static void fc_fcp_pkt_release(struct fc_fcp_pkt *fsp) -{ - if (atomic_dec_and_test(&fsp->ref_cnt)) { - struct fc_fcp_internal *si = fc_get_scsi_internal(fsp->lp); - - mempool_free(fsp, si->scsi_pkt_pool); - } -} - -static void fc_fcp_pkt_hold(struct fc_fcp_pkt *fsp) -{ - atomic_inc(&fsp->ref_cnt); -} - -/** - * fc_fcp_pkt_destory - release hold on scsi_pkt packet - * - * @seq: 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 *seq, void *fsp) -{ - fc_fcp_pkt_release(fsp); -} - -/** - * fc_fcp_lock_pkt - lock a packet and get a ref to it. - * @fsp: fcp packet - * - * We should only return error if we return a command to scsi-ml before - * getting a response. This can happen in cases where we send a abort, but - * do not wait for the response and the abort and command can be passing - * each other on the wire/network-layer. - * - * Note: this function locks the packet and gets a reference to allow - * callers to call the completion function while the lock is held and - * not have to worry about the packets refcount. - * - * TODO: Maybe we should just have callers grab/release the lock and - * have a function that they call to verify the fsp and grab a ref if - * needed. - */ -static inline int fc_fcp_lock_pkt(struct fc_fcp_pkt *fsp) -{ - spin_lock_bh(&fsp->scsi_pkt_lock); - if (fsp->state & FC_SRB_COMPL) { - spin_unlock_bh(&fsp->scsi_pkt_lock); - return -EPERM; - } - - fc_fcp_pkt_hold(fsp); - return 0; -} - -static inline void fc_fcp_unlock_pkt(struct fc_fcp_pkt *fsp) -{ - spin_unlock_bh(&fsp->scsi_pkt_lock); - fc_fcp_pkt_release(fsp); -} - -static void fc_fcp_timer_set(struct fc_fcp_pkt *fsp, unsigned long delay) -{ - if (!(fsp->state & FC_SRB_COMPL)) - mod_timer(&fsp->timer, jiffies + delay); -} - -static int fc_fcp_send_abort(struct fc_fcp_pkt *fsp) -{ - if (!fsp->seq_ptr) - return -EINVAL; - - fsp->state |= FC_SRB_ABORT_PENDING; - return fsp->lp->tt.seq_exch_abort(fsp->seq_ptr, 0); -} - -/* - * Retry command. - * An abort isn't needed. - */ -static void fc_fcp_retry_cmd(struct fc_fcp_pkt *fsp) -{ - if (fsp->seq_ptr) { - fsp->lp->tt.exch_done(fsp->seq_ptr); - fsp->seq_ptr = NULL; - } - - fsp->state &= ~FC_SRB_ABORT_PENDING; - fsp->io_status = SUGGEST_RETRY << 24; - fsp->status_code = FC_ERROR; - fc_fcp_complete_locked(fsp); -} - -/* - * Receive SCSI data from target. - * Called after receiving solicited data. - */ -static void fc_fcp_recv_data(struct fc_fcp_pkt *fsp, struct fc_frame *fp) -{ - struct scsi_cmnd *sc = fsp->cmd; - struct fc_lport *lp = fsp->lp; - struct fcoe_dev_stats *stats; - struct fc_frame_header *fh; - size_t start_offset; - size_t offset; - u32 crc; - u32 copy_len = 0; - size_t len; - void *buf; - struct scatterlist *sg; - size_t remaining; - - fh = fc_frame_header_get(fp); - offset = ntohl(fh->fh_parm_offset); - start_offset = offset; - len = fr_len(fp) - sizeof(*fh); - buf = fc_frame_payload_get(fp, 0); - - if (offset + len > fsp->data_len) { - /* - * this should never happen - */ - if ((fr_flags(fp) & FCPHF_CRC_UNCHECKED) && - fc_frame_crc_check(fp)) - goto crc_err; - FC_DEBUG_FCP("data received past end. len %zx offset %zx " - "data_len %x\n", len, offset, fsp->data_len); - fc_fcp_retry_cmd(fsp); - return; - } - if (offset != fsp->xfer_len) - fsp->state |= FC_SRB_DISCONTIG; - - crc = 0; - if (fr_flags(fp) & FCPHF_CRC_UNCHECKED) - crc = crc32(~0, (u8 *) fh, sizeof(*fh)); - - sg = scsi_sglist(sc); - remaining = len; - - while (remaining > 0 && sg) { - size_t off; - void *page_addr; - size_t sg_bytes; - - if (offset >= sg->length) { - offset -= sg->length; - sg = sg_next(sg); - continue; - } - sg_bytes = min(remaining, sg->length - offset); - - /* - * The scatterlist item may be bigger than PAGE_SIZE, - * but we are limited to mapping PAGE_SIZE at a time. - */ - off = offset + sg->offset; - sg_bytes = min(sg_bytes, (size_t) - (PAGE_SIZE - (off & ~PAGE_MASK))); - page_addr = kmap_atomic(sg_page(sg) + (off >> PAGE_SHIFT), - KM_SOFTIRQ0); - if (!page_addr) - break; /* XXX panic? */ - - if (fr_flags(fp) & FCPHF_CRC_UNCHECKED) - crc = crc32(crc, buf, sg_bytes); - memcpy((char *)page_addr + (off & ~PAGE_MASK), buf, - sg_bytes); - - kunmap_atomic(page_addr, KM_SOFTIRQ0); - buf += sg_bytes; - offset += sg_bytes; - remaining -= sg_bytes; - copy_len += sg_bytes; - } - - if (fr_flags(fp) & FCPHF_CRC_UNCHECKED) { - buf = fc_frame_payload_get(fp, 0); - if (len % 4) { - crc = crc32(crc, buf + len, 4 - (len % 4)); - len += 4 - (len % 4); - } - - if (~crc != le32_to_cpu(fr_crc(fp))) { -crc_err: - stats = lp->dev_stats[smp_processor_id()]; - stats->ErrorFrames++; - if (stats->InvalidCRCCount++ < 5) - FC_DBG("CRC error on data frame\n"); - /* - * Assume the frame is total garbage. - * We may have copied it over the good part - * of the buffer. - * If so, we need to retry the entire operation. - * Otherwise, ignore it. - */ - if (fsp->state & FC_SRB_DISCONTIG) - fc_fcp_retry_cmd(fsp); - return; - } - } - - if (fsp->xfer_contig_end == start_offset) - fsp->xfer_contig_end += copy_len; - fsp->xfer_len += copy_len; - - /* - * In the very rare event that this data arrived after the response - * and completes the transfer, call the completion handler. - */ - if (unlikely(fsp->state & FC_SRB_RCV_STATUS) && - fsp->xfer_len == fsp->data_len - fsp->scsi_resid) - fc_fcp_complete_locked(fsp); -} - -/* - * fc_fcp_send_data - Send SCSI data to target. - * @fsp: ptr to fc_fcp_pkt - * @sp: ptr to this sequence - * @offset: starting offset for this data request - * @seq_blen: the burst length for this data request - * - * Called after receiving a Transfer Ready data descriptor. - * if LLD is capable of seq offload then send down seq_blen - * size of data in single frame, otherwise send multiple FC - * frames of max FC frame payload supported by target port. - * - * Returns : 0 for success. - */ -static int fc_fcp_send_data(struct fc_fcp_pkt *fsp, struct fc_seq *seq, - size_t offset, size_t seq_blen) -{ - struct fc_exch *ep; - struct scsi_cmnd *sc; - struct scatterlist *sg; - struct fc_frame *fp = NULL; - struct fc_lport *lp = fsp->lp; - size_t remaining; - size_t t_blen; - size_t tlen; - size_t sg_bytes; - size_t frame_offset, fh_parm_offset; - int error; - void *data = NULL; - void *page_addr; - int using_sg = lp->sg_supp; - u32 f_ctl; - - WARN_ON(seq_blen <= 0); - if (unlikely(offset + seq_blen > fsp->data_len)) { - /* this should never happen */ - FC_DEBUG_FCP("xfer-ready past end. seq_blen %zx offset %zx\n", - seq_blen, offset); - fc_fcp_send_abort(fsp); - return 0; - } else if (offset != fsp->xfer_len) { - /* Out of Order Data Request - no problem, but unexpected. */ - FC_DEBUG_FCP("xfer-ready non-contiguous. " - "seq_blen %zx offset %zx\n", seq_blen, offset); - } - - /* - * if LLD is capable of seq_offload then set transport - * burst length (t_blen) to seq_blen, otherwise set t_blen - * to max FC frame payload previously set in fsp->max_payload. - */ - t_blen = lp->seq_offload ? seq_blen : fsp->max_payload; - WARN_ON(t_blen < FC_MIN_MAX_PAYLOAD); - if (t_blen > 512) - t_blen &= ~(512 - 1); /* round down to block size */ - WARN_ON(t_blen < FC_MIN_MAX_PAYLOAD); /* won't go below 256 */ - sc = fsp->cmd; - - remaining = seq_blen; - fh_parm_offset = frame_offset = offset; - tlen = 0; - seq = lp->tt.seq_start_next(seq); - f_ctl = FC_FC_REL_OFF; - WARN_ON(!seq); - - /* - * If a get_page()/put_page() will fail, don't use sg lists - * in the fc_frame structure. - * - * The put_page() may be long after the I/O has completed - * in the case of FCoE, since the network driver does it - * via free_skb(). See the test in free_pages_check(). - * - * Test this case with 'dd </dev/zero >/dev/st0 bs=64k'. - */ - if (using_sg) { - for (sg = scsi_sglist(sc); sg; sg = sg_next(sg)) { - if (page_count(sg_page(sg)) == 0 || - (sg_page(sg)->flags & (1 << PG_lru | - 1 << PG_private | - 1 << PG_locked | - 1 << PG_active | - 1 << PG_slab | - 1 << PG_swapcache | - 1 << PG_writeback | - 1 << PG_reserved | - 1 << PG_buddy))) { - using_sg = 0; - break; - } - } - } - sg = scsi_sglist(sc); - - while (remaining > 0 && sg) { - if (offset >= sg->length) { - offset -= sg->length; - sg = sg_next(sg); - continue; - } - if (!fp) { - tlen = min(t_blen, remaining); - - /* - * TODO. Temporary workaround. fc_seq_send() can't - * handle odd lengths in non-linear skbs. - * This will be the final fragment only. - */ - if (tlen % 4) - using_sg = 0; - if (using_sg) { - fp = _fc_frame_alloc(lp, 0); - if (!fp) - return -ENOMEM; - } else { - fp = fc_frame_alloc(lp, tlen); - if (!fp) - return -ENOMEM; - - data = (void *)(fr_hdr(fp)) + - sizeof(struct fc_frame_header); - } - fh_parm_offset = frame_offset; - fr_max_payload(fp) = fsp->max_payload; - } - sg_bytes = min(tlen, sg->length - offset); - if (using_sg) { - WARN_ON(skb_shinfo(fp_skb(fp))->nr_frags > - FC_FRAME_SG_LEN); - get_page(sg_page(sg)); - skb_fill_page_desc(fp_skb(fp), - skb_shinfo(fp_skb(fp))->nr_frags, - sg_page(sg), sg->offset + offset, - sg_bytes); - fp_skb(fp)->data_len += sg_bytes; - fr_len(fp) += sg_bytes; - fp_skb(fp)->truesize += PAGE_SIZE; - } else { - size_t off = offset + sg->offset; - - /* - * The scatterlist item may be bigger than PAGE_SIZE, - * but we must not cross pages inside the kmap. - */ - sg_bytes = min(sg_bytes, (size_t) (PAGE_SIZE - - (off & ~PAGE_MASK))); - page_addr = kmap_atomic(sg_page(sg) + - (off >> PAGE_SHIFT), - KM_SOFTIRQ0); - memcpy(data, (char *)page_addr + (off & ~PAGE_MASK), - sg_bytes); - kunmap_atomic(page_addr, KM_SOFTIRQ0); - data += sg_bytes; - } - offset += sg_bytes; - frame_offset += sg_bytes; - tlen -= sg_bytes; - remaining -= sg_bytes; - - if (tlen) - continue; - - /* - * Send sequence with transfer sequence initiative in case - * this is last FCP frame of the sequence. - */ - if (remaining == 0) - f_ctl |= FC_FC_SEQ_INIT | FC_FC_END_SEQ; - - ep = fc_seq_exch(seq); - fc_fill_fc_hdr(fp, FC_RCTL_DD_SOL_DATA, ep->did, ep->sid, - FC_TYPE_FCP, f_ctl, fh_parm_offset); - - /* - * send fragment using for a sequence. - */ - error = lp->tt.seq_send(lp, seq, fp); - if (error) { - WARN_ON(1); /* send error should be rare */ - fc_fcp_retry_cmd(fsp); - return 0; - } - fp = NULL; - } - fsp->xfer_len += seq_blen; /* premature count? */ - return 0; -} - -static void fc_fcp_abts_resp(struct fc_fcp_pkt *fsp, struct fc_frame *fp) -{ - 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; - - if (fsp->wait_for_comp) - complete(&fsp->tm_done); - else - fc_fcp_complete_locked(fsp); - } -} - -/* - * fc_fcp_reduce_can_queue - drop can_queue - * @lp: lport to drop queueing for - * - * If we are getting memory allocation failures, then we may - * be trying to execute too many commands. We let the running - * commands complete or timeout, then try again with a reduced - * can_queue. Eventually we will hit the point where we run - * on all reserved structs. - */ -static void fc_fcp_reduce_can_queue(struct fc_lport *lp) -{ - struct fc_fcp_internal *si = fc_get_scsi_internal(lp); - unsigned long flags; - int can_queue; - - spin_lock_irqsave(lp->host->host_lock, flags); - if (si->throttled) - goto done; - si->throttled = 1; - - can_queue = lp->host->can_queue; - can_queue >>= 1; - if (!can_queue) - can_queue = 1; - lp->host->can_queue = can_queue; - shost_printk(KERN_ERR, lp->host, "Could not allocate frame.\n" - "Reducing can_queue to %d.\n", can_queue); -done: - spin_unlock_irqrestore(lp->host->host_lock, flags); -} - -/* - * exch mgr calls this routine to process scsi - * exchanges. - * - * Return : None - * Context : called from Soft IRQ context - * can not called holding list lock - */ -static void fc_fcp_recv(struct fc_seq *seq, 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 fcp_txrdy *dd; - u8 r_ctl; - int rc = 0; - - if (IS_ERR(fp)) - goto errout; - - fh = fc_frame_header_get(fp); - r_ctl = fh->fh_r_ctl; - lp = fsp->lp; - - if (!(lp->state & LPORT_ST_READY)) - goto out; - if (fc_fcp_lock_pkt(fsp)) - goto out; - fsp->last_pkt_time = jiffies; - - if (fh->fh_type == FC_TYPE_BLS) { - fc_fcp_abts_resp(fsp, fp); - goto unlock; - } - - if (fsp->state & (FC_SRB_ABORTED | FC_SRB_ABORT_PENDING)) - goto unlock; - - if (r_ctl == FC_RCTL_DD_DATA_DESC) { - /* - * received XFER RDY from the target - * need to send data to the target - */ - WARN_ON(fr_flags(fp) & FCPHF_CRC_UNCHECKED); - dd = fc_frame_payload_get(fp, sizeof(*dd)); - WARN_ON(!dd); - - rc = fc_fcp_send_data(fsp, seq, - (size_t) ntohl(dd->ft_data_ro), - (size_t) ntohl(dd->ft_burst_len)); - if (!rc) - seq->rec_data = fsp->xfer_len; - else if (rc == -ENOMEM) - fsp->state |= FC_SRB_NOMEM; - } else if (r_ctl == FC_RCTL_DD_SOL_DATA) { - /* - * received a DATA frame - * next we will copy the data to the system buffer - */ - WARN_ON(fr_len(fp) < sizeof(*fh)); /* len may be 0 */ - fc_fcp_recv_data(fsp, fp); - seq->rec_data = fsp->xfer_contig_end; - } else if (r_ctl == FC_RCTL_DD_CMD_STATUS) { - WARN_ON(fr_flags(fp) & FCPHF_CRC_UNCHECKED); - - fc_fcp_resp(fsp, fp); - } else { - FC_DBG("unexpected frame. r_ctl %x\n", r_ctl); - } -unlock: - fc_fcp_unlock_pkt(fsp); -out: - fc_frame_free(fp); -errout: - if (IS_ERR(fp)) - fc_fcp_error(fsp, fp); - else if (rc == -ENOMEM) - fc_fcp_reduce_can_queue(lp); -} - -static void fc_fcp_resp(struct fc_fcp_pkt *fsp, struct fc_frame *fp) -{ - struct fc_frame_header *fh; - struct fcp_resp *fc_rp; - struct fcp_resp_ext *rp_ex; - struct fcp_resp_rsp_info *fc_rp_info; - u32 plen; - u32 expected_len; - u32 respl = 0; - u32 snsl = 0; - u8 flags = 0; - - plen = fr_len(fp); - fh = (struct fc_frame_header *)fr_hdr(fp); - if (unlikely(plen < sizeof(*fh) + sizeof(*fc_rp))) - goto len_err; - plen -= sizeof(*fh); - fc_rp = (struct fcp_resp *)(fh + 1); - fsp->cdb_status = fc_rp->fr_status; - flags = fc_rp->fr_flags; - fsp->scsi_comp_flags = flags; - expected_len = fsp->data_len; - - if (unlikely((flags & ~FCP_CONF_REQ) || fc_rp->fr_status)) { - rp_ex = (void *)(fc_rp + 1); - if (flags & (FCP_RSP_LEN_VAL | FCP_SNS_LEN_VAL)) { - if (plen < sizeof(*fc_rp) + sizeof(*rp_ex)) - goto len_err; - fc_rp_info = (struct fcp_resp_rsp_info *)(rp_ex + 1); - if (flags & FCP_RSP_LEN_VAL) { - respl = ntohl(rp_ex->fr_rsp_len); - if (respl != sizeof(*fc_rp_info)) - goto len_err; - if (fsp->wait_for_comp) { - /* Abuse cdb_status for rsp code */ - fsp->cdb_status = fc_rp_info->rsp_code; - complete(&fsp->tm_done); - /* - * tmfs will not have any scsi cmd so - * exit here - */ - return; - } else - goto err; - } - if (flags & FCP_SNS_LEN_VAL) { - snsl = ntohl(rp_ex->fr_sns_len); - if (snsl > SCSI_SENSE_BUFFERSIZE) - snsl = SCSI_SENSE_BUFFERSIZE; - memcpy(fsp->cmd->sense_buffer, - (char *)fc_rp_info + respl, snsl); - } - } - if (flags & (FCP_RESID_UNDER | FCP_RESID_OVER)) { - if (plen < sizeof(*fc_rp) + sizeof(rp_ex->fr_resid)) - goto len_err; - if (flags & FCP_RESID_UNDER) { - fsp->scsi_resid = ntohl(rp_ex->fr_resid); - /* - * The cmnd->underflow is the minimum number of - * bytes that must be transfered for this - * command. Provided a sense condition is not - * present, make sure the actual amount - * transferred is at least the underflow value - * or fail. - */ - if (!(flags & FCP_SNS_LEN_VAL) && - (fc_rp->fr_status == 0) && - (scsi_bufflen(fsp->cmd) - - fsp->scsi_resid) < fsp->cmd->underflow) - goto err; - expected_len -= fsp->scsi_resid; - } else { - fsp->status_code = FC_ERROR; - } - } - } - fsp->state |= FC_SRB_RCV_STATUS; - - /* - * Check for missing or extra data frames. - */ - if (unlikely(fsp->xfer_len != expected_len)) { - if (fsp->xfer_len < expected_len) { - /* - * Some data may be queued locally, - * Wait a at least one jiffy to see if it is delivered. - * If this expires without data, we may do SRR. - */ - fc_fcp_timer_set(fsp, 2); - return; - } - fsp->status_code = FC_DATA_OVRRUN; - FC_DBG("tgt %6x xfer len %zx greater than expected len %x. " - "data len %x\n", - fsp->rport->port_id, - fsp->xfer_len, expected_len, fsp->data_len); - } - fc_fcp_complete_locked(fsp); - return; - -len_err: - FC_DBG("short FCP response. flags 0x%x len %u respl %u snsl %u\n", - flags, fr_len(fp), respl, snsl); -err: - fsp->status_code = FC_ERROR; - fc_fcp_complete_locked(fsp); -} - -/** - * fc_fcp_complete_locked - complete processing of a fcp packet - * @fsp: fcp packet - * - * This function may sleep if a timer is pending. The packet lock must be - * held, and the host lock must not be held. - */ -static void fc_fcp_complete_locked(struct fc_fcp_pkt *fsp) -{ - struct fc_lport *lp = fsp->lp; - struct fc_seq *seq; - struct fc_exch *ep; - u32 f_ctl; - - if (fsp->state & FC_SRB_ABORT_PENDING) - return; - - if (fsp->state & FC_SRB_ABORTED) { - if (!fsp->status_code) - fsp->status_code = FC_CMD_ABORTED; - } else { - /* - * Test for transport underrun, independent of response - * underrun status. - */ - if (fsp->xfer_len < fsp->data_len && !fsp->io_status && - (!(fsp->scsi_comp_flags & FCP_RESID_UNDER) || - fsp->xfer_len < fsp->data_len - fsp->scsi_resid)) { - fsp->status_code = FC_DATA_UNDRUN; - fsp->io_status = SUGGEST_RETRY << 24; - } - } - - seq = fsp->seq_ptr; - if (seq) { - fsp->seq_ptr = NULL; - if (unlikely(fsp->scsi_comp_flags & FCP_CONF_REQ)) { - struct fc_frame *conf_frame; - struct fc_seq *csp; - - csp = lp->tt.seq_start_next(seq); - conf_frame = fc_frame_alloc(fsp->lp, 0); - if (conf_frame) { - f_ctl = FC_FC_SEQ_INIT; - f_ctl |= FC_FC_LAST_SEQ | FC_FC_END_SEQ; - ep = fc_seq_exch(seq); - fc_fill_fc_hdr(conf_frame, FC_RCTL_DD_SOL_CTL, - ep->did, ep->sid, - FC_TYPE_FCP, f_ctl, 0); - lp->tt.seq_send(lp, csp, conf_frame); - } - } - lp->tt.exch_done(seq); - } - fc_io_compl(fsp); -} - -static void fc_fcp_cleanup_cmd(struct fc_fcp_pkt *fsp, int error) -{ - struct fc_lport *lp = fsp->lp; - - if (fsp->seq_ptr) { - lp->tt.exch_done(fsp->seq_ptr); - fsp->seq_ptr = NULL; - } - fsp->status_code = error; -} - -/** - * fc_fcp_cleanup_each_cmd - run fn on each active command - * @lp: logical port - * @id: target id - * @lun: lun - * @error: fsp status code - * - * If lun or id is -1, they are ignored. - */ -static void fc_fcp_cleanup_each_cmd(struct fc_lport *lp, unsigned int id, - unsigned int lun, int error) -{ - struct fc_fcp_internal *si = fc_get_scsi_internal(lp); - struct fc_fcp_pkt *fsp; - struct scsi_cmnd *sc_cmd; - unsigned long flags; - - spin_lock_irqsave(lp->host->host_lock, flags); -restart: - list_for_each_entry(fsp, &si->scsi_pkt_queue, list) { - sc_cmd = fsp->cmd; - if (id != -1 && scmd_id(sc_cmd) != id) - continue; - - if (lun != -1 && sc_cmd->device->lun != lun) - continue; - - fc_fcp_pkt_hold(fsp); - spin_unlock_irqrestore(lp->host->host_lock, flags); - - if (!fc_fcp_lock_pkt(fsp)) { - fc_fcp_cleanup_cmd(fsp, error); - fc_io_compl(fsp); - fc_fcp_unlock_pkt(fsp); - } - - fc_fcp_pkt_release(fsp); - spin_lock_irqsave(lp->host->host_lock, flags); - /* - * while we dropped the lock multiple pkts could - * have been released, so we have to start over. - */ - goto restart; - } - spin_unlock_irqrestore(lp->host->host_lock, flags); -} - -static void fc_fcp_abort_io(struct fc_lport *lp) -{ - fc_fcp_cleanup_each_cmd(lp, -1, -1, FC_HRD_ERROR); -} - -/** - * fc_fcp_pkt_send - send a fcp packet to the lower level. - * @lp: fc lport - * @fsp: fc packet. - * - * This is called by upper layer protocol. - * Return : zero for success and -1 for failure - * Context : called from queuecommand which can be called from process - * or scsi soft irq. - * Locks : called with the host lock and irqs disabled. - */ -static int fc_fcp_pkt_send(struct fc_lport *lp, struct fc_fcp_pkt *fsp) -{ - struct fc_fcp_internal *si = fc_get_scsi_internal(lp); - int rc; - - fsp->cmd->SCp.ptr = (char *)fsp; - fsp->cdb_cmd.fc_dl = htonl(fsp->data_len); - fsp->cdb_cmd.fc_flags = fsp->req_flags & ~FCP_CFL_LEN_MASK; - - int_to_scsilun(fsp->cmd->device->lun, - (struct scsi_lun *)fsp->cdb_cmd.fc_lun); - memcpy(fsp->cdb_cmd.fc_cdb, fsp->cmd->cmnd, fsp->cmd->cmd_len); - list_add_tail(&fsp->list, &si->scsi_pkt_queue); - - spin_unlock_irq(lp->host->host_lock); - rc = lp->tt.fcp_cmd_send(lp, fsp, fc_fcp_recv); - spin_lock_irq(lp->host->host_lock); - if (rc) - list_del(&fsp->list); - - return rc; -} - -static int fc_fcp_cmd_send(struct fc_lport *lp, struct fc_fcp_pkt *fsp, - void (*resp)(struct fc_seq *, - struct fc_frame *fp, - void *arg)) -{ - struct fc_frame *fp; - struct fc_seq *seq; - struct fc_rport *rport; - struct fc_rport_libfc_priv *rp; - const size_t len = sizeof(fsp->cdb_cmd); - int rc = 0; - - if (fc_fcp_lock_pkt(fsp)) - return 0; - - fp = fc_frame_alloc(lp, sizeof(fsp->cdb_cmd)); - if (!fp) { - rc = -1; - goto unlock; - } - - memcpy(fc_frame_payload_get(fp, len), &fsp->cdb_cmd, len); - fr_cmd(fp) = fsp->cmd; - rport = fsp->rport; - fsp->max_payload = rport->maxframe_size; - rp = rport->dd_data; - - fc_fill_fc_hdr(fp, FC_RCTL_DD_UNSOL_CMD, rport->port_id, - fc_host_port_id(rp->local_port->host), FC_TYPE_FCP, - FC_FC_FIRST_SEQ | FC_FC_END_SEQ | FC_FC_SEQ_INIT, 0); - - seq = lp->tt.exch_seq_send(lp, fp, resp, fc_fcp_pkt_destroy, fsp, 0); - if (!seq) { - fc_frame_free(fp); - rc = -1; - goto unlock; - } - fsp->last_pkt_time = jiffies; - fsp->seq_ptr = seq; - 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, - (fsp->tgt_flags & FC_RP_FLAGS_REC_SUPPORTED) ? - FC_SCSI_REC_TOV : FC_SCSI_ER_TIMEOUT); -unlock: - fc_fcp_unlock_pkt(fsp); - return rc; -} - -/* - * transport error handler - */ -static void fc_fcp_error(struct fc_fcp_pkt *fsp, struct fc_frame *fp) -{ - int error = PTR_ERR(fp); - - if (fc_fcp_lock_pkt(fsp)) - return; - - switch (error) { - case -FC_EX_CLOSED: - fc_fcp_retry_cmd(fsp); - goto unlock; - default: - FC_DBG("unknown error %ld\n", PTR_ERR(fp)); - } - /* - * clear abort pending, because the lower layer - * decided to force completion. - */ - fsp->state &= ~FC_SRB_ABORT_PENDING; - fsp->status_code = FC_CMD_PLOGO; - fc_fcp_complete_locked(fsp); -unlock: - fc_fcp_unlock_pkt(fsp); -} - -/* - * Scsi abort handler- calls to send an abort - * and then wait for abort completion - */ -static int fc_fcp_pkt_abort(struct fc_lport *lp, struct fc_fcp_pkt *fsp) -{ - int rc = FAILED; - - if (fc_fcp_send_abort(fsp)) - return FAILED; - - init_completion(&fsp->tm_done); - fsp->wait_for_comp = 1; - - spin_unlock_bh(&fsp->scsi_pkt_lock); - rc = wait_for_completion_timeout(&fsp->tm_done, FC_SCSI_TM_TOV); - spin_lock_bh(&fsp->scsi_pkt_lock); - fsp->wait_for_comp = 0; - - if (!rc) { - FC_DBG("target abort cmd failed\n"); - rc = FAILED; - } else if (fsp->state & FC_SRB_ABORTED) { - FC_DBG("target abort cmd passed\n"); - rc = SUCCESS; - fc_fcp_complete_locked(fsp); - } - - return rc; -} - -/* - * Retry LUN reset after resource allocation failed. - */ -static void fc_lun_reset_send(unsigned long data) -{ - struct fc_fcp_pkt *fsp = (struct fc_fcp_pkt *)data; - struct fc_lport *lp = fsp->lp; - if (lp->tt.fcp_cmd_send(lp, fsp, fc_tm_done)) { - if (fsp->recov_retry++ >= FC_MAX_RECOV_RETRY) - return; - if (fc_fcp_lock_pkt(fsp)) - return; - setup_timer(&fsp->timer, fc_lun_reset_send, (unsigned long)fsp); - fc_fcp_timer_set(fsp, FC_SCSI_REC_TOV); - fc_fcp_unlock_pkt(fsp); - } -} - -/* - * Scsi device reset handler- send a LUN RESET to the device - * and wait for reset reply - */ -static int fc_lun_reset(struct fc_lport *lp, struct fc_fcp_pkt *fsp, - unsigned int id, unsigned int lun) -{ - int rc; - - fsp->cdb_cmd.fc_dl = htonl(fsp->data_len); - fsp->cdb_cmd.fc_tm_flags = FCP_TMF_LUN_RESET; - int_to_scsilun(lun, (struct scsi_lun *)fsp->cdb_cmd.fc_lun); - - fsp->wait_for_comp = 1; - init_completion(&fsp->tm_done); - - fc_lun_reset_send((unsigned long)fsp); - - /* - * wait for completion of reset - * after that make sure all commands are terminated - */ - rc = wait_for_completion_timeout(&fsp->tm_done, FC_SCSI_TM_TOV); - - spin_lock_bh(&fsp->scsi_pkt_lock); - fsp->state |= FC_SRB_COMPL; - spin_unlock_bh(&fsp->scsi_pkt_lock); - - del_timer_sync(&fsp->timer); - - spin_lock_bh(&fsp->scsi_pkt_lock); - if (fsp->seq_ptr) { - lp->tt.exch_done(fsp->seq_ptr); - fsp->seq_ptr = NULL; - } - fsp->wait_for_comp = 0; - spin_unlock_bh(&fsp->scsi_pkt_lock); - - if (!rc) { - FC_DBG("lun reset failed\n"); - return FAILED; - } - - /* cdb_status holds the tmf's rsp code */ - if (fsp->cdb_status != FCP_TMF_CMPL) - return FAILED; - - FC_DBG("lun reset to lun %u completed\n", lun); - fc_fcp_cleanup_each_cmd(lp, id, lun, FC_CMD_ABORTED); - return SUCCESS; -} - -/* - * Task Managment response handler - */ -static void fc_tm_done(struct fc_seq *seq, struct fc_frame *fp, void *arg) -{ - struct fc_fcp_pkt *fsp = arg; - struct fc_frame_header *fh; - - if (IS_ERR(fp)) { - /* - * If there is an error just let it timeout or wait - * for TMF to be aborted if it timedout. - * - * scsi-eh will escalate for when either happens. - */ - return; - } - - if (fc_fcp_lock_pkt(fsp)) - return; - - /* - * raced with eh timeout handler. - */ - if (!fsp->seq_ptr || !fsp->wait_for_comp) { - spin_unlock_bh(&fsp->scsi_pkt_lock); - return; - } - - fh = fc_frame_header_get(fp); - if (fh->fh_type != FC_TYPE_BLS) - fc_fcp_resp(fsp, fp); - fsp->seq_ptr = NULL; - fsp->lp->tt.exch_done(seq); - fc_frame_free(fp); - fc_fcp_unlock_pkt(fsp); -} - -static void fc_fcp_cleanup(struct fc_lport *lp) -{ - fc_fcp_cleanup_each_cmd(lp, -1, -1, FC_ERROR); -} - -/* - * fc_fcp_timeout: called by OS timer function. - * - * The timer has been inactivated and must be reactivated if desired - * using fc_fcp_timer_set(). - * - * Algorithm: - * - * If REC is supported, just issue it, and return. The REC exchange will - * complete or time out, and recovery can continue at that point. - * - * Otherwise, if the response has been received without all the data, - * it has been ER_TIMEOUT since the response was received. - * - * If the response has not been received, - * we see if data was received recently. If it has been, we continue waiting, - * otherwise, we abort the command. - */ -static void fc_fcp_timeout(unsigned long data) -{ - struct fc_fcp_pkt *fsp = (struct fc_fcp_pkt *)data; - struct fc_rport *rport = fsp->rport; - struct fc_rport_libfc_priv *rp = rport->dd_data; - - if (fc_fcp_lock_pkt(fsp)) - return; - - if (fsp->cdb_cmd.fc_tm_flags) - goto unlock; - - fsp->state |= FC_SRB_FCP_PROCESSING_TMO; - - if (rp->flags & FC_RP_FLAGS_REC_SUPPORTED) - fc_fcp_rec(fsp); - 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_locked(fsp); - else - fc_timeout_error(fsp); - fsp->state &= ~FC_SRB_FCP_PROCESSING_TMO; -unlock: - fc_fcp_unlock_pkt(fsp); -} - -/* - * Send a REC ELS request - */ -static void fc_fcp_rec(struct fc_fcp_pkt *fsp) -{ - struct fc_lport *lp; - struct fc_frame *fp; - struct fc_rport *rport; - struct fc_rport_libfc_priv *rp; - - lp = fsp->lp; - rport = fsp->rport; - rp = rport->dd_data; - if (!fsp->seq_ptr || rp->rp_state != RPORT_ST_READY) { - fsp->status_code = FC_HRD_ERROR; - fsp->io_status = SUGGEST_RETRY << 24; - fc_fcp_complete_locked(fsp); - return; - } - fp = fc_frame_alloc(lp, sizeof(struct fc_els_rec)); - if (!fp) - goto retry; - - fr_seq(fp) = fsp->seq_ptr; - fc_fill_fc_hdr(fp, FC_RCTL_ELS_REQ, rport->port_id, - fc_host_port_id(rp->local_port->host), FC_TYPE_ELS, - FC_FC_FIRST_SEQ | FC_FC_END_SEQ | FC_FC_SEQ_INIT, 0); - if (lp->tt.elsct_send(lp, rport, fp, ELS_REC, fc_fcp_rec_resp, - fsp, jiffies_to_msecs(FC_SCSI_REC_TOV))) { - fc_fcp_pkt_hold(fsp); /* hold while REC outstanding */ - return; - } - fc_frame_free(fp); -retry: - if (fsp->recov_retry++ < FC_MAX_RECOV_RETRY) - fc_fcp_timer_set(fsp, FC_SCSI_REC_TOV); - else - fc_timeout_error(fsp); -} - -/* - * Receive handler for REC ELS frame - * if it is a reject then let the scsi layer to handle - * the timeout. if it is a LS_ACC then if the io was not completed - * then set the timeout and return otherwise complete the exchange - * and tell the scsi layer to restart the I/O. - */ -static void fc_fcp_rec_resp(struct fc_seq *seq, struct fc_frame *fp, void *arg) -{ - struct fc_fcp_pkt *fsp = (struct fc_fcp_pkt *)arg; - struct fc_els_rec_acc *recp; - struct fc_els_ls_rjt *rjt; - u32 e_stat; - u8 opcode; - u32 offset; - enum dma_data_direction data_dir; - enum fc_rctl r_ctl; - struct fc_rport_libfc_priv *rp; - - if (IS_ERR(fp)) { - fc_fcp_rec_error(fsp, fp); - return; - } - - if (fc_fcp_lock_pkt(fsp)) - goto out; - - fsp->recov_retry = 0; - opcode = fc_frame_payload_op(fp); - if (opcode == ELS_LS_RJT) { - rjt = fc_frame_payload_get(fp, sizeof(*rjt)); - switch (rjt->er_reason) { - default: - FC_DEBUG_FCP("device %x unexpected REC reject " - "reason %d expl %d\n", - fsp->rport->port_id, rjt->er_reason, - rjt->er_explan); - /* fall through */ - case ELS_RJT_UNSUP: - FC_DEBUG_FCP("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; - fc_fcp_timer_set(fsp, FC_SCSI_ER_TIMEOUT); - break; - case ELS_RJT_LOGIC: - case ELS_RJT_UNAB: - /* - * If no data transfer, the command frame got dropped - * so we just retry. If data was transferred, we - * lost the response but the target has no record, - * so we abort and retry. - */ - if (rjt->er_explan == ELS_EXPL_OXID_RXID && - fsp->xfer_len == 0) { - fc_fcp_retry_cmd(fsp); - break; - } - fc_timeout_error(fsp); - break; - } - } else if (opcode == ELS_LS_ACC) { - if (fsp->state & FC_SRB_ABORTED) - goto unlock_out; - - data_dir = fsp->cmd->sc_data_direction; - recp = fc_frame_payload_get(fp, sizeof(*recp)); - offset = ntohl(recp->reca_fc4value); - e_stat = ntohl(recp->reca_e_stat); - - if (e_stat & ESB_ST_COMPLETE) { - - /* - * The exchange is complete. - * - * For output, we must've lost the response. - * For input, all data must've been sent. - * We lost may have lost the response - * (and a confirmation was requested) and maybe - * some data. - * - * If all data received, send SRR - * asking for response. If partial data received, - * or gaps, SRR requests data at start of gap. - * Recovery via SRR relies on in-order-delivery. - */ - if (data_dir == DMA_TO_DEVICE) { - r_ctl = FC_RCTL_DD_CMD_STATUS; - } else if (fsp->xfer_contig_end == offset) { - r_ctl = FC_RCTL_DD_CMD_STATUS; - } else { - offset = fsp->xfer_contig_end; - r_ctl = FC_RCTL_DD_SOL_DATA; - } - fc_fcp_srr(fsp, r_ctl, offset); - } else if (e_stat & ESB_ST_SEQ_INIT) { - - /* - * The remote port has the initiative, so just - * keep waiting for it to complete. - */ - fc_fcp_timer_set(fsp, FC_SCSI_REC_TOV); - } else { - - /* - * The exchange is incomplete, we have seq. initiative. - * Lost response with requested confirmation, - * lost confirmation, lost transfer ready or - * lost write data. - * - * For output, if not all data was received, ask - * for transfer ready to be repeated. - * - * If we received or sent all the data, send SRR to - * request response. - * - * If we lost a response, we may have lost some read - * data as well. - */ - r_ctl = FC_RCTL_DD_SOL_DATA; - if (data_dir == DMA_TO_DEVICE) { - r_ctl = FC_RCTL_DD_CMD_STATUS; - if (offset < fsp->data_len) - r_ctl = FC_RCTL_DD_DATA_DESC; - } else if (offset == fsp->xfer_contig_end) { - r_ctl = FC_RCTL_DD_CMD_STATUS; - } else if (fsp->xfer_contig_end < offset) { - offset = fsp->xfer_contig_end; - } - fc_fcp_srr(fsp, r_ctl, offset); - } - } -unlock_out: - fc_fcp_unlock_pkt(fsp); -out: - fc_fcp_pkt_release(fsp); /* drop hold for outstanding REC */ - fc_frame_free(fp); -} - -/* - * Handle error response or timeout for REC exchange. - */ -static void fc_fcp_rec_error(struct fc_fcp_pkt *fsp, struct fc_frame *fp) -{ - int error = PTR_ERR(fp); - - if (fc_fcp_lock_pkt(fsp)) - goto out; - - switch (error) { - case -FC_EX_CLOSED: - fc_fcp_retry_cmd(fsp); - break; - - default: - FC_DBG("REC %p fid %x error unexpected error %d\n", - fsp, fsp->rport->port_id, error); - fsp->status_code = FC_CMD_PLOGO; - /* fall through */ - - case -FC_EX_TIMEOUT: - /* - * Assume REC or LS_ACC was lost. - * The exchange manager will have aborted REC, so retry. - */ - FC_DBG("REC fid %x error error %d retry %d/%d\n", - fsp->rport->port_id, error, fsp->recov_retry, - FC_MAX_RECOV_RETRY); - if (fsp->recov_retry++ < FC_MAX_RECOV_RETRY) - fc_fcp_rec(fsp); - else - fc_timeout_error(fsp); - break; - } - fc_fcp_unlock_pkt(fsp); -out: - fc_fcp_pkt_release(fsp); /* drop hold for outstanding REC */ -} - -/* - * Time out error routine: - * abort's the I/O close the exchange and - * send completion notification to scsi layer - */ -static void fc_timeout_error(struct fc_fcp_pkt *fsp) -{ - fsp->status_code = FC_CMD_TIME_OUT; - fsp->cdb_status = 0; - fsp->io_status = 0; - /* - * if this fails then we let the scsi command timer fire and - * scsi-ml escalate. - */ - fc_fcp_send_abort(fsp); -} - -/* - * Sequence retransmission request. - * This is called after receiving status but insufficient data, or - * when expecting status but the request has timed out. - */ -static void fc_fcp_srr(struct fc_fcp_pkt *fsp, enum fc_rctl r_ctl, u32 offset) -{ - struct fc_lport *lp = fsp->lp; - struct fc_rport *rport; - struct fc_rport_libfc_priv *rp; - struct fc_exch *ep = fc_seq_exch(fsp->seq_ptr); - struct fc_seq *seq; - struct fcp_srr *srr; - struct fc_frame *fp; - u8 cdb_op; - - rport = fsp->rport; - rp = rport->dd_data; - cdb_op = fsp->cdb_cmd.fc_cdb[0]; - - if (!(rp->flags & FC_RP_FLAGS_RETRY) || rp->rp_state != RPORT_ST_READY) - goto retry; /* shouldn't happen */ - fp = fc_frame_alloc(lp, sizeof(*srr)); - if (!fp) - goto retry; - - srr = fc_frame_payload_get(fp, sizeof(*srr)); - memset(srr, 0, sizeof(*srr)); - srr->srr_op = ELS_SRR; - srr->srr_ox_id = htons(ep->oxid); - srr->srr_rx_id = htons(ep->rxid); - srr->srr_r_ctl = r_ctl; - srr->srr_rel_off = htonl(offset); - - fc_fill_fc_hdr(fp, FC_RCTL_ELS4_REQ, rport->port_id, - fc_host_port_id(rp->local_port->host), FC_TYPE_FCP, - FC_FC_FIRST_SEQ | FC_FC_END_SEQ | FC_FC_SEQ_INIT, 0); - - seq = lp->tt.exch_seq_send(lp, fp, fc_fcp_srr_resp, NULL, - fsp, jiffies_to_msecs(FC_SCSI_REC_TOV)); - if (!seq) { - fc_frame_free(fp); - goto retry; - } - fsp->recov_seq = seq; - fsp->xfer_len = offset; - fsp->xfer_contig_end = offset; - fsp->state &= ~FC_SRB_RCV_STATUS; - fc_fcp_pkt_hold(fsp); /* hold for outstanding SRR */ - return; -retry: - fc_fcp_retry_cmd(fsp); -} - -/* - * Handle response from SRR. - */ -static void fc_fcp_srr_resp(struct fc_seq *seq, struct fc_frame *fp, void *arg) -{ - struct fc_fcp_pkt *fsp = arg; - struct fc_frame_header *fh; - - if (IS_ERR(fp)) { - fc_fcp_srr_error(fsp, fp); - return; - } - - if (fc_fcp_lock_pkt(fsp)) - goto out; - - fh = fc_frame_header_get(fp); - /* - * BUG? fc_fcp_srr_error calls exch_done which would release - * the ep. But if fc_fcp_srr_error had got -FC_EX_TIMEOUT, - * then fc_exch_timeout would be sending an abort. The exch_done - * call by fc_fcp_srr_error would prevent fc_exch.c from seeing - * an abort response though. - */ - if (fh->fh_type == FC_TYPE_BLS) { - fc_fcp_unlock_pkt(fsp); - return; - } - - fsp->recov_seq = NULL; - switch (fc_frame_payload_op(fp)) { - case ELS_LS_ACC: - fsp->recov_retry = 0; - fc_fcp_timer_set(fsp, FC_SCSI_REC_TOV); - break; - case ELS_LS_RJT: - default: - fc_timeout_error(fsp); - break; - } - fc_fcp_unlock_pkt(fsp); - fsp->lp->tt.exch_done(seq); -out: - fc_frame_free(fp); - fc_fcp_pkt_release(fsp); /* drop hold for outstanding SRR */ -} - -static void fc_fcp_srr_error(struct fc_fcp_pkt *fsp, struct fc_frame *fp) -{ - if (fc_fcp_lock_pkt(fsp)) - goto out; - fsp->lp->tt.exch_done(fsp->recov_seq); - fsp->recov_seq = NULL; - switch (PTR_ERR(fp)) { - case -FC_EX_TIMEOUT: - if (fsp->recov_retry++ < FC_MAX_RECOV_RETRY) - fc_fcp_rec(fsp); - else - fc_timeout_error(fsp); - break; - case -FC_EX_CLOSED: /* e.g., link failure */ - /* fall through */ - default: - fc_fcp_retry_cmd(fsp); - break; - } - fc_fcp_unlock_pkt(fsp); -out: - fc_fcp_pkt_release(fsp); /* drop hold for outstanding SRR */ -} - -static inline int fc_fcp_lport_queue_ready(struct fc_lport *lp) -{ - /* lock ? */ - return (lp->state == LPORT_ST_READY) && (lp->link_status & FC_LINK_UP); -} - -/** - * fc_queuecommand - The queuecommand function of the scsi template - * @cmd: struct scsi_cmnd to be executed - * @done: Callback function to be called when cmd is completed - * - * this is the i/o strategy routine, called by the scsi layer - * this routine is called with holding the host_lock. - */ -int fc_queuecommand(struct scsi_cmnd *sc_cmd, void (*done)(struct scsi_cmnd *)) -{ - struct fc_lport *lp; - struct fc_rport *rport = starget_to_rport(scsi_target(sc_cmd->device)); - struct fc_fcp_pkt *fsp; - struct fc_rport_libfc_priv *rp; - int rval; - int rc = 0; - struct fcoe_dev_stats *stats; - - lp = shost_priv(sc_cmd->device->host); - - rval = fc_remote_port_chkready(rport); - if (rval) { - sc_cmd->result = rval; - done(sc_cmd); - goto out; - } - - if (!*(struct fc_remote_port **)rport->dd_data) { - /* - * rport is transitioning from blocked/deleted to - * online - */ - sc_cmd->result = DID_IMM_RETRY << 16; - done(sc_cmd); - goto out; - } - - rp = rport->dd_data; - - if (!fc_fcp_lport_queue_ready(lp)) { - rc = SCSI_MLQUEUE_HOST_BUSY; - goto out; - } - - fsp = fc_fcp_pkt_alloc(lp, GFP_ATOMIC); - if (fsp == NULL) { - rc = SCSI_MLQUEUE_HOST_BUSY; - goto out; - } - - /* - * build the libfc request pkt - */ - fsp->cmd = sc_cmd; /* save the cmd */ - fsp->lp = lp; /* save the softc ptr */ - fsp->rport = rport; /* set the remote port ptr */ - sc_cmd->scsi_done = done; - - /* - * set up the transfer length - */ - fsp->data_len = scsi_bufflen(sc_cmd); - fsp->xfer_len = 0; - - /* - * setup the data direction - */ - stats = lp->dev_stats[smp_processor_id()]; - if (sc_cmd->sc_data_direction == DMA_FROM_DEVICE) { - fsp->req_flags = FC_SRB_READ; - stats->InputRequests++; - stats->InputMegabytes = fsp->data_len; - } else if (sc_cmd->sc_data_direction == DMA_TO_DEVICE) { - fsp->req_flags = FC_SRB_WRITE; - stats->OutputRequests++; - stats->OutputMegabytes = fsp->data_len; - } else { - fsp->req_flags = 0; - stats->ControlRequests++; - } - - fsp->tgt_flags = rp->flags; - - init_timer(&fsp->timer); - fsp->timer.data = (unsigned long)fsp; - - /* - * send it to the lower layer - * if we get -1 return then put the request in the pending - * queue. - */ - rval = fc_fcp_pkt_send(lp, fsp); - if (rval != 0) { - fsp->state = FC_SRB_FREE; - fc_fcp_pkt_release(fsp); - rc = SCSI_MLQUEUE_HOST_BUSY; - } -out: - return rc; -} -EXPORT_SYMBOL(fc_queuecommand); - -/** - * fc_io_compl - Handle responses for completed commands - * @fsp: scsi packet - * - * Translates a error to a Linux SCSI error. - * - * The fcp packet lock must be held when calling. - */ -static void fc_io_compl(struct fc_fcp_pkt *fsp) -{ - struct fc_fcp_internal *si; - struct scsi_cmnd *sc_cmd; - struct fc_lport *lp; - unsigned long flags; - - fsp->state |= FC_SRB_COMPL; - if (!(fsp->state & FC_SRB_FCP_PROCESSING_TMO)) { - spin_unlock_bh(&fsp->scsi_pkt_lock); - del_timer_sync(&fsp->timer); - spin_lock_bh(&fsp->scsi_pkt_lock); - } - - lp = fsp->lp; - si = fc_get_scsi_internal(lp); - spin_lock_irqsave(lp->host->host_lock, flags); - if (!fsp->cmd) { - spin_unlock_irqrestore(lp->host->host_lock, flags); - return; - } - - /* - * if a command timed out while we had to try and throttle IO - * and it is now getting cleaned up, then we are about to - * try again so clear the throttled flag incase we get more - * time outs. - */ - if (si->throttled && fsp->state & FC_SRB_NOMEM) - si->throttled = 0; - - sc_cmd = fsp->cmd; - fsp->cmd = NULL; - - if (!sc_cmd->SCp.ptr) { - spin_unlock_irqrestore(lp->host->host_lock, flags); - return; - } - - CMD_SCSI_STATUS(sc_cmd) = fsp->cdb_status; - switch (fsp->status_code) { - case FC_COMPLETE: - if (fsp->cdb_status == 0) { - /* - * good I/O status - */ - sc_cmd->result = DID_OK << 16; - if (fsp->scsi_resid) - CMD_RESID_LEN(sc_cmd) = fsp->scsi_resid; - } else if (fsp->cdb_status == QUEUE_FULL) { - struct scsi_device *tmp_sdev; - struct scsi_device *sdev = sc_cmd->device; - - shost_for_each_device(tmp_sdev, sdev->host) { - if (tmp_sdev->id != sdev->id) - continue; - - if (tmp_sdev->queue_depth > 1) { - scsi_track_queue_full(tmp_sdev, - tmp_sdev-> - queue_depth - 1); - } - } - sc_cmd->result = (DID_OK << 16) | fsp->cdb_status; - } else { - /* - * transport level I/O was ok but scsi - * has non zero status - */ - sc_cmd->result = (DID_OK << 16) | fsp->cdb_status; - } - break; - case FC_ERROR: - sc_cmd->result = DID_ERROR << 16; - break; - case FC_DATA_UNDRUN: - if (fsp->cdb_status == 0) { - /* - * scsi status is good but transport level - * underrun. for read it should be an error?? - */ - sc_cmd->result = (DID_OK << 16) | fsp->cdb_status; - } else { - /* - * scsi got underrun, this is an error - */ - CMD_RESID_LEN(sc_cmd) = fsp->scsi_resid; - sc_cmd->result = (DID_ERROR << 16) | fsp->cdb_status; - } - break; - case FC_DATA_OVRRUN: - /* - * overrun is an error - */ - sc_cmd->result = (DID_ERROR << 16) | fsp->cdb_status; - break; - case FC_CMD_ABORTED: - sc_cmd->result = (DID_ABORT << 16) | fsp->io_status; - break; - case FC_CMD_TIME_OUT: - sc_cmd->result = (DID_BUS_BUSY << 16) | fsp->io_status; - break; - case FC_CMD_RESET: - sc_cmd->result = (DID_RESET << 16); - break; - case FC_HRD_ERROR: - sc_cmd->result = (DID_NO_CONNECT << 16); - break; - default: - sc_cmd->result = (DID_ERROR << 16); - break; - } - - list_del(&fsp->list); - sc_cmd->SCp.ptr = NULL; - sc_cmd->scsi_done(sc_cmd); - spin_unlock_irqrestore(lp->host->host_lock, flags); - - /* release ref from initial allocation in queue command */ - fc_fcp_pkt_release(fsp); -} - -/** - * fc_fcp_complete - complete processing of a fcp packet - * @fsp: fcp packet - * - * This function may sleep if a fsp timer is pending. - * The host lock must not be held by caller. - */ -void fc_fcp_complete(struct fc_fcp_pkt *fsp) -{ - if (fc_fcp_lock_pkt(fsp)) - return; - - fc_fcp_complete_locked(fsp); - fc_fcp_unlock_pkt(fsp); -} -EXPORT_SYMBOL(fc_fcp_complete); - -/** - * fc_eh_abort - Abort a command...from scsi host template - * @sc_cmd: scsi command to abort - * - * send ABTS to the target device and wait for the response - * sc_cmd is the pointer to the command to be aborted. - */ -int fc_eh_abort(struct scsi_cmnd *sc_cmd) -{ - struct fc_fcp_pkt *fsp; - struct fc_lport *lp; - int rc = FAILED; - unsigned long flags; - - lp = shost_priv(sc_cmd->device->host); - if (lp->state != LPORT_ST_READY) - return rc; - else if (!(lp->link_status & FC_LINK_UP)) - return rc; - - spin_lock_irqsave(lp->host->host_lock, flags); - fsp = CMD_SP(sc_cmd); - if (!fsp) { - /* command completed while scsi eh was setting up */ - spin_unlock_irqrestore(lp->host->host_lock, flags); - return SUCCESS; - } - /* grab a ref so the fsp and sc_cmd cannot be relased from under us */ - fc_fcp_pkt_hold(fsp); - spin_unlock_irqrestore(lp->host->host_lock, flags); - - if (fc_fcp_lock_pkt(fsp)) { - /* completed while we were waiting for timer to be deleted */ - rc = SUCCESS; - goto release_pkt; - } - - rc = fc_fcp_pkt_abort(lp, fsp); - fc_fcp_unlock_pkt(fsp); - -release_pkt: - fc_fcp_pkt_release(fsp); - return rc; -} -EXPORT_SYMBOL(fc_eh_abort); - -/** - * fc_eh_device_reset: Reset a single LUN - * @sc_cmd: scsi command - * - * Set from scsi host template to send tm cmd to the target and wait for the - * response. - */ -int fc_eh_device_reset(struct scsi_cmnd *sc_cmd) -{ - struct fc_lport *lp; - struct fc_fcp_pkt *fsp; - struct fc_rport *rport = starget_to_rport(scsi_target(sc_cmd->device)); - int rc = FAILED; - struct fc_rport_libfc_priv *rp; - int rval; - - rval = fc_remote_port_chkready(rport); - if (rval) - goto out; - - rp = rport->dd_data; - lp = shost_priv(sc_cmd->device->host); - - if (lp->state != LPORT_ST_READY) - return rc; - - fsp = fc_fcp_pkt_alloc(lp, GFP_NOIO); - if (fsp == NULL) { - FC_DBG("could not allocate scsi_pkt\n"); - sc_cmd->result = DID_NO_CONNECT << 16; - goto out; - } - - /* - * Build the libfc request pkt. Do not set the scsi cmnd, because - * the sc passed in is not setup for execution like when sent - * through the queuecommand callout. - */ - fsp->lp = lp; /* save the softc ptr */ - fsp->rport = rport; /* set the remote port ptr */ - - /* - * flush outstanding commands - */ - rc = fc_lun_reset(lp, fsp, scmd_id(sc_cmd), sc_cmd->device->lun); - fsp->state = FC_SRB_FREE; - fc_fcp_pkt_release(fsp); - -out: - return rc; -} -EXPORT_SYMBOL(fc_eh_device_reset); - -/** - * fc_eh_host_reset - The reset function will reset the ports on the host. - * @sc_cmd: scsi command - */ -int fc_eh_host_reset(struct scsi_cmnd *sc_cmd) -{ - struct Scsi_Host *shost = sc_cmd->device->host; - struct fc_lport *lp = shost_priv(shost); - unsigned long wait_tmo; - - lp->tt.lport_reset(lp); - wait_tmo = jiffies + FC_HOST_RESET_TIMEOUT; - while (!fc_fcp_lport_queue_ready(lp) && time_before(jiffies, wait_tmo)) - msleep(1000); - - if (fc_fcp_lport_queue_ready(lp)) { - shost_printk(KERN_INFO, shost, "Host reset succeeded.\n"); - return SUCCESS; - } else { - shost_printk(KERN_INFO, shost, "Host reset failed. " - "lport not ready.\n"); - return FAILED; - } -} -EXPORT_SYMBOL(fc_eh_host_reset); - -/** - * fc_slave_alloc - configure queue depth - * @sdev: scsi device - * - * Configures queue depth based on host's cmd_per_len. If not set - * then we use the libfc default. - */ -int fc_slave_alloc(struct scsi_device *sdev) -{ - struct fc_rport *rport = starget_to_rport(scsi_target(sdev)); - int queue_depth; - - if (!rport || fc_remote_port_chkready(rport)) - return -ENXIO; - - if (sdev->tagged_supported) { - if (sdev->host->hostt->cmd_per_lun) - queue_depth = sdev->host->hostt->cmd_per_lun; - else - queue_depth = FC_FCP_DFLT_QUEUE_DEPTH; - scsi_activate_tcq(sdev, queue_depth); - } - return 0; -} -EXPORT_SYMBOL(fc_slave_alloc); - -int fc_change_queue_depth(struct scsi_device *sdev, int qdepth) -{ - scsi_adjust_queue_depth(sdev, scsi_get_tag_type(sdev), qdepth); - return sdev->queue_depth; -} -EXPORT_SYMBOL(fc_change_queue_depth); - -int fc_change_queue_type(struct scsi_device *sdev, int tag_type) -{ - if (sdev->tagged_supported) { - scsi_set_tag_type(sdev, tag_type); - if (tag_type) - scsi_activate_tcq(sdev, sdev->queue_depth); - else - scsi_deactivate_tcq(sdev, sdev->queue_depth); - } else - tag_type = 0; - - return tag_type; -} -EXPORT_SYMBOL(fc_change_queue_type); - -void fc_fcp_destroy(struct fc_lport *lp) -{ - struct fc_fcp_internal *si = fc_get_scsi_internal(lp); - - if (!list_empty(&si->scsi_pkt_queue)) - printk(KERN_ERR "Leaked scsi packets.\n"); - - mempool_destroy(si->scsi_pkt_pool); - kfree(si); - lp->scsi_priv = NULL; -} -EXPORT_SYMBOL(fc_fcp_destroy); - -int fc_fcp_init(struct fc_lport *lp) -{ - int rc; - struct fc_fcp_internal *si; - - if (!lp->tt.fcp_cmd_send) - lp->tt.fcp_cmd_send = fc_fcp_cmd_send; - - if (!lp->tt.fcp_cleanup) - lp->tt.fcp_cleanup = fc_fcp_cleanup; - - if (!lp->tt.fcp_abort_io) - lp->tt.fcp_abort_io = fc_fcp_abort_io; - - si = kzalloc(sizeof(struct fc_fcp_internal), GFP_KERNEL); - if (!si) - return -ENOMEM; - lp->scsi_priv = si; - INIT_LIST_HEAD(&si->scsi_pkt_queue); - - si->scsi_pkt_pool = mempool_create_slab_pool(2, scsi_pkt_cachep); - if (!si->scsi_pkt_pool) { - rc = -ENOMEM; - goto free_internal; - } - return 0; - -free_internal: - kfree(si); - return rc; -} -EXPORT_SYMBOL(fc_fcp_init); - -static int __init libfc_init(void) -{ - int rc; - - scsi_pkt_cachep = kmem_cache_create("libfc_fcp_pkt", - sizeof(struct fc_fcp_pkt), - 0, SLAB_HWCACHE_ALIGN, NULL); - if (scsi_pkt_cachep == NULL) { - FC_DBG("Unable to allocate SRB cache...module load failed!"); - return -ENOMEM; - } - - rc = fc_setup_exch_mgr(); - if (rc) - goto destroy_pkt_cache; - - rc = fc_setup_rport(); - if (rc) - goto destroy_em; - - return rc; -destroy_em: - fc_destroy_exch_mgr(); -destroy_pkt_cache: - kmem_cache_destroy(scsi_pkt_cachep); - return rc; -} - -static void __exit libfc_exit(void) -{ - kmem_cache_destroy(scsi_pkt_cachep); - fc_destroy_exch_mgr(); - fc_destroy_rport(); -} - -module_init(libfc_init); -module_exit(libfc_exit); diff --git a/drivers/scsi/libfc/fc_frame.c b/drivers/scsi/libfc/fc_frame.c deleted file mode 100644 index 63fe00cfe667..000000000000 --- a/drivers/scsi/libfc/fc_frame.c +++ /dev/null @@ -1,89 +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 - */ - -/* - * Frame allocation. - */ -#include <linux/module.h> -#include <linux/kernel.h> -#include <linux/skbuff.h> -#include <linux/crc32.h> - -#include <scsi/fc_frame.h> - -/* - * Check the CRC in a frame. - */ -u32 fc_frame_crc_check(struct fc_frame *fp) -{ - u32 crc; - u32 error; - const u8 *bp; - unsigned int len; - - WARN_ON(!fc_frame_is_linear(fp)); - fr_flags(fp) &= ~FCPHF_CRC_UNCHECKED; - len = (fr_len(fp) + 3) & ~3; /* round up length to include fill */ - bp = (const u8 *) fr_hdr(fp); - crc = ~crc32(~0, bp, len); - error = crc ^ fr_crc(fp); - return error; -} -EXPORT_SYMBOL(fc_frame_crc_check); - -/* - * Allocate a frame intended to be sent via fcoe_xmit. - * Get an sk_buff for the frame and set the length. - */ -struct fc_frame *__fc_frame_alloc(size_t len) -{ - struct fc_frame *fp; - struct sk_buff *skb; - - WARN_ON((len % sizeof(u32)) != 0); - len += sizeof(struct fc_frame_header); - skb = dev_alloc_skb(len + FC_FRAME_HEADROOM + FC_FRAME_TAILROOM); - if (!skb) - return NULL; - fp = (struct fc_frame *) skb; - fc_frame_init(fp); - skb_reserve(skb, FC_FRAME_HEADROOM); - skb_put(skb, len); - return fp; -} -EXPORT_SYMBOL(__fc_frame_alloc); - - -struct fc_frame *fc_frame_alloc_fill(struct fc_lport *lp, size_t payload_len) -{ - struct fc_frame *fp; - size_t fill; - - fill = payload_len % 4; - if (fill != 0) - fill = 4 - fill; - fp = __fc_frame_alloc(payload_len + fill); - if (fp) { - memset((char *) fr_hdr(fp) + payload_len, 0, fill); - /* trim is OK, we just allocated it so there are no fragments */ - skb_trim(fp_skb(fp), - payload_len + sizeof(struct fc_frame_header)); - } - return fp; -} diff --git a/drivers/scsi/libfc/fc_lport.c b/drivers/scsi/libfc/fc_lport.c deleted file mode 100644 index 0b9bdb1fb807..000000000000 --- a/drivers/scsi/libfc/fc_lport.c +++ /dev/null @@ -1,1604 +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 - */ - -/* - * PORT LOCKING NOTES - * - * These comments only apply to the 'port code' which consists of the lport, - * disc and rport blocks. - * - * MOTIVATION - * - * The lport, disc and rport blocks all have mutexes that are used to protect - * those objects. The main motivation for these locks is to prevent from - * having an lport reset just before we send a frame. In that scenario the - * lport's FID would get set to zero and then we'd send a frame with an - * invalid SID. We also need to ensure that states don't change unexpectedly - * while processing another state. - * - * HEIRARCHY - * - * The following heirarchy defines the locking rules. A greater lock - * may be held before acquiring a lesser lock, but a lesser lock should never - * be held while attempting to acquire a greater lock. Here is the heirarchy- - * - * lport > disc, lport > rport, disc > rport - * - * CALLBACKS - * - * The callbacks cause complications with this scheme. There is a callback - * from the rport (to either lport or disc) and a callback from disc - * (to the lport). - * - * As rports exit the rport state machine a callback is made to the owner of - * the rport to notify success or failure. Since the callback is likely to - * cause the lport or disc to grab its lock we cannot hold the rport lock - * while making the callback. To ensure that the rport is not free'd while - * processing the callback the rport callbacks are serialized through a - * single-threaded workqueue. An rport would never be free'd while in a - * callback handler becuase no other rport work in this queue can be executed - * at the same time. - * - * When discovery succeeds or fails a callback is made to the lport as - * notification. Currently, succesful discovery causes the lport to take no - * action. A failure will cause the lport to reset. There is likely a circular - * locking problem with this implementation. - */ - -/* - * LPORT LOCKING - * - * The critical sections protected by the lport's mutex are quite broad and - * may be improved upon in the future. The lport code and its locking doesn't - * influence the I/O path, so excessive locking doesn't penalize I/O - * performance. - * - * The strategy is to lock whenever processing a request or response. Note - * that every _enter_* function corresponds to a state change. They generally - * change the lports state and then send a request out on the wire. We lock - * before calling any of these functions to protect that state change. This - * means that the entry points into the lport block manage the locks while - * the state machine can transition between states (i.e. _enter_* functions) - * while always staying protected. - * - * When handling responses we also hold the lport mutex broadly. When the - * lport receives the response frame it locks the mutex and then calls the - * appropriate handler for the particuar response. Generally a response will - * trigger a state change and so the lock must already be held. - * - * Retries also have to consider the locking. The retries occur from a work - * context and the work function will lock the lport and then retry the state - * (i.e. _enter_* function). - */ - -#include <linux/timer.h> -#include <asm/unaligned.h> - -#include <scsi/fc/fc_gs.h> - -#include <scsi/libfc.h> -#include <scsi/fc_encode.h> - -/* Fabric IDs to use for point-to-point mode, chosen on whims. */ -#define FC_LOCAL_PTP_FID_LO 0x010101 -#define FC_LOCAL_PTP_FID_HI 0x010102 - -#define DNS_DELAY 3 /* Discovery delay after RSCN (in seconds)*/ - -static int fc_lport_debug; - -#define FC_DEBUG_LPORT(fmt...) \ - do { \ - if (fc_lport_debug) \ - FC_DBG(fmt); \ - } while (0) - -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_RPN_ID] = "RPN_ID", - [LPORT_ST_RFT_ID] = "RFT_ID", - [LPORT_ST_SCR] = "SCR", - [LPORT_ST_READY] = "Ready", - [LPORT_ST_LOGO] = "LOGO", - [LPORT_ST_RESET] = "reset", -}; - -static int fc_frame_drop(struct fc_lport *lport, struct fc_frame *fp) -{ - fc_frame_free(fp); - return 0; -} - -/** - * fc_lport_rport_callback - Event handler for rport events - * @lport: The lport which is receiving the event - * @rport: The rport which the event has occured on - * @event: The event that occured - * - * Locking Note: The rport lock should not be held when calling - * this function. - */ -static void fc_lport_rport_callback(struct fc_lport *lport, - struct fc_rport *rport, - enum fc_rport_event event) -{ - FC_DEBUG_LPORT("Received a %d event for port (%6x)\n", event, - rport->port_id); - - switch (event) { - case RPORT_EV_CREATED: - if (rport->port_id == FC_FID_DIR_SERV) { - mutex_lock(&lport->lp_mutex); - if (lport->state == LPORT_ST_DNS) { - lport->dns_rp = rport; - fc_lport_enter_rpn_id(lport); - } else { - FC_DEBUG_LPORT("Received an CREATED event on " - "port (%6x) for the directory " - "server, but the lport is not " - "in the DNS state, it's in the " - "%d state", rport->port_id, - lport->state); - lport->tt.rport_logoff(rport); - } - mutex_unlock(&lport->lp_mutex); - } else - FC_DEBUG_LPORT("Received an event for port (%6x) " - "which is not the directory server\n", - rport->port_id); - break; - case RPORT_EV_LOGO: - case RPORT_EV_FAILED: - case RPORT_EV_STOP: - if (rport->port_id == FC_FID_DIR_SERV) { - mutex_lock(&lport->lp_mutex); - lport->dns_rp = NULL; - mutex_unlock(&lport->lp_mutex); - - } else - FC_DEBUG_LPORT("Received an event for port (%6x) " - "which is not the directory server\n", - rport->port_id); - break; - case RPORT_EV_NONE: - break; - } -} - -/** - * 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[lport->state]; - if (!cp) - cp = "unknown"; - return cp; -} - -/** - * 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) -{ - struct fc_disc_port dp; - - dp.lp = lport; - dp.ids.port_id = remote_fid; - dp.ids.port_name = remote_wwpn; - dp.ids.node_name = remote_wwnn; - dp.ids.roles = FC_RPORT_ROLE_UNKNOWN; - - if (lport->ptp_rp) { - lport->tt.rport_logoff(lport->ptp_rp); - lport->ptp_rp = NULL; - } - - lport->ptp_rp = fc_rport_rogue_create(&dp); - - lport->tt.rport_login(lport->ptp_rp); - - fc_lport_enter_ready(lport); -} - -void fc_get_host_port_type(struct Scsi_Host *shost) -{ - /* TODO - currently just NPORT */ - fc_host_port_type(shost) = FC_PORTTYPE_NPORT; -} -EXPORT_SYMBOL(fc_get_host_port_type); - -void fc_get_host_port_state(struct Scsi_Host *shost) -{ - struct fc_lport *lp = shost_priv(shost); - - if ((lp->link_status & FC_LINK_UP) == FC_LINK_UP) - fc_host_port_state(shost) = FC_PORTSTATE_ONLINE; - else - fc_host_port_state(shost) = FC_PORTSTATE_OFFLINE; -} -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 *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(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) 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(lport->e_d_tov); - - cp->cp_rdfs = htons((u16) lport->mfs); - cp->cp_con_seq = htons(255); - cp->cp_open_seq = 1; - } -} - -/* - * Add a supported FC-4 type. - */ -static void fc_lport_add_fc4_type(struct fc_lport *lport, enum fc_fh_type type) -{ - __be32 *mp; - - mp = &lport->fcts.ff_type_map[type / FC_NS_BPW]; - *mp = htonl(ntohl(*mp) | 1UL << (type % FC_NS_BPW)); -} - -/** - * 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_recv_rlir_req(struct fc_seq *sp, struct fc_frame *fp, - struct fc_lport *lport) -{ - FC_DEBUG_LPORT("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); -} - -/** - * 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_recv_echo_req(struct fc_seq *sp, struct fc_frame *in_fp, - struct fc_lport *lport) -{ - struct fc_frame *fp; - struct fc_exch *ep = fc_seq_exch(sp); - unsigned int len; - void *pp; - void *dp; - u32 f_ctl; - - FC_DEBUG_LPORT("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(lport, len); - if (fp) { - dp = fc_frame_payload_get(fp, len); - memcpy(dp, pp, len); - *((u32 *)dp) = htonl(ELS_LS_ACC << 24); - sp = lport->tt.seq_start_next(sp); - f_ctl = FC_FC_EX_CTX | FC_FC_LAST_SEQ | FC_FC_END_SEQ; - fc_fill_fc_hdr(fp, FC_RCTL_ELS_REP, ep->did, ep->sid, - FC_TYPE_ELS, f_ctl, 0); - lport->tt.seq_send(lport, sp, fp); - } - fc_frame_free(in_fp); -} - -/** - * fc_lport_recv_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_recv_rnid_req(struct fc_seq *sp, struct fc_frame *in_fp, - struct fc_lport *lport) -{ - struct fc_frame *fp; - struct fc_exch *ep = fc_seq_exch(sp); - struct fc_els_rnid *req; - struct { - struct fc_els_rnid_resp rnid; - struct fc_els_rnid_cid cid; - struct fc_els_rnid_gen gen; - } *rp; - struct fc_seq_els_data rjt_data; - u8 fmt; - size_t len; - u32 f_ctl; - - FC_DEBUG_LPORT("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; - 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(lport->rnid_gen.rnid_atype) == 0) { - fmt = ELS_RNIDF_NONE; /* nothing to provide */ - len -= sizeof(rp->gen); - } - 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(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, &lport->rnid_gen, - sizeof(rp->gen)); - } - sp = lport->tt.seq_start_next(sp); - f_ctl = FC_FC_EX_CTX | FC_FC_LAST_SEQ; - f_ctl |= FC_FC_END_SEQ | FC_FC_SEQ_INIT; - fc_fill_fc_hdr(fp, FC_RCTL_ELS_REP, ep->did, ep->sid, - FC_TYPE_ELS, f_ctl, 0); - lport->tt.seq_send(lport, sp, fp); - } - } - fc_frame_free(in_fp); -} - -/** - * fc_lport_recv_adisc_req - Handle received Address Discovery Request - * @lport: Fibre Channel local port recieving the ADISC - * @sp: current sequence in the ADISC exchange - * @fp: ADISC request frame - * - * Locking Note: The lport lock is expected to be held before calling - * this function. - */ -static void fc_lport_recv_adisc_req(struct fc_seq *sp, struct fc_frame *in_fp, - struct fc_lport *lport) -{ - struct fc_frame *fp; - struct fc_exch *ep = fc_seq_exch(sp); - struct fc_els_adisc *req, *rp; - struct fc_seq_els_data rjt_data; - size_t len; - u32 f_ctl; - - FC_DEBUG_LPORT("Received ADISC request while in state %s\n", - fc_lport_state(lport)); - - req = fc_frame_payload_get(in_fp, sizeof(*req)); - if (!req) { - rjt_data.fp = NULL; - rjt_data.reason = ELS_RJT_LOGIC; - rjt_data.explan = ELS_EXPL_NONE; - lport->tt.seq_els_rsp_send(sp, ELS_LS_RJT, &rjt_data); - } else { - len = sizeof(*rp); - fp = fc_frame_alloc(lport, len); - if (fp) { - rp = fc_frame_payload_get(fp, len); - memset(rp, 0, len); - rp->adisc_cmd = ELS_LS_ACC; - rp->adisc_wwpn = htonll(lport->wwpn); - rp->adisc_wwnn = htonll(lport->wwnn); - hton24(rp->adisc_port_id, - fc_host_port_id(lport->host)); - sp = lport->tt.seq_start_next(sp); - f_ctl = FC_FC_EX_CTX | FC_FC_LAST_SEQ; - f_ctl |= FC_FC_END_SEQ | FC_FC_SEQ_INIT; - fc_fill_fc_hdr(fp, FC_RCTL_ELS_REP, ep->did, ep->sid, - FC_TYPE_ELS, f_ctl, 0); - lport->tt.seq_send(lport, sp, fp); - } - } - fc_frame_free(in_fp); -} - -/** - * fc_lport_recv_logo_req - Handle received fabric LOGO request - * @lport: Fibre Channel local port recieving the LOGO - * @sp: current sequence in the LOGO exchange - * @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 *lport) -{ - lport->tt.seq_els_rsp_send(sp, ELS_LS_ACC, NULL); - fc_lport_enter_reset(lport); - fc_frame_free(fp); -} - -/** - * 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 *lport) -{ - int rc = -1; - - 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 - Handler for transport linkup events - * @lport: The lport whose link is up - */ -void fc_linkup(struct fc_lport *lport) -{ - FC_DEBUG_LPORT("Link is up for port (%6x)\n", - fc_host_port_id(lport->host)); - - 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 - Handler for transport linkdown events - * @lport: The lport whose link is down - */ -void fc_linkdown(struct fc_lport *lport) -{ - mutex_lock(&lport->lp_mutex); - FC_DEBUG_LPORT("Link is down for port (%6x)\n", - fc_host_port_id(lport->host)); - - if ((lport->link_status & FC_LINK_UP) == FC_LINK_UP) { - lport->link_status &= ~(FC_LINK_UP); - fc_lport_enter_reset(lport); - lport->tt.fcp_cleanup(lport); - } - mutex_unlock(&lport->lp_mutex); -} -EXPORT_SYMBOL(fc_linkdown); - -/** - * fc_pause - Pause the flow of frames - * @lport: The lport to be paused - */ -void fc_pause(struct fc_lport *lport) -{ - mutex_lock(&lport->lp_mutex); - lport->link_status |= FC_PAUSE; - mutex_unlock(&lport->lp_mutex); -} -EXPORT_SYMBOL(fc_pause); - -/** - * fc_unpause - Unpause the flow of frames - * @lport: The lport to be unpaused - */ -void fc_unpause(struct fc_lport *lport) -{ - mutex_lock(&lport->lp_mutex); - lport->link_status &= ~(FC_PAUSE); - mutex_unlock(&lport->lp_mutex); -} -EXPORT_SYMBOL(fc_unpause); - -/** - * 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) -{ - lport->tt.disc_stop_final(lport); - mutex_lock(&lport->lp_mutex); - fc_lport_enter_logo(lport); - mutex_unlock(&lport->lp_mutex); - return 0; -} -EXPORT_SYMBOL(fc_fabric_logoff); - -/** - * fc_lport_destroy - unregister a fc_lport - * @lport: fc_lport pointer to unregister - * - * Return value: - * None - * Note: - * exit routine for fc_lport instance - * clean-up all the allocated memory - * and free up other system resources. - * - **/ -int fc_lport_destroy(struct fc_lport *lport) -{ - lport->tt.frame_send = fc_frame_drop; - lport->tt.fcp_abort_io(lport); - lport->tt.exch_mgr_reset(lport->emp, 0, 0); - return 0; -} -EXPORT_SYMBOL(fc_lport_destroy); - -/** - * fc_set_mfs - sets up the mfs for the corresponding fc_lport - * @lport: fc_lport pointer to unregister - * @mfs: the new mfs for fc_lport - * - * Set mfs for the given fc_lport to the new mfs. - * - * Return: 0 for success - * - **/ -int fc_set_mfs(struct fc_lport *lport, u32 mfs) -{ - unsigned int old_mfs; - int rc = -EINVAL; - - mutex_lock(&lport->lp_mutex); - - old_mfs = lport->mfs; - - if (mfs >= FC_MIN_MAX_FRAME) { - mfs &= ~3; - if (mfs > FC_MAX_FRAME) - mfs = FC_MAX_FRAME; - mfs -= sizeof(struct fc_frame_header); - lport->mfs = mfs; - rc = 0; - } - - if (!rc && mfs < old_mfs) - fc_lport_enter_reset(lport); - - mutex_unlock(&lport->lp_mutex); - - return rc; -} -EXPORT_SYMBOL(fc_set_mfs); - -/** - * fc_lport_disc_callback - Callback for discovery events - * @lport: FC local port - * @event: The discovery event - */ -void fc_lport_disc_callback(struct fc_lport *lport, enum fc_disc_event event) -{ - switch (event) { - case DISC_EV_SUCCESS: - FC_DEBUG_LPORT("Got a SUCCESS event for port (%6x)\n", - fc_host_port_id(lport->host)); - break; - case DISC_EV_FAILED: - FC_DEBUG_LPORT("Got a FAILED event for port (%6x)\n", - fc_host_port_id(lport->host)); - mutex_lock(&lport->lp_mutex); - fc_lport_enter_reset(lport); - mutex_unlock(&lport->lp_mutex); - break; - case DISC_EV_NONE: - WARN_ON(1); - break; - } -} - -/** - * 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_ready(struct fc_lport *lport) -{ - FC_DEBUG_LPORT("Port (%6x) entered Ready from state %s\n", - fc_host_port_id(lport->host), fc_lport_state(lport)); - - fc_lport_state_enter(lport, LPORT_ST_READY); - - lport->tt.disc_start(fc_lport_disc_callback, 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 *lport) -{ - struct fc_frame *fp; - struct fc_frame_header *fh; - struct fc_seq *sp; - struct fc_exch *ep; - struct fc_els_flogi *flp; - struct fc_els_flogi *new_flp; - u64 remote_wwpn; - u32 remote_fid; - u32 local_fid; - u32 f_ctl; - - FC_DEBUG_LPORT("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 == 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); - - /* - * XXX what is the right thing to do for FIDs? - * The originator might expect our S_ID to be 0xfffffe. - * But if so, both of us could end up with the same FID. - */ - local_fid = FC_LOCAL_PTP_FID_LO; - 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_host_port_id(lport->host) = local_fid; - - fp = fc_frame_alloc(lport, sizeof(*flp)); - if (fp) { - sp = lport->tt.seq_start_next(fr_seq(rx_fp)); - new_flp = fc_frame_payload_get(fp, sizeof(*flp)); - fc_lport_flogi_fill(lport, new_flp, ELS_FLOGI); - new_flp->fl_cmd = (u8) ELS_LS_ACC; - - /* - * Send the response. If this fails, the originator should - * repeat the sequence. - */ - f_ctl = FC_FC_EX_CTX | FC_FC_LAST_SEQ | FC_FC_END_SEQ; - ep = fc_seq_exch(sp); - fc_fill_fc_hdr(fp, FC_RCTL_ELS_REP, ep->did, ep->sid, - FC_TYPE_ELS, f_ctl, 0); - lport->tt.seq_send(lport, sp, fp); - - } else { - fc_lport_error(lport, fp); - } - fc_lport_ptp_setup(lport, remote_fid, remote_wwpn, - get_unaligned_be64(&flp->fl_wwnn)); - - lport->tt.disc_start(fc_lport_disc_callback, lport); - -out: - sp = fr_seq(rx_fp); - fc_frame_free(rx_fp); -} - -/** - * 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 *); - struct fc_rport *rport; - u32 s_id; - 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. - * Even if we had a session, it might not be ready. - */ - if (fh->fh_type == FC_TYPE_ELS && fh->fh_r_ctl == FC_RCTL_ELS_REQ) { - /* - * Check opcode. - */ - recv = NULL; - switch (fc_frame_payload_op(fp)) { - case ELS_FLOGI: - recv = fc_lport_recv_flogi_req; - break; - case ELS_LOGO: - fh = fc_frame_header_get(fp); - if (ntoh24(fh->fh_s_id) == FC_FID_FLOGI) - recv = fc_lport_recv_logo_req; - break; - case ELS_RSCN: - recv = lport->tt.disc_recv_req; - break; - case ELS_ECHO: - recv = fc_lport_recv_echo_req; - break; - case ELS_RLIR: - recv = fc_lport_recv_rlir_req; - break; - case ELS_RNID: - recv = fc_lport_recv_rnid_req; - break; - case ELS_ADISC: - recv = fc_lport_recv_adisc_req; - break; - } - - if (recv) - recv(sp, fp, lport); - else { - /* - * Find session. - * If this is a new incoming PLOGI, we won't find it. - */ - s_id = ntoh24(fh->fh_s_id); - d_id = ntoh24(fh->fh_d_id); - - rport = lport->tt.rport_lookup(lport, s_id); - if (rport) - lport->tt.rport_recv_req(sp, fp, rport); - else { - rjt_data.fp = NULL; - rjt_data.reason = ELS_RJT_UNAB; - rjt_data.explan = ELS_EXPL_NONE; - lport->tt.seq_els_rsp_send(sp, - ELS_LS_RJT, - &rjt_data); - fc_frame_free(fp); - } - } - } else { - 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 - */ - lport->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); - -/** - * 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. - */ -static void fc_lport_enter_reset(struct fc_lport *lport) -{ - FC_DEBUG_LPORT("Port (%6x) entered RESET state from %s state\n", - fc_host_port_id(lport->host), fc_lport_state(lport)); - - fc_lport_state_enter(lport, LPORT_ST_RESET); - - if (lport->dns_rp) - lport->tt.rport_logoff(lport->dns_rp); - - if (lport->ptp_rp) { - lport->tt.rport_logoff(lport->ptp_rp); - lport->ptp_rp = NULL; - } - - lport->tt.disc_stop(lport); - - lport->tt.exch_mgr_reset(lport->emp, 0, 0); - fc_host_fabric_name(lport->host) = 0; - fc_host_port_id(lport->host) = 0; - - if ((lport->link_status & FC_LINK_UP) == FC_LINK_UP) - fc_lport_enter_flogi(lport); -} - -/** - * 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; - FC_DEBUG_LPORT("Error %ld in state %s, retries %d\n", - PTR_ERR(fp), fc_lport_state(lport), - lport->retry_count); - - if (!fp || PTR_ERR(fp) == -FC_EX_TIMEOUT) { - /* - * Memory allocation failure, or the exchange timed out. - * Retry after delay - */ - if (lport->retry_count < lport->max_retry_count) { - lport->retry_count++; - if (!fp) - delay = msecs_to_jiffies(500); - else - delay = 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; - - if (fp == ERR_PTR(-FC_EX_CLOSED)) - return; - - mutex_lock(&lport->lp_mutex); - - FC_DEBUG_LPORT("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 err; - } - - 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: - fc_frame_free(fp); -err: - mutex_unlock(&lport->lp_mutex); -} - -/** - * 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; - - if (fp == ERR_PTR(-FC_EX_CLOSED)) - return; - - mutex_lock(&lport->lp_mutex); - - FC_DEBUG_LPORT("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 err; - } - - 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: - fc_frame_free(fp); -err: - mutex_unlock(&lport->lp_mutex); -} - -/** - * 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_scr_resp(struct fc_seq *sp, struct fc_frame *fp, - void *lp_arg) -{ - struct fc_lport *lport = lp_arg; - u8 op; - - if (fp == ERR_PTR(-FC_EX_CLOSED)) - return; - - mutex_lock(&lport->lp_mutex); - - FC_DEBUG_LPORT("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 err; - } - - op = fc_frame_payload_op(fp); - if (op == ELS_LS_ACC) - fc_lport_enter_ready(lport); - else - fc_lport_error(lport, fp); - -out: - fc_frame_free(fp); -err: - mutex_unlock(&lport->lp_mutex); -} - -/** - * 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; - - FC_DEBUG_LPORT("Port (%6x) entered SCR state from %s state\n", - fc_host_port_id(lport->host), fc_lport_state(lport)); - - fc_lport_state_enter(lport, LPORT_ST_SCR); - - fp = fc_frame_alloc(lport, sizeof(struct fc_els_scr)); - if (!fp) { - fc_lport_error(lport, fp); - return; - } - - if (!lport->tt.elsct_send(lport, NULL, fp, ELS_SCR, - fc_lport_scr_resp, lport, lport->e_d_tov)) - 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 fc_ns_fts *lps; - int i; - - FC_DEBUG_LPORT("Port (%6x) entered RFT_ID state from %s state\n", - fc_host_port_id(lport->host), 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); - return; - } - - fp = fc_frame_alloc(lport, sizeof(struct fc_ct_hdr) + - sizeof(struct fc_ns_rft)); - if (!fp) { - fc_lport_error(lport, fp); - return; - } - - if (!lport->tt.elsct_send(lport, NULL, fp, FC_NS_RFT_ID, - fc_lport_rft_id_resp, - lport, lport->e_d_tov)) - 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; - - FC_DEBUG_LPORT("Port (%6x) entered RPN_ID state from %s state\n", - fc_host_port_id(lport->host), fc_lport_state(lport)); - - fc_lport_state_enter(lport, LPORT_ST_RPN_ID); - - fp = fc_frame_alloc(lport, sizeof(struct fc_ct_hdr) + - sizeof(struct fc_ns_rn_id)); - if (!fp) { - fc_lport_error(lport, fp); - return; - } - - if (!lport->tt.elsct_send(lport, NULL, fp, FC_NS_RPN_ID, - fc_lport_rpn_id_resp, - lport, lport->e_d_tov)) - fc_lport_error(lport, fp); -} - -static struct fc_rport_operations fc_lport_rport_ops = { - .event_callback = fc_lport_rport_callback, -}; - -/** - * 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; - - FC_DEBUG_LPORT("Port (%6x) entered DNS state from %s state\n", - fc_host_port_id(lport->host), fc_lport_state(lport)); - - fc_lport_state_enter(lport, LPORT_ST_DNS); - - rport = fc_rport_rogue_create(&dp); - if (!rport) - goto err; - - rdata = rport->dd_data; - rdata->ops = &fc_lport_rport_ops; - lport->tt.rport_login(rport); - return; - -err: - fc_lport_error(lport, NULL); -} - -/** - * 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 *lport = - container_of(work, struct fc_lport, - retry_work.work); - - 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 *lport = lp_arg; - u8 op; - - if (fp == ERR_PTR(-FC_EX_CLOSED)) - return; - - mutex_lock(&lport->lp_mutex); - - FC_DEBUG_LPORT("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 err; - } - - op = fc_frame_payload_op(fp); - if (op == ELS_LS_ACC) - fc_lport_enter_reset(lport); - else - fc_lport_error(lport, fp); - -out: - fc_frame_free(fp); -err: - mutex_unlock(&lport->lp_mutex); -} - -/** - * 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; - - FC_DEBUG_LPORT("Port (%6x) entered LOGO state from %s state\n", - fc_host_port_id(lport->host), fc_lport_state(lport)); - - fc_lport_state_enter(lport, LPORT_ST_LOGO); - - /* DNS session should be closed so we can release it here */ - if (lport->dns_rp) - lport->tt.rport_logoff(lport->dns_rp); - - fp = fc_frame_alloc(lport, sizeof(*logo)); - if (!fp) { - fc_lport_error(lport, fp); - return; - } - - if (!lport->tt.elsct_send(lport, NULL, fp, ELS_LOGO, fc_lport_logo_resp, - lport, lport->e_d_tov)) - fc_lport_error(lport, fp); -} - -/** - * 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) -{ - struct fc_lport *lport = lp_arg; - struct fc_frame_header *fh; - struct fc_els_flogi *flp; - u32 did; - u16 csp_flags; - unsigned int r_a_tov; - unsigned int e_d_tov; - u16 mfs; - - if (fp == ERR_PTR(-FC_EX_CLOSED)) - return; - - mutex_lock(&lport->lp_mutex); - - FC_DEBUG_LPORT("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(lport, fp); - goto err; - } - - fh = fc_frame_header_get(fp); - did = ntoh24(fh->fh_d_id); - if (fc_frame_payload_op(fp) == ELS_LS_ACC && did != 0) { - - FC_DEBUG_LPORT("Assigned fid %x\n", did); - fc_host_port_id(lport->host) = 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 < 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 > 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 { - 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); - } - } - - if (flp) { - csp_flags = ntohs(flp->fl_csp.sp_features); - if ((csp_flags & FC_SP_FT_FPORT) == 0) { - lport->tt.disc_start(fc_lport_disc_callback, - lport); - } - } - } else { - FC_DBG("bad FLOGI response\n"); - } - -out: - fc_frame_free(fp); -err: - mutex_unlock(&lport->lp_mutex); -} - -/** - * 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. - */ -void fc_lport_enter_flogi(struct fc_lport *lport) -{ - struct fc_frame *fp; - - FC_DEBUG_LPORT("Processing FLOGI state\n"); - - fc_lport_state_enter(lport, LPORT_ST_FLOGI); - - fp = fc_frame_alloc(lport, sizeof(struct fc_els_flogi)); - if (!fp) - return fc_lport_error(lport, fp); - - if (!lport->tt.elsct_send(lport, NULL, fp, ELS_FLOGI, - fc_lport_flogi_resp, lport, lport->e_d_tov)) - fc_lport_error(lport, fp); -} - -/* Configure a fc_lport */ -int fc_lport_config(struct fc_lport *lport) -{ - INIT_DELAYED_WORK(&lport->retry_work, fc_lport_timeout); - mutex_init(&lport->lp_mutex); - - fc_lport_state_enter(lport, LPORT_ST_NONE); - - fc_lport_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 *lport) -{ - 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; - - 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; -} -EXPORT_SYMBOL(fc_lport_init); diff --git a/drivers/scsi/libfc/fc_rport.c b/drivers/scsi/libfc/fc_rport.c deleted file mode 100644 index e780d8caf70e..000000000000 --- a/drivers/scsi/libfc/fc_rport.c +++ /dev/null @@ -1,1291 +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 - */ - -/* - * RPORT GENERAL INFO - * - * This file contains all processing regarding fc_rports. It contains the - * rport state machine and does all rport interaction with the transport class. - * There should be no other places in libfc that interact directly with the - * transport class in regards to adding and deleting rports. - * - * fc_rport's represent N_Port's within the fabric. - */ - -/* - * RPORT LOCKING - * - * The rport should never hold the rport mutex and then attempt to acquire - * either the lport or disc mutexes. The rport's mutex is considered lesser - * than both the lport's mutex and the disc mutex. Refer to fc_lport.c for - * more comments on the heirarchy. - * - * The locking strategy is similar to the lport's strategy. The lock protects - * the rport's states and is held and released by the entry points to the rport - * block. All _enter_* functions correspond to rport states and expect the rport - * mutex to be locked before calling them. This means that rports only handle - * one request or response at a time, since they're not critical for the I/O - * path this potential over-use of the mutex is acceptable. - */ - -#include <linux/kernel.h> -#include <linux/spinlock.h> -#include <linux/interrupt.h> -#include <linux/rcupdate.h> -#include <linux/timer.h> -#include <linux/workqueue.h> -#include <asm/unaligned.h> - -#include <scsi/libfc.h> -#include <scsi/fc_encode.h> - -static int fc_rport_debug; - -#define FC_DEBUG_RPORT(fmt...) \ - do { \ - if (fc_rport_debug) \ - FC_DBG(fmt); \ - } while (0) - -struct workqueue_struct *rport_event_queue; - -static void fc_rport_enter_plogi(struct fc_rport *); -static void fc_rport_enter_prli(struct fc_rport *); -static void fc_rport_enter_rtv(struct fc_rport *); -static void fc_rport_enter_ready(struct fc_rport *); -static void fc_rport_enter_logo(struct fc_rport *); - -static void fc_rport_recv_plogi_req(struct fc_rport *, - struct fc_seq *, struct fc_frame *); -static void fc_rport_recv_prli_req(struct fc_rport *, - struct fc_seq *, struct fc_frame *); -static void fc_rport_recv_prlo_req(struct fc_rport *, - struct fc_seq *, struct fc_frame *); -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 void fc_rport_work(struct work_struct *); - -static const char *fc_rport_state_names[] = { - [RPORT_ST_NONE] = "None", - [RPORT_ST_INIT] = "Init", - [RPORT_ST_PLOGI] = "PLOGI", - [RPORT_ST_PRLI] = "PRLI", - [RPORT_ST_RTV] = "RTV", - [RPORT_ST_READY] = "Ready", - [RPORT_ST_LOGO] = "LOGO", -}; - -static void fc_rport_rogue_destroy(struct device *dev) -{ - struct fc_rport *rport = dev_to_rport(dev); - FC_DEBUG_RPORT("Destroying rogue rport (%6x)\n", rport->port_id); - kfree(rport); -} - -struct fc_rport *fc_rport_rogue_create(struct fc_disc_port *dp) -{ - struct fc_rport *rport; - struct fc_rport_libfc_priv *rdata; - rport = kzalloc(sizeof(*rport) + sizeof(*rdata), GFP_KERNEL); - - 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; - /* - * Note: all this libfc rogue rport code will be removed for - * upstream so it fine that this is really ugly and hacky right now. - */ - device_initialize(&rport->dev); - rport->dev.release = fc_rport_rogue_destroy; - - mutex_init(&rdata->rp_mutex); - rdata->local_port = dp->lp; - rdata->trans_state = FC_PORTSTATE_ROGUE; - rdata->rp_state = RPORT_ST_INIT; - rdata->event = RPORT_EV_NONE; - rdata->flags = FC_RP_FLAGS_REC_SUPPORTED; - rdata->ops = NULL; - rdata->e_d_tov = dp->lp->e_d_tov; - rdata->r_a_tov = dp->lp->r_a_tov; - INIT_DELAYED_WORK(&rdata->retry_work, fc_rport_timeout); - INIT_WORK(&rdata->event_work, fc_rport_work); - /* - * For good measure, but not necessary as we should only - * add REAL rport to the lport list. - */ - INIT_LIST_HEAD(&rdata->peers); - - return 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_set_rport_loss_tmo - Set the remote port loss timeout in seconds. - * @rport: Pointer to Fibre Channel remote port structure - * @timeout: timeout in seconds - */ -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); - -/** - * fc_plogi_get_maxframe - Get max payload from the common service parameters - * @flp: FLOGI payload structure - * @maxval: upper limit, may be less than what is in the service parameters - */ -static unsigned int -fc_plogi_get_maxframe(struct fc_els_flogi *flp, unsigned int maxval) -{ - unsigned int mfs; - - /* - * Get max payload from the common service parameters and the - * class 3 receive data field size. - */ - mfs = ntohs(flp->fl_csp.sp_bb_data) & FC_SP_BB_DATA_MASK; - if (mfs >= FC_SP_MIN_MAX_PAYLOAD && mfs < maxval) - maxval = mfs; - mfs = ntohs(flp->fl_cssp[3 - 1].cp_rdfs); - if (mfs >= FC_SP_MIN_MAX_PAYLOAD && mfs < maxval) - maxval = mfs; - return maxval; -} - -/** - * 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 *rdata = rport->dd_data; - if (rdata->rp_state != new) - rdata->retries = 0; - rdata->rp_state = new; -} - -static void fc_rport_work(struct work_struct *work) -{ - struct fc_rport_libfc_priv *rdata = - container_of(work, struct fc_rport_libfc_priv, event_work); - enum fc_rport_event event; - enum fc_rport_trans_state trans_state; - struct fc_lport *lport = rdata->local_port; - struct fc_rport_operations *rport_ops; - struct fc_rport *rport = PRIV_TO_RPORT(rdata); - - mutex_lock(&rdata->rp_mutex); - event = rdata->event; - rport_ops = rdata->ops; - - if (event == RPORT_EV_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; - - mutex_unlock(&rdata->rp_mutex); - - new_rport = fc_remote_port_add(lport->host, 0, &ids); - if (new_rport) { - /* - * Switch from the rogue rport to the rport - * returned by the FC class. - */ - new_rport->maxframe_size = rport->maxframe_size; - - new_rdata = new_rport->dd_data; - new_rdata->e_d_tov = rdata->e_d_tov; - new_rdata->r_a_tov = rdata->r_a_tov; - new_rdata->ops = rdata->ops; - new_rdata->local_port = rdata->local_port; - new_rdata->flags = FC_RP_FLAGS_REC_SUPPORTED; - new_rdata->trans_state = FC_PORTSTATE_REAL; - mutex_init(&new_rdata->rp_mutex); - INIT_DELAYED_WORK(&new_rdata->retry_work, - fc_rport_timeout); - INIT_LIST_HEAD(&new_rdata->peers); - INIT_WORK(&new_rdata->event_work, fc_rport_work); - - fc_rport_state_enter(new_rport, RPORT_ST_READY); - } else { - FC_DBG("Failed to create the rport for port " - "(%6x).\n", ids.port_id); - event = RPORT_EV_FAILED; - } - put_device(&rport->dev); - rport = new_rport; - rdata = new_rport->dd_data; - if (rport_ops->event_callback) - rport_ops->event_callback(lport, rport, event); - } else if ((event == RPORT_EV_FAILED) || - (event == RPORT_EV_LOGO) || - (event == RPORT_EV_STOP)) { - trans_state = rdata->trans_state; - mutex_unlock(&rdata->rp_mutex); - if (rport_ops->event_callback) - rport_ops->event_callback(lport, rport, event); - if (trans_state == FC_PORTSTATE_ROGUE) - put_device(&rport->dev); - else - fc_remote_port_delete(rport); - } else - mutex_unlock(&rdata->rp_mutex); -} - -/** - * 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 *rdata = rport->dd_data; - - mutex_lock(&rdata->rp_mutex); - - FC_DEBUG_RPORT("Login to port (%6x)\n", rport->port_id); - - fc_rport_enter_plogi(rport); - - mutex_unlock(&rdata->rp_mutex); - - return 0; -} - -/** - * fc_rport_logoff - Logoff and remove an rport - * @rport: Fibre Channel remote port to be removed - * - * Locking Note: Called without the rport lock held. This - * function will hold the rport lock, call an _enter_* - * function and then unlock the rport. - */ -int fc_rport_logoff(struct fc_rport *rport) -{ - struct fc_rport_libfc_priv *rdata = rport->dd_data; - - mutex_lock(&rdata->rp_mutex); - - FC_DEBUG_RPORT("Remove port (%6x)\n", rport->port_id); - - fc_rport_enter_logo(rport); - - /* - * Change the state to NONE so that we discard - * the response. - */ - fc_rport_state_enter(rport, RPORT_ST_NONE); - - mutex_unlock(&rdata->rp_mutex); - - cancel_delayed_work_sync(&rdata->retry_work); - - mutex_lock(&rdata->rp_mutex); - - rdata->event = RPORT_EV_STOP; - queue_work(rport_event_queue, &rdata->event_work); - - mutex_unlock(&rdata->rp_mutex); - - return 0; -} - -/** - * 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 *rdata = rport->dd_data; - - fc_rport_state_enter(rport, RPORT_ST_READY); - - FC_DEBUG_RPORT("Port (%6x) is Ready\n", rport->port_id); - - rdata->event = RPORT_EV_CREATED; - queue_work(rport_event_queue, &rdata->event_work); -} - -/** - * 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 *rdata = - container_of(work, struct fc_rport_libfc_priv, retry_work.work); - struct fc_rport *rport = PRIV_TO_RPORT(rdata); - - mutex_lock(&rdata->rp_mutex); - - switch (rdata->rp_state) { - case RPORT_ST_PLOGI: - fc_rport_enter_plogi(rport); - break; - case RPORT_ST_PRLI: - fc_rport_enter_prli(rport); - break; - case RPORT_ST_RTV: - fc_rport_enter_rtv(rport); - break; - case RPORT_ST_LOGO: - fc_rport_enter_logo(rport); - break; - case RPORT_ST_READY: - case RPORT_ST_INIT: - case RPORT_ST_NONE: - break; - } - - mutex_unlock(&rdata->rp_mutex); - put_device(&rport->dev); -} - -/** - * 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 *rdata = rport->dd_data; - unsigned long delay = 0; - - FC_DEBUG_RPORT("Error %ld in state %s, retries %d\n", - PTR_ERR(fp), fc_rport_state(rport), rdata->retries); - - if (!fp || PTR_ERR(fp) == -FC_EX_TIMEOUT) { - /* - * Memory allocation failure, or the exchange timed out. - * Retry after delay - */ - 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(&rdata->retry_work, delay); - } else { - switch (rdata->rp_state) { - case RPORT_ST_PLOGI: - case RPORT_ST_PRLI: - case RPORT_ST_LOGO: - rdata->event = RPORT_EV_FAILED; - queue_work(rport_event_queue, - &rdata->event_work); - break; - case RPORT_ST_RTV: - fc_rport_enter_ready(rport); - break; - case RPORT_ST_NONE: - case RPORT_ST_READY: - case RPORT_ST_INIT: - break; - } - } - } -} - -/** - * 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_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; - unsigned int tov; - u16 csp_seq; - u16 cssp_seq; - u8 op; - - mutex_lock(&rdata->rp_mutex); - - FC_DEBUG_RPORT("Received a PLOGI response from port (%6x)\n", - rport->port_id); - - 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 err; - } - - op = fc_frame_payload_op(fp); - if (op == ELS_LS_ACC && - (plp = fc_frame_payload_get(fp, sizeof(*plp))) != NULL) { - rport->port_name = get_unaligned_be64(&plp->fl_wwpn); - rport->node_name = get_unaligned_be64(&plp->fl_wwnn); - - 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_frame_free(fp); -err: - mutex_unlock(&rdata->rp_mutex); - put_device(&rport->dev); -} - -/** - * 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; - - FC_DEBUG_RPORT("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(lport, sizeof(struct fc_els_flogi)); - if (!fp) { - fc_rport_error(rport, fp); - return; - } - rdata->e_d_tov = lport->e_d_tov; - - if (!lport->tt.elsct_send(lport, rport, fp, ELS_PLOGI, - fc_rport_plogi_resp, rport, lport->e_d_tov)) - fc_rport_error(rport, fp); - else - get_device(&rport->dev); -} - -/** - * fc_rport_prli_resp - Process Login (PRLI) response handler - * @sp: current sequence in the PRLI exchange - * @fp: response frame - * @rp_arg: Fibre Channel remote port - * - * 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 *rdata = rport->dd_data; - struct { - struct fc_els_prli prli; - struct fc_els_spp spp; - } *pp; - u32 roles = FC_RPORT_ROLE_UNKNOWN; - u32 fcp_parm = 0; - u8 op; - - mutex_lock(&rdata->rp_mutex); - - FC_DEBUG_RPORT("Received a PRLI response from port (%6x)\n", - rport->port_id); - - 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); - goto err; - } - - 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) - 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; - - rport->roles = roles; - fc_rport_enter_rtv(rport); - - } else { - FC_DBG("Bad ELS response\n"); - rdata->event = RPORT_EV_FAILED; - queue_work(rport_event_queue, &rdata->event_work); - } - -out: - fc_frame_free(fp); -err: - mutex_unlock(&rdata->rp_mutex); - put_device(&rport->dev); -} - -/** - * fc_rport_logo_resp - Logout (LOGO) response handler - * @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 *rdata = rport->dd_data; - u8 op; - - mutex_lock(&rdata->rp_mutex); - - FC_DEBUG_RPORT("Received a LOGO response from port (%6x)\n", - rport->port_id); - - if (IS_ERR(fp)) { - fc_rport_error(rport, fp); - goto err; - } - - if (rdata->rp_state != RPORT_ST_LOGO) { - FC_DEBUG_RPORT("Received a LOGO response, but in state %s\n", - fc_rport_state(rport)); - goto out; - } - - op = fc_frame_payload_op(fp); - if (op == ELS_LS_ACC) { - fc_rport_enter_rtv(rport); - } else { - FC_DBG("Bad ELS response\n"); - rdata->event = RPORT_EV_LOGO; - queue_work(rport_event_queue, &rdata->event_work); - } - -out: - fc_frame_free(fp); -err: - mutex_unlock(&rdata->rp_mutex); - put_device(&rport->dev); -} - -/** - * 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; - - FC_DEBUG_RPORT("Port (%6x) entered PRLI state from %s state\n", - rport->port_id, fc_rport_state(rport)); - - fc_rport_state_enter(rport, RPORT_ST_PRLI); - - fp = fc_frame_alloc(lport, sizeof(*pp)); - if (!fp) { - fc_rport_error(rport, fp); - return; - } - - if (!lport->tt.elsct_send(lport, rport, fp, ELS_PRLI, - fc_rport_prli_resp, rport, lport->e_d_tov)) - fc_rport_error(rport, fp); - else - get_device(&rport->dev); -} - -/** - * fc_rport_els_rtv_resp - Request Timeout Value response handler - * @sp: current sequence in the RTV exchange - * @fp: response frame - * @rp_arg: Fibre Channel remote port - * - * 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 *rdata = rport->dd_data; - u8 op; - - mutex_lock(&rdata->rp_mutex); - - FC_DEBUG_RPORT("Received a RTV response from port (%6x)\n", - rport->port_id); - - 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); - goto err; - } - - op = fc_frame_payload_op(fp); - if (op == ELS_LS_ACC) { - struct fc_els_rtv_acc *rtv; - u32 toq; - u32 tov; - - rtv = fc_frame_payload_get(fp, sizeof(*rtv)); - if (rtv) { - toq = ntohl(rtv->rtv_toq); - tov = ntohl(rtv->rtv_r_a_tov); - if (tov == 0) - tov = 1; - 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; - rdata->e_d_tov = tov; - } - } - - fc_rport_enter_ready(rport); - -out: - fc_frame_free(fp); -err: - mutex_unlock(&rdata->rp_mutex); - put_device(&rport->dev); -} - -/** - * 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_frame *fp; - struct fc_rport_libfc_priv *rdata = rport->dd_data; - struct fc_lport *lport = rdata->local_port; - - FC_DEBUG_RPORT("Port (%6x) entered RTV state from %s state\n", - rport->port_id, fc_rport_state(rport)); - - fc_rport_state_enter(rport, RPORT_ST_RTV); - - fp = fc_frame_alloc(lport, sizeof(struct fc_els_rtv)); - if (!fp) { - fc_rport_error(rport, fp); - return; - } - - if (!lport->tt.elsct_send(lport, rport, fp, ELS_RTV, - fc_rport_rtv_resp, rport, lport->e_d_tov)) - fc_rport_error(rport, fp); - else - get_device(&rport->dev); -} - -/** - * 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; - - FC_DEBUG_RPORT("Port (%6x) entered LOGO state from %s state\n", - rport->port_id, fc_rport_state(rport)); - - fc_rport_state_enter(rport, RPORT_ST_LOGO); - - fp = fc_frame_alloc(lport, sizeof(struct fc_els_logo)); - if (!fp) { - fc_rport_error(rport, fp); - return; - } - - if (!lport->tt.elsct_send(lport, rport, fp, ELS_LOGO, - fc_rport_logo_resp, rport, lport->e_d_tov)) - fc_rport_error(rport, fp); - else - get_device(&rport->dev); -} - - -/** - * 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 *rdata = rport->dd_data; - struct fc_lport *lport = rdata->local_port; - - struct fc_frame_header *fh; - struct fc_seq_els_data els_data; - u8 op; - - mutex_lock(&rdata->rp_mutex); - - els_data.fp = NULL; - els_data.explan = ELS_EXPL_NONE; - els_data.reason = ELS_RJT_NONE; - - fh = fc_frame_header_get(fp); - - if (fh->fh_r_ctl == FC_RCTL_ELS_REQ && fh->fh_type == FC_TYPE_ELS) { - op = fc_frame_payload_op(fp); - switch (op) { - case ELS_PLOGI: - fc_rport_recv_plogi_req(rport, sp, fp); - break; - case ELS_PRLI: - fc_rport_recv_prli_req(rport, sp, fp); - break; - case ELS_PRLO: - fc_rport_recv_prlo_req(rport, sp, fp); - break; - case ELS_LOGO: - fc_rport_recv_logo_req(rport, sp, fp); - break; - case ELS_RRQ: - els_data.fp = fp; - lport->tt.seq_els_rsp_send(sp, ELS_RRQ, &els_data); - break; - case ELS_REC: - els_data.fp = fp; - lport->tt.seq_els_rsp_send(sp, ELS_REC, &els_data); - break; - default: - els_data.reason = ELS_RJT_UNSUP; - lport->tt.seq_els_rsp_send(sp, ELS_LS_RJT, &els_data); - break; - } - } - - mutex_unlock(&rdata->rp_mutex); -} - -/** - * fc_rport_recv_plogi_req - Handle incoming Port Login (PLOGI) request - * @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 *rdata = rport->dd_data; - struct fc_lport *lport = rdata->local_port; - struct fc_frame *fp = rx_fp; - struct fc_exch *ep; - struct fc_frame_header *fh; - struct fc_els_flogi *pl; - struct fc_seq_els_data rjt_data; - u32 sid; - u64 wwpn; - u64 wwnn; - enum fc_els_rjt_reason reject = 0; - u32 f_ctl; - rjt_data.fp = NULL; - - fh = fc_frame_header_get(fp); - - FC_DEBUG_RPORT("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) { - FC_DBG("incoming PLOGI from %x too short\n", sid); - WARN_ON(1); - /* XXX TBD: send reject? */ - fc_frame_free(fp); - return; - } - wwpn = get_unaligned_be64(&pl->fl_wwpn); - wwnn = get_unaligned_be64(&pl->fl_wwnn); - - /* - * If the session was just created, possibly due to the incoming PLOGI, - * set the state appropriately and accept the PLOGI. - * - * If we had also sent a PLOGI, and if the received PLOGI is from a - * higher WWPN, we accept it, otherwise an LS_RJT is sent with reason - * "command already in progress". - * - * XXX TBD: If the session was ready before, the PLOGI should result in - * all outstanding exchanges being reset. - */ - switch (rdata->rp_state) { - case RPORT_ST_INIT: - FC_DEBUG_RPORT("incoming PLOGI from %6x wwpn %llx state INIT " - "- reject\n", sid, wwpn); - reject = ELS_RJT_UNSUP; - break; - case RPORT_ST_PLOGI: - FC_DEBUG_RPORT("incoming PLOGI from %x in PLOGI state %d\n", - sid, rdata->rp_state); - if (wwpn < lport->wwpn) - reject = ELS_RJT_INPROG; - break; - case RPORT_ST_PRLI: - case RPORT_ST_READY: - FC_DEBUG_RPORT("incoming PLOGI from %x in logged-in state %d " - "- ignored for now\n", sid, rdata->rp_state); - /* XXX TBD - should reset */ - break; - case RPORT_ST_NONE: - default: - FC_DEBUG_RPORT("incoming PLOGI from %x in unexpected " - "state %d\n", sid, rdata->rp_state); - break; - } - - if (reject) { - rjt_data.reason = reject; - rjt_data.explan = ELS_EXPL_NONE; - lport->tt.seq_els_rsp_send(sp, ELS_LS_RJT, &rjt_data); - fc_frame_free(fp); - } else { - fp = fc_frame_alloc(lport, sizeof(*pl)); - if (fp == NULL) { - fp = rx_fp; - rjt_data.reason = ELS_RJT_UNAB; - rjt_data.explan = ELS_EXPL_NONE; - lport->tt.seq_els_rsp_send(sp, ELS_LS_RJT, &rjt_data); - fc_frame_free(fp); - } else { - sp = lport->tt.seq_start_next(sp); - WARN_ON(!sp); - fc_rport_set_name(rport, wwpn, wwnn); - - /* - * Get session payload size from incoming PLOGI. - */ - rport->maxframe_size = - fc_plogi_get_maxframe(pl, lport->mfs); - fc_frame_free(rx_fp); - fc_plogi_fill(lport, fp, ELS_LS_ACC); - - /* - * Send LS_ACC. If this fails, - * the originator should retry. - */ - f_ctl = FC_FC_EX_CTX | FC_FC_LAST_SEQ; - f_ctl |= FC_FC_END_SEQ | FC_FC_SEQ_INIT; - ep = fc_seq_exch(sp); - fc_fill_fc_hdr(fp, FC_RCTL_ELS_REP, ep->did, ep->sid, - FC_TYPE_ELS, f_ctl, 0); - lport->tt.seq_send(lport, sp, fp); - if (rdata->rp_state == RPORT_ST_PLOGI) - fc_rport_enter_prli(rport); - } - } -} - -/** - * fc_rport_recv_prli_req - Handle incoming Process Login (PRLI) request - * @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 *rdata = rport->dd_data; - struct fc_lport *lport = rdata->local_port; - struct fc_exch *ep; - struct fc_frame *fp; - struct fc_frame_header *fh; - struct { - struct fc_els_prli prli; - struct fc_els_spp spp; - } *pp; - struct fc_els_spp *rspp; /* request service param page */ - struct fc_els_spp *spp; /* response spp */ - unsigned int len; - unsigned int plen; - enum fc_els_rjt_reason reason = ELS_RJT_UNAB; - enum fc_els_rjt_explan explan = ELS_EXPL_NONE; - enum fc_els_spp_resp resp; - struct fc_seq_els_data rjt_data; - u32 f_ctl; - u32 fcp_parm; - u32 roles = FC_RPORT_ROLE_UNKNOWN; - rjt_data.fp = NULL; - - fh = fc_frame_header_get(rx_fp); - - FC_DEBUG_RPORT("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; - break; - default: - break; - } - len = fr_len(rx_fp) - sizeof(*fh); - pp = fc_frame_payload_get(rx_fp, sizeof(*pp)); - if (pp == NULL) { - reason = ELS_RJT_PROT; - explan = ELS_EXPL_INV_LEN; - } else { - plen = ntohs(pp->prli.prli_len); - if ((plen % 4) != 0 || plen > len) { - reason = ELS_RJT_PROT; - explan = ELS_EXPL_INV_LEN; - } else if (plen < len) { - len = plen; - } - plen = pp->prli.prli_spp_len; - if ((plen % 4) != 0 || plen < sizeof(*spp) || - plen > len || len < sizeof(*pp)) { - reason = ELS_RJT_PROT; - explan = ELS_EXPL_INV_LEN; - } - rspp = &pp->spp; - } - if (reason != ELS_RJT_NONE || - (fp = fc_frame_alloc(lport, len)) == NULL) { - rjt_data.reason = reason; - rjt_data.explan = explan; - lport->tt.seq_els_rsp_send(sp, ELS_LS_RJT, &rjt_data); - } else { - sp = lport->tt.seq_start_next(sp); - WARN_ON(!sp); - pp = fc_frame_payload_get(fp, len); - WARN_ON(!pp); - memset(pp, 0, len); - pp->prli.prli_cmd = ELS_LS_ACC; - pp->prli.prli_spp_len = plen; - pp->prli.prli_len = htons(len); - len -= sizeof(struct fc_els_prli); - - /* - * Go through all the service parameter pages and build - * response. If plen indicates longer SPP than standard, - * use that. The entire response has been pre-cleared above. - */ - spp = &pp->spp; - while (len >= plen) { - spp->spp_type = rspp->spp_type; - spp->spp_type_ext = rspp->spp_type_ext; - spp->spp_flags = rspp->spp_flags & FC_SPP_EST_IMG_PAIR; - resp = FC_SPP_RESP_ACK; - if (rspp->spp_flags & FC_SPP_RPA_VAL) - resp = FC_SPP_RESP_NO_PA; - switch (rspp->spp_type) { - case 0: /* common to all FC-4 types */ - break; - case FC_TYPE_FCP: - fcp_parm = ntohl(rspp->spp_params); - if (fcp_parm * FCP_SPPF_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; - rport->roles = roles; - - spp->spp_params = - htonl(lport->service_params); - break; - default: - resp = FC_SPP_RESP_INVL; - break; - } - spp->spp_flags |= resp; - len -= plen; - rspp = (struct fc_els_spp *)((char *)rspp + plen); - spp = (struct fc_els_spp *)((char *)spp + plen); - } - - /* - * Send LS_ACC. If this fails, the originator should retry. - */ - f_ctl = FC_FC_EX_CTX | FC_FC_LAST_SEQ; - f_ctl |= FC_FC_END_SEQ | FC_FC_SEQ_INIT; - ep = fc_seq_exch(sp); - fc_fill_fc_hdr(fp, FC_RCTL_ELS_REP, ep->did, ep->sid, - FC_TYPE_ELS, f_ctl, 0); - lport->tt.seq_send(lport, sp, fp); - - /* - * Get lock and re-check state. - */ - switch (rdata->rp_state) { - case RPORT_ST_PRLI: - fc_rport_enter_ready(rport); - break; - case RPORT_ST_READY: - break; - default: - break; - } - } - fc_frame_free(rx_fp); -} - -/** - * fc_rport_recv_prlo_req - Handle incoming Process Logout (PRLO) request - * @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 *rdata = rport->dd_data; - struct fc_lport *lport = rdata->local_port; - - struct fc_frame_header *fh; - struct fc_seq_els_data rjt_data; - - fh = fc_frame_header_get(fp); - - FC_DEBUG_RPORT("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; - lport->tt.seq_els_rsp_send(sp, ELS_LS_RJT, &rjt_data); - fc_frame_free(fp); -} - -/** - * fc_rport_recv_logo_req - Handle incoming Logout (LOGO) request - * @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 *rdata = rport->dd_data; - struct fc_lport *lport = rdata->local_port; - - fh = fc_frame_header_get(fp); - - FC_DEBUG_RPORT("Received LOGO request from port (%6x) " - "while in state %s\n", ntoh24(fh->fh_s_id), - fc_rport_state(rport)); - - rdata->event = RPORT_EV_LOGO; - queue_work(rport_event_queue, &rdata->event_work); - - lport->tt.seq_els_rsp_send(sp, ELS_LS_ACC, NULL); - fc_frame_free(fp); -} - -static void fc_rport_flush_queue(void) -{ - flush_workqueue(rport_event_queue); -} - - -int fc_rport_init(struct fc_lport *lport) -{ - if (!lport->tt.rport_login) - lport->tt.rport_login = fc_rport_login; - - if (!lport->tt.rport_logoff) - lport->tt.rport_logoff = fc_rport_logoff; - - if (!lport->tt.rport_recv_req) - lport->tt.rport_recv_req = fc_rport_recv_req; - - if (!lport->tt.rport_flush_queue) - lport->tt.rport_flush_queue = fc_rport_flush_queue; - - return 0; -} -EXPORT_SYMBOL(fc_rport_init); - -int fc_setup_rport() -{ - rport_event_queue = create_singlethread_workqueue("fc_rport_eq"); - if (!rport_event_queue) - return -ENOMEM; - return 0; -} -EXPORT_SYMBOL(fc_setup_rport); - -void fc_destroy_rport() -{ - destroy_workqueue(rport_event_queue); -} -EXPORT_SYMBOL(fc_destroy_rport); - -void fc_rport_terminate_io(struct fc_rport *rport) -{ - struct fc_rport_libfc_priv *rdata = rport->dd_data; - struct fc_lport *lport = rdata->local_port; - - lport->tt.exch_mgr_reset(lport->emp, 0, rport->port_id); - lport->tt.exch_mgr_reset(lport->emp, rport->port_id, 0); -} -EXPORT_SYMBOL(fc_rport_terminate_io); diff --git a/include/scsi/fc_encode.h b/include/scsi/fc_encode.h deleted file mode 100644 index 6300f556bce5..000000000000 --- a/include/scsi/fc_encode.h +++ /dev/null @@ -1,309 +0,0 @@ -/* - * Copyright(c) 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 - */ - -#ifndef _FC_ENCODE_H_ -#define _FC_ENCODE_H_ -#include <asm/unaligned.h> - -struct fc_ns_rft { - struct fc_ns_fid fid; /* port ID object */ - struct fc_ns_fts fts; /* FC4-types object */ -}; - -struct fc_ct_req { - struct fc_ct_hdr hdr; - union { - struct fc_ns_gid_ft gid; - struct fc_ns_rn_id rn; - struct fc_ns_rft rft; - } payload; -}; - -/** - * fill FC header fields in specified fc_frame - */ -static inline void fc_fill_fc_hdr(struct fc_frame *fp, enum fc_rctl r_ctl, - u32 did, u32 sid, enum fc_fh_type type, - u32 f_ctl, u32 parm_offset) -{ - struct fc_frame_header *fh; - - fh = fc_frame_header_get(fp); - WARN_ON(r_ctl == 0); - fh->fh_r_ctl = r_ctl; - hton24(fh->fh_d_id, did); - hton24(fh->fh_s_id, sid); - fh->fh_type = type; - hton24(fh->fh_f_ctl, f_ctl); - fh->fh_cs_ctl = 0; - fh->fh_df_ctl = 0; - fh->fh_parm_offset = htonl(parm_offset); -} - -/** - * fc_ct_hdr_fill- fills ct header and reset ct payload - * returns pointer to ct request. - */ -static inline struct fc_ct_req *fc_ct_hdr_fill(const struct fc_frame *fp, - unsigned int op, size_t req_size) -{ - struct fc_ct_req *ct; - size_t ct_plen; - - ct_plen = sizeof(struct fc_ct_hdr) + req_size; - ct = fc_frame_payload_get(fp, ct_plen); - memset(ct, 0, ct_plen); - ct->hdr.ct_rev = FC_CT_REV; - ct->hdr.ct_fs_type = FC_FST_DIR; - ct->hdr.ct_fs_subtype = FC_NS_SUBTYPE; - ct->hdr.ct_cmd = htons((u16) op); - return ct; -} - -/** - * fc_ct_fill - Fill in a name service request frame - */ -static inline int fc_ct_fill(struct fc_lport *lport, struct fc_frame *fp, - unsigned int op, enum fc_rctl *r_ctl, u32 *did, - enum fc_fh_type *fh_type) -{ - struct fc_ct_req *ct; - - switch (op) { - case FC_NS_GPN_FT: - ct = fc_ct_hdr_fill(fp, op, sizeof(struct fc_ns_gid_ft)); - ct->payload.gid.fn_fc4_type = FC_TYPE_FCP; - break; - - case FC_NS_RFT_ID: - ct = fc_ct_hdr_fill(fp, op, sizeof(struct fc_ns_rft)); - hton24(ct->payload.rft.fid.fp_fid, - fc_host_port_id(lport->host)); - ct->payload.rft.fts = lport->fcts; - break; - - case FC_NS_RPN_ID: - ct = fc_ct_hdr_fill(fp, op, sizeof(struct fc_ns_rn_id)); - hton24(ct->payload.rn.fr_fid.fp_fid, - fc_host_port_id(lport->host)); - ct->payload.rft.fts = lport->fcts; - put_unaligned_be64(lport->wwpn, &ct->payload.rn.fr_wwn); - break; - - default: - FC_DBG("Invalid op code %x \n", op); - return -EINVAL; - } - *r_ctl = FC_RCTL_DD_UNSOL_CTL; - *did = FC_FID_DIR_SERV; - *fh_type = FC_TYPE_CT; - return 0; -} - -/** - * fc_plogi_fill - Fill in plogi request frame - */ -static inline void fc_plogi_fill(struct fc_lport *lport, struct fc_frame *fp, - unsigned int op) -{ - struct fc_els_flogi *plogi; - struct fc_els_csp *csp; - struct fc_els_cssp *cp; - - plogi = fc_frame_payload_get(fp, sizeof(*plogi)); - memset(plogi, 0, sizeof(*plogi)); - plogi->fl_cmd = (u8) op; - put_unaligned_be64(lport->wwpn, &plogi->fl_wwpn); - put_unaligned_be64(lport->wwnn, &plogi->fl_wwnn); - - csp = &plogi->fl_csp; - csp->sp_hi_ver = 0x20; - csp->sp_lo_ver = 0x20; - csp->sp_bb_cred = htons(10); /* this gets set by gateway */ - csp->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); - csp->sp_features = htons(FC_SP_FT_CIRO); - csp->sp_tot_seq = htons(255); /* seq. we accept */ - csp->sp_rel_off = htons(0x1f); - csp->sp_e_d_tov = htonl(lport->e_d_tov); - - cp->cp_rdfs = htons((u16) lport->mfs); - cp->cp_con_seq = htons(255); - cp->cp_open_seq = 1; -} - -/** - * fc_flogi_fill - Fill in a flogi request frame. - */ -static inline void fc_flogi_fill(struct fc_lport *lport, struct fc_frame *fp) -{ - struct fc_els_csp *sp; - struct fc_els_cssp *cp; - struct fc_els_flogi *flogi; - - flogi = fc_frame_payload_get(fp, sizeof(*flogi)); - memset(flogi, 0, sizeof(*flogi)); - flogi->fl_cmd = (u8) ELS_FLOGI; - 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) lport->mfs); - cp = &flogi->fl_cssp[3 - 1]; /* class 3 parameters */ - cp->cp_class = htons(FC_CPC_VALID | FC_CPC_SEQ); -} - -/** - * fc_logo_fill - Fill in a logo request frame. - */ -static inline void fc_logo_fill(struct fc_lport *lport, struct fc_frame *fp) -{ - struct fc_els_logo *logo; - - logo = fc_frame_payload_get(fp, sizeof(*logo)); - memset(logo, 0, sizeof(*logo)); - logo->fl_cmd = ELS_LOGO; - hton24(logo->fl_n_port_id, fc_host_port_id(lport->host)); - logo->fl_n_port_wwn = htonll(lport->wwpn); -} - -/** - * fc_rtv_fill - Fill in RTV (read timeout value) request frame. - */ -static inline void fc_rtv_fill(struct fc_lport *lport, struct fc_frame *fp) -{ - struct fc_els_rtv *rtv; - - rtv = fc_frame_payload_get(fp, sizeof(*rtv)); - memset(rtv, 0, sizeof(*rtv)); - rtv->rtv_cmd = ELS_RTV; -} - -/** - * fc_rec_fill - Fill in rec request frame - */ -static inline void fc_rec_fill(struct fc_lport *lport, struct fc_frame *fp) -{ - struct fc_els_rec *rec; - struct fc_exch *ep = fc_seq_exch(fr_seq(fp)); - - rec = fc_frame_payload_get(fp, sizeof(*rec)); - memset(rec, 0, sizeof(*rec)); - rec->rec_cmd = ELS_REC; - hton24(rec->rec_s_id, fc_host_port_id(lport->host)); - rec->rec_ox_id = htons(ep->oxid); - rec->rec_rx_id = htons(ep->rxid); -} - -/** - * fc_prli_fill - Fill in prli request frame - */ -static inline void fc_prli_fill(struct fc_lport *lport, struct fc_frame *fp) -{ - struct { - struct fc_els_prli prli; - struct fc_els_spp spp; - } *pp; - - pp = fc_frame_payload_get(fp, sizeof(*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(lport->service_params); -} - -/** - * fc_scr_fill - Fill in a scr request frame. - */ -static inline void fc_scr_fill(struct fc_lport *lport, struct fc_frame *fp) -{ - struct fc_els_scr *scr; - - 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_els_fill - Fill in an ELS request frame - */ -static inline int fc_els_fill(struct fc_lport *lport, struct fc_rport *rport, - struct fc_frame *fp, unsigned int op, - enum fc_rctl *r_ctl, u32 *did, enum fc_fh_type *fh_type) -{ - switch (op) { - case ELS_PLOGI: - fc_plogi_fill(lport, fp, ELS_PLOGI); - *did = rport->port_id; - break; - - case ELS_FLOGI: - fc_flogi_fill(lport, fp); - *did = FC_FID_FLOGI; - break; - - case ELS_LOGO: - fc_logo_fill(lport, fp); - *did = FC_FID_FLOGI; - /* - * if rport is valid then it - * is port logo, therefore - * set did to rport id. - */ - if (rport) - *did = rport->port_id; - break; - - case ELS_RTV: - fc_rtv_fill(lport, fp); - *did = rport->port_id; - break; - - case ELS_REC: - fc_rec_fill(lport, fp); - *did = rport->port_id; - break; - - case ELS_PRLI: - fc_prli_fill(lport, fp); - *did = rport->port_id; - break; - - case ELS_SCR: - fc_scr_fill(lport, fp); - *did = FC_FID_FCTRL; - break; - - default: - FC_DBG("Invalid op code %x \n", op); - return -EINVAL; - } - - *r_ctl = FC_RCTL_ELS_REQ; - *fh_type = FC_TYPE_ELS; - return 0; -} -#endif /* _FC_ENCODE_H_ */ diff --git a/include/scsi/fc_frame.h b/include/scsi/fc_frame.h deleted file mode 100644 index 04d34a71355f..000000000000 --- a/include/scsi/fc_frame.h +++ /dev/null @@ -1,242 +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 - */ - -#ifndef _FC_FRAME_H_ -#define _FC_FRAME_H_ - -#include <linux/scatterlist.h> -#include <linux/skbuff.h> -#include <scsi/scsi_cmnd.h> - -#include <scsi/fc/fc_fs.h> -#include <scsi/fc/fc_fcp.h> -#include <scsi/fc/fc_encaps.h> - -/* - * The fc_frame interface is used to pass frame data between functions. - * The frame includes the data buffer, length, and SOF / EOF delimiter types. - * A pointer to the port structure of the receiving port is also includeded. - */ - -#define FC_FRAME_HEADROOM 32 /* headroom for VLAN + FCoE headers */ -#define FC_FRAME_TAILROOM 8 /* trailer space for FCoE */ - -/* - * Information about an individual fibre channel frame received or to be sent. - * The buffer may be in up to 4 additional non-contiguous sections, - * but the linear section must hold the frame header. - */ -#define FC_FRAME_SG_LEN 4 /* scatter/gather list maximum length */ - -#define fp_skb(fp) (&((fp)->skb)) -#define fr_hdr(fp) ((fp)->skb.data) -#define fr_len(fp) ((fp)->skb.len) -#define fr_cb(fp) ((struct fcoe_rcv_info *)&((fp)->skb.cb[0])) -#define fr_dev(fp) (fr_cb(fp)->fr_dev) -#define fr_seq(fp) (fr_cb(fp)->fr_seq) -#define fr_sof(fp) (fr_cb(fp)->fr_sof) -#define fr_eof(fp) (fr_cb(fp)->fr_eof) -#define fr_flags(fp) (fr_cb(fp)->fr_flags) -#define fr_max_payload(fp) (fr_cb(fp)->fr_max_payload) -#define fr_cmd(fp) (fr_cb(fp)->fr_cmd) -#define fr_dir(fp) (fr_cmd(fp)->sc_data_direction) -#define fr_crc(fp) (fr_cb(fp)->fr_crc) - -struct fc_frame { - struct sk_buff skb; -}; - -struct fcoe_rcv_info { - struct packet_type *ptype; - struct fc_lport *fr_dev; /* transport layer private pointer */ - struct fc_seq *fr_seq; /* for use with exchange manager */ - struct scsi_cmnd *fr_cmd; /* for use of scsi command */ - u32 fr_crc; - u16 fr_max_payload; /* max FC payload */ - enum fc_sof fr_sof; /* start of frame delimiter */ - enum fc_eof fr_eof; /* end of frame delimiter */ - u8 fr_flags; /* flags - see below */ -}; - - -/* - * Get fc_frame pointer for an skb that's already been imported. - */ -static inline struct fcoe_rcv_info *fcoe_dev_from_skb(const struct sk_buff *skb) -{ - BUILD_BUG_ON(sizeof(struct fcoe_rcv_info) > sizeof(skb->cb)); - return (struct fcoe_rcv_info *) skb->cb; -} - -/* - * fr_flags. - */ -#define FCPHF_CRC_UNCHECKED 0x01 /* CRC not computed, still appended */ - -/* - * Initialize a frame. - * We don't do a complete memset here for performance reasons. - * The caller must set fr_free, fr_hdr, fr_len, fr_sof, and fr_eof eventually. - */ -static inline void fc_frame_init(struct fc_frame *fp) -{ - fr_dev(fp) = NULL; - fr_seq(fp) = NULL; - fr_flags(fp) = 0; -} - -struct fc_frame *fc_frame_alloc_fill(struct fc_lport *, size_t payload_len); - -struct fc_frame *__fc_frame_alloc(size_t payload_len); - -/* - * Get frame for sending via port. - */ -static inline struct fc_frame *_fc_frame_alloc(struct fc_lport *dev, - size_t payload_len) -{ - return __fc_frame_alloc(payload_len); -} - -/* - * Allocate fc_frame structure and buffer. Set the initial length to - * payload_size + sizeof (struct fc_frame_header). - */ -static inline struct fc_frame *fc_frame_alloc(struct fc_lport *dev, size_t len) -{ - struct fc_frame *fp; - - /* - * Note: Since len will often be a constant multiple of 4, - * this check will usually be evaluated and eliminated at compile time. - */ - if ((len % 4) != 0) - fp = fc_frame_alloc_fill(dev, len); - else - fp = _fc_frame_alloc(dev, len); - return fp; -} - -/* - * Free the fc_frame structure and buffer. - */ -static inline void fc_frame_free(struct fc_frame *fp) -{ - kfree_skb(fp_skb(fp)); -} - -static inline int fc_frame_is_linear(struct fc_frame *fp) -{ - return !skb_is_nonlinear(fp_skb(fp)); -} - -/* - * Get frame header from message in fc_frame structure. - * This hides a cast and provides a place to add some checking. - */ -static inline -struct fc_frame_header *fc_frame_header_get(const struct fc_frame *fp) -{ - WARN_ON(fr_len(fp) < sizeof(struct fc_frame_header)); - return (struct fc_frame_header *) fr_hdr(fp); -} - -/* - * Get frame payload from message in fc_frame structure. - * This hides a cast and provides a place to add some checking. - * The len parameter is the minimum length for the payload portion. - * Returns NULL if the frame is too short. - * - * This assumes the interesting part of the payload is in the first part - * of the buffer for received data. This may not be appropriate to use for - * buffers being transmitted. - */ -static inline void *fc_frame_payload_get(const struct fc_frame *fp, - size_t len) -{ - void *pp = NULL; - - if (fr_len(fp) >= sizeof(struct fc_frame_header) + len) - pp = fc_frame_header_get(fp) + 1; - return pp; -} - -/* - * Get frame payload opcode (first byte) from message in fc_frame structure. - * This hides a cast and provides a place to add some checking. Return 0 - * if the frame has no payload. - */ -static inline u8 fc_frame_payload_op(const struct fc_frame *fp) -{ - u8 *cp; - - cp = fc_frame_payload_get(fp, sizeof(u8)); - if (!cp) - return 0; - return *cp; - -} - -/* - * Get FC class from frame. - */ -static inline enum fc_class fc_frame_class(const struct fc_frame *fp) -{ - return fc_sof_class(fr_sof(fp)); -} - -/* - * Check the CRC in a frame. - * The CRC immediately follows the last data item *AFTER* the length. - * The return value is zero if the CRC matches. - */ -u32 fc_frame_crc_check(struct fc_frame *); - -static inline u8 fc_frame_rctl(const struct fc_frame *fp) -{ - return fc_frame_header_get(fp)->fh_r_ctl; -} - -static inline bool fc_frame_is_cmd(const struct fc_frame *fp) -{ - return fc_frame_rctl(fp) == FC_RCTL_DD_UNSOL_CMD; -} - -static inline bool fc_frame_is_read(const struct fc_frame *fp) -{ - if (fc_frame_is_cmd(fp) && fr_cmd(fp)) - return fr_dir(fp) == DMA_FROM_DEVICE; - return false; -} - -static inline bool fc_frame_is_write(const struct fc_frame *fp) -{ - if (fc_frame_is_cmd(fp) && fr_cmd(fp)) - return fr_dir(fp) == DMA_TO_DEVICE; - return false; -} - -/* - * Check for leaks. - * Print the frame header of any currently allocated frame, assuming there - * should be none at this point. - */ -void fc_frame_leak_check(void); - -#endif /* _FC_FRAME_H_ */ diff --git a/include/scsi/libfc.h b/include/scsi/libfc.h deleted file mode 100644 index 9f2876397dda..000000000000 --- a/include/scsi/libfc.h +++ /dev/null @@ -1,938 +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 - */ - -#ifndef _LIBFC_H_ -#define _LIBFC_H_ - -#include <linux/timer.h> -#include <linux/if.h> - -#include <scsi/scsi_transport.h> -#include <scsi/scsi_transport_fc.h> - -#include <scsi/fc/fc_fcp.h> -#include <scsi/fc/fc_ns.h> -#include <scsi/fc/fc_els.h> -#include <scsi/fc/fc_gs.h> - -#include <scsi/fc_frame.h> - -#define LIBFC_DEBUG - -#ifdef LIBFC_DEBUG -/* Log messages */ -#define FC_DBG(fmt, args...) \ - do { \ - printk(KERN_INFO "%s " fmt, __func__, ##args); \ - } while (0) -#else -#define FC_DBG(fmt, args...) -#endif - -/* - * libfc error codes - */ -#define FC_NO_ERR 0 /* no error */ -#define FC_EX_TIMEOUT 1 /* Exchange timeout */ -#define FC_EX_CLOSED 2 /* Exchange closed */ - -/* some helpful macros */ - -#define ntohll(x) be64_to_cpu(x) -#define htonll(x) cpu_to_be64(x) - -#define ntoh24(p) (((p)[0] << 16) | ((p)[1] << 8) | ((p)[2])) - -#define hton24(p, v) do { \ - p[0] = (((v) >> 16) & 0xFF); \ - p[1] = (((v) >> 8) & 0xFF); \ - p[2] = ((v) & 0xFF); \ - } while (0) - -/* - * FC HBA status - */ -#define FC_PAUSE (1 << 1) -#define FC_LINK_UP (1 << 0) - -enum fc_lport_state { - LPORT_ST_NONE = 0, - LPORT_ST_FLOGI, - LPORT_ST_DNS, - LPORT_ST_RPN_ID, - LPORT_ST_RFT_ID, - LPORT_ST_SCR, - LPORT_ST_READY, - LPORT_ST_LOGO, - LPORT_ST_RESET -}; - -enum fc_disc_event { - DISC_EV_NONE = 0, - DISC_EV_SUCCESS, - DISC_EV_FAILED -}; - -enum fc_rport_state { - RPORT_ST_NONE = 0, - RPORT_ST_INIT, /* initialized */ - RPORT_ST_PLOGI, /* waiting for PLOGI completion */ - RPORT_ST_PRLI, /* waiting for PRLI completion */ - RPORT_ST_RTV, /* waiting for RTV completion */ - 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; -}; - -enum fc_rport_event { - RPORT_EV_NONE = 0, - RPORT_EV_CREATED, - RPORT_EV_FAILED, - RPORT_EV_STOP, - RPORT_EV_LOGO -}; - -struct fc_rport_operations { - void (*event_callback)(struct fc_lport *, struct fc_rport *, - enum fc_rport_event); -}; - -/** - * struct fc_rport_libfc_priv - libfc internal information about a remote port - * @local_port: Fibre Channel host port instance - * @rp_state: state tracks progress of PLOGI, PRLI, and RTV exchanges - * @flags: REC and RETRY supported flags - * @max_seq: maximum number of concurrent sequences - * @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_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; - enum fc_rport_state rp_state; - u16 flags; - #define FC_RP_FLAGS_REC_SUPPORTED (1 << 0) - #define FC_RP_FLAGS_RETRY (1 << 1) - u16 max_seq; - unsigned int retries; - unsigned int e_d_tov; - unsigned int r_a_tov; - enum fc_rport_trans_state trans_state; - struct mutex rp_mutex; - struct delayed_work retry_work; - enum fc_rport_event event; - struct fc_rport_operations *ops; - struct list_head peers; - struct work_struct event_work; -}; - -#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_rogue_create(struct fc_disc_port *); - -static inline void fc_rport_set_name(struct fc_rport *rport, u64 wwpn, u64 wwnn) -{ - rport->node_name = wwnn; - rport->port_name = wwpn; -} - -/* - * fcoe stats structure - */ -struct fcoe_dev_stats { - u64 SecondsSinceLastReset; - u64 TxFrames; - u64 TxWords; - u64 RxFrames; - u64 RxWords; - u64 ErrorFrames; - u64 DumpedFrames; - u64 LinkFailureCount; - u64 LossOfSignalCount; - u64 InvalidTxWordCount; - u64 InvalidCRCCount; - u64 InputRequests; - u64 OutputRequests; - u64 ControlRequests; - u64 InputMegabytes; - u64 OutputMegabytes; -}; - -/* - * els data is used for passing ELS respone specific - * data to send ELS response mainly using infomation - * in exchange and sequence in EM layer. - */ -struct fc_seq_els_data { - struct fc_frame *fp; - enum fc_els_rjt_reason reason; - enum fc_els_rjt_explan explan; -}; - -/* - * FCP request structure, one for each scsi cmd request - */ -struct fc_fcp_pkt { - /* - * housekeeping stuff - */ - struct fc_lport *lp; /* handle to hba struct */ - u16 state; /* scsi_pkt state state */ - u16 tgt_flags; /* target flags */ - atomic_t ref_cnt; /* fcp pkt ref count */ - spinlock_t scsi_pkt_lock; /* Must be taken before the host lock - * if both are held at the same time */ - /* - * SCSI I/O related stuff - */ - struct scsi_cmnd *cmd; /* scsi command pointer. set/clear - * under host lock */ - struct list_head list; /* tracks queued commands. access under - * host lock */ - /* - * timeout related stuff - */ - struct timer_list timer; /* command timer */ - struct completion tm_done; - int wait_for_comp; - unsigned long start_time; /* start jiffie */ - unsigned long end_time; /* end jiffie */ - unsigned long last_pkt_time; /* jiffies of last frame received */ - - /* - * scsi cmd and data transfer information - */ - u32 data_len; - /* - * transport related veriables - */ - struct fcp_cmnd cdb_cmd; - size_t xfer_len; - u32 xfer_contig_end; /* offset of end of contiguous xfer */ - u16 max_payload; /* max payload size in bytes */ - - /* - * scsi/fcp return status - */ - u32 io_status; /* SCSI result upper 24 bits */ - u8 cdb_status; - u8 status_code; /* FCP I/O status */ - /* bit 3 Underrun bit 2: overrun */ - u8 scsi_comp_flags; - u32 req_flags; /* bit 0: read bit:1 write */ - u32 scsi_resid; /* residule length */ - - struct fc_rport *rport; /* remote port pointer */ - struct fc_seq *seq_ptr; /* current sequence pointer */ - /* - * Error Processing - */ - u8 recov_retry; /* count of recovery retries */ - struct fc_seq *recov_seq; /* sequence for REC or SRR */ -}; - -/* - * Structure and function definitions for managing Fibre Channel Exchanges - * and Sequences - * - * fc_exch holds state for one exchange and links to its active sequence. - * - * fc_seq holds the state for an individual sequence. - */ - -struct fc_exch_mgr; - -/* - * Sequence. - */ -struct fc_seq { - u8 id; /* seq ID */ - u16 ssb_stat; /* status flags for sequence status block */ - u16 cnt; /* frames sent so far on sequence */ - u32 rec_data; /* FC-4 value for REC */ -}; - -#define FC_EX_DONE (1 << 0) /* ep is completed */ -#define FC_EX_RST_CLEANUP (1 << 1) /* reset is forcing completion */ - -/* - * Exchange. - * - * Locking notes: The ex_lock protects following items: - * state, esb_stat, f_ctl, seq.ssb_stat - * seq_id - * sequence allocation - */ -struct fc_exch { - struct fc_exch_mgr *em; /* exchange manager */ - u32 state; /* internal driver state */ - u16 xid; /* our exchange ID */ - struct list_head ex_list; /* free or busy list linkage */ - spinlock_t ex_lock; /* lock covering exchange state */ - atomic_t ex_refcnt; /* reference counter */ - struct delayed_work timeout_work; /* timer for upper level protocols */ - struct fc_lport *lp; /* fc device instance */ - u16 oxid; /* originator's exchange ID */ - u16 rxid; /* responder's exchange ID */ - u32 oid; /* originator's FCID */ - u32 sid; /* source FCID */ - u32 did; /* destination FCID */ - u32 esb_stat; /* exchange status for ESB */ - u32 r_a_tov; /* r_a_tov from rport (msec) */ - u8 seq_id; /* next sequence ID to use */ - u32 f_ctl; /* F_CTL flags for sequences */ - u8 fh_type; /* frame type */ - enum fc_class class; /* class of service */ - struct fc_seq seq; /* single sequence */ - /* - * Handler for responses to this current exchange. - */ - void (*resp)(struct fc_seq *, struct fc_frame *, void *); - void (*destructor)(struct fc_seq *, void *); - /* - * arg is passed as void pointer to exchange - * resp and destructor handlers - */ - void *arg; -}; -#define fc_seq_exch(sp) container_of(sp, struct fc_exch, seq) - -struct libfc_function_template { - - /** - * Mandatory Fields - * - * These handlers must be implemented by the LLD. - */ - - /* - * Interface to send a FC frame - */ - int (*frame_send)(struct fc_lport *lp, struct fc_frame *fp); - - /** - * Optional Fields - * - * The LLD may choose to implement any of the following handlers. - * If LLD doesn't specify hander and leaves its pointer NULL then - * the default libfc function will be used for that handler. - */ - - /** - * ELS/CT interfaces - */ - - /* - * elsct_send - sends ELS/CT frame - */ - struct fc_seq *(*elsct_send)(struct fc_lport *lport, - struct fc_rport *rport, - struct fc_frame *fp, - unsigned int op, - void (*resp)(struct fc_seq *, - struct fc_frame *fp, - void *arg), - void *arg, u32 timer_msec); - /** - * Exhance Manager interfaces - */ - - /* - * Send the FC frame payload using a new exchange and sequence. - * - * The frame pointer with some of the header's fields must be - * filled before calling exch_seq_send(), those fields are, - * - * - routing control - * - FC port did - * - FC port sid - * - FC header type - * - frame control - * - parameter or relative offset - * - * The exchange response handler is set in this routine to resp() - * function pointer. It can be called in two scenarios: if a timeout - * occurs or if a response frame is received for the exchange. The - * fc_frame pointer in response handler will also indicate timeout - * as error using IS_ERR related macros. - * - * 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 - * it fires or when the exchange is done. The exchange timeout handler - * is registered by EM layer. - */ - struct fc_seq *(*exch_seq_send)(struct fc_lport *lp, - struct fc_frame *fp, - void (*resp)(struct fc_seq *sp, - struct fc_frame *fp, - void *arg), - void (*destructor)(struct fc_seq *sp, - void *arg), - void *arg, unsigned int timer_msec); - - /* - * send a frame using existing sequence and exchange. - */ - int (*seq_send)(struct fc_lport *lp, struct fc_seq *sp, - struct fc_frame *fp); - - /* - * Send ELS response using mainly infomation - * in exchange and sequence in EM layer. - */ - void (*seq_els_rsp_send)(struct fc_seq *sp, enum fc_els_cmd els_cmd, - struct fc_seq_els_data *els_data); - - /* - * Abort an exchange and sequence. Generally called because of a - * exchange timeout or an abort from the upper layer. - * - * A timer_msec can be specified for abort timeout, if non-zero - * timer_msec value is specified then exchange resp handler - * will be called with timeout error if no response to abort. - */ - int (*seq_exch_abort)(const struct fc_seq *req_sp, - unsigned int timer_msec); - - /* - * Indicate that an exchange/sequence tuple is complete and the memory - * allocated for the related objects may be freed. - */ - void (*exch_done)(struct fc_seq *sp); - - /* - * Assigns a EM and a free XID for an new exchange and then - * allocates a new exchange and sequence pair. - * The fp can be used to determine free XID. - */ - struct fc_exch *(*exch_get)(struct fc_lport *lp, struct fc_frame *fp); - - /* - * Release previously assigned XID by exch_get API. - * The LLD may implement this if XID is assigned by LLD - * in exch_get(). - */ - void (*exch_put)(struct fc_lport *lp, struct fc_exch_mgr *mp, - u16 ex_id); - - /* - * Start a new sequence on the same exchange/sequence tuple. - */ - struct fc_seq *(*seq_start_next)(struct fc_seq *sp); - - /* - * Reset an exchange manager, completing all sequences and exchanges. - * If s_id is non-zero, reset only exchanges originating from that FID. - * If d_id is non-zero, reset only exchanges sending to that FID. - */ - void (*exch_mgr_reset)(struct fc_exch_mgr *, - u32 s_id, u32 d_id); - - void (*rport_flush_queue)(void); - /** - * Local Port interfaces - */ - - /* - * Receive a frame to a local port. - */ - void (*lport_recv)(struct fc_lport *lp, struct fc_seq *sp, - struct fc_frame *fp); - - int (*lport_reset)(struct fc_lport *); - - /** - * Remote Port interfaces - */ - - /* - * Initiates the RP state machine. It is called from the LP module. - * This function will issue the following commands to the N_Port - * identified by the FC ID provided. - * - * - PLOGI - * - PRLI - * - RTV - */ - int (*rport_login)(struct fc_rport *rport); - - /* - * Logoff, and remove the rport from the transport if - * it had been added. This will send a LOGO to the target. - */ - int (*rport_logoff)(struct fc_rport *rport); - - /* - * Recieve a request from a remote port. - */ - void (*rport_recv_req)(struct fc_seq *, struct fc_frame *, - struct fc_rport *); - - struct fc_rport *(*rport_lookup)(const struct fc_lport *, u32); - - /** - * FCP interfaces - */ - - /* - * Send a fcp cmd from fsp pkt. - * Called with the SCSI host lock unlocked and irqs disabled. - * - * The resp handler is called when FCP_RSP received. - * - */ - int (*fcp_cmd_send)(struct fc_lport *lp, struct fc_fcp_pkt *fsp, - void (*resp)(struct fc_seq *, struct fc_frame *fp, - void *arg)); - - /* - * Used at least durring linkdown and reset - */ - void (*fcp_cleanup)(struct fc_lport *lp); - - /* - * Abort all I/O on a local port - */ - void (*fcp_abort_io)(struct fc_lport *lp); - - /** - * Discovery interfaces - */ - - void (*disc_recv_req)(struct fc_seq *, - struct fc_frame *, struct fc_lport *); - - /* - * Start discovery for a local port. - */ - void (*disc_start)(void (*disc_callback)(struct fc_lport *, - enum fc_disc_event), - struct fc_lport *); - - /* - * Stop discovery for a given lport. This will remove - * all discovered rports - */ - void (*disc_stop) (struct fc_lport *); - - /* - * Stop discovery for a given lport. This will block - * until all discovered rports are deleted from the - * FC transport class - */ - void (*disc_stop_final) (struct fc_lport *); -}; - -/* information used by the discovery layer */ -struct fc_disc { - unsigned char retry_count; - unsigned char delay; - unsigned char pending; - unsigned char requested; - unsigned short seq_count; - unsigned char buf_len; - enum fc_disc_event event; - - void (*disc_callback)(struct fc_lport *, - enum fc_disc_event); - - struct list_head rports; - struct fc_lport *lport; - struct mutex disc_mutex; - struct fc_gpn_ft_resp partial_buf; /* partial name buffer */ - struct delayed_work disc_work; -}; - -struct fc_lport { - struct list_head list; - - /* Associations */ - struct Scsi_Host *host; - struct fc_exch_mgr *emp; - struct fc_rport *dns_rp; - struct fc_rport *ptp_rp; - void *scsi_priv; - struct fc_disc disc; - - /* Operational Information */ - struct libfc_function_template tt; - u16 link_status; - enum fc_lport_state state; - unsigned long boot_time; - - struct fc_host_statistics host_stats; - struct fcoe_dev_stats *dev_stats[NR_CPUS]; - u64 wwpn; - u64 wwnn; - u8 retry_count; - - /* Capabilities */ - u32 sg_supp:1; /* scatter gather supported */ - u32 seq_offload:1; /* seq offload supported */ - u32 crc_offload:1; /* crc offload supported */ - u32 lro_enabled:1; /* large receive offload */ - u32 mfs; /* max FC payload size */ - unsigned int service_params; - unsigned int e_d_tov; - unsigned int r_a_tov; - u8 max_retry_count; - u16 link_speed; - u16 link_supported_speeds; - u16 lro_xid; /* max xid for fcoe lro */ - struct fc_ns_fts fcts; /* FC-4 type masks */ - struct fc_els_rnid_gen rnid_gen; /* RNID information */ - - /* Semaphores */ - struct mutex lp_mutex; - - /* Miscellaneous */ - struct delayed_work retry_work; - struct delayed_work disc_work; -}; - -/** - * FC_LPORT HELPER FUNCTIONS - *****************************/ -static inline void *lport_priv(const struct fc_lport *lp) -{ - return (void *)(lp + 1); -} - -static inline int fc_lport_test_ready(struct fc_lport *lp) -{ - return lp->state == LPORT_ST_READY; -} - -static inline void fc_set_wwnn(struct fc_lport *lp, u64 wwnn) -{ - lp->wwnn = wwnn; -} - -static inline void fc_set_wwpn(struct fc_lport *lp, u64 wwnn) -{ - lp->wwpn = wwnn; -} - -static inline void fc_lport_state_enter(struct fc_lport *lp, - enum fc_lport_state state) -{ - if (state != lp->state) - lp->retry_count = 0; - lp->state = state; -} - - -/** - * LOCAL PORT LAYER - *****************************/ -int fc_lport_init(struct fc_lport *lp); - -/* - * Destroy the specified local port by finding and freeing all - * fc_rports associated with it and then by freeing the fc_lport - * itself. - */ -int fc_lport_destroy(struct fc_lport *lp); - -/* - * Logout the specified local port from the fabric - */ -int fc_fabric_logoff(struct fc_lport *lp); - -/* - * Initiate the LP state machine. This handler will use fc_host_attr - * to store the FLOGI service parameters, so fc_host_attr must be - * initialized before calling this handler. - */ -int fc_fabric_login(struct fc_lport *lp); - -/* - * The link is up for the given local port. - */ -void fc_linkup(struct fc_lport *); - -/* - * Link is down for the given local port. - */ -void fc_linkdown(struct fc_lport *); - -/* - * Pause and unpause traffic. - */ -void fc_pause(struct fc_lport *); -void fc_unpause(struct fc_lport *); - -/* - * Configure the local port. - */ -int fc_lport_config(struct fc_lport *); - -/* - * Reset the local port. - */ -int fc_lport_reset(struct fc_lport *); - -/* - * Set the mfs or reset - */ -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); - -/** - * DISCOVERY LAYER - *****************************/ -int fc_disc_init(struct fc_lport *lp); - - -/** - * SCSI LAYER - *****************************/ -/* - * Initialize the SCSI block of libfc - */ -int fc_fcp_init(struct fc_lport *); - -/* - * This section provides an API which allows direct interaction - * with the SCSI-ml. Each of these functions satisfies a function - * pointer defined in Scsi_Host and therefore is always called - * directly from the SCSI-ml. - */ -int fc_queuecommand(struct scsi_cmnd *sc_cmd, - void (*done)(struct scsi_cmnd *)); - -/* - * complete processing of a fcp packet - * - * This function may sleep if a fsp timer is pending. - * The host lock must not be held by caller. - */ -void fc_fcp_complete(struct fc_fcp_pkt *fsp); - -/* - * Send an ABTS frame to the target device. The sc_cmd argument - * is a pointer to the SCSI command to be aborted. - */ -int fc_eh_abort(struct scsi_cmnd *sc_cmd); - -/* - * Reset a LUN by sending send the tm cmd to the target. - */ -int fc_eh_device_reset(struct scsi_cmnd *sc_cmd); - -/* - * Reset the host adapter. - */ -int fc_eh_host_reset(struct scsi_cmnd *sc_cmd); - -/* - * Check rport status. - */ -int fc_slave_alloc(struct scsi_device *sdev); - -/* - * Adjust the queue depth. - */ -int fc_change_queue_depth(struct scsi_device *sdev, int qdepth); - -/* - * Change the tag type. - */ -int fc_change_queue_type(struct scsi_device *sdev, int tag_type); - -/* - * Free memory pools used by the FCP layer. - */ -void fc_fcp_destroy(struct fc_lport *); - -/** - * ELS/CT interface - *****************************/ -/* - * Initializes ELS/CT interface - */ -int fc_elsct_init(struct fc_lport *lp); - - -/** - * EXCHANGE MANAGER LAYER - *****************************/ -/* - * Initializes Exchange Manager related - * function pointers in struct libfc_function_template. - */ -int fc_exch_init(struct fc_lport *lp); - -/* - * Allocates an Exchange Manager (EM). - * - * The EM manages exchanges for their allocation and - * free, also allows exchange lookup for received - * frame. - * - * The class is used for initializing FC class of - * allocated exchange from EM. - * - * The min_xid and max_xid will limit new - * exchange ID (XID) within this range for - * a new exchange. - * The LLD may choose to have multiple EMs, - * e.g. one EM instance per CPU receive thread in LLD. - * The LLD can use exch_get() of struct libfc_function_template - * to specify XID for a new exchange within - * a specified EM instance. - * - * The em_idx to uniquely identify an EM instance. - */ -struct fc_exch_mgr *fc_exch_mgr_alloc(struct fc_lport *lp, - enum fc_class class, - u16 min_xid, - u16 max_xid); - -/* - * Free an exchange manager. - */ -void fc_exch_mgr_free(struct fc_exch_mgr *mp); - -/* - * Receive a frame on specified local port and exchange manager. - */ -void fc_exch_recv(struct fc_lport *lp, struct fc_exch_mgr *mp, - struct fc_frame *fp); - -/* - * This function is for exch_seq_send function pointer in - * struct libfc_function_template, see comment block on - * exch_seq_send for description of this function. - */ -struct fc_seq *fc_exch_seq_send(struct fc_lport *lp, - struct fc_frame *fp, - void (*resp)(struct fc_seq *sp, - struct fc_frame *fp, - void *arg), - void (*destructor)(struct fc_seq *sp, - void *arg), - void *arg, u32 timer_msec); - -/* - * send a frame using existing sequence and exchange. - */ -int fc_seq_send(struct fc_lport *lp, struct fc_seq *sp, struct fc_frame *fp); - -/* - * Send ELS response using mainly infomation - * in exchange and sequence in EM layer. - */ -void fc_seq_els_rsp_send(struct fc_seq *sp, enum fc_els_cmd els_cmd, - struct fc_seq_els_data *els_data); - -/* - * This function is for seq_exch_abort function pointer in - * struct libfc_function_template, see comment block on - * seq_exch_abort for description of this function. - */ -int fc_seq_exch_abort(const struct fc_seq *req_sp, unsigned int timer_msec); - -/* - * Indicate that an exchange/sequence tuple is complete and the memory - * allocated for the related objects may be freed. - */ -void fc_exch_done(struct fc_seq *sp); - -/* - * Assigns a EM and XID for a frame and then allocates - * a new exchange and sequence pair. - * The fp can be used to determine free XID. - */ -struct fc_exch *fc_exch_get(struct fc_lport *lp, struct fc_frame *fp); - -/* - * Allocate a new exchange and sequence pair. - * if ex_id is zero then next free exchange id - * from specified exchange manger mp will be assigned. - */ -struct fc_exch *fc_exch_alloc(struct fc_exch_mgr *mp, - struct fc_frame *fp, u16 ex_id); -/* - * Start a new sequence on the same exchange as the supplied sequence. - */ -struct fc_seq *fc_seq_start_next(struct fc_seq *sp); - -/* - * Reset an exchange manager, completing all sequences and exchanges. - * If s_id is non-zero, reset only exchanges originating from that FID. - * If d_id is non-zero, reset only exchanges sending to that FID. - */ -void fc_exch_mgr_reset(struct fc_exch_mgr *, u32 s_id, u32 d_id); - -/* - * 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_set_rport_loss_tmo(struct fc_rport *rport, u32 timeout); -struct fc_host_statistics *fc_get_host_stats(struct Scsi_Host *); - -/* - * module setup functions. - */ -int fc_setup_exch_mgr(void); -void fc_destroy_exch_mgr(void); -int fc_setup_rport(void); -void fc_destroy_rport(void); - -#endif /* _LIBFC_H_ */ |