From: Oliver Smith-Denny <osmithde@cisco.com>
To: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Cc: Sesidhar Baddela <sebaddel@cisco.com>,
Gian Carlo Boffa <gcboffa@cisco.com>,
linux-scsi@vger.kernel.org, target-devel@vger.kernel.org,
linux-kernel@vger.kernel.org,
Oliver Smith-Denny <osmithde@cisco.com>
Subject: [PATCH 06/10] staging: fnic2 add main frame processing
Date: Thu, 5 Apr 2018 14:21:05 -0700 [thread overview]
Message-ID: <20180405212105.GG12584@osmithde-lnx.cisco.com> (raw)
In-Reply-To: <20180405211519.GA12584@osmithde-lnx.cisco.com>
These files contain logic for handling Fibre Channel frames
and communication with firmware and LIO.
Signed-off-by: Oliver Smith-Denny <osmithde@cisco.com>
Signed-off-by: Sesidhar Baddela <sebaddel@cisco.com>
Signed-off-by: Anil Chintalapati <achintal@cisco.com>
Signed-off-by: Arulprabhu Ponnusamy <arulponn@cisco.com>
Signed-off-by: Gian Carlo Boffa <gcboffa@cisco.com>
Co-Developed-by: Arulprabhu Ponnusamy <arulponn@cisco.com>
Co-Developed-by: Gian Carlo Boffa <gcboffa@cisco.com>
Co-Developed-by: Oliver Smith-Denny <osmithde@cisco.com>
---
drivers/staging/fnic2/src/fcpio.h | 347 ++++++++++
drivers/staging/fnic2/src/fnic2_cmd.c | 1164 +++++++++++++++++++++++++++++++++
drivers/staging/fnic2/src/fnic2_io.h | 32 +
3 files changed, 1543 insertions(+)
create mode 100644 drivers/staging/fnic2/src/fcpio.h
create mode 100644 drivers/staging/fnic2/src/fnic2_cmd.c
create mode 100644 drivers/staging/fnic2/src/fnic2_io.h
diff --git a/drivers/staging/fnic2/src/fcpio.h b/drivers/staging/fnic2/src/fcpio.h
new file mode 100644
index 0000000..7c8b879
--- /dev/null
+++ b/drivers/staging/fnic2/src/fcpio.h
@@ -0,0 +1,347 @@
+/*
+ * SPDX-License-Identifier: GPL-2.0
+ * Copyright 2018 Cisco Systems, Inc. All rights reserved.
+ *
+ * This program is free software; you may redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+#ifndef _FCPIO_H_
+#define _FCPIO_H_
+
+#include <linux/if_ether.h>
+
+/*
+ * This header file includes all of the data structures used for
+ * communication by the host driver to the fcp firmware.
+ */
+
+/*
+ * Exchange and sequence id space allocated to the host driver
+ */
+#define FCPIO_HOST_EXCH_RANGE_START 0x1000
+
+/*
+ * Command entry type
+ */
+enum fcpio_type {
+ /*
+ * Initiator request types
+ */
+ FCPIO_ICMND_16 = 0x1,
+ FCPIO_ICMND_32,
+ FCPIO_ICMND_CMPL,
+ FCPIO_ITMF,
+ FCPIO_ITMF_CMPL,
+
+ /*
+ * Target request types
+ */
+ FCPIO_TCMD_SEND_DATA = 0x11, /* Send RD DATA, Send Resp, Free Xchg */
+ FCPIO_TCMD_RECV_DATA, /* Send Xfer Rdy, Xfer recvd data to SGL */
+ FCPIO_TCMD_FREE_CMD, /* Free the resources of cmd with given tag */
+ FCPIO_TCMD_SEND_RESP, /* Send Resp for Cmd, Free Xchg */
+ FCPIO_TCMD_ABORT_CMD,
+ FCPIO_TCMD_TASK_MGMT,
+
+ /*
+ * Misc request types
+ */
+ FCPIO_ACK = 0x20,
+ FCPIO_RESET,
+ FCPIO_RESET_CMPL,
+ FCPIO_FLOGI_REG,
+ FCPIO_FLOGI_REG_CMPL,
+ FCPIO_ECHO,
+ FCPIO_ECHO_CMPL,
+ FCPIO_LUNMAP_CHNG,
+ FCPIO_LUNMAP_REQ,
+ FCPIO_LUNMAP_REQ_CMPL,
+ FCPIO_FLOGI_FIP_REG,
+ FCPIO_FLOGI_FIP_REG_CMPL,
+};
+
+/*
+ * Header status codes from the firmware
+ */
+enum fcpio_status {
+ FCPIO_SUCCESS = 0, /* request was successful */
+
+ /*
+ * If a request to the firmware is rejected, the original request
+ * header will be returned with the status set to one of the following:
+ */
+ FCPIO_INVALID_HEADER, /* header contains invalid data */
+ FCPIO_OUT_OF_RESOURCE, /* out of resources to complete request */
+ FCPIO_INVALID_PARAM, /* some parameter in request is invalid */
+ FCPIO_REQ_NOT_SUPPORTED, /* request type is not supported */
+ FCPIO_IO_NOT_FOUND, /* requested I/O was not found */
+
+ /*
+ * Once a request is processed, the firmware will usually return
+ * a cmpl message type. In cases where errors occurred,
+ * the header status field would be filled in with one of the following:
+ */
+ FCPIO_ABORTED = 0x41, /* request was aborted */
+ FCPIO_TIMEOUT, /* request was timed out */
+ FCPIO_SGL_INVALID, /* request was aborted due to sgl error */
+ FCPIO_MSS_INVALID, /* request was aborted due to mss error */
+ FCPIO_DATA_CNT_MISMATCH, /* recv/sent more/less data than exp. */
+ FCPIO_FW_ERR, /* request was terminated due to fw error */
+ FCPIO_ITMF_REJECTED, /* itmf req was rejected by remote node */
+ FCPIO_ITMF_FAILED, /* itmf req was failed by remote node */
+ FCPIO_ITMF_INCORRECT_LUN, /* itmf req targeted incorrect LUN */
+ FCPIO_CMND_REJECTED, /* request was invalid and rejected */
+ FCPIO_NO_PATH_AVAIL, /* no paths to the lun was available */
+ FCPIO_PATH_FAILED, /* i/o sent to current path failed */
+ FCPIO_LUNMAP_CHNG_PEND, /* i/o rejected due to lunmap change */
+};
+
+/*
+ * The header for an fcpio request, whether from the firmware or from the
+ * host driver
+ */
+struct fcpio_header {
+ uint8_t type; /* enum fcpio_type */
+ uint8_t status; /* header status entry */
+ uint16_t _resvd; /* reserved */
+ uint32_t fcpio_tag; /* header tag */
+};
+
+static inline void fcpio_header_enc(struct fcpio_header *hdr,
+ uint8_t type, uint8_t status,
+ uint32_t tag)
+{
+ hdr->type = type;
+ hdr->status = status;
+ hdr->_resvd = 0;
+ hdr->fcpio_tag = tag;
+}
+
+static inline void fcpio_header_dec(struct fcpio_header *hdr,
+ uint8_t *type, uint8_t *status,
+ uint32_t *tag)
+{
+ *type = hdr->type;
+ *status = hdr->status;
+ *tag = hdr->fcpio_tag;
+}
+
+#define CDB_16 16
+#define CDB_32 32
+#define LUN_ADDRESS 8
+
+/*
+ * fcpio_tcmd_data: host -> firmware request
+ * used to request the firmware to send data (rd) or receive data(wr)
+ * in target mode
+ * Used by: FCPIO_TCMD_SEND_DATA & FCPIO_TCMD_RECV_DATA
+ */
+
+struct fcpio_tcmd_data {
+ uint8_t d_id[3];
+ uint8_t rsvd;
+ uint8_t s_id[3];
+ uint8_t rsvd1;
+ uint16_t ox_id;
+ uint8_t rsvd2[2];
+ uint32_t data_len;
+ uint64_t sgl_pa;
+ uint16_t sgl_cnt;
+ uint8_t send_resp; /* change it to common flags? */
+ uint8_t rsp_iu_flags;
+ uint32_t resid;
+ uint8_t rsvd3[2];
+ u_int8_t type; /* command type read/Write */
+ u_int8_t rsvd4; /* reserved */
+ u_int32_t rel_offset; /* data sequence relative offset */
+ u_int16_t npiv_id; /* FC vNIC only: npiv id of request */
+ u_int16_t mss; /* FC vNIC only: max burst */
+ u_int32_t r_a_tov; /* FC vNIC only: R_A_TOV in msec */
+ u_int32_t e_d_tov;
+} __attribute__((__packed__));
+
+/*
+ * fcpio_tcmd_fw_resp: firmware -> host response
+ * used by firmware to send response for a tcmd request
+ * Used as a response for:
+ * FCPIO_TCMD_SEND_DATA
+ * FCPIO_TCMD_RECV_DATA
+ * FCPIO_TCMD_FREE_CMD
+ *
+ */
+struct fcpio_tcmd_fw_rsp {
+ uint8_t error_code;
+ uint8_t rsvd;
+ uint16_t rx_id;
+};
+
+/*
+ * fcpio_tabort: host -> firmware request
+ *
+ * used by the host to request the firmware to abort a target request that was
+ * received by the firmware
+ */
+struct fcpio_tabort {
+ uint32_t cmd_tag; /* tag of the target request */
+};
+
+enum fcpio_flogi_reg_format_type {
+ FCPIO_FLOGI_REG_DEF_DEST = 0, /* Use the oui | s_id mac format */
+ FCPIO_FLOGI_REG_GW_DEST, /* Use the fixed gateway mac */
+};
+
+/*
+ * fcpio_flogi_reg: host -> firmware request
+ *
+ * fc vnic only
+ * used by the host to notify the firmware of the lif's s_id
+ * and destination mac address format
+ */
+struct fcpio_flogi_reg {
+ uint8_t format;
+ uint8_t s_id[3]; /* FC vNIC only: Source S_ID */
+ uint8_t gateway_mac[ETH_ALEN]; /* Destination gateway mac */
+ uint16_t _resvd;
+ uint32_t r_a_tov; /* R_A_TOV in msec */
+ uint32_t e_d_tov; /* E_D_TOV in msec */
+};
+
+/*
+ * fcpio_flogi_fip_reg: host -> firmware request
+ *
+ * fc vnic only
+ * used by the host to notify the firmware of the lif's s_id
+ * and destination mac address format
+ */
+struct fcpio_flogi_fip_reg {
+ uint8_t _resvd0;
+ uint8_t s_id[3]; /* FC vNIC only: Source S_ID */
+ uint8_t fcf_mac[ETH_ALEN]; /* FCF Target destination mac */
+ uint16_t _resvd1;
+ uint32_t r_a_tov; /* R_A_TOV in msec */
+ uint32_t e_d_tov; /* E_D_TOV in msec */
+ uint8_t ha_mac[ETH_ALEN]; /* Host adapter source mac */
+ uint16_t _resvd2;
+};
+
+/*
+ * Basic structure for all fcpio structures that are sent from the host to the
+ * firmware. They are 128 bytes per structure.
+ */
+#define FCPIO_HOST_REQ_LEN 128 /* expected length of host requests */
+
+struct fcpio_host_req {
+ struct fcpio_header hdr;
+
+ union {
+ /*
+ * Defines space needed for request
+ */
+ uint8_t buf[FCPIO_HOST_REQ_LEN - sizeof(struct fcpio_header)];
+
+ /*
+ * Target host requests
+ */
+ struct fcpio_tabort tabort;
+ struct fcpio_tcmd_data tdata;
+
+ /*
+ * Misc requests
+ */
+ struct fcpio_flogi_reg flogi_reg;
+ struct fcpio_flogi_fip_reg flogi_fip_reg;
+ } u;
+};
+
+/*
+ * Task Management request
+ */
+enum tmr_type {
+ FCPIO_TTMF_ABT_TASK = 0x01, /* abort task */
+ FCPIO_TTMF_ABT_TASK_SET = 0x02, /* abort task set */
+ FCPIO_TTMF_CLR_TASK_SET = 0x04, /* clear task set */
+ FCPIO_TTMF_LUN_RESET = 0x10, /* logical unit reset task mgmt */
+ FCPIO_TTMF_CLR_ACA = 0x40 /* Clear ACA condition */
+};
+
+/*
+ * fcpio_ack: firmware -> host response
+ *
+ * used by firmware to notify the host of the last work request received
+ */
+struct fcpio_ack {
+ uint16_t request_out; /* last host entry received */
+ uint16_t _resvd;
+};
+
+/*
+ * fcpio_reset_cmpl: firmware -> host response
+ *
+ * use by firmware to respond to the host's reset request
+ */
+struct fcpio_reset_cmpl {
+ uint16_t vnic_id;
+};
+
+/*
+ * Basic structure for all fcpio structures that are sent from the firmware to
+ * the host. They are 64 bytes per structure.
+ */
+#define FCPIO_FW_REQ_LEN 64 /* expected length of fw requests */
+struct fcpio_fw_req {
+ struct fcpio_header hdr;
+
+ union {
+ /*
+ * Defines space needed for request
+ */
+ uint8_t buf[FCPIO_FW_REQ_LEN - sizeof(struct fcpio_header)];
+
+ /*
+ * Target firmware responses
+ */
+ struct fcpio_tcmd_fw_rsp trsp;
+
+ /*
+ * Firmware response to work received
+ */
+ struct fcpio_ack ack;
+
+ /*
+ * Misc requests
+ */
+ } u;
+};
+
+static inline void fcpio_color_dec(struct fcpio_fw_req *fw_req, uint8_t *color)
+{
+ uint8_t *c = ((uint8_t *) fw_req) + sizeof(struct fcpio_fw_req) - 1;
+
+ *color = *c >> 7;
+
+ /*
+ * Make sure color bit is read from desc *before* other fields
+ * are read from desc. Hardware guarantees color bit is last
+ * bit (byte) written. Adding the rmb() prevents the compiler
+ * and/or CPU from reordering the reads which would potentially
+ * result in reading stale values.
+ */
+
+ rmb();
+
+}
+
+#define FNIC2_SGL_SZ (256 * 16)
+#define FNIC2_SGL_ALIGN (16)
+
+#endif /* _FCPIO_H_ */
diff --git a/drivers/staging/fnic2/src/fnic2_cmd.c b/drivers/staging/fnic2/src/fnic2_cmd.c
new file mode 100644
index 0000000..0315825
--- /dev/null
+++ b/drivers/staging/fnic2/src/fnic2_cmd.c
@@ -0,0 +1,1164 @@
+/*
+ * SPDX-License-Identifier: GPL-2.0
+ * Copyright 2017 Cisco Systems, Inc. All rights reserved
+ */
+#include <linux/workqueue.h>
+#include "fnic2.h"
+#include "fcpio.h"
+#include "fnic2_io.h"
+#include "fdls_fc.h"
+#include <asm/unaligned.h>
+#include <target/target_core_base.h>
+#include <target/target_core_fabric.h>
+#include <linux/highmem.h>
+
+static void fnic2_format_wwnn(char *buf, int len, uint64_t wwn);
+static struct fcpio_host_req *fnic2_host_req_alloc_init(struct fnic2_cmd *tcmd, int req_type);
+static void fnic2_process_fcp_cmd(struct work_struct *work);
+static void fnic2_unmap_free_sgl(struct fnic2 *fnic2, struct fnic2_cmd *tcmd);
+static void tcmd_process_abts_req(struct fnic2_cmd *abort_tcmd);
+static void fnic2_send_tm(struct fnic2_cmd *tcmd);
+static void fnic2_process_tcmd_timeout(struct work_struct *work);
+static void fnic2_recv_sess_timer_intr(struct timer_list *timer);
+
+extern void fdls_construct_logo_req(struct fnic2_lport *lport, struct fc_hdr *fchdr, struct fnic2_sess *sess);
+extern void fdls_delete_rport(struct fnic2_lport *lport, struct fnic2_rport *rport);
+
+extern struct list_head fnic2_list;
+
+/* Frame Initialization */
+
+/* FCP RESP */
+struct fc_fcp_rsp fnic2_fcp_rsp = {
+ .fchdr = {.r_ctl = 0x07, .type = 0x08,
+ .f_ctl = {FNIC2_FCP_RSP_FCTL, 0x00, 0x00},
+ .seq_id = 0x02}
+};
+
+#define BUF_ALIGN_16(_addr) (((uint64_t)_addr + 16 - 1) & ~0x0F)
+
+static int total_cmds;
+
+static struct fnic2_cmd *fnic2_get_tcmd_from_pool(struct fnic2 *fnic2)
+{
+ struct fnic2_cmd *tcmd;
+ unsigned long flags;
+
+ spin_lock_irqsave(&fnic2->free_list_lock, flags);
+
+ tcmd = list_first_entry_or_null(&fnic2->tcmd_list_free,
+ struct fnic2_cmd, free_list);
+ if (tcmd) {
+ list_del(&tcmd->free_list);
+ fnic2->freecmds--;
+ pr_debug("Got tcmd from pool tag %x freecmds %d fnic2->fnic2_num %d\n", tcmd->cmd_tag, fnic2->freecmds, fnic2->fnic2_num);
+ total_cmds++;
+
+ tcmd->abort_tag = 0xFFFFFFFF;
+ }
+
+ spin_unlock_irqrestore(&fnic2->free_list_lock, flags);
+
+ return tcmd;
+}
+
+/*
+ * fnic2_free_tcmd - free tcmd memory and add to fnic2 free list
+ *
+ * @tcmd - tcmd to be freed
+ *
+ * It will delete the I/O timer and maintain the state of
+ * the spinlock.
+ */
+void fnic2_free_tcmd(struct fnic2_cmd *tcmd)
+{
+ uint32_t tag;
+ struct fnic2 *fnic2 = tcmd->fnic2;
+ unsigned long flags;
+ unsigned long flags2;
+
+ /* Delete I/O timer on tcmd if it is active */
+ if ((try_to_del_timer_sync(&tcmd->io_timer)) < 0)
+ pr_err("Could not delete timer on tcmd tag %x\n", tcmd->cmd_tag);
+
+ spin_lock_irqsave(&tcmd->lock, flags2);
+
+ pr_err("freeing %pK tag %x\n", tcmd, tcmd->cmd_tag);
+
+ if (tcmd->rx_frame)
+ kfree(tcmd->rx_frame);
+
+ if (tcmd->sgcnt)
+ fnic2_unmap_free_sgl(fnic2, tcmd);
+
+ if (tcmd->host_req) {
+ kfree(tcmd->host_req);
+ tcmd->host_req = NULL;
+ }
+
+ tag = tcmd->cmd_tag;
+
+ /* Don't erase lock or timer */
+ memset(tcmd, 0, sizeof(struct fnic2_cmd) - sizeof(spinlock_t) - sizeof(struct timer_list));
+
+ tcmd->cmd_tag = tag;
+ tcmd->fnic2 = fnic2;
+
+ spin_unlock_irqrestore(&tcmd->lock, flags2);
+
+ spin_lock_irqsave(&fnic2->free_list_lock, flags);
+ list_add_tail(&tcmd->free_list, &fnic2->tcmd_list_free);
+ fnic2->freecmds++;
+ pr_debug("fnic2 num: %d freecmds %d tag %x rx_frame %pK\n", fnic2->fnic2_num, fnic2->freecmds, tcmd->cmd_tag, tcmd->rx_frame);
+ spin_unlock_irqrestore(&fnic2->free_list_lock, flags);
+
+}
+
+/*
+ * Send error or task management response.
+ * Always frees the cmd and associated state.
+ */
+static void fnic2_send_tmr_resp_and_free(struct fnic2_cmd *tcmd,
+ int code)
+{
+ fnic2_send_tmr_resp(tcmd, SAM_STAT_GOOD, code);
+ fnic2_free_tcmd(tcmd);
+
+}
+
+void fnic2_fcp_recv(struct fnic2 *fnic2, uint8_t *fp, int frame_len)
+{
+ struct fc_hdr *fchdr = (struct fc_hdr *)fp;
+ struct fnic2_cmd *tcmd;
+ int cpu;
+ bool found;
+ struct fnic2_cmd *tcmd_idx;
+ int tag_idx;
+ struct fc_fcp_cmnd *fc_cmnd;
+
+ /* Get a tcmd from free pool */
+ tcmd = fnic2_get_tcmd_from_pool(fnic2);
+ if (!tcmd) {
+ pr_err("No TCMD Available\n");
+ // TBD return BUSY
+ kfree(fp);
+ return;
+ }
+
+ /* Initial settings of the cmd */
+ tcmd->ox_id = ntohs(fchdr->ox_id);
+ tcmd->s_id = ntoh24(fchdr->s_id);
+ tcmd->rx_frame = fp;
+ tcmd->frame_len = frame_len;
+
+ /* Setup tcmd timeout */
+ tcmd->io_timer.expires = jiffies + JIFFIES_PER_MINUTE;
+ tcmd->io_timer.function = (void *)fnic2_recv_tcmd_timeout_intr;
+
+ fchdr = (struct fc_hdr *)tcmd->rx_frame;
+
+ fc_cmnd = (struct fc_fcp_cmnd *)fchdr;
+
+ /* Only start the I/O timer if this tcmd is representing a R/W */
+ if (IS_SCSI_READ_CMD(fc_cmnd) || IS_SCSI_WRITE_CMD(fc_cmnd))
+ add_timer(&tcmd->io_timer);
+
+ /* Queue */
+ cpu = get_cpu_to_queue(tcmd->cmd_tag);
+
+ if (fchdr->r_ctl == FNIC2_FC_R_CTL_ABTS) {
+
+ // BGC TEST
+ // fnic2_free_tcmd(tcmd);
+ // return;
+
+ tcmd->ox_id |= ABTS_MASK;
+ tcmd_idx = &fnic2->tcmd_pool[0];
+ found = 0;
+ for (tag_idx = 0; tag_idx < FNIC2_MAX_TCMDS; tag_idx++, tcmd_idx++) {
+ if ((ntohs(fchdr->ox_id) == tcmd_idx->ox_id) &&
+ (tcmd_idx->s_id == ntoh24(fchdr->s_id))) {
+ cpu = get_cpu_to_queue(tcmd_idx->cmd_tag);
+ found = 1;
+ break;
+ }
+ }
+ if (!found) {
+ pr_debug("Did not find I/O ox_id %x to ABORT\n", tcmd->ox_id);
+ fdls_send_ba_acc(&fnic2->lport, fchdr);
+ fnic2_free_tcmd(tcmd);
+ return;
+ }
+ }
+
+ pr_err("New cmd %pK tag %x\n", tcmd, tcmd->cmd_tag);
+
+ INIT_WORK(&tcmd->work, fnic2_process_fcp_cmd);
+ queue_work_on(cpu, fnic2_tcmd_wq, &tcmd->work);
+}
+
+static void fnic2_process_fcp_cmd(struct work_struct *work)
+{
+ struct fnic2_cmd *tcmd =
+ container_of(work, struct fnic2_cmd, work);
+ struct fnic2 *fnic2 = tcmd->fnic2;
+ struct fc_fcp_cmnd *fcp_cmd_req;
+ struct fc_hdr *fchdr;
+ struct fnic2_sess *sess;
+ uint32_t s_id;
+ int addl_len;
+ int task_attr = 0;
+ int flags = 0;
+ int ret;
+
+ fchdr = (struct fc_hdr *)tcmd->rx_frame;
+
+ if (fchdr->r_ctl == FNIC2_FC_R_CTL_ABTS) {
+ tcmd_process_abts_req(tcmd);
+ return;
+ }
+
+ /* Find the session that the request belongs to */
+ s_id = ntoh24(fchdr->s_id);
+ sess = fnic2_find_sess_s_id(fnic2, s_id);
+ if (!sess) {
+ /* We shouldn't be here */
+ pr_err("%s Could not find the login for: %x\n",
+ __func__, s_id);
+ fdls_send_logout(&fnic2->lport, fchdr);
+ //WARN_ON(!sess);
+ goto err;
+ }
+
+ if ((try_to_del_timer_sync(&sess->sess_timer)) < 0)
+ pr_err("%s failed to delete sess %pK timer\n", __func__, sess);
+
+ fcp_cmd_req = (struct fc_fcp_cmnd *)fchdr;
+ addl_len = FC_CMND_ADDL_CDB_LEN(fcp_cmd_req->add_cdblen_flags);
+ if (addl_len > 0) {
+ pr_err("Addl. len CDB not supported ini: %x, ox_id: %x\n",
+ s_id, fchdr->ox_id);
+ goto err;
+ }
+
+ tcmd->sess = sess;
+ tcmd->data_len_req = ntohl(fcp_cmd_req->dl);
+ tcmd->se_cmd.tag = tcmd->cmd_tag;
+
+ /*
+ * Check for FCP task management flags
+ */
+ if (fcp_cmd_req->tm_flags) {
+ fnic2_send_tm(tcmd);
+ return;
+ }
+
+ if (ntohl(fcp_cmd_req->dl) > MAX_DATA_LENGTH) {
+ pr_err("DEBUG Unsupported length: %x\n", ntohl(fcp_cmd_req->dl));
+ goto err;
+ }
+
+ /* Initialize tcmd based on the request received */
+ switch (fcp_cmd_req->add_cdblen_flags &
+ (FC_CMND_REQ_RD | FC_CMND_REQ_WR)) {
+ case FC_CMND_REQ_NONE:
+ pr_debug("DEBUG neither read nor write recvd! ln: %d\n", __LINE__);
+ case FC_CMND_REQ_RD:
+ tcmd->data_dir = DMA_FROM_DEVICE;
+ break;
+ case FC_CMND_REQ_WR:
+ tcmd->data_dir = DMA_TO_DEVICE;
+ tcmd->flags |= FNIC2_TCMD_WRITECMD;
+ break;
+ default:
+ pr_debug("DEBUG Bidirectional cmd recvd ln: %d\n", __LINE__);
+ /* Bi-directional not supported */
+ goto err;
+ break;
+ }
+
+ switch (fcp_cmd_req->pri_ta & FC_CMND_TASK_ATTR) {
+ case FC_TA_SIMPLE_TAG:
+ task_attr = TCM_SIMPLE_TAG;
+ break;
+ case FC_TA_HEAD_OF_QUEUE:
+ task_attr = TCM_HEAD_TAG;
+ break;
+ case FC_TA_ORDERED:
+ task_attr = TCM_ORDERED_TAG;
+ break;
+ case FC_TA_ACA:
+ task_attr = TCM_ACA_TAG;
+ break;
+ default:
+ break;
+ }
+
+ flags = TARGET_SCF_ACK_KREF;
+
+ pr_err("New Cmd tag: %x, is_write: %d, ox_id: %x, op %x len: %d\n",
+ tcmd->cmd_tag, tcmd->flags & FNIC2_TCMD_WRITECMD, tcmd->ox_id, fcp_cmd_req->cdb[0],
+ tcmd->data_len_req);
+
+ /* Submit */
+ ret = target_submit_cmd(&tcmd->se_cmd, sess->se_sess, fcp_cmd_req->cdb,
+ tcmd->sense_buf, scsilun_to_int(&fcp_cmd_req->fcp_lun),
+ tcmd->data_len_req, task_attr, tcmd->data_dir, flags);
+
+ if (ret) {
+ pr_err("target_submit_cmd returned failure %d\n",
+ ret);
+ goto err;
+ }
+
+ return;
+err:
+ pr_err("ERROR fn: %s, ln: %d\n", __func__, __LINE__);
+ fnic2_free_tcmd(tcmd);
+ return;
+}
+
+/*
+ * tcmd_process_abts_req
+ * An ABTS request was received
+ * Lookup the tcmd in the LIO queue and start the abort process in fw
+ */
+static void tcmd_process_abts_req(struct fnic2_cmd *abort_tcmd)
+{
+ struct fc_hdr *fchdr = (struct fc_hdr *)abort_tcmd->rx_frame;
+ struct fnic2 *fnic2 = abort_tcmd->fnic2;
+ struct fnic2_sess *sess;
+ struct se_cmd *se_cmd;
+ struct fnic2_cmd *tcmd_loop;
+ uint32_t s_id;
+ unsigned long flags;
+ unsigned long abts_flags;
+ struct fc_fcp_cmnd *fcp_cmd_req;
+
+ pr_err("processing ABTS s_id %x ox_id %x\n",
+ ntoh24(fchdr->s_id), ntohs(fchdr->ox_id));
+
+ s_id = ntoh24(fchdr->s_id);
+ sess = fnic2_find_sess_s_id(fnic2, s_id);
+ if (!sess) {
+ /* We shouldn't be here */
+ WARN_ON(!sess);
+ goto abts_error;
+ }
+ pr_err("processing ABTS found session %pK\n",
+ sess);
+
+ if ((try_to_del_timer_sync(&sess->sess_timer)) < 0)
+ pr_err("%s failed to delete sess %pK timer\n", __func__, sess);
+
+ spin_lock_irqsave(&sess->se_sess->sess_cmd_lock, flags);
+
+ list_for_each_entry(se_cmd, &sess->se_sess->sess_cmd_list, se_cmd_list) {
+ tcmd_loop = container_of(se_cmd, struct fnic2_cmd, se_cmd);
+ pr_err("scan tcmd ox_id %x\n", tcmd_loop->ox_id);
+ if (tcmd_loop->ox_id == ntohs(fchdr->ox_id)) {
+
+ fcp_cmd_req = (struct fc_fcp_cmnd *)tcmd_loop->rx_frame;
+ pr_err("ABORTING original tag: %x abort_tag %x\n",
+ tcmd_loop->cmd_tag, abort_tcmd->cmd_tag);
+ pr_err("se_cmd %pK se_sess %pK lun %llu\n",
+ &tcmd_loop->se_cmd, sess->se_sess,
+ (unsigned long long)scsilun_to_int(&fcp_cmd_req->fcp_lun));
+
+ spin_unlock_irqrestore(&sess->se_sess->sess_cmd_lock, flags);
+
+ /* Delete I/O timer on original command if active */
+ if ((try_to_del_timer_sync(&tcmd_loop->io_timer)) < 0)
+ pr_err("Could not delete timer on tcmd tag %x\n", tcmd_loop->cmd_tag);
+
+ abort_tcmd->flags |= FNIC2_TCMD_IS_ABTS;
+
+ spin_lock_irqsave(&tcmd_loop->lock, abts_flags);
+
+ if (tcmd_loop->flags & FNIC2_TCMD_ABORTED) {
+ pr_err("tcmd %x already being aborted\n", tcmd_loop->abort_tag);
+ spin_unlock_irqrestore(&tcmd_loop->lock, abts_flags);
+ goto abts_error;
+ }
+
+ tcmd_loop->flags |= FNIC2_TCMD_ABORTED;
+
+ tcmd_loop->abort_tag = abort_tcmd->cmd_tag;
+ abort_tcmd->killed_tcmd = tcmd_loop;
+ tcmd_loop->abort_tcmd = abort_tcmd;
+
+ spin_unlock_irqrestore(&tcmd_loop->lock, abts_flags);
+
+ fnic2_send_abort_to_fw(tcmd_loop);
+
+ return;
+ }
+ }
+
+ spin_unlock_irqrestore(&sess->se_sess->sess_cmd_lock, flags);
+ pr_err("ABTS for ox_id %x NOT FOUND\n",
+ ntohs(fchdr->ox_id));
+
+abts_error:
+
+ fdls_send_ba_acc(&fnic2->lport, fchdr);
+ fnic2_free_tcmd(abort_tcmd);
+
+ return;
+}
+
+void fnic2_send_abort_to_fw(struct fnic2_cmd *tcmd)
+{
+ struct fnic2 *fnic2 = tcmd->fnic2;
+
+ unsigned long flags;
+ struct fcpio_host_req *host_req;
+ struct fcpio_host_req *desc;
+ struct vnic_wq_copy *wq;
+
+ pr_err("tcmd %pK send abort to fw\n", tcmd);
+ pr_err("cmd_tag %x abort_tag %x\n", tcmd->cmd_tag, tcmd->abort_tag);
+
+ /* Fill the structure to send it to fw */
+ host_req = kzalloc(sizeof(struct fcpio_host_req), GFP_KERNEL);
+ WARN_ON(host_req == NULL);
+ host_req->hdr.type = FCPIO_TCMD_ABORT_CMD;
+ host_req->hdr.fcpio_tag = tcmd->abort_tag;
+ host_req->u.tabort.cmd_tag = tcmd->cmd_tag;
+
+ spin_lock_irqsave(&fnic2->wq_copy_lock[0], flags);
+ /* Use the Copy WQ to send it to FW */
+ wq = &fnic2->wq_copy[0];
+
+ if (vnic_wq_copy_desc_avail(wq) <= fnic2->wq_copy_desc_low[0])
+ free_wq_copy_descs(fnic2, wq);
+
+ if (unlikely(!vnic_wq_copy_desc_avail(wq))) {
+ pr_err("DEBUG ERROR no wq desc for free_cmd\n");
+ kfree(host_req);
+ spin_unlock_irqrestore(&fnic2->wq_copy_lock[0], flags);
+ return;
+ }
+
+ desc = vnic_wq_copy_next_desc(wq);
+ memcpy(desc, host_req, sizeof(struct fcpio_host_req));
+
+ vnic_wq_copy_post(wq);
+ spin_unlock_irqrestore(&fnic2->wq_copy_lock[0], flags);
+
+ kfree(host_req);
+}
+
+static int fnic2_session_alloc_cb(struct se_portal_group *se_tpg,
+ struct se_session *se_sess, void *arg)
+{
+ struct fnic2_sess *sess = (struct fnic2_sess *)arg;
+ struct fnic2 *fnic2;
+
+ fnic2 = sess->fnic2;
+ list_add_tail(&sess->list, &fnic2->lio.sess_list);
+ fnic2->lio.sess_count++;
+ return 0;
+}
+
+int fnic2_session_create(struct fnic2 *fnic2, struct fnic2_rport *rport)
+{
+ struct fnic2_sess *sess;
+ struct fnic2_tpg *tpg = fnic2->lio.tpg;
+ uint8_t portname_initiator[36];
+ unsigned int flags;
+
+ sess = fnic2_find_session(fnic2, rport->wwpn);
+ if (sess) {
+ pr_err("%s: Session already found %llx\n",
+ __func__, rport->wwpn);
+ return -1;
+ }
+
+ fnic2_format_wwnn(portname_initiator, 36, rport->wwpn);
+ pr_debug("Creating session for %s\n", portname_initiator);
+
+ if (tpg == NULL) {
+ pr_err("No tpg\n");
+ return -1;
+ }
+
+ sess = kzalloc(sizeof(struct fnic2_sess), GFP_KERNEL);
+ if (!sess) {
+ pr_err("%s: Unable to alloc memory for sess\n",
+ __func__);
+ WARN_ON(1);
+ return -1;
+ }
+
+ kref_init(&sess->kref); /* ref for table entry */
+ sess->rport = rport;
+ sess->lport = tpg->lport;
+ sess->fnic2 = fnic2;
+ INIT_LIST_HEAD(&sess->cmd_list);
+
+ sess->se_sess = target_alloc_session(&tpg->se_tpg, FNIC2_MAX_TCMDS,
+ sizeof(struct fnic2_cmd),
+ TARGET_PROT_NORMAL, portname_initiator,
+ sess, fnic2_session_alloc_cb);
+
+ if (IS_ERR(sess->se_sess)) {
+ pr_err("Session creating failed wwpn: %llx\n",
+ rport->wwpn);
+ kfree(sess);
+ return -1;
+ }
+
+ timer_setup(&sess->sess_timer, fnic2_recv_sess_timer_intr, flags);
+
+ return 0;
+}
+
+int fnic2_send_to_fw(struct fnic2_cmd *tcmd, int req_type)
+{
+ struct fnic2 *fnic2 = tcmd->fnic2;
+ struct vnic_wq_copy *wq;
+ struct fcpio_host_req *desc;
+ unsigned long flags;
+
+ pr_debug("sending tag %x to FW\n", tcmd->cmd_tag);
+
+ tcmd->host_req = fnic2_host_req_alloc_init(tcmd, req_type);
+ if (!tcmd->host_req) {
+ return -1;
+ }
+
+ spin_lock_irqsave(&fnic2->wq_copy_lock[0], flags);
+
+ /* Use the Copy WQ to send it to FW */
+ wq = &fnic2->wq_copy[0];
+
+ if (vnic_wq_copy_desc_avail(wq) <= fnic2->wq_copy_desc_low[0]) {
+ pr_debug("Freeing the copy wq desc avail: %d\n", vnic_wq_copy_desc_avail(wq));
+ free_wq_copy_descs(fnic2, wq);
+ }
+
+ if (unlikely(!vnic_wq_copy_desc_avail(wq))) {
+ pr_err("DEBUG %s FAILED No WQ_COPY desc\n", __func__);
+ WARN_ON(0);
+ spin_unlock_irqrestore(&fnic2->wq_copy_lock[0], flags);
+ return -1;
+ }
+
+ desc = vnic_wq_copy_next_desc(wq);
+ memcpy(desc, tcmd->host_req, sizeof(struct fcpio_host_req));
+
+ vnic_wq_copy_post(wq);
+
+ spin_unlock_irqrestore(&fnic2->wq_copy_lock[0], flags);
+
+ return 0;
+}
+
+static struct fcpio_host_req *fnic2_host_req_alloc_init(struct fnic2_cmd *tcmd, int req_type)
+{
+ struct fnic2 *fnic2 = tcmd->fnic2;
+ struct fcpio_host_req *host_req;
+ struct fcpio_tcmd_data *tdata;
+ int i, len, offset;
+ struct page *page = NULL;
+ void *page_addr;
+ struct host_sg_desc *desc_sge;
+ uint64_t page_addr_pa;
+ int remaining;
+ int j;
+ struct scatterlist *sg = NULL;
+ struct se_cmd *se_cmd = &tcmd->se_cmd;
+
+ host_req = kzalloc(sizeof(struct fcpio_host_req), GFP_KERNEL);
+ if (!host_req)
+ return NULL;
+
+
+ host_req->hdr.type = req_type;
+ host_req->hdr.fcpio_tag = tcmd->cmd_tag;
+
+ tdata = &host_req->u.tdata;
+ hton24(tdata->d_id, tcmd->sess->rport->fcid);
+ hton24(tdata->s_id, fnic2->lport.fcid);
+ tdata->ox_id = tcmd->ox_id;
+
+ tdata->data_len = tcmd->data_len_xfer;
+ tdata->mss = FNIC2_FCOE_MAX_PAYLOAD;
+
+ tcmd->sg_desc_va_unaligned = (struct host_sg_desc *)kzalloc(FNIC2_SGL_SZ + FNIC2_SGL_ALIGN, GFP_KERNEL);
+ if (!tcmd->sg_desc_va_unaligned)
+ return NULL;
+
+ tcmd->sg_desc_va = (struct host_sg_desc *)BUF_ALIGN_16(tcmd->sg_desc_va_unaligned);
+
+ pr_debug("DEBUG fn: %s, ln: %d, tag: %d, va_unal: %pK, va: %pK\n",
+ __func__, __LINE__, tcmd->cmd_tag, tcmd->sg_desc_va_unaligned, tcmd->sg_desc_va);
+ pr_debug("DEBUG tag: %x, sgcnt: %d\n", tcmd->cmd_tag, tcmd->sgcnt);
+
+
+ desc_sge = &tcmd->sg_desc_va[0];
+
+ tcmd->sg_desc_count = tcmd->sgcnt;
+
+ i = 0; j = 0;
+ remaining = tcmd->se_cmd.data_length;
+ pr_debug("DEBUG datalenxfer: %d\n", remaining);
+
+ sg = tcmd->se_cmd.t_data_sg;
+ len = sg->length;
+ offset = sg->offset;
+ page = sg_page(sg);
+ if (len == 0) {
+ pr_debug("DEBUG zero len first entry: %d\n", 0);
+ } else {
+ page_addr = kmap_atomic(page + (offset >> PAGE_SHIFT));
+ tcmd->kmap_addr[j] = page_addr;
+
+ page_addr_pa = pci_map_single(fnic2->pdev, page_addr, len, tcmd->dma_direction);
+ desc_sge->addr = page_addr_pa;
+
+ desc_sge->len = len;
+ pr_debug("DEBUG addr: %llx len: %d\n", desc_sge->addr, len);
+ pr_debug("DEBUG page %pK pa %llx sge len %x rmain: %d", page_addr, page_addr_pa, desc_sge->len, remaining);
+ desc_sge++;
+ j++;
+
+ }
+ remaining = remaining - len;
+
+ pr_debug("DEBUG remaining: %d\n", remaining);
+
+ while (remaining) {
+
+ sg = sg_next(sg);
+ if (sg == NULL) {
+ pr_debug("DEBUG no mpre entry: %d\n", i);
+ break;
+ }
+ len = sg->length;
+ offset = sg->offset;
+ page = sg_page(sg);
+ pr_debug("DEBUG parse sge i: %d, len: %d\n", i, len);
+ if (!len) {
+ i++;
+ continue;
+ }
+ page_addr = kmap_atomic(page + (offset >> PAGE_SHIFT));
+ tcmd->kmap_addr[j] = page_addr;
+
+ page_addr_pa = pci_map_single(fnic2->pdev, page_addr, len, tcmd->dma_direction);
+ desc_sge->addr = page_addr_pa;
+
+ desc_sge->len = len;
+ pr_debug("DEBUG page %pK pa %llx sge len %x rmain: %d", page_addr, page_addr_pa, desc_sge->len, remaining);
+ desc_sge++;
+
+ remaining = remaining - len;
+ j++; i++;
+
+ }
+
+ tcmd->sg_desc_pa = pci_map_single(fnic2->pdev, tcmd->sg_desc_va,
+ sizeof(struct host_sg_desc) * tcmd->sgcnt,
+ PCI_DMA_TODEVICE);
+
+ pr_debug("DEBUG sg_desc %llx", tcmd->sg_desc_pa);
+
+ if (se_cmd->se_cmd_flags & (SCF_OVERFLOW_BIT | SCF_UNDERFLOW_BIT)) {
+ pr_debug("DBEUG resid valid flag: %x, count: %d\n",
+ se_cmd->se_cmd_flags, se_cmd->residual_count);
+ if (se_cmd->se_cmd_flags & SCF_OVERFLOW_BIT)
+ tdata->rsp_iu_flags |= FCP_RSP_FLAG_OVERFLOW;
+ else
+ tdata->rsp_iu_flags |= FCP_RSP_FLAG_UNDERFLOW;
+ tdata->resid = se_cmd->residual_count;
+ }
+
+ tdata->sgl_pa = tcmd->sg_desc_pa;
+ tdata->sgl_cnt = tcmd->sgcnt;
+ tdata->send_resp = (req_type == FCPIO_TCMD_SEND_DATA) ? 1 : 0;
+
+ return host_req;
+}
+
+static void fnic2_send_rd_data_cmpl(struct fnic2_cmd *tcmd)
+{
+ /* Notify LIO that the cmd is completed */
+ transport_generic_free_cmd(&tcmd->se_cmd, 0);
+}
+
+static void fnic2_recv_wr_data_cmpl(struct fnic2_cmd *tcmd)
+{
+#if 0
+ // BGC
+ static int x = 0;
+
+ if (x++%10 == 0) {
+ return;
+ }
+#endif
+
+ pr_debug("DEBUG wr_data_cmpl tag: %x\n", tcmd->cmd_tag);
+
+ /* Notify LIO that the cmd is completed */
+ target_execute_cmd(&tcmd->se_cmd);
+}
+
+void fnic2_send_tmr_resp(struct fnic2_cmd *tcmd, u32 status, u8 code)
+{
+ struct fnic2 *fnic2 = tcmd->fnic2;
+ struct fnic2_sess *sess = tcmd->sess;
+ struct fnic2_rport *rport = sess->rport;
+ struct fc_fcp_rsp *fcp_rsp;
+ uint8_t s_id[3];
+ uint8_t d_id[3];
+ struct fc_hdr *fchdr;
+ struct se_cmd *se_cmd = &tcmd->se_cmd;
+ int addl_len = 0;
+ struct fcp_resp_rsp_info *info;
+
+ pr_err("SEND_TMR_RESP tcmd %pK fnic %pK sess %pK se_cmd%pK\n",
+ tcmd, fnic2, sess, se_cmd);
+
+ fcp_rsp = (struct fc_fcp_rsp *)kzalloc(2112, GFP_ATOMIC);
+ WARN_ON(!fcp_rsp);
+ fchdr = &fcp_rsp->fchdr;
+
+ hton24(s_id, fnic2->lport.fcid);
+ hton24(d_id, rport->fcid);
+
+ memcpy(fcp_rsp, &fnic2_fcp_rsp, sizeof(struct fc_fcp_rsp));
+ memcpy(fchdr->d_id, d_id, 3);
+ memcpy(fchdr->s_id, s_id, 3);
+ fchdr->ox_id = htons(tcmd->ox_id);
+ fchdr->rx_id = htons(tcmd->rx_id);
+
+ fcp_rsp->scsi_status = status;
+
+ if (se_cmd->scsi_status == SAM_STAT_GOOD) {
+ fcp_rsp->flags |= FCP_RSP_LEN_VAL;
+ addl_len = sizeof(struct fcp_resp_rsp_info);
+ info = (struct fcp_resp_rsp_info *)(fcp_rsp + 1);
+ info->rsp_code = code;
+ }
+
+ fnic2_send_fcoe_frame(&fnic2->lport, fcp_rsp, sizeof(struct fc_fcp_rsp) + addl_len);
+ kfree(fcp_rsp);
+}
+
+void fnic2_send_fcp_resp(struct fnic2_cmd *tcmd)
+{
+ struct fnic2 *fnic2 = tcmd->fnic2;
+ struct fnic2_sess *sess = tcmd->sess;
+ struct fnic2_rport *rport = sess->rport;
+ struct fc_fcp_rsp *fcp_rsp;
+ uint8_t s_id[3];
+ uint8_t d_id[3];
+ struct fc_hdr *fchdr;
+ struct se_cmd *se_cmd = &tcmd->se_cmd;
+ int sense_len = 0;
+
+ fcp_rsp = (struct fc_fcp_rsp *)kzalloc(2112, GFP_ATOMIC);
+ WARN_ON(!fcp_rsp);
+ fchdr = &fcp_rsp->fchdr;
+
+ hton24(s_id, fnic2->lport.fcid);
+ hton24(d_id, rport->fcid);
+
+ memcpy(fcp_rsp, &fnic2_fcp_rsp, sizeof(struct fc_fcp_rsp));
+ memcpy(fchdr->d_id, d_id, 3);
+ memcpy(fchdr->s_id, s_id, 3);
+ fchdr->ox_id = htons(tcmd->ox_id);
+ fchdr->rx_id = htons(tcmd->rx_id);
+
+ fcp_rsp->scsi_status = se_cmd->scsi_status;
+
+ if (se_cmd->se_cmd_flags & (SCF_OVERFLOW_BIT | SCF_UNDERFLOW_BIT)) {
+ pr_debug("DBEUG resid valid flag: %x, count: %d\n",
+ se_cmd->se_cmd_flags, se_cmd->residual_count);
+ if (se_cmd->se_cmd_flags & SCF_OVERFLOW_BIT)
+ fcp_rsp->flags |= FCP_RSP_FLAG_OVERFLOW;
+ else
+ fcp_rsp->flags |= FCP_RSP_FLAG_UNDERFLOW;
+ fcp_rsp->resid = htonl(se_cmd->residual_count);
+ }
+
+ if (se_cmd->scsi_status != SAM_STAT_GOOD) {
+ sense_len = se_cmd->scsi_sense_length;
+ if (sense_len > 0) {
+ fcp_rsp->flags |= FCP_SNS_LEN_VAL;
+ fcp_rsp->sense_len = htonl(sense_len);
+ memcpy((fcp_rsp + 1), se_cmd->sense_buffer, sense_len);
+ }
+ }
+
+ fnic2_send_fcoe_frame(&fnic2->lport, fcp_rsp, sizeof(struct fc_fcp_rsp) + sense_len);
+ kfree(fcp_rsp);
+}
+
+void fnic2_free_fw_res(struct fnic2_cmd *tcmd)
+{
+ struct fcpio_host_req *host_req;
+ struct fcpio_host_req *desc;
+ struct fnic2 *fnic2 = tcmd->fnic2;
+ struct vnic_wq_copy *wq;
+ unsigned long flags;
+
+ /* Fill the structure to send it to fw */
+ host_req = kzalloc(sizeof(struct fcpio_host_req), GFP_KERNEL);
+ WARN_ON(host_req == NULL);
+
+ host_req->hdr.type = FCPIO_TCMD_FREE_CMD;
+ /* FW uses rx_id for freeing write commands */
+ host_req->hdr.fcpio_tag = tcmd->rx_id;
+
+ spin_lock_irqsave(&fnic2->wq_copy_lock[0], flags);
+
+ /* Use the Copy WQ to send it to FW */
+ wq = &fnic2->wq_copy[0];
+
+ if (vnic_wq_copy_desc_avail(wq) <= fnic2->wq_copy_desc_low[0])
+ free_wq_copy_descs(fnic2, wq);
+
+ if (unlikely(!vnic_wq_copy_desc_avail(wq))) {
+ pr_err("DEBUG ERROR no wq desc for free_cmd\n");
+ spin_unlock_irqrestore(&fnic2->wq_copy_lock[0], flags);
+ kfree(host_req);
+ return;
+ }
+
+ desc = vnic_wq_copy_next_desc(wq);
+ memcpy(desc, host_req, sizeof(struct fcpio_host_req));
+
+ pr_debug("Sent free req to FW\n");
+
+ vnic_wq_copy_post(wq);
+
+ spin_unlock_irqrestore(&fnic2->wq_copy_lock[0], flags);
+
+ kfree(host_req);
+}
+
+static void fnic2_unmap_free_sgl(struct fnic2 *fnic2, struct fnic2_cmd *tcmd)
+{
+ struct host_sg_desc *desc_sge;
+ int i;
+
+ pci_unmap_single(fnic2->pdev, tcmd->sg_desc_pa, sizeof(struct host_sg_desc) * tcmd->sgcnt,
+ PCI_DMA_TODEVICE);
+
+ desc_sge = &tcmd->sg_desc_va[0];
+
+ /* Now, each entries in the sglist pci_unmap and kunmap */
+ for (i = 0; i < tcmd->sgcnt; i++) {
+ pci_unmap_single(fnic2->pdev, desc_sge->addr, desc_sge->len, tcmd->dma_direction);
+ desc_sge++;
+ kunmap(tcmd->kmap_addr[i]);
+ }
+
+ /* free the descriptor list */
+ kfree(tcmd->sg_desc_va_unaligned);
+}
+
+void fnic2_fcpio_tcmd_cmpl_handler(struct work_struct *work)
+{
+ struct fnic2_cmd *tcmd =
+ container_of(work, struct fnic2_cmd, work);
+ struct fcpio_fw_req *desc = &tcmd->fw_desc;
+
+ if (!desc) {
+ pr_err("Cmd tag %x freed before %s\n", tcmd->cmd_tag, __func__ );
+ return;
+ }
+
+ pr_debug("DEBUG FW completion tcmd: %pK, tag: %x, hdrtype: %d, hdrstatus: %x\n",
+ tcmd, tcmd->cmd_tag, desc->hdr.type, desc->hdr.status);
+
+ /* For now, only success cases */
+ if (desc->hdr.status != FCPIO_SUCCESS) {
+ pr_debug("ERROR hdrstatus not success: %d\n",
+ desc->hdr.status);
+ }
+
+ switch (desc->hdr.type) {
+
+ case FCPIO_TCMD_SEND_DATA:
+ fnic2_send_rd_data_cmpl(tcmd);
+ break;
+
+ case FCPIO_TCMD_RECV_DATA:
+
+ pr_debug("recv_data cmpl hdrtype: %d, tag: %x, rx_id: %x\nWrite completed ox_id %x\n",
+ desc->hdr.type, tcmd->cmd_tag, desc->u.trsp.rx_id,
+ tcmd->ox_id);
+
+ tcmd->rx_id = desc->u.trsp.rx_id;
+ fnic2_recv_wr_data_cmpl(tcmd);
+ break;
+
+ case FCPIO_TCMD_FREE_CMD:
+ pr_debug("free_cmd cmpl hdrtype: %d, tag: %x\n",
+ desc->hdr.type, tcmd->cmd_tag);
+
+ transport_generic_free_cmd(&tcmd->se_cmd, 0);
+ break;
+
+ case FCPIO_TCMD_ABORT_CMD:
+ pr_debug("abort_cmd cmpl hdrtype: %d, tag: %x\n",
+ desc->hdr.type, tcmd->cmd_tag);
+ fnic2_fw_abort_done(tcmd);
+ break;
+
+ case FCPIO_TCMD_TASK_MGMT:
+ pr_debug("task mgmt cmpl hdrtype: %d, tag: %x\n",
+ desc->hdr.type, tcmd->cmd_tag);
+ fnic2_complete_tm_rsp(tcmd);
+ break;
+
+ default:
+ break;
+ }
+}
+
+/* Utilities */
+
+struct fnic2_cmd *fnic2_find_tcmd(struct fnic2 *fnic2, uint32_t cmd_tag)
+{
+ struct fnic2_cmd *tcmd;
+
+ pr_debug("DEBUG find tcmd: tag: %d\n", cmd_tag);
+
+ WARN_ON(cmd_tag >= FNIC2_MAX_TCMDS);
+
+ tcmd = &fnic2->tcmd_pool[cmd_tag];
+ pr_debug("DEBUG tcmd: %pK\n", tcmd);
+ return tcmd;
+}
+
+/*
+ * find_fnic2 - Routine to find fnic2 structure using wwpn provided
+ */
+struct fnic2 *find_fnic2_wwpn(uint64_t wwpn)
+{
+ struct fnic2 *fnic2;
+ struct fnic2_lport *lport;
+
+ list_for_each_entry(fnic2, &fnic2_list, list) {
+ lport = &fnic2->lport;
+ if (wwpn == lport->wwpn) {
+ return fnic2;
+ }
+ }
+ return NULL;
+}
+
+struct fnic2_sess *fnic2_find_sess_s_id(struct fnic2 *fnic2, uint32_t s_id)
+{
+ struct fnic2_sess *sess;
+
+ list_for_each_entry(sess, &fnic2->lio.sess_list, list) {
+ if (sess->rport->fcid == s_id)
+ return sess;
+ }
+ return NULL;
+}
+
+struct fnic2_sess *fnic2_find_session(struct fnic2 *fnic2, uint64_t wwpn)
+{
+ struct fnic2_sess *sess;
+
+ list_for_each_entry(sess, &fnic2->lio.sess_list, list) {
+ if (sess->rport && sess->lport->lport_wwpn == wwpn)
+ return sess;
+ }
+ return NULL;
+}
+
+static void fnic2_format_wwnn(char *buf, int len, uint64_t wwn)
+{
+ uint8_t b[8];
+
+ put_unaligned_be64(wwn, b);
+ snprintf(buf, len,
+ "%2.2x:%2.2x:%2.2x:%2.2x:%2.2x:%2.2x:%2.2x:%2.2x",
+ b[7], b[6], b[5], b[4], b[3], b[2], b[1], b[0]);
+}
+
+/*
+ * Handle Task Management Request.
+ */
+static void fnic2_send_tm(struct fnic2_cmd *tcmd)
+{
+ struct fc_fcp_cmnd *fcp_cmd_req;
+ uint8_t tm_func;
+ int rc;
+
+ fcp_cmd_req = (struct fc_fcp_cmnd *) tcmd->rx_frame;
+
+ pr_err("GOT TASK MANAGEMENT %x", fcp_cmd_req->tm_flags);
+
+ switch (fcp_cmd_req->tm_flags) {
+ case FCP_TMF_LUN_RESET:
+ tm_func = TMR_LUN_RESET;
+ break;
+ case FCP_TMF_TGT_RESET:
+ tm_func = TMR_TARGET_WARM_RESET;
+ break;
+ case FCP_TMF_CLR_TASK_SET:
+ tm_func = TMR_CLEAR_TASK_SET;
+ break;
+ case FCP_TMF_ABT_TASK_SET:
+ tm_func = TMR_ABORT_TASK_SET;
+ break;
+ case FCP_TMF_CLR_ACA:
+ tm_func = TMR_CLEAR_ACA;
+ break;
+ default:
+ pr_err("invalid FCP tm_flags %x\n", fcp_cmd_req->tm_flags);
+ fnic2_send_tmr_resp_and_free(tcmd, FCP_CMND_FIELDS_INVALID);
+ return;
+ }
+
+ rc = target_submit_tmr(&tcmd->se_cmd, tcmd->sess->se_sess,
+ tcmd->sense_buf, scsilun_to_int(&fcp_cmd_req->fcp_lun),
+ tcmd, tm_func, GFP_KERNEL, tcmd->cmd_tag,
+ TARGET_SCF_ACK_KREF);
+
+ if (rc) {
+ pr_err("target_submit_tmr FAILED rc %d\n", rc);
+ fnic2_send_tmr_resp_and_free(tcmd, FCP_CMND_FIELDS_INVALID);
+ }
+}
+
+
+/*
+ * fnic2_recv_tcmd_timeout_intr - Process intr from tcmd timeout
+ *
+ * @intr_data - pointer to a tcmd that
+ * received an interrupt
+ *
+ * This function is called in an interrupt context when a tcmd
+ * exceeds the timeout value. It queues work on another
+ * function to clean up fnic2 resources.
+ */
+void fnic2_recv_tcmd_timeout_intr(struct timer_list *timer)
+{
+ struct fnic2_cmd *tcmd = container_of(timer, struct fnic2_cmd, io_timer);
+ int cpu;
+
+ pr_debug("Got interrupt on tag %x on fnic2 %d tcmds left: %d\n", tcmd->cmd_tag, tcmd->fnic2->fnic2_num, tcmd->fnic2->freecmds);
+
+ /* Queue on proper cpu */
+ cpu = get_cpu_to_queue(tcmd->cmd_tag);
+
+ INIT_WORK(&tcmd->work, fnic2_process_tcmd_timeout);
+ queue_work_on(cpu, fnic2_tcmd_wq, &tcmd->work);
+
+ return;
+}
+
+/*
+ * fnic2_process_tcmd_timeout - Process a tcmd that has timed out
+ *
+ * @work - work_struct that this function was queued with
+ *
+ * Free a tcmd that has been held by driver for the timeout period
+ * Create a dummy ABTS command and submit to LIO and FW
+ */
+static void fnic2_process_tcmd_timeout(struct work_struct *work)
+{
+ /* tcmd is original IO to be aborted, abort_tcmd is used as the ABTS cmd */
+ struct fnic2_cmd *tcmd, *abort_tcmd;
+ struct fnic2 *fnic2;
+ struct fnic2_sess *sess;
+ unsigned long flags;
+ unsigned long fnic2_flags;
+
+ tcmd = container_of(work, struct fnic2_cmd, work);
+
+ spin_lock_irqsave(&tcmd->lock, flags);
+
+ if (tcmd->abort_tag == 0) {
+ pr_err("tcmd tag %x freed before timeout\n", tcmd->cmd_tag);
+ spin_unlock_irqrestore(&tcmd->lock, flags);
+ return;
+ }
+
+ fnic2 = tcmd->fnic2;
+ abort_tcmd = fnic2_get_tcmd_from_pool(fnic2);
+
+ if (!abort_tcmd) {
+ pr_err("No TCMDs available! %s\n", __func__);
+ spin_unlock_irqrestore(&tcmd->lock, flags);
+ return;
+ }
+
+ abort_tcmd->flags |= FNIC2_TCMD_IS_ABTS;
+
+ pr_err("Got tcmd tag %x for timeout for io %x on fnic %d, tcmd->abort_tag %x ox_id %x\n", abort_tcmd->cmd_tag, tcmd->cmd_tag, fnic2->fnic2_num, tcmd->abort_tag, tcmd->ox_id);
+
+ if (tcmd->flags & FNIC2_TCMD_ABORTED) {
+ pr_err("tcmd tag %x already being freed not in timeout\n", tcmd->cmd_tag);
+ goto timeout_error;
+ }
+
+ tcmd->flags |= FNIC2_TCMD_ABORTED;
+
+ sess = fnic2_find_sess_s_id(fnic2, tcmd->s_id);
+
+ if (!sess) {
+ pr_err("No session found for %s\n", __func__);
+ goto timeout_error;
+ }
+
+ spin_lock_irqsave(&fnic2->fnic2_lock, fnic2_flags);
+
+ if (!timer_pending(&sess->sess_timer)) {
+
+ fdls_construct_logo_req(&fnic2->lport, (struct fc_hdr *)tcmd->rx_frame, sess);
+
+ sess->sess_timer.expires = jiffies + (JIFFIES_PER_MINUTE * 5);
+
+ add_timer(&sess->sess_timer);
+ }
+
+ spin_unlock_irqrestore(&fnic2->fnic2_lock, fnic2_flags);
+
+ tcmd->abort_tag = abort_tcmd->cmd_tag;
+ abort_tcmd->killed_tcmd = tcmd;
+ tcmd->abort_tcmd = abort_tcmd;
+
+ spin_unlock_irqrestore(&tcmd->lock, flags);
+
+ fnic2_send_abort_to_fw(tcmd);
+
+ return;
+
+timeout_error:
+ spin_unlock_irqrestore(&tcmd->lock, flags);
+ fnic2_free_tcmd(abort_tcmd);
+ return;
+}
+
+/*
+ * fnic2_recv_sess_timer_intr - receive an interrupt on a dead session
+ *
+ * @timer - timer inside session that received interrupt
+ *
+ * Session has sat idle for extended period of time
+ * Send a logo and delete the session
+ */
+static void fnic2_recv_sess_timer_intr(struct timer_list *timer)
+{
+ struct fnic2_sess *sess = container_of(timer, struct fnic2_sess, sess_timer);
+ struct fnic2 *fnic2 = sess->fnic2;
+
+ pr_err("Got sess %pK timer interrupt\n", sess);
+
+ fnic2_send_fcoe_frame(&fnic2->lport, &sess->timer_logo_req, sizeof(struct fc_logo_req));
+
+ fdls_delete_rport(&fnic2->lport, sess->rport);
+
+ return;
+}
diff --git a/drivers/staging/fnic2/src/fnic2_io.h b/drivers/staging/fnic2/src/fnic2_io.h
new file mode 100644
index 0000000..59cb97e
--- /dev/null
+++ b/drivers/staging/fnic2/src/fnic2_io.h
@@ -0,0 +1,32 @@
+/*
+ * SPDX-License-Identifier: GPL-2.0
+ * Copyright 2018 Cisco Systems, Inc. All rights reserved.
+ *
+ * This program is free software; you may redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+#ifndef _FNIC2_IO_H_
+#define _FNIC2_IO_H_
+
+#include <scsi/fc/fc_fcp.h>
+
+#define FNIC2_DFLT_SG_DESC_CNT 32
+#define FNIC2_MAX_SG_DESC_CNT 256 /* Maximum descriptors per sgl */
+
+struct host_sg_desc {
+ __le64 addr;
+ __le32 len;
+ uint32_t _resvd;
+};
+
+#endif /* _FNIC2_IO_H_ */
--
1.8.3.1
next prev parent reply other threads:[~2018-04-05 21:21 UTC|newest]
Thread overview: 19+ messages / expand[flat|nested] mbox.gz Atom feed top
2018-04-05 21:15 [PATCH 00/10] staging: fnic2 Driver Introduction Oliver Smith-Denny
2018-04-05 21:16 ` [PATCH 01/10] staging: fnic2 add initialization Oliver Smith-Denny
2018-04-06 5:07 ` Greg Kroah-Hartman
2018-04-06 22:00 ` Oliver Smith-Denny
2018-04-07 6:09 ` Greg Kroah-Hartman
2018-04-09 19:49 ` Oliver Smith-Denny
2018-04-09 20:57 ` Martin K. Petersen
2018-04-12 17:32 ` Oliver Smith-Denny
2018-04-05 21:17 ` [PATCH 02/10] staging: fnic2 add resource allocation Oliver Smith-Denny
2018-04-06 5:08 ` Greg Kroah-Hartman
2018-04-05 21:18 ` [PATCH 03/10] staging: fnic2 add fip handling Oliver Smith-Denny
2018-04-06 5:08 ` Greg Kroah-Hartman
2018-04-05 21:19 ` [PATCH 04/10] staging: fnic2 add fdls system Oliver Smith-Denny
2018-04-05 21:20 ` [PATCH 05/10] staging: fnic2 add LIO interface Oliver Smith-Denny
2018-04-05 21:21 ` Oliver Smith-Denny [this message]
2018-04-05 21:21 ` [PATCH 07/10] staging: fnic2 add queue descriptors Oliver Smith-Denny
2018-04-05 21:22 ` [PATCH 08/10] staging: fnic2 add vnic queue interface Oliver Smith-Denny
2018-04-05 21:23 ` [PATCH 09/10] staging: fnic2 add vnic handling Oliver Smith-Denny
2018-04-05 21:24 ` [PATCH 10/10] staging: fnic2 add build and config Oliver Smith-Denny
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20180405212105.GG12584@osmithde-lnx.cisco.com \
--to=osmithde@cisco.com \
--cc=gcboffa@cisco.com \
--cc=gregkh@linuxfoundation.org \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-scsi@vger.kernel.org \
--cc=sebaddel@cisco.com \
--cc=target-devel@vger.kernel.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®