aboutsummaryrefslogtreecommitdiff
path: root/drivers/scsi
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/scsi')
-rw-r--r--drivers/scsi/3w-9xxx.c6
-rw-r--r--drivers/scsi/3w-sas.c1924
-rw-r--r--drivers/scsi/3w-sas.h396
-rw-r--r--drivers/scsi/3w-xxxx.c6
-rw-r--r--drivers/scsi/53c700.c7
-rw-r--r--drivers/scsi/Kconfig29
-rw-r--r--drivers/scsi/Makefile3
-rw-r--r--drivers/scsi/aacraid/linit.c6
-rw-r--r--drivers/scsi/arcmsr/arcmsr_hba.c5
-rw-r--r--drivers/scsi/be2iscsi/be.h24
-rw-r--r--drivers/scsi/be2iscsi/be_cmds.c263
-rw-r--r--drivers/scsi/be2iscsi/be_cmds.h37
-rw-r--r--drivers/scsi/be2iscsi/be_iscsi.c23
-rw-r--r--drivers/scsi/be2iscsi/be_main.c1019
-rw-r--r--drivers/scsi/be2iscsi/be_main.h49
-rw-r--r--drivers/scsi/be2iscsi/be_mgmt.c69
-rw-r--r--drivers/scsi/be2iscsi/be_mgmt.h8
-rw-r--r--drivers/scsi/bfa/bfa_cb_ioim_macros.h10
-rw-r--r--drivers/scsi/bfa/bfa_cee.c2
-rw-r--r--drivers/scsi/bfa/bfa_csdebug.c4
-rw-r--r--drivers/scsi/bfa/bfa_fcpim.c4
-rw-r--r--drivers/scsi/bfa/bfa_fcpim_priv.h4
-rw-r--r--drivers/scsi/bfa/bfa_fcport.c59
-rw-r--r--drivers/scsi/bfa/bfa_fcs_lport.c11
-rw-r--r--drivers/scsi/bfa/bfa_fcxp.c8
-rw-r--r--drivers/scsi/bfa/bfa_intr.c2
-rw-r--r--drivers/scsi/bfa/bfa_intr_priv.h18
-rw-r--r--drivers/scsi/bfa/bfa_ioc.c10
-rw-r--r--drivers/scsi/bfa/bfa_ioc.h12
-rw-r--r--drivers/scsi/bfa/bfa_iocfc.c8
-rw-r--r--drivers/scsi/bfa/bfa_iocfc.h8
-rw-r--r--drivers/scsi/bfa/bfa_ioim.c4
-rw-r--r--drivers/scsi/bfa/bfa_itnim.c6
-rw-r--r--drivers/scsi/bfa/bfa_log.c4
-rw-r--r--drivers/scsi/bfa/bfa_port_priv.h4
-rw-r--r--drivers/scsi/bfa/bfa_rport.c6
-rw-r--r--drivers/scsi/bfa/bfa_tskim.c9
-rw-r--r--drivers/scsi/bfa/bfa_uf.c2
-rw-r--r--drivers/scsi/bfa/bfad.c6
-rw-r--r--drivers/scsi/bfa/bfad_fwimg.c8
-rw-r--r--drivers/scsi/bfa/bfad_im.c2
-rw-r--r--drivers/scsi/bfa/bfad_im_compat.h2
-rw-r--r--drivers/scsi/bfa/bfad_intr.c3
-rw-r--r--drivers/scsi/bfa/fabric.c18
-rw-r--r--drivers/scsi/bfa/fcbuild.c190
-rw-r--r--drivers/scsi/bfa/fcbuild.h6
-rw-r--r--drivers/scsi/bfa/fcpim.c7
-rw-r--r--drivers/scsi/bfa/fcs.h2
-rw-r--r--drivers/scsi/bfa/fdmi.c8
-rw-r--r--drivers/scsi/bfa/include/aen/bfa_aen.h2
-rw-r--r--drivers/scsi/bfa/include/bfa.h10
-rw-r--r--drivers/scsi/bfa/include/bfa_svc.h8
-rw-r--r--drivers/scsi/bfa/include/bfi/bfi.h12
-rw-r--r--drivers/scsi/bfa/include/bfi/bfi_ioc.h2
-rw-r--r--drivers/scsi/bfa/include/bfi/bfi_lps.h4
-rw-r--r--drivers/scsi/bfa/include/bfi/bfi_rport.h8
-rw-r--r--drivers/scsi/bfa/include/cs/bfa_checksum.h6
-rw-r--r--drivers/scsi/bfa/include/cs/bfa_sm.h6
-rw-r--r--drivers/scsi/bfa/include/cs/bfa_trc.h2
-rw-r--r--drivers/scsi/bfa/include/fcs/bfa_fcs_fabric.h2
-rw-r--r--drivers/scsi/bfa/include/fcs/bfa_fcs_lport.h17
-rw-r--r--drivers/scsi/bfa/include/protocol/ct.h10
-rw-r--r--drivers/scsi/bfa/include/protocol/fc.h22
-rw-r--r--drivers/scsi/bfa/loop.c233
-rw-r--r--drivers/scsi/bfa/lport_api.c15
-rw-r--r--drivers/scsi/bfa/ns.c5
-rw-r--r--drivers/scsi/bfa/plog.c2
-rw-r--r--drivers/scsi/bfa/rport_ftrs.c28
-rw-r--r--drivers/scsi/bfa/vfapi.c2
-rw-r--r--drivers/scsi/bfa/vport.c20
-rw-r--r--drivers/scsi/bnx2i/bnx2i.h1
-rw-r--r--drivers/scsi/bnx2i/bnx2i_iscsi.c10
-rw-r--r--drivers/scsi/constants.c1
-rw-r--r--drivers/scsi/cxgb3i/cxgb3i_iscsi.c2
-rw-r--r--drivers/scsi/device_handler/scsi_dh.c28
-rw-r--r--drivers/scsi/device_handler/scsi_dh_alua.c139
-rw-r--r--drivers/scsi/device_handler/scsi_dh_emc.c7
-rw-r--r--drivers/scsi/device_handler/scsi_dh_hp_sw.c94
-rw-r--r--drivers/scsi/device_handler/scsi_dh_rdac.c115
-rw-r--r--drivers/scsi/fcoe/fcoe.c1376
-rw-r--r--drivers/scsi/fcoe/fcoe.h82
-rw-r--r--drivers/scsi/fcoe/libfcoe.c433
-rw-r--r--drivers/scsi/fnic/fnic.h25
-rw-r--r--drivers/scsi/fnic/fnic_fcs.c499
-rw-r--r--drivers/scsi/fnic/fnic_isr.c18
-rw-r--r--drivers/scsi/fnic/fnic_main.c104
-rw-r--r--drivers/scsi/fnic/fnic_res.c5
-rw-r--r--drivers/scsi/fnic/fnic_res.h54
-rw-r--r--drivers/scsi/fnic/fnic_scsi.c96
-rw-r--r--drivers/scsi/fnic/vnic_scsi.h1
-rw-r--r--drivers/scsi/hosts.c13
-rw-r--r--drivers/scsi/hptiop.c5
-rw-r--r--drivers/scsi/ibmvscsi/ibmvfc.c335
-rw-r--r--drivers/scsi/ibmvscsi/ibmvfc.h12
-rw-r--r--drivers/scsi/ibmvscsi/ibmvscsi.c7
-rw-r--r--drivers/scsi/ipr.c7
-rw-r--r--drivers/scsi/iscsi_tcp.c2
-rw-r--r--drivers/scsi/libfc/Makefile4
-rw-r--r--drivers/scsi/libfc/fc_disc.c85
-rw-r--r--drivers/scsi/libfc/fc_elsct.c79
-rw-r--r--drivers/scsi/libfc/fc_exch.c932
-rw-r--r--drivers/scsi/libfc/fc_fcp.c1044
-rw-r--r--drivers/scsi/libfc/fc_frame.c13
-rw-r--r--drivers/scsi/libfc/fc_libfc.c134
-rw-r--r--drivers/scsi/libfc/fc_libfc.h112
-rw-r--r--drivers/scsi/libfc/fc_lport.c853
-rw-r--r--drivers/scsi/libfc/fc_npiv.c161
-rw-r--r--drivers/scsi/libfc/fc_rport.c409
-rw-r--r--drivers/scsi/libiscsi.c389
-rw-r--r--drivers/scsi/libsas/sas_scsi_host.c6
-rw-r--r--drivers/scsi/lpfc/lpfc.h17
-rw-r--r--drivers/scsi/lpfc/lpfc_attr.c262
-rw-r--r--drivers/scsi/lpfc/lpfc_bsg.c5
-rw-r--r--drivers/scsi/lpfc/lpfc_crtn.h14
-rw-r--r--drivers/scsi/lpfc/lpfc_ct.c53
-rw-r--r--drivers/scsi/lpfc/lpfc_debugfs.c10
-rw-r--r--drivers/scsi/lpfc/lpfc_disc.h4
-rw-r--r--drivers/scsi/lpfc/lpfc_els.c76
-rwxr-xr-x[-rw-r--r--]drivers/scsi/lpfc/lpfc_hbadisc.c129
-rw-r--r--drivers/scsi/lpfc/lpfc_hw.h34
-rw-r--r--drivers/scsi/lpfc/lpfc_hw4.h131
-rw-r--r--drivers/scsi/lpfc/lpfc_init.c515
-rw-r--r--drivers/scsi/lpfc/lpfc_mbox.c35
-rw-r--r--drivers/scsi/lpfc/lpfc_nportdisc.c7
-rw-r--r--drivers/scsi/lpfc/lpfc_scsi.c394
-rw-r--r--drivers/scsi/lpfc/lpfc_sli.c1240
-rw-r--r--drivers/scsi/lpfc/lpfc_sli.h27
-rw-r--r--drivers/scsi/lpfc/lpfc_sli4.h34
-rw-r--r--drivers/scsi/lpfc/lpfc_version.h3
-rw-r--r--drivers/scsi/lpfc/lpfc_vport.c4
-rw-r--r--drivers/scsi/megaraid/megaraid_mbox.c7
-rw-r--r--drivers/scsi/megaraid/megaraid_sas.c738
-rw-r--r--drivers/scsi/megaraid/megaraid_sas.h149
-rw-r--r--drivers/scsi/mpt2sas/mpi/mpi2.h5
-rw-r--r--drivers/scsi/mpt2sas/mpi/mpi2_cnfg.h274
-rw-r--r--drivers/scsi/mpt2sas/mpi/mpi2_ioc.h18
-rw-r--r--drivers/scsi/mpt2sas/mpi/mpi2_raid.h14
-rw-r--r--drivers/scsi/mpt2sas/mpi/mpi2_tool.h16
-rw-r--r--drivers/scsi/mpt2sas/mpt2sas_base.c152
-rw-r--r--drivers/scsi/mpt2sas/mpt2sas_base.h37
-rw-r--r--drivers/scsi/mpt2sas/mpt2sas_ctl.c197
-rw-r--r--drivers/scsi/mpt2sas/mpt2sas_ctl.h4
-rw-r--r--drivers/scsi/mpt2sas/mpt2sas_scsih.c819
-rw-r--r--drivers/scsi/mpt2sas/mpt2sas_transport.c86
-rw-r--r--drivers/scsi/osd/osd_initiator.c121
-rw-r--r--drivers/scsi/osd/osd_uld.c260
-rw-r--r--drivers/scsi/pm8001/Makefile12
-rw-r--r--drivers/scsi/pm8001/pm8001_chips.h89
-rw-r--r--drivers/scsi/pm8001/pm8001_ctl.c573
-rw-r--r--drivers/scsi/pm8001/pm8001_ctl.h67
-rw-r--r--drivers/scsi/pm8001/pm8001_defs.h112
-rw-r--r--drivers/scsi/pm8001/pm8001_hwi.c4458
-rw-r--r--drivers/scsi/pm8001/pm8001_hwi.h1030
-rw-r--r--drivers/scsi/pm8001/pm8001_init.c891
-rw-r--r--drivers/scsi/pm8001/pm8001_sas.c1103
-rw-r--r--drivers/scsi/pm8001/pm8001_sas.h481
-rw-r--r--drivers/scsi/pmcraid.c7
-rw-r--r--drivers/scsi/qla2xxx/qla_attr.c3
-rw-r--r--drivers/scsi/qla2xxx/qla_dbg.c78
-rw-r--r--drivers/scsi/qla2xxx/qla_def.h9
-rw-r--r--drivers/scsi/qla2xxx/qla_gbl.h3
-rw-r--r--drivers/scsi/qla2xxx/qla_init.c12
-rw-r--r--drivers/scsi/qla2xxx/qla_isr.c144
-rw-r--r--drivers/scsi/qla2xxx/qla_mbx.c50
-rw-r--r--drivers/scsi/qla2xxx/qla_mid.c2
-rw-r--r--drivers/scsi/qla2xxx/qla_os.c67
-rw-r--r--drivers/scsi/qla2xxx/qla_version.h2
-rw-r--r--drivers/scsi/qlogicpti.h2
-rw-r--r--drivers/scsi/scsi.c10
-rw-r--r--drivers/scsi/scsi_debug.c351
-rw-r--r--drivers/scsi/scsi_devinfo.c13
-rw-r--r--drivers/scsi/scsi_error.c65
-rw-r--r--drivers/scsi/scsi_ioctl.c3
-rw-r--r--drivers/scsi/scsi_lib.c11
-rw-r--r--drivers/scsi/scsi_lib_dma.c4
-rw-r--r--drivers/scsi/scsi_scan.c3
-rw-r--r--drivers/scsi/scsi_sysctl.c11
-rw-r--r--drivers/scsi/scsi_sysfs.c44
-rw-r--r--drivers/scsi/scsi_transport_fc.c99
-rw-r--r--drivers/scsi/scsi_transport_iscsi.c13
-rw-r--r--drivers/scsi/st.c3
-rw-r--r--drivers/scsi/stex.c282
-rw-r--r--drivers/scsi/vmw_pvscsi.c1407
-rw-r--r--drivers/scsi/vmw_pvscsi.h397
184 files changed, 24818 insertions, 5413 deletions
diff --git a/drivers/scsi/3w-9xxx.c b/drivers/scsi/3w-9xxx.c
index b59e240f238..3bf75924741 100644
--- a/drivers/scsi/3w-9xxx.c
+++ b/drivers/scsi/3w-9xxx.c
@@ -186,8 +186,12 @@ static ssize_t twa_show_stats(struct device *dev,
} /* End twa_show_stats() */
/* This function will set a devices queue depth */
-static int twa_change_queue_depth(struct scsi_device *sdev, int queue_depth)
+static int twa_change_queue_depth(struct scsi_device *sdev, int queue_depth,
+ int reason)
{
+ if (reason != SCSI_QDEPTH_DEFAULT)
+ return -EOPNOTSUPP;
+
if (queue_depth > TW_Q_LENGTH-2)
queue_depth = TW_Q_LENGTH-2;
scsi_adjust_queue_depth(sdev, MSG_ORDERED_TAG, queue_depth);
diff --git a/drivers/scsi/3w-sas.c b/drivers/scsi/3w-sas.c
new file mode 100644
index 00000000000..4d314d740de
--- /dev/null
+++ b/drivers/scsi/3w-sas.c
@@ -0,0 +1,1924 @@
+/*
+ 3w-sas.c -- LSI 3ware SAS/SATA-RAID Controller device driver for Linux.
+
+ Written By: Adam Radford <linuxraid@lsi.com>
+
+ Copyright (C) 2009 LSI Corporation.
+
+ This program is free software; you can 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.
+
+ This program is distributed in the hope that 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.
+
+ NO WARRANTY
+ THE PROGRAM IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR
+ CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT
+ LIMITATION, ANY WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT,
+ MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Each Recipient is
+ solely responsible for determining the appropriateness of using and
+ distributing the Program and assumes all risks associated with its
+ exercise of rights under this Agreement, including but not limited to
+ the risks and costs of program errors, damage to or loss of data,
+ programs or equipment, and unavailability or interruption of operations.
+
+ DISCLAIMER OF LIABILITY
+ NEITHER RECIPIENT NOR ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY
+ DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ DAMAGES (INCLUDING WITHOUT LIMITATION LOST PROFITS), HOWEVER CAUSED AND
+ ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
+ TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+ USE OR DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED
+ HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES
+
+ 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+ Controllers supported by this driver:
+
+ LSI 3ware 9750 6Gb/s SAS/SATA-RAID
+
+ Bugs/Comments/Suggestions should be mailed to:
+ linuxraid@lsi.com
+
+ For more information, goto:
+ http://www.lsi.com
+
+ History
+ -------
+ 3.26.02.000 - Initial driver release.
+*/
+
+#include <linux/module.h>
+#include <linux/reboot.h>
+#include <linux/spinlock.h>
+#include <linux/interrupt.h>
+#include <linux/moduleparam.h>
+#include <linux/errno.h>
+#include <linux/types.h>
+#include <linux/delay.h>
+#include <linux/pci.h>
+#include <linux/time.h>
+#include <linux/mutex.h>
+#include <linux/smp_lock.h>
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/uaccess.h>
+#include <scsi/scsi.h>
+#include <scsi/scsi_host.h>
+#include <scsi/scsi_tcq.h>
+#include <scsi/scsi_cmnd.h>
+#include "3w-sas.h"
+
+/* Globals */
+#define TW_DRIVER_VERSION "3.26.02.000"
+static TW_Device_Extension *twl_device_extension_list[TW_MAX_SLOT];
+static unsigned int twl_device_extension_count;
+static int twl_major = -1;
+extern struct timezone sys_tz;
+
+/* Module parameters */
+MODULE_AUTHOR ("LSI");
+MODULE_DESCRIPTION ("LSI 3ware SAS/SATA-RAID Linux Driver");
+MODULE_LICENSE("GPL");
+MODULE_VERSION(TW_DRIVER_VERSION);
+
+static int use_msi;
+module_param(use_msi, int, S_IRUGO);
+MODULE_PARM_DESC(use_msi, "Use Message Signaled Interrupts. Default: 0");
+
+/* Function prototypes */
+static int twl_reset_device_extension(TW_Device_Extension *tw_dev, int ioctl_reset);
+
+/* Functions */
+
+/* This function returns AENs through sysfs */
+static ssize_t twl_sysfs_aen_read(struct kobject *kobj,
+ struct bin_attribute *bin_attr,
+ char *outbuf, loff_t offset, size_t count)
+{
+ struct device *dev = container_of(kobj, struct device, kobj);
+ struct Scsi_Host *shost = class_to_shost(dev);
+ TW_Device_Extension *tw_dev = (TW_Device_Extension *)shost->hostdata;
+ unsigned long flags = 0;
+ ssize_t ret;
+
+ if (!capable(CAP_SYS_ADMIN))
+ return -EACCES;
+
+ spin_lock_irqsave(tw_dev->host->host_lock, flags);
+ ret = memory_read_from_buffer(outbuf, count, &offset, tw_dev->event_queue[0], sizeof(TW_Event) * TW_Q_LENGTH);
+ spin_unlock_irqrestore(tw_dev->host->host_lock, flags);
+
+ return ret;
+} /* End twl_sysfs_aen_read() */
+
+/* aen_read sysfs attribute initializer */
+static struct bin_attribute twl_sysfs_aen_read_attr = {
+ .attr = {
+ .name = "3ware_aen_read",
+ .mode = S_IRUSR,
+ },
+ .size = 0,
+ .read = twl_sysfs_aen_read
+};
+
+/* This function returns driver compatibility info through sysfs */
+static ssize_t twl_sysfs_compat_info(struct kobject *kobj,
+ struct bin_attribute *bin_attr,
+ char *outbuf, loff_t offset, size_t count)
+{
+ struct device *dev = container_of(kobj, struct device, kobj);
+ struct Scsi_Host *shost = class_to_shost(dev);
+ TW_Device_Extension *tw_dev = (TW_Device_Extension *)shost->hostdata;
+ unsigned long flags = 0;
+ ssize_t ret;
+
+ if (!capable(CAP_SYS_ADMIN))
+ return -EACCES;
+
+ spin_lock_irqsave(tw_dev->host->host_lock, flags);
+ ret = memory_read_from_buffer(outbuf, count, &offset, &tw_dev->tw_compat_info, sizeof(TW_Compatibility_Info));
+ spin_unlock_irqrestore(tw_dev->host->host_lock, flags);
+
+ return ret;
+} /* End twl_sysfs_compat_info() */
+
+/* compat_info sysfs attribute initializer */
+static struct bin_attribute twl_sysfs_compat_info_attr = {
+ .attr = {
+ .name = "3ware_compat_info",
+ .mode = S_IRUSR,
+ },
+ .size = 0,
+ .read = twl_sysfs_compat_info
+};
+
+/* Show some statistics about the card */
+static ssize_t twl_show_stats(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct Scsi_Host *host = class_to_shost(dev);
+ TW_Device_Extension *tw_dev = (TW_Device_Extension *)host->hostdata;
+ unsigned long flags = 0;
+ ssize_t len;
+
+ spin_lock_irqsave(tw_dev->host->host_lock, flags);
+ len = snprintf(buf, PAGE_SIZE, "3w-sas Driver version: %s\n"
+ "Current commands posted: %4d\n"
+ "Max commands posted: %4d\n"
+ "Last sgl length: %4d\n"
+ "Max sgl length: %4d\n"
+ "Last sector count: %4d\n"
+ "Max sector count: %4d\n"
+ "SCSI Host Resets: %4d\n"
+ "AEN's: %4d\n",
+ TW_DRIVER_VERSION,
+ tw_dev->posted_request_count,
+ tw_dev->max_posted_request_count,
+ tw_dev->sgl_entries,
+ tw_dev->max_sgl_entries,
+ tw_dev->sector_count,
+ tw_dev->max_sector_count,
+ tw_dev->num_resets,
+ tw_dev->aen_count);
+ spin_unlock_irqrestore(tw_dev->host->host_lock, flags);
+ return len;
+} /* End twl_show_stats() */
+
+/* This function will set a devices queue depth */
+static int twl_change_queue_depth(struct scsi_device *sdev, int queue_depth,
+ int reason)
+{
+ if (reason != SCSI_QDEPTH_DEFAULT)
+ return -EOPNOTSUPP;
+
+ if (queue_depth > TW_Q_LENGTH-2)
+ queue_depth = TW_Q_LENGTH-2;
+ scsi_adjust_queue_depth(sdev, MSG_ORDERED_TAG, queue_depth);
+ return queue_depth;
+} /* End twl_change_queue_depth() */
+
+/* stats sysfs attribute initializer */
+static struct device_attribute twl_host_stats_attr = {
+ .attr = {
+ .name = "3ware_stats",
+ .mode = S_IRUGO,
+ },
+ .show = twl_show_stats
+};
+
+/* Host attributes initializer */
+static struct device_attribute *twl_host_attrs[] = {
+ &twl_host_stats_attr,
+ NULL,
+};
+
+/* This function will look up an AEN severity string */
+static char *twl_aen_severity_lookup(unsigned char severity_code)
+{
+ char *retval = NULL;
+
+ if ((severity_code < (unsigned char) TW_AEN_SEVERITY_ERROR) ||
+ (severity_code > (unsigned char) TW_AEN_SEVERITY_DEBUG))
+ goto out;
+
+ retval = twl_aen_severity_table[severity_code];
+out:
+ return retval;
+} /* End twl_aen_severity_lookup() */
+
+/* This function will queue an event */
+static void twl_aen_queue_event(TW_Device_Extension *tw_dev, TW_Command_Apache_Header *header)
+{
+ u32 local_time;
+ struct timeval time;
+ TW_Event *event;
+ unsigned short aen;
+ char host[16];
+ char *error_str;
+
+ tw_dev->aen_count++;
+
+ /* Fill out event info */
+ event = tw_dev->event_queue[tw_dev->error_index];
+
+ host[0] = '\0';
+ if (tw_dev->host)
+ sprintf(host, " scsi%d:", tw_dev->host->host_no);
+
+ aen = le16_to_cpu(header->status_block.error);
+ memset(event, 0, sizeof(TW_Event));
+
+ event->severity = TW_SEV_OUT(header->status_block.severity__reserved);
+ do_gettimeofday(&time);
+ local_time = (u32)(time.tv_sec - (sys_tz.tz_minuteswest * 60));
+ event->time_stamp_sec = local_time;
+ event->aen_code = aen;
+ event->retrieved = TW_AEN_NOT_RETRIEVED;
+ event->sequence_id = tw_dev->error_sequence_id;
+ tw_dev->error_sequence_id++;
+
+ /* Check for embedded error string */
+ error_str = &(header->err_specific_desc[strlen(header->err_specific_desc)+1]);
+
+ header->err_specific_desc[sizeof(header->err_specific_desc) - 1] = '\0';
+ event->parameter_len = strlen(header->err_specific_desc);
+ memcpy(event->parameter_data, header->err_specific_desc, event->parameter_len + 1 + strlen(error_str));
+ if (event->severity != TW_AEN_SEVERITY_DEBUG)
+ printk(KERN_WARNING "3w-sas:%s AEN: %s (0x%02X:0x%04X): %s:%s.\n",
+ host,
+ twl_aen_severity_lookup(TW_SEV_OUT(header->status_block.severity__reserved)),
+ TW_MESSAGE_SOURCE_CONTROLLER_EVENT, aen, error_str,
+ header->err_specific_desc);
+ else
+ tw_dev->aen_count--;
+
+ tw_dev->error_index = (tw_dev->error_index + 1 ) % TW_Q_LENGTH;
+} /* End twl_aen_queue_event() */
+
+/* This function will attempt to post a command packet to the board */
+static int twl_post_command_packet(TW_Device_Extension *tw_dev, int request_id)
+{
+ dma_addr_t command_que_value;
+
+ command_que_value = tw_dev->command_packet_phys[request_id];
+ command_que_value += TW_COMMAND_OFFSET;
+
+ /* First write upper 4 bytes */
+ writel((u32)((u64)command_que_value >> 32), TWL_HIBQPH_REG_ADDR(tw_dev));
+ /* Then the lower 4 bytes */
+ writel((u32)(command_que_value | TWL_PULL_MODE), TWL_HIBQPL_REG_ADDR(tw_dev));
+
+ tw_dev->state[request_id] = TW_S_POSTED;
+ tw_dev->posted_request_count++;
+ if (tw_dev->posted_request_count > tw_dev->max_posted_request_count)
+ tw_dev->max_posted_request_count = tw_dev->posted_request_count;
+
+ return 0;
+} /* End twl_post_command_packet() */
+
+/* This function will perform a pci-dma mapping for a scatter gather list */
+static int twl_map_scsi_sg_data(TW_Device_Extension *tw_dev, int request_id)
+{
+ int use_sg;
+ struct scsi_cmnd *cmd = tw_dev->srb[request_id];
+
+ use_sg = scsi_dma_map(cmd);
+ if (!use_sg)
+ return 0;
+ else if (use_sg < 0) {
+ TW_PRINTK(tw_dev->host, TW_DRIVER, 0x1, "Failed to map scatter gather list");
+ return 0;
+ }
+
+ cmd->SCp.phase = TW_PHASE_SGLIST;
+ cmd->SCp.have_data_in = use_sg;
+
+ return use_sg;
+} /* End twl_map_scsi_sg_data() */
+
+/* This function hands scsi cdb's to the firmware */
+static int twl_scsiop_execute_scsi(TW_Device_Extension *tw_dev, int request_id, char *cdb, int use_sg, TW_SG_Entry_ISO *sglistarg)
+{
+ TW_Command_Full *full_command_packet;
+ TW_Command_Apache *command_packet;
+ int i, sg_count;
+ struct scsi_cmnd *srb = NULL;
+ struct scatterlist *sglist = NULL, *sg;
+ int retval = 1;
+
+ if (tw_dev->srb[request_id]) {
+ srb = tw_dev->srb[request_id];
+ if (scsi_sglist(srb))
+ sglist = scsi_sglist(srb);
+ }
+
+ /* Initialize command packet */
+ full_command_packet = tw_dev->command_packet_virt[request_id];
+ full_command_packet->header.header_desc.size_header = 128;
+ full_command_packet->header.status_block.error = 0;
+ full_command_packet->header.status_block.severity__reserved = 0;
+
+ command_packet = &full_command_packet->command.newcommand;
+ command_packet->status = 0;
+ command_packet->opcode__reserved = TW_OPRES_IN(0, TW_OP_EXECUTE_SCSI);
+
+ /* We forced 16 byte cdb use earlier */
+ if (!cdb)
+ memcpy(command_packet->cdb, srb->cmnd, TW_MAX_CDB_LEN);
+ else
+ memcpy(command_packet->cdb, cdb, TW_MAX_CDB_LEN);
+
+ if (srb) {
+ command_packet->unit = srb->device->id;
+ command_packet->request_id__lunl =
+ cpu_to_le16(TW_REQ_LUN_IN(srb->device->lun, request_id));
+ } else {
+ command_packet->request_id__lunl =
+ cpu_to_le16(TW_REQ_LUN_IN(0, request_id));
+ command_packet->unit = 0;
+ }
+
+ command_packet->sgl_offset = 16;
+
+ if (!sglistarg) {
+ /* Map sglist from scsi layer to cmd packet */
+ if (scsi_sg_count(srb)) {
+ sg_count = twl_map_scsi_sg_data(tw_dev, request_id);
+ if (sg_count == 0)
+ goto out;
+
+ scsi_for_each_sg(srb, sg, sg_count, i) {
+ command_packet->sg_list[i].address = TW_CPU_TO_SGL(sg_dma_address(sg));
+ command_packet->sg_list[i].length = TW_CPU_TO_SGL(sg_dma_len(sg));
+ }
+ command_packet->sgl_entries__lunh = cpu_to_le16(TW_REQ_LUN_IN((srb->device->lun >> 4), scsi_sg_count(tw_dev->srb[request_id])));
+ }
+ } else {
+ /* Internal cdb post */
+ for (i = 0; i < use_sg; i++) {
+ command_packet->sg_list[i].address = TW_CPU_TO_SGL(sglistarg[i].address);
+ command_packet->sg_list[i].length = TW_CPU_TO_SGL(sglistarg[i].length);
+ }
+ command_packet->sgl_entries__lunh = cpu_to_le16(TW_REQ_LUN_IN(0, use_sg));
+ }
+
+ /* Update some stats */
+ if (srb) {
+ tw_dev->sector_count = scsi_bufflen(srb) / 512;
+ if (tw_dev->sector_count > tw_dev->max_sector_count)
+ tw_dev->max_sector_count = tw_dev->sector_count;
+ tw_dev->sgl_entries = scsi_sg_count(srb);
+ if (tw_dev->sgl_entries > tw_dev->max_sgl_entries)
+ tw_dev->max_sgl_entries = tw_dev->sgl_entries;
+ }
+
+ /* Now post the command to the board */
+ retval = twl_post_command_packet(tw_dev, request_id);
+
+out:
+ return retval;
+} /* End twl_scsiop_execute_scsi() */
+
+/* This function will read the aen queue from the isr */
+static int twl_aen_read_queue(TW_Device_Extension *tw_dev, int request_id)
+{
+ char cdb[TW_MAX_CDB_LEN];
+ TW_SG_Entry_ISO sglist[1];
+ TW_Command_Full *full_command_packet;
+ int retval = 1;
+
+ full_command_packet = tw_dev->command_packet_virt[request_id];
+ memset(full_command_packet, 0, sizeof(TW_Command_Full));
+
+ /* Initialize cdb */
+ memset(&cdb, 0, TW_MAX_CDB_LEN);
+ cdb[0] = REQUEST_SENSE; /* opcode */
+ cdb[4] = TW_ALLOCATION_LENGTH; /* allocation length */
+
+ /* Initialize sglist */
+ memset(&sglist, 0, sizeof(TW_SG_Entry_ISO));
+ sglist[0].length = TW_SECTOR_SIZE;
+ sglist[0].address = tw_dev->generic_buffer_phys[request_id];
+
+ /* Mark internal command */
+ tw_dev->srb[request_id] = NULL;
+
+ /* Now post the command packet */
+ if (twl_scsiop_execute_scsi(tw_dev, request_id, cdb, 1, sglist)) {
+ TW_PRINTK(tw_dev->host, TW_DRIVER, 0x2, "Post failed while reading AEN queue");
+ goto out;
+ }
+ retval = 0;
+out:
+ return retval;
+} /* End twl_aen_read_queue() */
+
+/* This function will sync firmware time with the host time */
+static void twl_aen_sync_time(TW_Device_Extension *tw_dev, int request_id)
+{
+ u32 schedulertime;
+ struct timeval utc;
+ TW_Command_Full *full_command_packet;
+ TW_Command *command_packet;
+ TW_Param_Apache *param;
+ u32 local_time;
+
+ /* Fill out the command packet */
+ full_command_packet = tw_dev->command_packet_virt[request_id];
+ memset(full_command_packet, 0, sizeof(TW_Command_Full));
+ command_packet = &full_command_packet->command.oldcommand;
+ command_packet->opcode__sgloffset = TW_OPSGL_IN(2, TW_OP_SET_PARAM);
+ command_packet->request_id = request_id;
+ command_packet->byte8_offset.param.sgl[0].address = TW_CPU_TO_SGL(tw_dev->generic_buffer_phys[request_id]);
+ command_packet->byte8_offset.param.sgl[0].length = TW_CPU_TO_SGL(TW_SECTOR_SIZE);
+ command_packet->size = TW_COMMAND_SIZE;
+ command_packet->byte6_offset.parameter_count = cpu_to_le16(1);
+
+ /* Setup the param */
+ param = (TW_Param_Apache *)tw_dev->generic_buffer_virt[request_id];
+ memset(param, 0, TW_SECTOR_SIZE);
+ param->table_id = cpu_to_le16(TW_TIMEKEEP_TABLE | 0x8000); /* Controller time keep table */
+ param->parameter_id = cpu_to_le16(0x3); /* SchedulerTime */
+ param->parameter_size_bytes = cpu_to_le16(4);
+
+ /* Convert system time in UTC to local time seconds since last
+ Sunday 12:00AM */
+ do_gettimeofday(&utc);
+ local_time = (u32)(utc.tv_sec - (sys_tz.tz_minuteswest * 60));
+ schedulertime = local_time - (3 * 86400);
+ schedulertime = cpu_to_le32(schedulertime % 604800);
+
+ memcpy(param->data, &schedulertime, sizeof(u32));
+
+ /* Mark internal command */
+ tw_dev->srb[request_id] = NULL;
+
+ /* Now post the command */
+ twl_post_command_packet(tw_dev, request_id);
+} /* End twl_aen_sync_time() */
+
+/* This function will assign an available request id */
+static void twl_get_request_id(TW_Device_Extension *tw_dev, int *request_id)
+{
+ *request_id = tw_dev->free_queue[tw_dev->free_head];
+ tw_dev->free_head = (tw_dev->free_head + 1) % TW_Q_LENGTH;
+ tw_dev->state[*request_id] = TW_S_STARTED;
+} /* End twl_get_request_id() */
+
+/* This function will free a request id */
+static void twl_free_request_id(TW_Device_Extension *tw_dev, int request_id)
+{
+ tw_dev->free_queue[tw_dev->free_tail] = request_id;
+ tw_dev->state[request_id] = TW_S_FINISHED;
+ tw_dev->free_tail = (tw_dev->free_tail + 1) % TW_Q_LENGTH;
+} /* End twl_free_request_id() */
+
+/* This function will complete an aen request from the isr */
+static int twl_aen_complete(TW_Device_Extension *tw_dev, int request_id)
+{
+ TW_Command_Full *full_command_packet;
+ TW_Command *command_packet;
+ TW_Command_Apache_Header *header;
+ unsigned short aen;
+ int retval = 1;
+
+ header = (TW_Command_Apache_Header *)tw_dev->generic_buffer_virt[request_id];
+ tw_dev->posted_request_count--;
+ aen = le16_to_cpu(header->status_block.error);
+ full_command_packet = tw_dev->command_packet_virt[request_id];
+ command_packet = &full_command_packet->command.oldcommand;
+
+ /* First check for internal completion of set param for time sync */
+ if (TW_OP_OUT(command_packet->opcode__sgloffset) == TW_OP_SET_PARAM) {
+ /* Keep reading the queue in case there are more aen's */
+ if (twl_aen_read_queue(tw_dev, request_id))
+ goto out2;
+ else {
+ retval = 0;
+ goto out;
+ }
+ }
+
+ switch (aen) {
+ case TW_AEN_QUEUE_EMPTY:
+ /* Quit reading the queue if this is the last one */
+ break;
+ case TW_AEN_SYNC_TIME_WITH_HOST:
+ twl_aen_sync_time(tw_dev, request_id);
+ retval = 0;
+ goto out;
+ default:
+ twl_aen_queue_event(tw_dev, header);
+
+ /* If there are more aen's, keep reading the queue */
+ if (twl_aen_read_queue(tw_dev, request_id))
+ goto out2;
+ else {
+ retval = 0;
+ goto out;
+ }
+ }
+ retval = 0;
+out2:
+ tw_dev->state[request_id] = TW_S_COMPLETED;
+ twl_free_request_id(tw_dev, request_id);
+ clear_bit(TW_IN_ATTENTION_LOOP, &tw_dev->flags);
+out:
+ return retval;
+} /* End twl_aen_complete() */
+
+/* This function will poll for a response */
+static int twl_poll_response(TW_Device_Extension *tw_dev, int request_id, int seconds)
+{
+ unsigned long before;
+ dma_addr_t mfa;
+ u32 regh, regl;
+ u32 response;
+ int retval = 1;
+ int found = 0;
+
+ before = jiffies;
+
+ while (!found) {
+ if (sizeof(dma_addr_t) > 4) {
+ regh = readl(TWL_HOBQPH_REG_ADDR(tw_dev));
+ regl = readl(TWL_HOBQPL_REG_ADDR(tw_dev));
+ mfa = ((u64)regh << 32) | regl;
+ } else
+ mfa = readl(TWL_HOBQPL_REG_ADDR(tw_dev));
+
+ response = (u32)mfa;
+
+ if (TW_RESID_OUT(response) == request_id)
+ found = 1;
+
+ if (time_after(jiffies, before + HZ * seconds))
+ goto out;
+
+ msleep(50);
+ }
+ retval = 0;
+out:
+ return retval;
+} /* End twl_poll_response() */
+
+/* This function will drain the aen queue */
+static int twl_aen_drain_queue(TW_Device_Extension *tw_dev, int no_check_reset)
+{
+ int request_id = 0;
+ char cdb[TW_MAX_CDB_LEN];
+ TW_SG_Entry_ISO sglist[1];
+ int finished = 0, count = 0;
+ TW_Command_Full *full_command_packet;
+ TW_Command_Apache_Header *header;
+ unsigned short aen;
+ int first_reset = 0, queue = 0, retval = 1;
+
+ if (no_check_reset)
+ first_reset = 0;
+ else
+ first_reset = 1;
+
+ full_command_packet = tw_dev->command_packet_virt[request_id];
+ memset(full_command_packet, 0, sizeof(TW_Command_Full));
+
+ /* Initialize cdb */
+ memset(&cdb, 0, TW_MAX_CDB_LEN);
+ cdb[0] = REQUEST_SENSE; /* opcode */
+ cdb[4] = TW_ALLOCATION_LENGTH; /* allocation length */
+
+ /* Initialize sglist */
+ memset(&sglist, 0, sizeof(TW_SG_Entry_ISO));
+ sglist[0].length = TW_SECTOR_SIZE;
+ sglist[0].address = tw_dev->generic_buffer_phys[request_id];
+
+ /* Mark internal command */
+ tw_dev->srb[request_id] = NULL;
+
+ do {
+ /* Send command to the board */
+ if (twl_scsiop_execute_scsi(tw_dev, request_id, cdb, 1, sglist)) {
+ TW_PRINTK(tw_dev->host, TW_DRIVER, 0x3, "Error posting request sense");
+ goto out;
+ }
+
+ /* Now poll for completion */
+ if (twl_poll_response(tw_dev, request_id, 30)) {
+ TW_PRINTK(tw_dev->host, TW_DRIVER, 0x4, "No valid response while draining AEN queue");
+ tw_dev->posted_request_count--;
+ goto out;
+ }
+
+ tw_dev->posted_request_count--;
+ header = (TW_Command_Apache_Header *)tw_dev->generic_buffer_virt[request_id];
+ aen = le16_to_cpu(header->status_block.error);
+ queue = 0;
+ count++;
+
+ switch (aen) {
+ case TW_AEN_QUEUE_EMPTY:
+ if (first_reset != 1)
+ goto out;
+ else
+ finished = 1;
+ break;
+ case TW_AEN_SOFT_RESET:
+ if (first_reset == 0)
+ first_reset = 1;
+ else
+ queue = 1;
+ break;
+ case TW_AEN_SYNC_TIME_WITH_HOST:
+ break;
+ default:
+ queue = 1;
+ }
+
+ /* Now queue an event info */
+ if (queue)
+ twl_aen_queue_event(tw_dev, header);
+ } while ((finished == 0) && (count < TW_MAX_AEN_DRAIN));
+
+ if (count == TW_MAX_AEN_DRAIN)
+ goto out;
+
+ retval = 0;
+out:
+ tw_dev->state[request_id] = TW_S_INITIAL;
+ return retval;
+} /* End twl_aen_drain_queue() */
+
+/* This function will allocate memory and check if it is correctly aligned */
+static int twl_allocate_memory(TW_Device_Extension *tw_dev, int size, int which)
+{
+ int i;
+ dma_addr_t dma_handle;
+ unsigned long *cpu_addr;
+ int retval = 1;
+
+ cpu_addr = pci_alloc_consistent(tw_dev->tw_pci_dev, size*TW_Q_LENGTH, &dma_handle);
+ if (!cpu_addr) {
+ TW_PRINTK(tw_dev->host, TW_DRIVER, 0x5, "Memory allocation failed");
+ goto out;
+ }
+
+ memset(cpu_addr, 0, size*TW_Q_LENGTH);
+
+ for (i = 0; i < TW_Q_LENGTH; i++) {
+ switch(which) {
+ case 0:
+ tw_dev->command_packet_phys[i] = dma_handle+(i*size);
+ tw_dev->command_packet_virt[i] = (TW_Command_Full *)((unsigned char *)cpu_addr + (i*size));
+ break;
+ case 1:
+ tw_dev->generic_buffer_phys[i] = dma_handle+(i*size);
+ tw_dev->generic_buffer_virt[i] = (unsigned long *)((unsigned char *)cpu_addr + (i*size));
+ break;
+ case 2:
+ tw_dev->sense_buffer_phys[i] = dma_handle+(i*size);
+ tw_dev->sense_buffer_virt[i] = (TW_Command_Apache_Header *)((unsigned char *)cpu_addr + (i*size));
+ break;
+ }
+ }
+ retval = 0;
+out:
+ return retval;
+} /* End twl_allocate_memory() */
+
+/* This function will load the request id and various sgls for ioctls */
+static void twl_load_sgl(TW_Device_Extension *tw_dev, TW_Command_Full *full_command_packet, int request_id, dma_addr_t dma_handle, int length)
+{
+ TW_Command *oldcommand;
+ TW_Command_Apache *newcommand;
+ TW_SG_Entry_ISO *sgl;
+ unsigned int pae = 0;
+
+ if ((sizeof(long) < 8) && (sizeof(dma_addr_t) > 4))
+ pae = 1;
+
+ if (TW_OP_OUT(full_command_packet->command.newcommand.opcode__reserved) == TW_OP_EXECUTE_SCSI) {
+ newcommand = &full_command_packet->command.newcommand;
+ newcommand->request_id__lunl =
+ cpu_to_le16(TW_REQ_LUN_IN(TW_LUN_OUT(newcommand->request_id__lunl), request_id));
+ if (length) {
+ newcommand->sg_list[0].address = TW_CPU_TO_SGL(dma_handle + sizeof(TW_Ioctl_Buf_Apache) - 1);
+ newcommand->sg_list[0].length = TW_CPU_TO_SGL(length);
+ }
+ newcommand->sgl_entries__lunh =
+ cpu_to_le16(TW_REQ_LUN_IN(TW_LUN_OUT(newcommand->sgl_entries__lunh), length ? 1 : 0));
+ } else {
+ oldcommand = &full_command_packet->command.oldcommand;
+ oldcommand->request_id = request_id;
+
+ if (TW_SGL_OUT(oldcommand->opcode__sgloffset)) {
+ /* Load the sg list */
+ sgl = (TW_SG_Entry_ISO *)((u32 *)oldcommand+oldcommand->size - (sizeof(TW_SG_Entry_ISO)/4) + pae + (sizeof(dma_addr_t) > 4 ? 1 : 0));
+ sgl->address = TW_CPU_TO_SGL(dma_handle + sizeof(TW_Ioctl_Buf_Apache) - 1);
+ sgl->length = TW_CPU_TO_SGL(length);
+ oldcommand->size += pae;
+ oldcommand->size += sizeof(dma_addr_t) > 4 ? 1 : 0;
+ }
+ }
+} /* End twl_load_sgl() */
+
+/* This function handles ioctl for the character device
+ This interface is used by smartmontools open source software */
+static int twl_chrdev_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg)
+{
+ long timeout;
+ unsigned long *cpu_addr, data_buffer_length_adjusted = 0, flags = 0;
+ dma_addr_t dma_handle;
+ int request_id = 0;
+ TW_Ioctl_Driver_Command driver_command;
+ TW_Ioctl_Buf_Apache *tw_ioctl;
+ TW_Command_Full *full_command_packet;
+ TW_Device_Extension *tw_dev = twl_device_extension_list[iminor(inode)];
+ int retval = -EFAULT;
+ void __user *argp = (void __user *)arg;
+
+ /* Only let one of these through at a time */
+ if (mutex_lock_interruptible(&tw_dev->ioctl_lock)) {
+ retval = -EINTR;
+ goto out;
+ }
+
+ /* First copy down the driver command */
+ if (copy_from_user(&driver_command, argp, sizeof(TW_Ioctl_Driver_Command)))
+ goto out2;
+
+ /* Check data buffer size */
+ if (driver_command.buffer_length > TW_MAX_SECTORS * 2048) {
+ retval = -EINVAL;
+ goto out2;
+ }
+
+ /* Hardware can only do multiple of 512 byte transfers */
+ data_buffer_length_adjusted = (driver_command.buffer_length + 511) & ~511;
+
+ /* Now allocate ioctl buf memory */
+ cpu_addr = dma_alloc_coherent(&tw_dev->tw_pci_dev->dev, data_buffer_length_adjusted+sizeof(TW_Ioctl_Buf_Apache) - 1, &dma_handle, GFP_KERNEL);
+ if (!cpu_addr) {
+ retval = -ENOMEM;
+ goto out2;
+ }
+
+ tw_ioctl = (TW_Ioctl_Buf_Apache *)cpu_addr;
+
+ /* Now copy down the entire ioctl */
+ if (copy_from_user(tw_ioctl, argp, driver_command.buffer_length + sizeof(TW_Ioctl_Buf_Apache) - 1))
+ goto out3;
+
+ /* See which ioctl we are doing */
+ switch (cmd) {
+ case TW_IOCTL_FIRMWARE_PASS_THROUGH:
+ spin_lock_irqsave(tw_dev->host->host_lock, flags);
+ twl_get_request_id(tw_dev, &request_id);
+
+ /* Flag internal command */
+ tw_dev->srb[request_id] = NULL;
+
+ /* Flag chrdev ioctl */
+ tw_dev->chrdev_request_id = request_id;
+
+ full_command_packet = (TW_Command_Full *)&tw_ioctl->firmware_command;
+
+ /* Load request id and sglist for both command types */
+ twl_load_sgl(tw_dev, full_command_packet, request_id, dma_handle, data_buffer_length_adjusted);
+
+ memcpy(tw_dev->command_packet_virt[request_id], &(tw_ioctl->firmware_command), sizeof(TW_Command_Full));
+
+ /* Now post the command packet to the controller */
+ twl_post_command_packet(tw_dev, request_id);
+ spin_unlock_irqrestore(tw_dev->host->host_lock, flags);
+
+ timeout = TW_IOCTL_CHRDEV_TIMEOUT*HZ;
+
+ /* Now wait for command to complete */
+ timeout = wait_event_timeout(tw_dev->ioctl_wqueue, tw_dev->chrdev_request_id == TW_IOCTL_CHRDEV_FREE, timeout);
+
+ /* We timed out, and didn't get an interrupt */
+ if (tw_dev->chrdev_request_id != TW_IOCTL_CHRDEV_FREE) {
+ /* Now we need to reset the board */
+ printk(KERN_WARNING "3w-sas: scsi%d: WARNING: (0x%02X:0x%04X): Character ioctl (0x%x) timed out, resetting card.\n",
+ tw_dev->host->host_no, TW_DRIVER, 0x6,
+ cmd);
+ retval = -EIO;
+ twl_reset_device_extension(tw_dev, 1);
+ goto out3;
+ }
+
+ /* Now copy in the command packet response */
+ memcpy(&(tw_ioctl->firmware_command), tw_dev->command_packet_virt[request_id], sizeof(TW_Command_Full));
+
+ /* Now complete the io */
+ spin_lock_irqsave(tw_dev->host->host_lock, flags);
+ tw_dev->posted_request_count--;
+ tw_dev->state[request_id] = TW_S_COMPLETED;
+ twl_free_request_id(tw_dev, request_id);
+ spin_unlock_irqrestore(tw_dev->host->host_lock, flags);
+ break;
+ default:
+ retval = -ENOTTY;
+ goto out3;
+ }
+
+ /* Now copy the entire response to userspace */
+ if (copy_to_user(argp, tw_ioctl, sizeof(TW_Ioctl_Buf_Apache) + driver_command.buffer_length - 1) == 0)
+ retval = 0;
+out3:
+ /* Now free ioctl buf memory */
+ dma_free_coherent(&tw_dev->tw_pci_dev->dev, data_buffer_length_adjusted+sizeof(TW_Ioctl_Buf_Apache) - 1, cpu_addr, dma_handle);
+out2:
+ mutex_unlock(&tw_dev->ioctl_lock);
+out:
+ return retval;
+} /* End twl_chrdev_ioctl() */
+
+/* This function handles open for the character device */
+static int twl_chrdev_open(struct inode *inode, struct file *file)
+{
+ unsigned int minor_number;
+ int retval = -ENODEV;
+
+ if (!capable(CAP_SYS_ADMIN)) {
+ retval = -EACCES;
+ goto out;
+ }
+
+ cycle_kernel_lock();
+ minor_number = iminor(inode);
+ if (minor_number >= twl_device_extension_count)
+ goto out;
+ retval = 0;
+out:
+ return retval;
+} /* End twl_chrdev_open() */
+
+/* File operations struct for character device */
+static const struct file_operations twl_fops = {
+ .owner = THIS_MODULE,
+ .ioctl = twl_chrdev_ioctl,
+ .open = twl_chrdev_open,
+ .release = NULL
+};
+
+/* This function passes sense data from firmware to scsi layer */
+static int twl_fill_sense(TW_Device_Extension *tw_dev, int i, int request_id, int copy_sense, int print_host)
+{
+ TW_Command_Apache_Header *header;
+ TW_Command_Full *full_command_packet;
+ unsigned short error;
+ char *error_str;
+ int retval = 1;
+
+ header = tw_dev->sense_buffer_virt[i];
+ full_command_packet = tw_dev->command_packet_virt[request_id];
+
+ /* Get embedded firmware error string */
+ error_str = &(header->err_specific_desc[strlen(header->err_specific_desc) + 1]);
+
+ /* Don't print error for Logical unit not supported during rollcall */
+ error = le16_to_cpu(header->status_block.error);
+ if ((error != TW_ERROR_LOGICAL_UNIT_NOT_SUPPORTED) && (error != TW_ERROR_UNIT_OFFLINE) && (error != TW_ERROR_INVALID_FIELD_IN_CDB)) {
+ if (print_host)
+ printk(KERN_WARNING "3w-sas: scsi%d: ERROR: (0x%02X:0x%04X): %s:%s.\n",
+ tw_dev->host->host_no,
+ TW_MESSAGE_SOURCE_CONTROLLER_ERROR,
+ header->status_block.error,
+ error_str,
+ header->err_specific_desc);
+ else
+ printk(KERN_WARNING "3w-sas: ERROR: (0x%02X:0x%04X): %s:%s.\n",
+ TW_MESSAGE_SOURCE_CONTROLLER_ERROR,
+ header->status_block.error,
+ error_str,
+ header->err_specific_desc);
+ }
+
+ if (copy_sense) {
+ memcpy(tw_dev->srb[request_id]->sense_buffer, header->sense_data, TW_SENSE_DATA_LENGTH);
+ tw_dev->srb[request_id]->result = (full_command_packet->command.newcommand.status << 1);
+ goto out;
+ }
+out:
+ return retval;
+} /* End twl_fill_sense() */
+
+/* This function will free up device extension resources */
+static void twl_free_device_extension(TW_Device_Extension *tw_dev)
+{
+ if (tw_dev->command_packet_virt[0])
+ pci_free_consistent(tw_dev->tw_pci_dev,
+ sizeof(TW_Command_Full)*TW_Q_LENGTH,
+ tw_dev->command_packet_virt[0],
+ tw_dev->command_packet_phys[0]);
+
+ if (tw_dev->generic_buffer_virt[0])
+ pci_free_consistent(tw_dev->tw_pci_dev,
+ TW_SECTOR_SIZE*TW_Q_LENGTH,
+ tw_dev->generic_buffer_virt[0],
+ tw_dev->generic_buffer_phys[0]);
+
+ if (tw_dev->sense_buffer_virt[0])
+ pci_free_consistent(tw_dev->tw_pci_dev,
+ sizeof(TW_Command_Apache_Header)*
+ TW_Q_LENGTH,
+ tw_dev->sense_buffer_virt[0],
+ tw_dev->sense_buffer_phys[0]);
+
+ kfree(tw_dev->event_queue[0]);
+} /* End twl_free_device_extension() */
+
+/* This function will get parameter table entries from the firmware */
+static void *twl_get_param(TW_Device_Extension *tw_dev, int request_id, int table_id, int parameter_id, int parameter_size_bytes)
+{
+ TW_Command_Full *full_command_packet;
+ TW_Command *command_packet;
+ TW_Param_Apache *param;
+ void *retval = NULL;
+
+ /* Setup the command packet */
+ full_command_packet = tw_dev->command_packet_virt[request_id];
+ memset(full_command_packet, 0, sizeof(TW_Command_Full));
+ command_packet = &full_command_packet->command.oldcommand;
+
+ command_packet->opcode__sgloffset = TW_OPSGL_IN(2, TW_OP_GET_PARAM);
+ command_packet->size = TW_COMMAND_SIZE;
+ command_packet->request_id = request_id;
+ command_packet->byte6_offset.block_count = cpu_to_le16(1);
+
+ /* Now setup the param */
+ param = (TW_Param_Apache *)tw_dev->generic_buffer_virt[request_id];
+ memset(param, 0, TW_SECTOR_SIZE);
+ param->table_id = cpu_to_le16(table_id | 0x8000);
+ param->parameter_id = cpu_to_le16(parameter_id);
+ param->parameter_size_bytes = cpu_to_le16(parameter_size_bytes);
+
+ command_packet->byte8_offset.param.sgl[0].address = TW_CPU_TO_SGL(tw_dev->generic_buffer_phys[request_id]);
+ command_packet->byte8_offset.param.sgl[0].length = TW_CPU_TO_SGL(TW_SECTOR_SIZE);
+
+ /* Post the command packet to the board */
+ twl_post_command_packet(tw_dev, request_id);
+
+ /* Poll for completion */
+ if (twl_poll_response(tw_dev, request_id, 30))
+ TW_PRINTK(tw_dev->host, TW_DRIVER, 0x7, "No valid response during get param")
+ else
+ retval = (void *)&(param->data[0]);
+
+ tw_dev->posted_request_count--;
+ tw_dev->state[request_id] = TW_S_INITIAL;
+
+ return retval;
+} /* End twl_get_param() */
+
+/* This function will send an initconnection command to controller */
+static int twl_initconnection(TW_Device_Extension *tw_dev, int message_credits,
+ u32 set_features, unsigned short current_fw_srl,
+ unsigned short current_fw_arch_id,
+ unsigned short current_fw_branch,
+ unsigned short current_fw_build,
+ unsigned short *fw_on_ctlr_srl,
+ unsigned short *fw_on_ctlr_arch_id,
+ unsigned short *fw_on_ctlr_branch,
+ unsigned short *fw_on_ctlr_build,
+ u32 *init_connect_result)
+{
+ TW_Command_Full *full_command_packet;
+ TW_Initconnect *tw_initconnect;
+ int request_id = 0, retval = 1;
+
+ /* Initialize InitConnection command packet */
+ full_command_packet = tw_dev->command_packet_virt[request_id];
+ memset(full_command_packet, 0, sizeof(TW_Command_Full));
+ full_command_packet->header.header_desc.size_header = 128;
+
+ tw_initconnect = (TW_Initconnect *)&full_command_packet->command.oldcommand;
+ tw_initconnect->opcode__reserved = TW_OPRES_IN(0, TW_OP_INIT_CONNECTION);
+ tw_initconnect->request_id = request_id;
+ tw_initconnect->message_credits = cpu_to_le16(message_credits);
+ tw_initconnect->features = set_features;
+
+ /* Turn on 64-bit sgl support if we need to */
+ tw_initconnect->features |= sizeof(dma_addr_t) > 4 ? 1 : 0;
+
+ tw_initconnect->features = cpu_to_le32(tw_initconnect->features);
+
+ if (set_features & TW_EXTENDED_INIT_CONNECT) {
+ tw_initconnect->size = TW_INIT_COMMAND_PACKET_SIZE_EXTENDED;
+ tw_initconnect->fw_srl = cpu_to_le16(current_fw_srl);
+ tw_initconnect->fw_arch_id = cpu_to_le16(current_fw_arch_id);
+ tw_initconnect->fw_branch = cpu_to_le16(current_fw_branch);
+ tw_initconnect->fw_build = cpu_to_le16(current_fw_build);
+ } else
+ tw_initconnect->size = TW_INIT_COMMAND_PACKET_SIZE;
+
+ /* Send command packet to the board */
+ twl_post_command_packet(tw_dev, request_id);
+
+ /* Poll for completion */
+ if (twl_poll_response(tw_dev, request_id, 30)) {
+ TW_PRINTK(tw_dev->host, TW_DRIVER, 0x8, "No valid response during init connection");
+ } else {
+ if (set_features & TW_EXTENDED_INIT_CONNECT) {
+ *fw_on_ctlr_srl = le16_to_cpu(tw_initconnect->fw_srl);
+ *fw_on_ctlr_arch_id = le16_to_cpu(tw_initconnect->fw_arch_id);
+ *fw_on_ctlr_branch = le16_to_cpu(tw_initconnect->fw_branch);
+ *fw_on_ctlr_build = le16_to_cpu(tw_initconnect->fw_build);
+ *init_connect_result = le32_to_cpu(tw_initconnect->result);
+ }
+ retval = 0;
+ }
+
+ tw_dev->posted_request_count--;
+ tw_dev->state[request_id] = TW_S_INITIAL;
+
+ return retval;
+} /* End twl_initconnection() */
+
+/* This function will initialize the fields of a device extension */
+static int twl_initialize_device_extension(TW_Device_Extension *tw_dev)
+{
+ int i, retval = 1;
+
+ /* Initialize command packet buffers */
+ if (twl_allocate_memory(tw_dev, sizeof(TW_Command_Full), 0)) {
+ TW_PRINTK(tw_dev->host, TW_DRIVER, 0x9, "Command packet memory allocation failed");
+ goto out;
+ }
+
+ /* Initialize generic buffer */
+ if (twl_allocate_memory(tw_dev, TW_SECTOR_SIZE, 1)) {
+ TW_PRINTK(tw_dev->host, TW_DRIVER, 0xa, "Generic memory allocation failed");
+ goto out;
+ }
+
+ /* Allocate sense buffers */
+ if (twl_allocate_memory(tw_dev, sizeof(TW_Command_Apache_Header), 2)) {
+ TW_PRINTK(tw_dev->host, TW_DRIVER, 0xb, "Sense buffer allocation failed");
+ goto out;
+ }
+
+ /* Allocate event info space */
+ tw_dev->event_queue[0] = kcalloc(TW_Q_LENGTH, sizeof(TW_Event), GFP_KERNEL);
+ if (!tw_dev->event_queue[0]) {
+ TW_PRINTK(tw_dev->host, TW_DRIVER, 0xc, "Event info memory allocation failed");
+ goto out;
+ }
+
+ for (i = 0; i < TW_Q_LENGTH; i++) {
+ tw_dev->event_queue[i] = (TW_Event *)((unsigned char *)tw_dev->event_queue[0] + (i * sizeof(TW_Event)));
+ tw_dev->free_queue[i] = i;
+ tw_dev->state[i] = TW_S_INITIAL;
+ }
+
+ tw_dev->free_head = TW_Q_START;
+ tw_dev->free_tail = TW_Q_START;
+ tw_dev->error_sequence_id = 1;
+ tw_dev->chrdev_request_id = TW_IOCTL_CHRDEV_FREE;
+
+ mutex_init(&tw_dev->ioctl_lock);
+ init_waitqueue_head(&tw_dev->ioctl_wqueue);
+
+ retval = 0;
+out:
+ return retval;
+} /* End twl_initialize_device_extension() */
+
+/* This function will perform a pci-dma unmap */
+static void twl_unmap_scsi_data(TW_Device_Extension *tw_dev, int request_id)
+{
+ struct scsi_cmnd *cmd = tw_dev->srb[request_id];
+
+ if (cmd->SCp.phase == TW_PHASE_SGLIST)
+ scsi_dma_unmap(cmd);
+} /* End twl_unmap_scsi_data() */
+
+/* This function will handle attention interrupts */
+static int twl_handle_attention_interrupt(TW_Device_Extension *tw_dev)
+{
+ int retval = 1;
+ u32 request_id, doorbell;
+
+ /* Read doorbell status */
+ doorbell = readl(TWL_HOBDB_REG_ADDR(tw_dev));
+
+ /* Check for controller errors */
+ if (doorbell & TWL_DOORBELL_CONTROLLER_ERROR) {
+ TW_PRINTK(tw_dev->host, TW_DRIVER, 0xd, "Microcontroller Error: clearing");
+ goto out;
+ }
+
+ /* Check if we need to perform an AEN drain */
+ if (doorbell & TWL_DOORBELL_ATTENTION_INTERRUPT) {
+ if (!(test_and_set_bit(TW_IN_ATTENTION_LOOP, &tw_dev->flags))) {
+ twl_get_request_id(tw_dev, &request_id);
+ if (twl_aen_read_queue(tw_dev, request_id)) {
+ tw_dev->state[request_id] = TW_S_COMPLETED;
+ twl_free_request_id(tw_dev, request_id);
+ clear_bit(TW_IN_ATTENTION_LOOP, &tw_dev->flags);
+ }
+ }
+ }
+
+ retval = 0;
+out:
+ /* Clear doorbell interrupt */
+ TWL_CLEAR_DB_INTERRUPT(tw_dev);
+
+ /* Make sure the clear was flushed by reading it back */
+ readl(TWL_HOBDBC_REG_ADDR(tw_dev));
+
+ return retval;
+} /* End twl_handle_attention_interrupt() */
+
+/* Interrupt service routine */
+static irqreturn_t twl_interrupt(int irq, void *dev_instance)
+{
+ TW_Device_Extension *tw_dev = (TW_Device_Extension *)dev_instance;
+ int i, handled = 0, error = 0;
+ dma_addr_t mfa = 0;
+ u32 reg, regl, regh, response, request_id = 0;
+ struct scsi_cmnd *cmd;
+ TW_Command_Full *full_command_packet;
+
+ spin_lock(tw_dev->host->host_lock);
+
+ /* Read host interrupt status */
+ reg = readl(TWL_HISTAT_REG_ADDR(tw_dev));
+
+ /* Check if this is our interrupt, otherwise bail */
+ if (!(reg & TWL_HISTATUS_VALID_INTERRUPT))
+ goto twl_interrupt_bail;
+
+ handled = 1;
+
+ /* If we are resetting, bail */
+ if (test_bit(TW_IN_RESET, &tw_dev->flags))
+ goto twl_interrupt_bail;
+
+ /* Attention interrupt */
+ if (reg & TWL_HISTATUS_ATTENTION_INTERRUPT) {
+ if (twl_handle_attention_interrupt(tw_dev)) {
+ TWL_MASK_INTERRUPTS(tw_dev);
+ goto twl_interrupt_bail;
+ }
+ }
+
+ /* Response interrupt */
+ while (reg & TWL_HISTATUS_RESPONSE_INTERRUPT) {
+ if (sizeof(dma_addr_t) > 4) {
+ regh = readl(TWL_HOBQPH_REG_ADDR(tw_dev));
+ regl = readl(TWL_HOBQPL_REG_ADDR(tw_dev));
+ mfa = ((u64)regh << 32) | regl;
+ } else
+ mfa = readl(TWL_HOBQPL_REG_ADDR(tw_dev));
+
+ error = 0;
+ response = (u32)mfa;
+
+ /* Check for command packet error */
+ if (!TW_NOTMFA_OUT(response)) {
+ for (i=0;i<TW_Q_LENGTH;i++) {
+ if (tw_dev->sense_buffer_phys[i] == mfa) {
+ request_id = le16_to_cpu(tw_dev->sense_buffer_virt[i]->header_desc.request_id);
+ if (tw_dev->srb[request_id] != NULL)
+ error = twl_fill_sense(tw_dev, i, request_id, 1, 1);
+ else {
+ /* Skip ioctl error prints */
+ if (request_id != tw_dev->chrdev_request_id)
+ error = twl_fill_sense(tw_dev, i, request_id, 0, 1);
+ else
+ memcpy(tw_dev->command_packet_virt[request_id], tw_dev->sense_buffer_virt[i], sizeof(TW_Command_Apache_Header));
+ }
+
+ /* Now re-post the sense buffer */
+ writel((u32)((u64)tw_dev->sense_buffer_phys[i] >> 32), TWL_HOBQPH_REG_ADDR(tw_dev));
+ writel((u32)tw_dev->sense_buffer_phys[i], TWL_HOBQPL_REG_ADDR(tw_dev));
+ break;
+ }
+ }
+ } else
+ request_id = TW_RESID_OUT(response);
+
+ full_command_packet = tw_dev->command_packet_virt[request_id];
+
+ /* Check for correct state */
+ if (tw_dev->state[request_id] != TW_S_POSTED) {
+ if (tw_dev->srb[request_id] != NULL) {
+ TW_PRINTK(tw_dev->host, TW_DRIVER, 0xe, "Received a request id that wasn't posted");
+ TWL_MASK_INTERRUPTS(tw_dev);
+ goto twl_interrupt_bail;
+ }
+ }
+
+ /* Check for internal command completion */
+ if (tw_dev->srb[request_id] == NULL) {
+ if (request_id != tw_dev->chrdev_request_id) {
+ if (twl_aen_complete(tw_dev, request_id))
+ TW_PRINTK(tw_dev->host, TW_DRIVER, 0xf, "Error completing AEN during attention interrupt");
+ } else {
+ tw_dev->chrdev_request_id = TW_IOCTL_CHRDEV_FREE;
+ wake_up(&tw_dev->ioctl_wqueue);
+ }
+ } else {
+ cmd = tw_dev->srb[request_id];
+
+ if (!error)
+ cmd->result = (DID_OK << 16);
+
+ /* Report residual bytes for single sgl */
+ if ((scsi_sg_count(cmd) <= 1) && (full_command_packet->command.newcommand.status == 0)) {
+ if (full_command_packet->command.newcommand.sg_list[0].length < scsi_bufflen(tw_dev->srb[request_id]))
+ scsi_set_resid(cmd, scsi_bufflen(cmd) - full_command_packet->command.newcommand.sg_list[0].length);
+ }
+
+ /* Now complete the io */
+ tw_dev->state[request_id] = TW_S_COMPLETED;
+ twl_free_request_id(tw_dev, request_id);
+ tw_dev->posted_request_count--;
+ tw_dev->srb[request_id]->scsi_done(tw_dev->srb[request_id]);
+ twl_unmap_scsi_data(tw_dev, request_id);
+ }
+
+ /* Check for another response interrupt */
+ reg = readl(TWL_HISTAT_REG_ADDR(tw_dev));
+ }
+
+twl_interrupt_bail:
+ spin_unlock(tw_dev->host->host_lock);
+ return IRQ_RETVAL(handled);
+} /* End twl_interrupt() */
+
+/* This function will poll for a register change */
+static int twl_poll_register(TW_Device_Extension *tw_dev, void *reg, u32 value, u32 result, int seconds)
+{
+ unsigned long before;
+ int retval = 1;
+ u32 reg_value;
+
+ reg_value = readl(reg);
+ before = jiffies;
+
+ while ((reg_value & value) != result) {
+ reg_value = readl(reg);
+ if (time_after(jiffies, before + HZ * seconds))
+ goto out;
+ msleep(50);
+ }
+ retval = 0;
+out:
+ return retval;
+} /* End twl_poll_register() */
+
+/* This function will reset a controller */
+static int twl_reset_sequence(TW_Device_Extension *tw_dev, int soft_reset)
+{
+ int retval = 1;
+ int i = 0;
+ u32 status = 0;
+ unsigned short fw_on_ctlr_srl = 0, fw_on_ctlr_arch_id = 0;
+ unsigned short fw_on_ctlr_branch = 0, fw_on_ctlr_build = 0;
+ u32 init_connect_result = 0;
+ int tries = 0;
+ int do_soft_reset = soft_reset;
+
+ while (tries < TW_MAX_RESET_TRIES) {
+ /* Do a soft reset if one is needed */
+ if (do_soft_reset) {
+ TWL_SOFT_RESET(tw_dev);
+
+ /* Make sure controller is in a good state */
+ if (twl_poll_register(tw_dev, TWL_SCRPD3_REG_ADDR(tw_dev), TWL_CONTROLLER_READY, 0x0, 30)) {
+ TW_PRINTK(tw_dev->host, TW_DRIVER, 0x10, "Controller never went non-ready during reset sequence");
+ tries++;
+ continue;
+ }
+ if (twl_poll_register(tw_dev, TWL_SCRPD3_REG_ADDR(tw_dev), TWL_CONTROLLER_READY, TWL_CONTROLLER_READY, 60)) {
+ TW_PRINTK(tw_dev->host, TW_DRIVER, 0x11, "Controller not ready during reset sequence");
+ tries++;
+ continue;
+ }
+ }
+
+ /* Initconnect */
+ if (twl_initconnection(tw_dev, TW_INIT_MESSAGE_CREDITS,
+ TW_EXTENDED_INIT_CONNECT, TW_CURRENT_DRIVER_SRL,
+ TW_9750_ARCH_ID, TW_CURRENT_DRIVER_BRANCH,
+ TW_CURRENT_DRIVER_BUILD, &fw_on_ctlr_srl,
+ &fw_on_ctlr_arch_id, &fw_on_ctlr_branch,
+ &fw_on_ctlr_build, &init_connect_result)) {
+ TW_PRINTK(tw_dev->host, TW_DRIVER, 0x12, "Initconnection failed while checking SRL");
+ do_soft_reset = 1;
+ tries++;
+ continue;
+ }
+
+ /* Load sense buffers */
+ while (i < TW_Q_LENGTH) {
+ writel((u32)((u64)tw_dev->sense_buffer_phys[i] >> 32), TWL_HOBQPH_REG_ADDR(tw_dev));
+ writel((u32)tw_dev->sense_buffer_phys[i], TWL_HOBQPL_REG_ADDR(tw_dev));
+
+ /* Check status for over-run after each write */
+ status = readl(TWL_STATUS_REG_ADDR(tw_dev));
+ if (!(status & TWL_STATUS_OVERRUN_SUBMIT))
+ i++;
+ }
+
+ /* Now check status */
+ status = readl(TWL_STATUS_REG_ADDR(tw_dev));
+ if (status) {
+ TW_PRINTK(tw_dev->host, TW_DRIVER, 0x13, "Bad controller status after loading sense buffers");
+ do_soft_reset = 1;
+ tries++;
+ continue;
+ }
+
+ /* Drain the AEN queue */
+ if (twl_aen_drain_queue(tw_dev, soft_reset)) {
+ TW_PRINTK(tw_dev->host, TW_DRIVER, 0x14, "AEN drain failed during reset sequence");
+ do_soft_reset = 1;
+ tries++;
+ continue;
+ }
+
+ /* Load rest of compatibility struct */
+ strncpy(tw_dev->tw_compat_info.driver_version, TW_DRIVER_VERSION, strlen(TW_DRIVER_VERSION));
+ tw_dev->tw_compat_info.driver_srl_high = TW_CURRENT_DRIVER_SRL;
+ tw_dev->tw_compat_info.driver_branch_high = TW_CURRENT_DRIVER_BRANCH;
+ tw_dev->tw_compat_info.driver_build_high = TW_CURRENT_DRIVER_BUILD;
+ tw_dev->tw_compat_info.driver_srl_low = TW_BASE_FW_SRL;
+ tw_dev->tw_compat_info.driver_branch_low = TW_BASE_FW_BRANCH;
+ tw_dev->tw_compat_info.driver_build_low = TW_BASE_FW_BUILD;
+ tw_dev->tw_compat_info.fw_on_ctlr_srl = fw_on_ctlr_srl;
+ tw_dev->tw_compat_info.fw_on_ctlr_branch = fw_on_ctlr_branch;
+ tw_dev->tw_compat_info.fw_on_ctlr_build = fw_on_ctlr_build;
+
+ /* If we got here, controller is in a good state */
+ retval = 0;
+ goto out;
+ }
+out:
+ return retval;
+} /* End twl_reset_sequence() */
+
+/* This function will reset a device extension */
+static int twl_reset_device_extension(TW_Device_Extension *tw_dev, int ioctl_reset)
+{
+ int i = 0, retval = 1;
+ unsigned long flags = 0;
+
+ /* Block SCSI requests while we are resetting */
+ if (ioctl_reset)
+ scsi_block_requests(tw_dev->host);
+
+ set_bit(TW_IN_RESET, &tw_dev->flags);
+ TWL_MASK_INTERRUPTS(tw_dev);
+ TWL_CLEAR_DB_INTERRUPT(tw_dev);
+
+ spin_lock_irqsave(tw_dev->host->host_lock, flags);
+
+ /* Abort all requests that are in progress */
+ for (i = 0; i < TW_Q_LENGTH; i++) {
+ if ((tw_dev->state[i] != TW_S_FINISHED) &&
+ (tw_dev->state[i] != TW_S_INITIAL) &&
+ (tw_dev->state[i] != TW_S_COMPLETED)) {
+ if (tw_dev->srb[i]) {
+ tw_dev->srb[i]->result = (DID_RESET << 16);
+ tw_dev->srb[i]->scsi_done(tw_dev->srb[i]);
+ twl_unmap_scsi_data(tw_dev, i);
+ }
+ }
+ }
+
+ /* Reset queues and counts */
+ for (i = 0; i < TW_Q_LENGTH; i++) {
+ tw_dev->free_queue[i] = i;
+ tw_dev->state[i] = TW_S_INITIAL;
+ }
+ tw_dev->free_head = TW_Q_START;
+ tw_dev->free_tail = TW_Q_START;
+ tw_dev->posted_request_count = 0;
+
+ spin_unlock_irqrestore(tw_dev->host->host_lock, flags);
+
+ if (twl_reset_sequence(tw_dev, 1))
+ goto out;
+
+ TWL_UNMASK_INTERRUPTS(tw_dev);
+
+ clear_bit(TW_IN_RESET, &tw_dev->flags);
+ tw_dev->chrdev_request_id = TW_IOCTL_CHRDEV_FREE;
+
+ retval = 0;
+out:
+ if (ioctl_reset)
+ scsi_unblock_requests(tw_dev->host);
+ return retval;
+} /* End twl_reset_device_extension() */
+
+/* This funciton returns unit geometry in cylinders/heads/sectors */
+static int twl_scsi_biosparam(struct scsi_device *sdev, struct block_device *bdev, sector_t capacity, int geom[])
+{
+ int heads, sectors;
+ TW_Device_Extension *tw_dev;
+
+ tw_dev = (TW_Device_Extension *)sdev->host->hostdata;
+
+ if (capacity >= 0x200000) {
+ heads = 255;
+ sectors = 63;
+ } else {
+ heads = 64;
+ sectors = 32;
+ }
+
+ geom[0] = heads;
+ geom[1] = sectors;
+ geom[2] = sector_div(capacity, heads * sectors); /* cylinders */
+
+ return 0;
+} /* End twl_scsi_biosparam() */
+
+/* This is the new scsi eh reset function */
+static int twl_scsi_eh_reset(struct scsi_cmnd *SCpnt)
+{
+ TW_Device_Extension *tw_dev = NULL;
+ int retval = FAILED;
+
+ tw_dev = (TW_Device_Extension *)SCpnt->device->host->hostdata;
+
+ tw_dev->num_resets++;
+
+ sdev_printk(KERN_WARNING, SCpnt->device,
+ "WARNING: (0x%02X:0x%04X): Command (0x%x) timed out, resetting card.\n",
+ TW_DRIVER, 0x2c, SCpnt->cmnd[0]);
+
+ /* Make sure we are not issuing an ioctl or resetting from ioctl */
+ mutex_lock(&tw_dev->ioctl_lock);
+
+ /* Now reset the card and some of the device extension data */
+ if (twl_reset_device_extension(tw_dev, 0)) {
+ TW_PRINTK(tw_dev->host, TW_DRIVER, 0x15, "Controller reset failed during scsi host reset");
+ goto out;
+ }
+
+ retval = SUCCESS;
+out:
+ mutex_unlock(&tw_dev->ioctl_lock);
+ return retval;
+} /* End twl_scsi_eh_reset() */
+
+/* This is the main scsi queue function to handle scsi opcodes */
+static int twl_scsi_queue(struct scsi_cmnd *SCpnt, void (*done)(struct scsi_cmnd *))
+{
+ int request_id, retval;
+ TW_Device_Extension *tw_dev = (TW_Device_Extension *)SCpnt->device->host->hostdata;
+
+ /* If we are resetting due to timed out ioctl, report as busy */
+ if (test_bit(TW_IN_RESET, &tw_dev->flags)) {
+ retval = SCSI_MLQUEUE_HOST_BUSY;
+ goto out;
+ }
+
+ /* Save done function into scsi_cmnd struct */
+ SCpnt->scsi_done = done;
+
+ /* Get a free request id */
+ twl_get_request_id(tw_dev, &request_id);
+
+ /* Save the scsi command for use by the ISR */
+ tw_dev->srb[request_id] = SCpnt;
+
+ /* Initialize phase to zero */
+ SCpnt->SCp.phase = TW_PHASE_INITIAL;
+
+ retval = twl_scsiop_execute_scsi(tw_dev, request_id, NULL, 0, NULL);
+ if (retval) {
+ tw_dev->state[request_id] = TW_S_COMPLETED;
+ twl_free_request_id(tw_dev, request_id);
+ SCpnt->result = (DID_ERROR << 16);
+ done(SCpnt);
+ retval = 0;
+ }
+out:
+ return retval;
+} /* End twl_scsi_queue() */
+
+/* This function tells the controller to shut down */
+static void __twl_shutdown(TW_Device_Extension *tw_dev)
+{
+ /* Disable interrupts */
+ TWL_MASK_INTERRUPTS(tw_dev);
+
+ /* Free up the IRQ */
+ free_irq(tw_dev->tw_pci_dev->irq, tw_dev);
+
+ printk(KERN_WARNING "3w-sas: Shutting down host %d.\n", tw_dev->host->host_no);
+
+ /* Tell the card we are shutting down */
+ if (twl_initconnection(tw_dev, 1, 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL, NULL)) {
+ TW_PRINTK(tw_dev->host, TW_DRIVER, 0x16, "Connection shutdown failed");
+ } else {
+ printk(KERN_WARNING "3w-sas: Shutdown complete.\n");
+ }
+
+ /* Clear doorbell interrupt just before exit */
+ TWL_CLEAR_DB_INTERRUPT(tw_dev);
+} /* End __twl_shutdown() */
+
+/* Wrapper for __twl_shutdown */
+static void twl_shutdown(struct pci_dev *pdev)
+{
+ struct Scsi_Host *host = pci_get_drvdata(pdev);
+ TW_Device_Extension *tw_dev;
+
+ if (!host)
+ return;
+
+ tw_dev = (TW_Device_Extension *)host->hostdata;
+
+ if (tw_dev->online)
+ __twl_shutdown(tw_dev);
+} /* End twl_shutdown() */
+
+/* This function configures unit settings when a unit is coming on-line */
+static int twl_slave_configure(struct scsi_device *sdev)
+{
+ /* Force 60 second timeout */
+ blk_queue_rq_timeout(sdev->request_queue, 60 * HZ);
+
+ return 0;
+} /* End twl_slave_configure() */
+
+/* scsi_host_template initializer */
+static struct scsi_host_template driver_template = {
+ .module = THIS_MODULE,
+ .name = "3w-sas",
+ .queuecommand = twl_scsi_queue,
+ .eh_host_reset_handler = twl_scsi_eh_reset,
+ .bios_param = twl_scsi_biosparam,
+ .change_queue_depth = twl_change_queue_depth,
+ .can_queue = TW_Q_LENGTH-2,
+ .slave_configure = twl_slave_configure,
+ .this_id = -1,
+ .sg_tablesize = TW_LIBERATOR_MAX_SGL_LENGTH,
+ .max_sectors = TW_MAX_SECTORS,
+ .cmd_per_lun = TW_MAX_CMDS_PER_LUN,
+ .use_clustering = ENABLE_CLUSTERING,
+ .shost_attrs = twl_host_attrs,
+ .emulated = 1
+};
+
+/* This function will probe and initialize a card */
+static int __devinit twl_probe(struct pci_dev *pdev, const struct pci_device_id *dev_id)
+{
+ struct Scsi_Host *host = NULL;
+ TW_Device_Extension *tw_dev;
+ int retval = -ENODEV;
+ int *ptr_phycount, phycount=0;
+
+ retval = pci_enable_device(pdev);
+ if (retval) {
+ TW_PRINTK(host, TW_DRIVER, 0x17, "Failed to enable pci device");
+ goto out_disable_device;
+ }
+
+ pci_set_master(pdev);
+ pci_try_set_mwi(pdev);
+
+ if (pci_set_dma_mask(pdev, DMA_BIT_MASK(64))
+ || pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(64)))
+ if (pci_set_dma_mask(pdev, DMA_BIT_MASK(32))
+ || pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(32))) {
+ TW_PRINTK(host, TW_DRIVER, 0x18, "Failed to set dma mask");
+ retval = -ENODEV;
+ goto out_disable_device;
+ }
+
+ host = scsi_host_alloc(&driver_template, sizeof(TW_Device_Extension));
+ if (!host) {
+ TW_PRINTK(host, TW_DRIVER, 0x19, "Failed to allocate memory for device extension");
+ retval = -ENOMEM;
+ goto out_disable_device;
+ }
+ tw_dev = shost_priv(host);
+
+ /* Save values to device extension */
+ tw_dev->host = host;
+ tw_dev->tw_pci_dev = pdev;
+
+ if (twl_initialize_device_extension(tw_dev)) {
+ TW_PRINTK(tw_dev->host, TW_DRIVER, 0x1a, "Failed to initialize device extension");
+ goto out_free_device_extension;
+ }
+
+ /* Request IO regions */
+ retval = pci_request_regions(pdev, "3w-sas");
+ if (retval) {
+ TW_PRINTK(tw_dev->host, TW_DRIVER, 0x1b, "Failed to get mem region");
+ goto out_free_device_extension;
+ }
+
+ /* Save base address, use region 1 */
+ tw_dev->base_addr = pci_iomap(pdev, 1, 0);
+ if (!tw_dev->base_addr) {
+ TW_PRINTK(tw_dev->host, TW_DRIVER, 0x1c, "Failed to ioremap");
+ goto out_release_mem_region;
+ }
+
+ /* Disable interrupts on the card */
+ TWL_MASK_INTERRUPTS(tw_dev);
+
+ /* Initialize the card */
+ if (twl_reset_sequence(tw_dev, 0)) {
+ TW_PRINTK(tw_dev->host, TW_DRIVER, 0x1d, "Controller reset failed during probe");
+ goto out_iounmap;
+ }
+
+ /* Set host specific parameters */
+ host->max_id = TW_MAX_UNITS;
+ host->max_cmd_len = TW_MAX_CDB_LEN;
+ host->max_lun = TW_MAX_LUNS;
+ host->max_channel = 0;
+
+ /* Register the card with the kernel SCSI layer */
+ retval = scsi_add_host(host, &pdev->dev);
+ if (retval) {
+ TW_PRINTK(tw_dev->host, TW_DRIVER, 0x1e, "scsi add host failed");
+ goto out_iounmap;
+ }
+
+ pci_set_drvdata(pdev, host);
+
+ printk(KERN_WARNING "3w-sas: scsi%d: Found an LSI 3ware %s Controller at 0x%llx, IRQ: %d.\n",
+ host->host_no,
+ (char *)twl_get_param(tw_dev, 1, TW_VERSION_TABLE,
+ TW_PARAM_MODEL, TW_PARAM_MODEL_LENGTH),
+ (u64)pci_resource_start(pdev, 1), pdev->irq);
+
+ ptr_phycount = twl_get_param(tw_dev, 2, TW_PARAM_PHY_SUMMARY_TABLE,
+ TW_PARAM_PHYCOUNT, TW_PARAM_PHYCOUNT_LENGTH);
+ if (ptr_phycount)
+ phycount = le32_to_cpu(*(int *)ptr_phycount);
+
+ printk(KERN_WARNING "3w-sas: scsi%d: Firmware %s, BIOS %s, Phys: %d.\n",
+ host->host_no,
+ (char *)twl_get_param(tw_dev, 1, TW_VERSION_TABLE,
+ TW_PARAM_FWVER, TW_PARAM_FWVER_LENGTH),
+ (char *)twl_get_param(tw_dev, 2, TW_VERSION_TABLE,
+ TW_PARAM_BIOSVER, TW_PARAM_BIOSVER_LENGTH),
+ phycount);
+
+ /* Try to enable MSI */
+ if (use_msi && !pci_enable_msi(pdev))
+ set_bit(TW_USING_MSI, &tw_dev->flags);
+
+ /* Now setup the interrupt handler */
+ retval = request_irq(pdev->irq, twl_interrupt, IRQF_SHARED, "3w-sas", tw_dev);
+ if (retval) {
+ TW_PRINTK(tw_dev->host, TW_DRIVER, 0x1f, "Error requesting IRQ");
+ goto out_remove_host;
+ }
+
+ twl_device_extension_list[twl_device_extension_count] = tw_dev;
+ twl_device_extension_count++;
+
+ /* Re-enable interrupts on the card */
+ TWL_UNMASK_INTERRUPTS(tw_dev);
+
+ /* Finally, scan the host */
+ scsi_scan_host(host);
+
+ /* Add sysfs binary files */
+ if (sysfs_create_bin_file(&host->shost_dev.kobj, &twl_sysfs_aen_read_attr))
+ TW_PRINTK(tw_dev->host, TW_DRIVER, 0x20, "Failed to create sysfs binary file: 3ware_aen_read");
+ if (sysfs_create_bin_file(&host->shost_dev.kobj, &twl_sysfs_compat_info_attr))
+ TW_PRINTK(tw_dev->host, TW_DRIVER, 0x21, "Failed to create sysfs binary file: 3ware_compat_info");
+
+ if (twl_major == -1) {
+ if ((twl_major = register_chrdev (0, "twl", &twl_fops)) < 0)
+ TW_PRINTK(host, TW_DRIVER, 0x22, "Failed to register character device");
+ }
+ tw_dev->online = 1;
+ return 0;
+
+out_remove_host:
+ if (test_bit(TW_USING_MSI, &tw_dev->flags))
+ pci_disable_msi(pdev);
+ scsi_remove_host(host);
+out_iounmap:
+ iounmap(tw_dev->base_addr);
+out_release_mem_region:
+ pci_release_regions(pdev);
+out_free_device_extension:
+ twl_free_device_extension(tw_dev);
+ scsi_host_put(host);
+out_disable_device:
+ pci_disable_device(pdev);
+
+ return retval;
+} /* End twl_probe() */
+
+/* This function is called to remove a device */
+static void twl_remove(struct pci_dev *pdev)
+{
+ struct Scsi_Host *host = pci_get_drvdata(pdev);
+ TW_Device_Extension *tw_dev;
+
+ if (!host)
+ return;
+
+ tw_dev = (TW_Device_Extension *)host->hostdata;
+
+ if (!tw_dev->online)
+ return;
+
+ /* Remove sysfs binary files */
+ sysfs_remove_bin_file(&host->shost_dev.kobj, &twl_sysfs_aen_read_attr);
+ sysfs_remove_bin_file(&host->shost_dev.kobj, &twl_sysfs_compat_info_attr);
+
+ scsi_remove_host(tw_dev->host);
+
+ /* Unregister character device */
+ if (twl_major >= 0) {
+ unregister_chrdev(twl_major, "twl");
+ twl_major = -1;
+ }
+
+ /* Shutdown the card */
+ __twl_shutdown(tw_dev);
+
+ /* Disable MSI if enabled */
+ if (test_bit(TW_USING_MSI, &tw_dev->flags))
+ pci_disable_msi(pdev);
+
+ /* Free IO remapping */
+ iounmap(tw_dev->base_addr);
+
+ /* Free up the mem region */
+ pci_release_regions(pdev);
+
+ /* Free up device extension resources */
+ twl_free_device_extension(tw_dev);
+
+ scsi_host_put(tw_dev->host);
+ pci_disable_device(pdev);
+ twl_device_extension_count--;
+} /* End twl_remove() */
+
+#ifdef CONFIG_PM
+/* This function is called on PCI suspend */
+static int twl_suspend(struct pci_dev *pdev, pm_message_t state)
+{
+ struct Scsi_Host *host = pci_get_drvdata(pdev);
+ TW_Device_Extension *tw_dev = (TW_Device_Extension *)host->hostdata;
+
+ printk(KERN_WARNING "3w-sas: Suspending host %d.\n", tw_dev->host->host_no);
+ /* Disable interrupts */
+ TWL_MASK_INTERRUPTS(tw_dev);
+
+ free_irq(tw_dev->tw_pci_dev->irq, tw_dev);
+
+ /* Tell the card we are shutting down */
+ if (twl_initconnection(tw_dev, 1, 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL, NULL)) {
+ TW_PRINTK(tw_dev->host, TW_DRIVER, 0x23, "Connection shutdown failed during suspend");
+ } else {
+ printk(KERN_WARNING "3w-sas: Suspend complete.\n");
+ }
+
+ /* Clear doorbell interrupt */
+ TWL_CLEAR_DB_INTERRUPT(tw_dev);
+
+ pci_save_state(pdev);
+ pci_disable_device(pdev);
+ pci_set_power_state(pdev, pci_choose_state(pdev, state));
+
+ return 0;
+} /* End twl_suspend() */
+
+/* This function is called on PCI resume */
+static int twl_resume(struct pci_dev *pdev)
+{
+ int retval = 0;
+ struct Scsi_Host *host = pci_get_drvdata(pdev);
+ TW_Device_Extension *tw_dev = (TW_Device_Extension *)host->hostdata;
+
+ printk(KERN_WARNING "3w-sas: Resuming host %d.\n", tw_dev->host->host_no);
+ pci_set_power_state(pdev, PCI_D0);
+ pci_enable_wake(pdev, PCI_D0, 0);
+ pci_restore_state(pdev);
+
+ retval = pci_enable_device(pdev);
+ if (retval) {
+ TW_PRINTK(tw_dev->host, TW_DRIVER, 0x24, "Enable device failed during resume");
+ return retval;
+ }
+
+ pci_set_master(pdev);
+ pci_try_set_mwi(pdev);
+
+ if (pci_set_dma_mask(pdev, DMA_BIT_MASK(64))
+ || pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(64)))
+ if (pci_set_dma_mask(pdev, DMA_BIT_MASK(32))
+ || pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(32))) {
+ TW_PRINTK(host, TW_DRIVER, 0x25, "Failed to set dma mask during resume");
+ retval = -ENODEV;
+ goto out_disable_device;
+ }
+
+ /* Initialize the card */
+ if (twl_reset_sequence(tw_dev, 0)) {
+ retval = -ENODEV;
+ goto out_disable_device;
+ }
+
+ /* Now setup the interrupt handler */
+ retval = request_irq(pdev->irq, twl_interrupt, IRQF_SHARED, "3w-sas", tw_dev);
+ if (retval) {
+ TW_PRINTK(tw_dev->host, TW_DRIVER, 0x26, "Error requesting IRQ during resume");
+ retval = -ENODEV;
+ goto out_disable_device;
+ }
+
+ /* Now enable MSI if enabled */
+ if (test_bit(TW_USING_MSI, &tw_dev->flags))
+ pci_enable_msi(pdev);
+
+ /* Re-enable interrupts on the card */
+ TWL_UNMASK_INTERRUPTS(tw_dev);
+
+ printk(KERN_WARNING "3w-sas: Resume complete.\n");
+ return 0;
+
+out_disable_device:
+ scsi_remove_host(host);
+ pci_disable_device(pdev);
+
+ return retval;
+} /* End twl_resume() */
+#endif
+
+/* PCI Devices supported by this driver */
+static struct pci_device_id twl_pci_tbl[] __devinitdata = {
+ { PCI_VDEVICE(3WARE, PCI_DEVICE_ID_3WARE_9750) },
+ { }
+};
+MODULE_DEVICE_TABLE(pci, twl_pci_tbl);
+
+/* pci_driver initializer */
+static struct pci_driver twl_driver = {
+ .name = "3w-sas",
+ .id_table = twl_pci_tbl,
+ .probe = twl_probe,
+ .remove = twl_remove,
+#ifdef CONFIG_PM
+ .suspend = twl_suspend,
+ .resume = twl_resume,
+#endif
+ .shutdown = twl_shutdown
+};
+
+/* This function is called on driver initialization */
+static int __init twl_init(void)
+{
+ printk(KERN_INFO "LSI 3ware SAS/SATA-RAID Controller device driver for Linux v%s.\n", TW_DRIVER_VERSION);
+
+ return pci_register_driver(&twl_driver);
+} /* End twl_init() */
+
+/* This function is called on driver exit */
+static void __exit twl_exit(void)
+{
+ pci_unregister_driver(&twl_driver);
+} /* End twl_exit() */
+
+module_init(twl_init);
+module_exit(twl_exit);
+
diff --git a/drivers/scsi/3w-sas.h b/drivers/scsi/3w-sas.h
new file mode 100644
index 00000000000..d474892701d
--- /dev/null
+++ b/drivers/scsi/3w-sas.h
@@ -0,0 +1,396 @@
+/*
+ 3w-sas.h -- LSI 3ware SAS/SATA-RAID Controller device driver for Linux.
+
+ Written By: Adam Radford <linuxraid@lsi.com>
+
+ Copyright (C) 2009 LSI Corporation.
+
+ This program is free software; you can 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.
+
+ This program is distributed in the hope that 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.
+
+ NO WARRANTY
+ THE PROGRAM IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR
+ CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT
+ LIMITATION, ANY WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT,
+ MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Each Recipient is
+ solely responsible for determining the appropriateness of using and
+ distributing the Program and assumes all risks associated with its
+ exercise of rights under this Agreement, including but not limited to
+ the risks and costs of program errors, damage to or loss of data,
+ programs or equipment, and unavailability or interruption of operations.
+
+ DISCLAIMER OF LIABILITY
+ NEITHER RECIPIENT NOR ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY
+ DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ DAMAGES (INCLUDING WITHOUT LIMITATION LOST PROFITS), HOWEVER CAUSED AND
+ ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
+ TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+ USE OR DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED
+ HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES
+
+ 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+ Bugs/Comments/Suggestions should be mailed to:
+ linuxraid@lsi.com
+
+ For more information, goto:
+ http://www.lsi.com
+*/
+
+#ifndef _3W_SAS_H
+#define _3W_SAS_H
+
+/* AEN severity table */
+static char *twl_aen_severity_table[] =
+{
+ "None", "ERROR", "WARNING", "INFO", "DEBUG", NULL
+};
+
+/* Liberator register offsets */
+#define TWL_STATUS 0x0 /* Status */
+#define TWL_HIBDB 0x20 /* Inbound doorbell */
+#define TWL_HISTAT 0x30 /* Host interrupt status */
+#define TWL_HIMASK 0x34 /* Host interrupt mask */
+#define TWL_HOBDB 0x9C /* Outbound doorbell */
+#define TWL_HOBDBC 0xA0 /* Outbound doorbell clear */
+#define TWL_SCRPD3 0xBC /* Scratchpad */
+#define TWL_HIBQPL 0xC0 /* Host inbound Q low */
+#define TWL_HIBQPH 0xC4 /* Host inbound Q high */
+#define TWL_HOBQPL 0xC8 /* Host outbound Q low */
+#define TWL_HOBQPH 0xCC /* Host outbound Q high */
+#define TWL_HISTATUS_VALID_INTERRUPT 0xC
+#define TWL_HISTATUS_ATTENTION_INTERRUPT 0x4
+#define TWL_HISTATUS_RESPONSE_INTERRUPT 0x8
+#define TWL_STATUS_OVERRUN_SUBMIT 0x2000
+#define TWL_ISSUE_SOFT_RESET 0x100
+#define TWL_CONTROLLER_READY 0x2000
+#define TWL_DOORBELL_CONTROLLER_ERROR 0x200000
+#define TWL_DOORBELL_ATTENTION_INTERRUPT 0x40000
+#define TWL_PULL_MODE 0x1
+
+/* Command packet opcodes used by the driver */
+#define TW_OP_INIT_CONNECTION 0x1
+#define TW_OP_GET_PARAM 0x12
+#define TW_OP_SET_PARAM 0x13
+#define TW_OP_EXECUTE_SCSI 0x10
+
+/* Asynchronous Event Notification (AEN) codes used by the driver */
+#define TW_AEN_QUEUE_EMPTY 0x0000
+#define TW_AEN_SOFT_RESET 0x0001
+#define TW_AEN_SYNC_TIME_WITH_HOST 0x031
+#define TW_AEN_SEVERITY_ERROR 0x1
+#define TW_AEN_SEVERITY_DEBUG 0x4
+#define TW_AEN_NOT_RETRIEVED 0x1
+
+/* Command state defines */
+#define TW_S_INITIAL 0x1 /* Initial state */
+#define TW_S_STARTED 0x2 /* Id in use */
+#define TW_S_POSTED 0x4 /* Posted to the controller */
+#define TW_S_COMPLETED 0x8 /* Completed by isr */
+#define TW_S_FINISHED 0x10 /* I/O completely done */
+
+/* Compatibility defines */
+#define TW_9750_ARCH_ID 10
+#define TW_CURRENT_DRIVER_SRL 40
+#define TW_CURRENT_DRIVER_BUILD 0
+#define TW_CURRENT_DRIVER_BRANCH 0
+
+/* Phase defines */
+#define TW_PHASE_INITIAL 0
+#define TW_PHASE_SGLIST 2
+
+/* Misc defines */
+#define TW_SECTOR_SIZE 512
+#define TW_MAX_UNITS 32
+#define TW_INIT_MESSAGE_CREDITS 0x100
+#define TW_INIT_COMMAND_PACKET_SIZE 0x3
+#define TW_INIT_COMMAND_PACKET_SIZE_EXTENDED 0x6
+#define TW_EXTENDED_INIT_CONNECT 0x2
+#define TW_BASE_FW_SRL 24
+#define TW_BASE_FW_BRANCH 0
+#define TW_BASE_FW_BUILD 1
+#define TW_Q_LENGTH 256
+#define TW_Q_START 0
+#define TW_MAX_SLOT 32
+#define TW_MAX_RESET_TRIES 2
+#define TW_MAX_CMDS_PER_LUN 254
+#define TW_MAX_AEN_DRAIN 255
+#define TW_IN_RESET 2
+#define TW_USING_MSI 3
+#define TW_IN_ATTENTION_LOOP 4
+#define TW_MAX_SECTORS 256
+#define TW_MAX_CDB_LEN 16
+#define TW_IOCTL_CHRDEV_TIMEOUT 60 /* 60 seconds */
+#define TW_IOCTL_CHRDEV_FREE -1
+#define TW_COMMAND_OFFSET 128 /* 128 bytes */
+#define TW_VERSION_TABLE 0x0402
+#define TW_TIMEKEEP_TABLE 0x040A
+#define TW_INFORMATION_TABLE 0x0403
+#define TW_PARAM_FWVER 3
+#define TW_PARAM_FWVER_LENGTH 16
+#define TW_PARAM_BIOSVER 4
+#define TW_PARAM_BIOSVER_LENGTH 16
+#define TW_PARAM_MODEL 8
+#define TW_PARAM_MODEL_LENGTH 16
+#define TW_PARAM_PHY_SUMMARY_TABLE 1
+#define TW_PARAM_PHYCOUNT 2
+#define TW_PARAM_PHYCOUNT_LENGTH 1
+#define TW_IOCTL_FIRMWARE_PASS_THROUGH 0x108 // Used by smartmontools
+#define TW_ALLOCATION_LENGTH 128
+#define TW_SENSE_DATA_LENGTH 18
+#define TW_ERROR_LOGICAL_UNIT_NOT_SUPPORTED 0x10a
+#define TW_ERROR_INVALID_FIELD_IN_CDB 0x10d
+#define TW_ERROR_UNIT_OFFLINE 0x128
+#define TW_MESSAGE_SOURCE_CONTROLLER_ERROR 3
+#define TW_MESSAGE_SOURCE_CONTROLLER_EVENT 4
+#define TW_DRIVER 6
+#ifndef PCI_DEVICE_ID_3WARE_9750
+#define PCI_DEVICE_ID_3WARE_9750 0x1010
+#endif
+
+/* Bitmask macros to eliminate bitfields */
+
+/* opcode: 5, reserved: 3 */
+#define TW_OPRES_IN(x,y) ((x << 5) | (y & 0x1f))
+#define TW_OP_OUT(x) (x & 0x1f)
+
+/* opcode: 5, sgloffset: 3 */
+#define TW_OPSGL_IN(x,y) ((x << 5) | (y & 0x1f))
+#define TW_SGL_OUT(x) ((x >> 5) & 0x7)
+
+/* severity: 3, reserved: 5 */
+#define TW_SEV_OUT(x) (x & 0x7)
+
+/* not_mfa: 1, reserved: 7, status: 8, request_id: 16 */
+#define TW_RESID_OUT(x) ((x >> 16) & 0xffff)
+#define TW_NOTMFA_OUT(x) (x & 0x1)
+
+/* request_id: 12, lun: 4 */
+#define TW_REQ_LUN_IN(lun, request_id) (((lun << 12) & 0xf000) | (request_id & 0xfff))
+#define TW_LUN_OUT(lun) ((lun >> 12) & 0xf)
+
+/* Register access macros */
+#define TWL_STATUS_REG_ADDR(x) ((unsigned char __iomem *)x->base_addr + TWL_STATUS)
+#define TWL_HOBQPL_REG_ADDR(x) ((unsigned char __iomem *)x->base_addr + TWL_HOBQPL)
+#define TWL_HOBQPH_REG_ADDR(x) ((unsigned char __iomem *)x->base_addr + TWL_HOBQPH)
+#define TWL_HOBDB_REG_ADDR(x) ((unsigned char __iomem *)x->base_addr + TWL_HOBDB)
+#define TWL_HOBDBC_REG_ADDR(x) ((unsigned char __iomem *)x->base_addr + TWL_HOBDBC)
+#define TWL_HIMASK_REG_ADDR(x) ((unsigned char __iomem *)x->base_addr + TWL_HIMASK)
+#define TWL_HISTAT_REG_ADDR(x) ((unsigned char __iomem *)x->base_addr + TWL_HISTAT)
+#define TWL_HIBQPH_REG_ADDR(x) ((unsigned char __iomem *)x->base_addr + TWL_HIBQPH)
+#define TWL_HIBQPL_REG_ADDR(x) ((unsigned char __iomem *)x->base_addr + TWL_HIBQPL)
+#define TWL_HIBDB_REG_ADDR(x) ((unsigned char __iomem *)x->base_addr + TWL_HIBDB)
+#define TWL_SCRPD3_REG_ADDR(x) ((unsigned char __iomem *)x->base_addr + TWL_SCRPD3)
+#define TWL_MASK_INTERRUPTS(x) (writel(~0, TWL_HIMASK_REG_ADDR(tw_dev)))
+#define TWL_UNMASK_INTERRUPTS(x) (writel(~TWL_HISTATUS_VALID_INTERRUPT, TWL_HIMASK_REG_ADDR(tw_dev)))
+#define TWL_CLEAR_DB_INTERRUPT(x) (writel(~0, TWL_HOBDBC_REG_ADDR(tw_dev)))
+#define TWL_SOFT_RESET(x) (writel(TWL_ISSUE_SOFT_RESET, TWL_HIBDB_REG_ADDR(tw_dev)))
+
+/* Macros */
+#define TW_PRINTK(h,a,b,c) { \
+if (h) \
+printk(KERN_WARNING "3w-sas: scsi%d: ERROR: (0x%02X:0x%04X): %s.\n",h->host_no,a,b,c); \
+else \
+printk(KERN_WARNING "3w-sas: ERROR: (0x%02X:0x%04X): %s.\n",a,b,c); \
+}
+#define TW_MAX_LUNS 16
+#define TW_COMMAND_SIZE (sizeof(dma_addr_t) > 4 ? 6 : 4)
+#define TW_LIBERATOR_MAX_SGL_LENGTH (sizeof(dma_addr_t) > 4 ? 46 : 92)
+#define TW_LIBERATOR_MAX_SGL_LENGTH_OLD (sizeof(dma_addr_t) > 4 ? 47 : 94)
+#define TW_PADDING_LENGTH_LIBERATOR 136
+#define TW_PADDING_LENGTH_LIBERATOR_OLD 132
+#define TW_CPU_TO_SGL(x) (sizeof(dma_addr_t) > 4 ? cpu_to_le64(x) : cpu_to_le32(x))
+
+#pragma pack(1)
+
+/* SGL entry */
+typedef struct TAG_TW_SG_Entry_ISO {
+ dma_addr_t address;
+ dma_addr_t length;
+} TW_SG_Entry_ISO;
+
+/* Old Command Packet with ISO SGL */
+typedef struct TW_Command {
+ unsigned char opcode__sgloffset;
+ unsigned char size;
+ unsigned char request_id;
+ unsigned char unit__hostid;
+ /* Second DWORD */
+ unsigned char status;
+ unsigned char flags;
+ union {
+ unsigned short block_count;
+ unsigned short parameter_count;
+ } byte6_offset;
+ union {
+ struct {
+ u32 lba;
+ TW_SG_Entry_ISO sgl[TW_LIBERATOR_MAX_SGL_LENGTH_OLD];
+ unsigned char padding[TW_PADDING_LENGTH_LIBERATOR_OLD];
+ } io;
+ struct {
+ TW_SG_Entry_ISO sgl[TW_LIBERATOR_MAX_SGL_LENGTH_OLD];
+ u32 padding;
+ unsigned char padding2[TW_PADDING_LENGTH_LIBERATOR_OLD];
+ } param;
+ } byte8_offset;
+} TW_Command;
+
+/* New Command Packet with ISO SGL */
+typedef struct TAG_TW_Command_Apache {
+ unsigned char opcode__reserved;
+ unsigned char unit;
+ unsigned short request_id__lunl;
+ unsigned char status;
+ unsigned char sgl_offset;
+ unsigned short sgl_entries__lunh;
+ unsigned char cdb[16];
+ TW_SG_Entry_ISO sg_list[TW_LIBERATOR_MAX_SGL_LENGTH];
+ unsigned char padding[TW_PADDING_LENGTH_LIBERATOR];
+} TW_Command_Apache;
+
+/* New command packet header */
+typedef struct TAG_TW_Command_Apache_Header {
+ unsigned char sense_data[TW_SENSE_DATA_LENGTH];
+ struct {
+ char reserved[4];
+ unsigned short error;
+ unsigned char padding;
+ unsigned char severity__reserved;
+ } status_block;
+ unsigned char err_specific_desc[98];
+ struct {
+ unsigned char size_header;
+ unsigned short request_id;
+ unsigned char size_sense;
+ } header_desc;
+} TW_Command_Apache_Header;
+
+/* This struct is a union of the 2 command packets */
+typedef struct TAG_TW_Command_Full {
+ TW_Command_Apache_Header header;
+ union {
+ TW_Command oldcommand;
+ TW_Command_Apache newcommand;
+ } command;
+} TW_Command_Full;
+
+/* Initconnection structure */
+typedef struct TAG_TW_Initconnect {
+ unsigned char opcode__reserved;
+ unsigned char size;
+ unsigned char request_id;
+ unsigned char res2;
+ unsigned char status;
+ unsigned char flags;
+ unsigned short message_credits;
+ u32 features;
+ unsigned short fw_srl;
+ unsigned short fw_arch_id;
+ unsigned short fw_branch;
+ unsigned short fw_build;
+ u32 result;
+} TW_Initconnect;
+
+/* Event info structure */
+typedef struct TAG_TW_Event
+{
+ unsigned int sequence_id;
+ unsigned int time_stamp_sec;
+ unsigned short aen_code;
+ unsigned char severity;
+ unsigned char retrieved;
+ unsigned char repeat_count;
+ unsigned char parameter_len;
+ unsigned char parameter_data[98];
+} TW_Event;
+
+typedef struct TAG_TW_Ioctl_Driver_Command {
+ unsigned int control_code;
+ unsigned int status;
+ unsigned int unique_id;
+ unsigned int sequence_id;
+ unsigned int os_specific;
+ unsigned int buffer_length;
+} TW_Ioctl_Driver_Command;
+
+typedef struct TAG_TW_Ioctl_Apache {
+ TW_Ioctl_Driver_Command driver_command;
+ char padding[488];
+ TW_Command_Full firmware_command;
+ char data_buffer[1];
+} TW_Ioctl_Buf_Apache;
+
+/* GetParam descriptor */
+typedef struct {
+ unsigned short table_id;
+ unsigned short parameter_id;
+ unsigned short parameter_size_bytes;
+ unsigned short actual_parameter_size_bytes;
+ unsigned char data[1];
+} TW_Param_Apache;
+
+/* Compatibility information structure */
+typedef struct TAG_TW_Compatibility_Info
+{
+ char driver_version[32];
+ unsigned short working_srl;
+ unsigned short working_branch;
+ unsigned short working_build;
+ unsigned short driver_srl_high;
+ unsigned short driver_branch_high;
+ unsigned short driver_build_high;
+ unsigned short driver_srl_low;
+ unsigned short driver_branch_low;
+ unsigned short driver_build_low;
+ unsigned short fw_on_ctlr_srl;
+ unsigned short fw_on_ctlr_branch;
+ unsigned short fw_on_ctlr_build;
+} TW_Compatibility_Info;
+
+#pragma pack()
+
+typedef struct TAG_TW_Device_Extension {
+ void __iomem *base_addr;
+ unsigned long *generic_buffer_virt[TW_Q_LENGTH];
+ dma_addr_t generic_buffer_phys[TW_Q_LENGTH];
+ TW_Command_Full *command_packet_virt[TW_Q_LENGTH];
+ dma_addr_t command_packet_phys[TW_Q_LENGTH];
+ TW_Command_Apache_Header *sense_buffer_virt[TW_Q_LENGTH];
+ dma_addr_t sense_buffer_phys[TW_Q_LENGTH];
+ struct pci_dev *tw_pci_dev;
+ struct scsi_cmnd *srb[TW_Q_LENGTH];
+ unsigned char free_queue[TW_Q_LENGTH];
+ unsigned char free_head;
+ unsigned char free_tail;
+ int state[TW_Q_LENGTH];
+ unsigned int posted_request_count;
+ unsigned int max_posted_request_count;
+ unsigned int max_sgl_entries;
+ unsigned int sgl_entries;
+ unsigned int num_resets;
+ unsigned int sector_count;
+ unsigned int max_sector_count;
+ unsigned int aen_count;
+ struct Scsi_Host *host;
+ long flags;
+ TW_Event *event_queue[TW_Q_LENGTH];
+ unsigned char error_index;
+ unsigned int error_sequence_id;
+ int chrdev_request_id;
+ wait_queue_head_t ioctl_wqueue;
+ struct mutex ioctl_lock;
+ TW_Compatibility_Info tw_compat_info;
+ char online;
+} TW_Device_Extension;
+
+#endif /* _3W_SAS_H */
+
diff --git a/drivers/scsi/3w-xxxx.c b/drivers/scsi/3w-xxxx.c
index ff04821549a..f65a1e92340 100644
--- a/drivers/scsi/3w-xxxx.c
+++ b/drivers/scsi/3w-xxxx.c
@@ -521,8 +521,12 @@ static ssize_t tw_show_stats(struct device *dev, struct device_attribute *attr,
} /* End tw_show_stats() */
/* This function will set a devices queue depth */
-static int tw_change_queue_depth(struct scsi_device *sdev, int queue_depth)
+static int tw_change_queue_depth(struct scsi_device *sdev, int queue_depth,
+ int reason)
{
+ if (reason != SCSI_QDEPTH_DEFAULT)
+ return -EOPNOTSUPP;
+
if (queue_depth > TW_Q_LENGTH-2)
queue_depth = TW_Q_LENGTH-2;
scsi_adjust_queue_depth(sdev, MSG_ORDERED_TAG, queue_depth);
diff --git a/drivers/scsi/53c700.c b/drivers/scsi/53c700.c
index 07ce9bfcdf0..9f4a911a6d8 100644
--- a/drivers/scsi/53c700.c
+++ b/drivers/scsi/53c700.c
@@ -175,7 +175,7 @@ STATIC void NCR_700_chip_reset(struct Scsi_Host *host);
STATIC int NCR_700_slave_alloc(struct scsi_device *SDpnt);
STATIC int NCR_700_slave_configure(struct scsi_device *SDpnt);
STATIC void NCR_700_slave_destroy(struct scsi_device *SDpnt);
-static int NCR_700_change_queue_depth(struct scsi_device *SDpnt, int depth);
+static int NCR_700_change_queue_depth(struct scsi_device *SDpnt, int depth, int reason);
static int NCR_700_change_queue_type(struct scsi_device *SDpnt, int depth);
STATIC struct device_attribute *NCR_700_dev_attrs[];
@@ -2082,8 +2082,11 @@ NCR_700_slave_destroy(struct scsi_device *SDp)
}
static int
-NCR_700_change_queue_depth(struct scsi_device *SDp, int depth)
+NCR_700_change_queue_depth(struct scsi_device *SDp, int depth, int reason)
{
+ if (reason != SCSI_QDEPTH_DEFAULT)
+ return -EOPNOTSUPP;
+
if (depth > NCR_700_MAX_TAGS)
depth = NCR_700_MAX_TAGS;
diff --git a/drivers/scsi/Kconfig b/drivers/scsi/Kconfig
index e11cca4c784..36900c71a59 100644
--- a/drivers/scsi/Kconfig
+++ b/drivers/scsi/Kconfig
@@ -399,6 +399,17 @@ config SCSI_3W_9XXX
Please read the comments at the top of
<file:drivers/scsi/3w-9xxx.c>.
+config SCSI_3W_SAS
+ tristate "3ware 97xx SAS/SATA-RAID support"
+ depends on PCI && SCSI
+ help
+ This driver supports the LSI 3ware 9750 6Gb/s SAS/SATA-RAID cards.
+
+ <http://www.lsi.com>
+
+ Please read the comments at the top of
+ <file:drivers/scsi/3w-sas.c>.
+
config SCSI_7000FASST
tristate "7000FASST SCSI support"
depends on ISA && SCSI && ISA_DMA_API
@@ -621,6 +632,14 @@ config SCSI_FLASHPOINT
substantial, so users of MultiMaster Host Adapters may not
wish to include it.
+config VMWARE_PVSCSI
+ tristate "VMware PVSCSI driver support"
+ depends on PCI && SCSI && X86
+ help
+ This driver supports VMware's para virtualized SCSI HBA.
+ To compile this driver as a module, choose M here: the
+ module will be called vmw_pvscsi.
+
config LIBFC
tristate "LibFC module"
select SCSI_FC_ATTRS
@@ -644,7 +663,7 @@ config FCOE
config FCOE_FNIC
tristate "Cisco FNIC Driver"
depends on PCI && X86
- select LIBFC
+ select LIBFCOE
help
This is support for the Cisco PCI-Express FCoE HBA.
@@ -1818,6 +1837,14 @@ config SCSI_PMCRAID
---help---
This driver supports the PMC SIERRA MaxRAID adapters.
+config SCSI_PM8001
+ tristate "PMC-Sierra SPC 8001 SAS/SATA Based Host Adapter driver"
+ depends on PCI && SCSI
+ select SCSI_SAS_LIBSAS
+ help
+ This driver supports PMC-Sierra PCIE SAS/SATA 8x6G SPC 8001 chip
+ based host adapters.
+
config SCSI_SRP
tristate "SCSI RDMA Protocol helper library"
depends on SCSI && PCI
diff --git a/drivers/scsi/Makefile b/drivers/scsi/Makefile
index 3ad61db5e3f..280d3c657d6 100644
--- a/drivers/scsi/Makefile
+++ b/drivers/scsi/Makefile
@@ -70,6 +70,7 @@ obj-$(CONFIG_SCSI_AIC79XX) += aic7xxx/
obj-$(CONFIG_SCSI_AACRAID) += aacraid/
obj-$(CONFIG_SCSI_AIC7XXX_OLD) += aic7xxx_old.o
obj-$(CONFIG_SCSI_AIC94XX) += aic94xx/
+obj-$(CONFIG_SCSI_PM8001) += pm8001/
obj-$(CONFIG_SCSI_IPS) += ips.o
obj-$(CONFIG_SCSI_FD_MCS) += fd_mcs.o
obj-$(CONFIG_SCSI_FUTURE_DOMAIN)+= fdomain.o
@@ -113,6 +114,7 @@ obj-$(CONFIG_SCSI_MESH) += mesh.o
obj-$(CONFIG_SCSI_MAC53C94) += mac53c94.o
obj-$(CONFIG_BLK_DEV_3W_XXXX_RAID) += 3w-xxxx.o
obj-$(CONFIG_SCSI_3W_9XXX) += 3w-9xxx.o
+obj-$(CONFIG_SCSI_3W_SAS) += 3w-sas.o
obj-$(CONFIG_SCSI_PPA) += ppa.o
obj-$(CONFIG_SCSI_IMM) += imm.o
obj-$(CONFIG_JAZZ_ESP) += esp_scsi.o jazz_esp.o
@@ -133,6 +135,7 @@ obj-$(CONFIG_SCSI_CXGB3_ISCSI) += libiscsi.o libiscsi_tcp.o cxgb3i/
obj-$(CONFIG_SCSI_BNX2_ISCSI) += libiscsi.o bnx2i/
obj-$(CONFIG_BE2ISCSI) += libiscsi.o be2iscsi/
obj-$(CONFIG_SCSI_PMCRAID) += pmcraid.o
+obj-$(CONFIG_VMWARE_PVSCSI) += vmw_pvscsi.o
obj-$(CONFIG_ARM) += arm/
diff --git a/drivers/scsi/aacraid/linit.c b/drivers/scsi/aacraid/linit.c
index 9b97c3e016f..e9373a2d14f 100644
--- a/drivers/scsi/aacraid/linit.c
+++ b/drivers/scsi/aacraid/linit.c
@@ -472,8 +472,12 @@ static int aac_slave_configure(struct scsi_device *sdev)
* total capacity and the queue depth supported by the target device.
*/
-static int aac_change_queue_depth(struct scsi_device *sdev, int depth)
+static int aac_change_queue_depth(struct scsi_device *sdev, int depth,
+ int reason)
{
+ if (reason != SCSI_QDEPTH_DEFAULT)
+ return -EOPNOTSUPP;
+
if (sdev->tagged_supported && (sdev->type == TYPE_DISK) &&
(sdev_channel(sdev) == CONTAINER_CHANNEL)) {
struct scsi_device * dev;
diff --git a/drivers/scsi/arcmsr/arcmsr_hba.c b/drivers/scsi/arcmsr/arcmsr_hba.c
index 80aac01b5a6..47d5d19f8c9 100644
--- a/drivers/scsi/arcmsr/arcmsr_hba.c
+++ b/drivers/scsi/arcmsr/arcmsr_hba.c
@@ -98,8 +98,11 @@ static void arcmsr_flush_hbb_cache(struct AdapterControlBlock *acb);
static const char *arcmsr_info(struct Scsi_Host *);
static irqreturn_t arcmsr_interrupt(struct AdapterControlBlock *acb);
static int arcmsr_adjust_disk_queue_depth(struct scsi_device *sdev,
- int queue_depth)
+ int queue_depth, int reason)
{
+ if (reason != SCSI_QDEPTH_DEFAULT)
+ return -EOPNOTSUPP;
+
if (queue_depth > ARCMSR_MAX_CMD_PERLUN)
queue_depth = ARCMSR_MAX_CMD_PERLUN;
scsi_adjust_queue_depth(sdev, MSG_ORDERED_TAG, queue_depth);
diff --git a/drivers/scsi/be2iscsi/be.h b/drivers/scsi/be2iscsi/be.h
index b36020dcf01..a93a5040f08 100644
--- a/drivers/scsi/be2iscsi/be.h
+++ b/drivers/scsi/be2iscsi/be.h
@@ -20,8 +20,10 @@
#include <linux/pci.h>
#include <linux/if_vlan.h>
-
-#define FW_VER_LEN 32
+#include <linux/blk-iopoll.h>
+#define FW_VER_LEN 32
+#define MCC_Q_LEN 128
+#define MCC_CQ_LEN 256
struct be_dma_mem {
void *va;
@@ -74,18 +76,14 @@ static inline void queue_tail_inc(struct be_queue_info *q)
struct be_eq_obj {
struct be_queue_info q;
- char desc[32];
-
- /* Adaptive interrupt coalescing (AIC) info */
- bool enable_aic;
- u16 min_eqd; /* in usecs */
- u16 max_eqd; /* in usecs */
- u16 cur_eqd; /* in usecs */
+ struct beiscsi_hba *phba;
+ struct be_queue_info *cq;
+ struct blk_iopoll iopoll;
};
struct be_mcc_obj {
- struct be_queue_info *q;
- struct be_queue_info *cq;
+ struct be_queue_info q;
+ struct be_queue_info cq;
};
struct be_ctrl_info {
@@ -176,8 +174,4 @@ static inline void swap_dws(void *wrb, int len)
} while (len);
#endif /* __BIG_ENDIAN */
}
-
-extern void beiscsi_cq_notify(struct be_ctrl_info *ctrl, u16 qid, bool arm,
- u16 num_popped);
-
#endif /* BEISCSI_H */
diff --git a/drivers/scsi/be2iscsi/be_cmds.c b/drivers/scsi/be2iscsi/be_cmds.c
index 08007b6e42d..698a527d6cc 100644
--- a/drivers/scsi/be2iscsi/be_cmds.c
+++ b/drivers/scsi/be2iscsi/be_cmds.c
@@ -19,6 +19,16 @@
#include "be_mgmt.h"
#include "be_main.h"
+static void be_mcc_notify(struct beiscsi_hba *phba)
+{
+ struct be_queue_info *mccq = &phba->ctrl.mcc_obj.q;
+ u32 val = 0;
+
+ val |= mccq->id & DB_MCCQ_RING_ID_MASK;
+ val |= 1 << DB_MCCQ_NUM_POSTED_SHIFT;
+ iowrite32(val, phba->db_va + DB_MCCQ_OFFSET);
+}
+
static inline bool be_mcc_compl_is_new(struct be_mcc_compl *compl)
{
if (compl->flags != 0) {
@@ -54,13 +64,56 @@ static int be_mcc_compl_process(struct be_ctrl_info *ctrl,
return 0;
}
+
static inline bool is_link_state_evt(u32 trailer)
{
return (((trailer >> ASYNC_TRAILER_EVENT_CODE_SHIFT) &
- ASYNC_TRAILER_EVENT_CODE_MASK) == ASYNC_EVENT_CODE_LINK_STATE);
+ ASYNC_TRAILER_EVENT_CODE_MASK) ==
+ ASYNC_EVENT_CODE_LINK_STATE);
+}
+
+static struct be_mcc_compl *be_mcc_compl_get(struct beiscsi_hba *phba)
+{
+ struct be_queue_info *mcc_cq = &phba->ctrl.mcc_obj.cq;
+ struct be_mcc_compl *compl = queue_tail_node(mcc_cq);
+
+ if (be_mcc_compl_is_new(compl)) {
+ queue_tail_inc(mcc_cq);
+ return compl;
+ }
+ return NULL;
+}
+
+static void be2iscsi_fail_session(struct iscsi_cls_session *cls_session)
+{
+ iscsi_session_failure(cls_session->dd_data, ISCSI_ERR_CONN_FAILED);
+}
+
+static void beiscsi_async_link_state_process(struct beiscsi_hba *phba,
+ struct be_async_event_link_state *evt)
+{
+ switch (evt->port_link_status) {
+ case ASYNC_EVENT_LINK_DOWN:
+ SE_DEBUG(DBG_LVL_1, "Link Down on Physical Port %d \n",
+ evt->physical_port);
+ phba->state |= BE_ADAPTER_LINK_DOWN;
+ break;
+ case ASYNC_EVENT_LINK_UP:
+ phba->state = BE_ADAPTER_UP;
+ SE_DEBUG(DBG_LVL_1, "Link UP on Physical Port %d \n",
+ evt->physical_port);
+ iscsi_host_for_each_session(phba->shost,
+ be2iscsi_fail_session);
+ break;
+ default:
+ SE_DEBUG(DBG_LVL_1, "Unexpected Async Notification %d on"
+ "Physical Port %d \n",
+ evt->port_link_status,
+ evt->physical_port);
+ }
}
-void beiscsi_cq_notify(struct be_ctrl_info *ctrl, u16 qid, bool arm,
+static void beiscsi_cq_notify(struct beiscsi_hba *phba, u16 qid, bool arm,
u16 num_popped)
{
u32 val = 0;
@@ -68,7 +121,66 @@ void beiscsi_cq_notify(struct be_ctrl_info *ctrl, u16 qid, bool arm,
if (arm)
val |= 1 << DB_CQ_REARM_SHIFT;
val |= num_popped << DB_CQ_NUM_POPPED_SHIFT;
- iowrite32(val, ctrl->db + DB_CQ_OFFSET);
+ iowrite32(val, phba->db_va + DB_CQ_OFFSET);
+}
+
+
+int beiscsi_process_mcc(struct beiscsi_hba *phba)
+{
+ struct be_mcc_compl *compl;
+ int num = 0, status = 0;
+ struct be_ctrl_info *ctrl = &phba->ctrl;
+
+ spin_lock_bh(&phba->ctrl.mcc_cq_lock);
+ while ((compl = be_mcc_compl_get(phba))) {
+ if (compl->flags & CQE_FLAGS_ASYNC_MASK) {
+ /* Interpret flags as an async trailer */
+ BUG_ON(!is_link_state_evt(compl->flags));
+
+ /* Interpret compl as a async link evt */
+ beiscsi_async_link_state_process(phba,
+ (struct be_async_event_link_state *) compl);
+ } else if (compl->flags & CQE_FLAGS_COMPLETED_MASK) {
+ status = be_mcc_compl_process(ctrl, compl);
+ atomic_dec(&phba->ctrl.mcc_obj.q.used);
+ }
+ be_mcc_compl_use(compl);
+ num++;
+ }
+
+ if (num)
+ beiscsi_cq_notify(phba, phba->ctrl.mcc_obj.cq.id, true, num);
+
+ spin_unlock_bh(&phba->ctrl.mcc_cq_lock);
+ return status;
+}
+
+/* Wait till no more pending mcc requests are present */
+static int be_mcc_wait_compl(struct beiscsi_hba *phba)
+{
+#define mcc_timeout 120000 /* 5s timeout */
+ int i, status;
+ for (i = 0; i < mcc_timeout; i++) {
+ status = beiscsi_process_mcc(phba);
+ if (status)
+ return status;
+
+ if (atomic_read(&phba->ctrl.mcc_obj.q.used) == 0)
+ break;
+ udelay(100);
+ }
+ if (i == mcc_timeout) {
+ dev_err(&phba->pcidev->dev, "mccq poll timed out\n");
+ return -1;
+ }
+ return 0;
+}
+
+/* Notify MCC requests and wait for completion */
+int be_mcc_notify_wait(struct beiscsi_hba *phba)
+{
+ be_mcc_notify(phba);
+ return be_mcc_wait_compl(phba);
}
static int be_mbox_db_ready_wait(struct be_ctrl_info *ctrl)
@@ -142,6 +254,52 @@ int be_mbox_notify(struct be_ctrl_info *ctrl)
return 0;
}
+/*
+ * Insert the mailbox address into the doorbell in two steps
+ * Polls on the mbox doorbell till a command completion (or a timeout) occurs
+ */
+static int be_mbox_notify_wait(struct beiscsi_hba *phba)
+{
+ int status;
+ u32 val = 0;
+ void __iomem *db = phba->ctrl.db + MPU_MAILBOX_DB_OFFSET;
+ struct be_dma_mem *mbox_mem = &phba->ctrl.mbox_mem;
+ struct be_mcc_mailbox *mbox = mbox_mem->va;
+ struct be_mcc_compl *compl = &mbox->compl;
+ struct be_ctrl_info *ctrl = &phba->ctrl;
+
+ val |= MPU_MAILBOX_DB_HI_MASK;
+ /* at bits 2 - 31 place mbox dma addr msb bits 34 - 63 */
+ val |= (upper_32_bits(mbox_mem->dma) >> 2) << 2;
+ iowrite32(val, db);
+
+ /* wait for ready to be set */
+ status = be_mbox_db_ready_wait(ctrl);
+ if (status != 0)
+ return status;
+
+ val = 0;
+ /* at bits 2 - 31 place mbox dma addr lsb bits 4 - 33 */
+ val |= (u32)(mbox_mem->dma >> 4) << 2;
+ iowrite32(val, db);
+
+ status = be_mbox_db_ready_wait(ctrl);
+ if (status != 0)
+ return status;
+
+ /* A cq entry has been made now */
+ if (be_mcc_compl_is_new(compl)) {
+ status = be_mcc_compl_process(ctrl, &mbox->compl);
+ be_mcc_compl_use(compl);
+ if (status)
+ return status;
+ } else {
+ dev_err(&phba->pcidev->dev, "invalid mailbox completion\n");
+ return -1;
+ }
+ return 0;
+}
+
void be_wrb_hdr_prepare(struct be_mcc_wrb *wrb, int payload_len,
bool embedded, u8 sge_cnt)
{
@@ -203,6 +361,20 @@ struct be_mcc_wrb *wrb_from_mbox(struct be_dma_mem *mbox_mem)
return &((struct be_mcc_mailbox *)(mbox_mem->va))->wrb;
}
+struct be_mcc_wrb *wrb_from_mccq(struct beiscsi_hba *phba)
+{
+ struct be_queue_info *mccq = &phba->ctrl.mcc_obj.q;
+ struct be_mcc_wrb *wrb;
+
+ BUG_ON(atomic_read(&mccq->used) >= mccq->len);
+ wrb = queue_head_node(mccq);
+ queue_head_inc(mccq);
+ atomic_inc(&mccq->used);
+ memset(wrb, 0, sizeof(*wrb));
+ return wrb;
+}
+
+
int beiscsi_cmd_eq_create(struct be_ctrl_info *ctrl,
struct be_queue_info *eq, int eq_delay)
{
@@ -212,6 +384,7 @@ int beiscsi_cmd_eq_create(struct be_ctrl_info *ctrl,
struct be_dma_mem *q_mem = &eq->dma_mem;
int status;
+ SE_DEBUG(DBG_LVL_8, "In beiscsi_cmd_eq_create\n");
spin_lock(&ctrl->mbox_lock);
memset(wrb, 0, sizeof(*wrb));
@@ -249,6 +422,7 @@ int be_cmd_fw_initialize(struct be_ctrl_info *ctrl)
int status;
u8 *endian_check;
+ SE_DEBUG(DBG_LVL_8, "In be_cmd_fw_initialize\n");
spin_lock(&ctrl->mbox_lock);
memset(wrb, 0, sizeof(*wrb));
@@ -282,6 +456,7 @@ int beiscsi_cmd_cq_create(struct be_ctrl_info *ctrl,
void *ctxt = &req->context;
int status;
+ SE_DEBUG(DBG_LVL_8, "In beiscsi_cmd_cq_create \n");
spin_lock(&ctrl->mbox_lock);
memset(wrb, 0, sizeof(*wrb));
@@ -289,7 +464,6 @@ int beiscsi_cmd_cq_create(struct be_ctrl_info *ctrl,
be_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_COMMON,
OPCODE_COMMON_CQ_CREATE, sizeof(*req));
-
if (!q_mem->va)
SE_DEBUG(DBG_LVL_1, "uninitialized q_mem->va\n");
@@ -329,6 +503,53 @@ static u32 be_encoded_q_len(int q_len)
len_encoded = 0;
return len_encoded;
}
+
+int beiscsi_cmd_mccq_create(struct beiscsi_hba *phba,
+ struct be_queue_info *mccq,
+ struct be_queue_info *cq)
+{
+ struct be_mcc_wrb *wrb;
+ struct be_cmd_req_mcc_create *req;
+ struct be_dma_mem *q_mem = &mccq->dma_mem;
+ struct be_ctrl_info *ctrl;
+ void *ctxt;
+ int status;
+
+ spin_lock(&phba->ctrl.mbox_lock);
+ ctrl = &phba->ctrl;
+ wrb = wrb_from_mbox(&ctrl->mbox_mem);
+ req = embedded_payload(wrb);
+ ctxt = &req->context;
+
+ be_wrb_hdr_prepare(wrb, sizeof(*req), true, 0);
+
+ be_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_COMMON,
+ OPCODE_COMMON_MCC_CREATE, sizeof(*req));
+
+ req->num_pages = PAGES_4K_SPANNED(q_mem->va, q_mem->size);
+
+ AMAP_SET_BITS(struct amap_mcc_context, fid, ctxt,
+ PCI_FUNC(phba->pcidev->devfn));
+ AMAP_SET_BITS(struct amap_mcc_context, valid, ctxt, 1);
+ AMAP_SET_BITS(struct amap_mcc_context, ring_size, ctxt,
+ be_encoded_q_len(mccq->len));
+ AMAP_SET_BITS(struct amap_mcc_context, cq_id, ctxt, cq->id);
+
+ be_dws_cpu_to_le(ctxt, sizeof(req->context));
+
+ be_cmd_page_addrs_prepare(req->pages, ARRAY_SIZE(req->pages), q_mem);
+
+ status = be_mbox_notify_wait(phba);
+ if (!status) {
+ struct be_cmd_resp_mcc_create *resp = embedded_payload(wrb);
+ mccq->id = le16_to_cpu(resp->id);
+ mccq->created = true;
+ }
+ spin_unlock(&phba->ctrl.mbox_lock);
+
+ return status;
+}
+
int beiscsi_cmd_q_destroy(struct be_ctrl_info *ctrl, struct be_queue_info *q,
int queue_type)
{
@@ -337,6 +558,7 @@ int beiscsi_cmd_q_destroy(struct be_ctrl_info *ctrl, struct be_queue_info *q,
u8 subsys = 0, opcode = 0;
int status;
+ SE_DEBUG(DBG_LVL_8, "In beiscsi_cmd_q_destroy \n");
spin_lock(&ctrl->mbox_lock);
memset(wrb, 0, sizeof(*wrb));
be_wrb_hdr_prepare(wrb, sizeof(*req), true, 0);
@@ -350,6 +572,10 @@ int beiscsi_cmd_q_destroy(struct be_ctrl_info *ctrl, struct be_queue_info *q,
subsys = CMD_SUBSYSTEM_COMMON;
opcode = OPCODE_COMMON_CQ_DESTROY;
break;
+ case QTYPE_MCCQ:
+ subsys = CMD_SUBSYSTEM_COMMON;
+ opcode = OPCODE_COMMON_MCC_DESTROY;
+ break;
case QTYPE_WRBQ:
subsys = CMD_SUBSYSTEM_ISCSI;
opcode = OPCODE_COMMON_ISCSI_WRBQ_DESTROY;
@@ -377,30 +603,6 @@ int beiscsi_cmd_q_destroy(struct be_ctrl_info *ctrl, struct be_queue_info *q,
return status;
}
-int be_cmd_get_mac_addr(struct be_ctrl_info *ctrl, u8 *mac_addr)
-{
- struct be_mcc_wrb *wrb = wrb_from_mbox(&ctrl->mbox_mem);
- struct be_cmd_req_get_mac_addr *req = embedded_payload(wrb);
- int status;
-
- spin_lock(&ctrl->mbox_lock);
- memset(wrb, 0, sizeof(*wrb));
- be_wrb_hdr_prepare(wrb, sizeof(*req), true, 0);
- be_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_ISCSI,
- OPCODE_COMMON_ISCSI_NTWK_GET_NIC_CONFIG,
- sizeof(*req));
-
- status = be_mbox_notify(ctrl);
- if (!status) {
- struct be_cmd_resp_get_mac_addr *resp = embedded_payload(wrb);
-
- memcpy(mac_addr, resp->mac_address, ETH_ALEN);
- }
-
- spin_unlock(&ctrl->mbox_lock);
- return status;
-}
-
int be_cmd_create_default_pdu_queue(struct be_ctrl_info *ctrl,
struct be_queue_info *cq,
struct be_queue_info *dq, int length,
@@ -412,6 +614,7 @@ int be_cmd_create_default_pdu_queue(struct be_ctrl_info *ctrl,
void *ctxt = &req->context;
int status;
+ SE_DEBUG(DBG_LVL_8, "In be_cmd_create_default_pdu_queue\n");
spin_lock(&ctrl->mbox_lock);
memset(wrb, 0, sizeof(*wrb));
@@ -468,8 +671,10 @@ int be_cmd_wrbq_create(struct be_ctrl_info *ctrl, struct be_dma_mem *q_mem,
be_cmd_page_addrs_prepare(req->pages, ARRAY_SIZE(req->pages), q_mem);
status = be_mbox_notify(ctrl);
- if (!status)
+ if (!status) {
wrbq->id = le16_to_cpu(resp->cid);
+ wrbq->created = true;
+ }
spin_unlock(&ctrl->mbox_lock);
return status;
}
diff --git a/drivers/scsi/be2iscsi/be_cmds.h b/drivers/scsi/be2iscsi/be_cmds.h
index c20d686cbb4..5de8acb924c 100644
--- a/drivers/scsi/be2iscsi/be_cmds.h
+++ b/drivers/scsi/be2iscsi/be_cmds.h
@@ -47,6 +47,8 @@ struct be_mcc_wrb {
#define CQE_FLAGS_VALID_MASK (1 << 31)
#define CQE_FLAGS_ASYNC_MASK (1 << 30)
+#define CQE_FLAGS_COMPLETED_MASK (1 << 28)
+#define CQE_FLAGS_CONSUMED_MASK (1 << 27)
/* Completion Status */
#define MCC_STATUS_SUCCESS 0x0
@@ -173,7 +175,7 @@ struct be_cmd_req_hdr {
u8 domain; /* dword 0 */
u32 timeout; /* dword 1 */
u32 request_length; /* dword 2 */
- u32 rsvd; /* dword 3 */
+ u32 rsvd0; /* dword 3 */
};
struct be_cmd_resp_hdr {
@@ -382,7 +384,6 @@ struct be_cmd_req_modify_eq_delay {
#define ETH_ALEN 6
-
struct be_cmd_req_get_mac_addr {
struct be_cmd_req_hdr hdr;
u32 nic_port_count;
@@ -417,14 +418,21 @@ int beiscsi_cmd_cq_create(struct be_ctrl_info *ctrl,
int beiscsi_cmd_q_destroy(struct be_ctrl_info *ctrl, struct be_queue_info *q,
int type);
+int beiscsi_cmd_mccq_create(struct beiscsi_hba *phba,
+ struct be_queue_info *mccq,
+ struct be_queue_info *cq);
+
int be_poll_mcc(struct be_ctrl_info *ctrl);
-unsigned char mgmt_check_supported_fw(struct be_ctrl_info *ctrl);
-int be_cmd_get_mac_addr(struct be_ctrl_info *ctrl, u8 *mac_addr);
+unsigned char mgmt_check_supported_fw(struct be_ctrl_info *ctrl,
+ struct beiscsi_hba *phba);
+int be_cmd_get_mac_addr(struct beiscsi_hba *phba, u8 *mac_addr);
/*ISCSI Functuions */
int be_cmd_fw_initialize(struct be_ctrl_info *ctrl);
struct be_mcc_wrb *wrb_from_mbox(struct be_dma_mem *mbox_mem);
+struct be_mcc_wrb *wrb_from_mccq(struct beiscsi_hba *phba);
+int be_mcc_notify_wait(struct beiscsi_hba *phba);
int be_mbox_notify(struct be_ctrl_info *ctrl);
@@ -531,6 +539,23 @@ struct amap_sol_cqe {
u8 valid; /* dword 3 */
} __packed;
+#define SOL_ICD_INDEX_MASK 0x0003FFC0
+struct amap_sol_cqe_ring {
+ u8 hw_sts[8]; /* dword 0 */
+ u8 i_sts[8]; /* dword 0 */
+ u8 i_resp[8]; /* dword 0 */
+ u8 i_flags[7]; /* dword 0 */
+ u8 s; /* dword 0 */
+ u8 i_exp_cmd_sn[32]; /* dword 1 */
+ u8 code[6]; /* dword 2 */
+ u8 icd_index[12]; /* dword 2 */
+ u8 rsvd[6]; /* dword 2 */
+ u8 i_cmd_wnd[8]; /* dword 2 */
+ u8 i_res_cnt[31]; /* dword 3 */
+ u8 valid; /* dword 3 */
+} __packed;
+
+
/**
* Post WRB Queue Doorbell Register used by the host Storage
@@ -664,8 +689,8 @@ struct be_fw_cfg {
#define OPCODE_COMMON_TCP_UPLOAD 56
#define OPCODE_COMMON_ISCSI_ERROR_RECOVERY_INVALIDATE_COMMANDS 1
/* --- CMD_ISCSI_INVALIDATE_CONNECTION_TYPE --- */
-#define CMD_ISCSI_CONNECTION_INVALIDATE 1
-#define CMD_ISCSI_CONNECTION_ISSUE_TCP_RST 2
+#define CMD_ISCSI_CONNECTION_INVALIDATE 0x8001
+#define CMD_ISCSI_CONNECTION_ISSUE_TCP_RST 0x8002
#define OPCODE_ISCSI_INI_DRIVER_INVALIDATE_CONNECTION 42
#define INI_WR_CMD 1 /* Initiator write command */
diff --git a/drivers/scsi/be2iscsi/be_iscsi.c b/drivers/scsi/be2iscsi/be_iscsi.c
index 2fd25442cfa..d587b0362f1 100644
--- a/drivers/scsi/be2iscsi/be_iscsi.c
+++ b/drivers/scsi/be2iscsi/be_iscsi.c
@@ -67,11 +67,11 @@ struct iscsi_cls_session *beiscsi_session_create(struct iscsi_endpoint *ep,
cmds_max = beiscsi_ep->phba->params.wrbs_per_cxn;
}
- cls_session = iscsi_session_setup(&beiscsi_iscsi_transport,
- shost, cmds_max,
- sizeof(*beiscsi_sess),
- sizeof(*io_task),
- initial_cmdsn, ISCSI_MAX_TARGET);
+ cls_session = iscsi_session_setup(&beiscsi_iscsi_transport,
+ shost, cmds_max,
+ sizeof(*beiscsi_sess),
+ sizeof(*io_task),
+ initial_cmdsn, ISCSI_MAX_TARGET);
if (!cls_session)
return NULL;
sess = cls_session->dd_data;
@@ -297,7 +297,7 @@ int beiscsi_get_host_param(struct Scsi_Host *shost,
switch (param) {
case ISCSI_HOST_PARAM_HWADDRESS:
- be_cmd_get_mac_addr(&phba->ctrl, phba->mac_address);
+ be_cmd_get_mac_addr(phba, phba->mac_address);
len = sysfs_format_mac(buf, phba->mac_address, ETH_ALEN);
break;
default:
@@ -377,16 +377,12 @@ int beiscsi_conn_start(struct iscsi_cls_conn *cls_conn)
struct beiscsi_conn *beiscsi_conn = conn->dd_data;
struct beiscsi_endpoint *beiscsi_ep;
struct beiscsi_offload_params params;
- struct iscsi_session *session = conn->session;
- struct Scsi_Host *shost = iscsi_session_to_shost(session->cls_session);
- struct beiscsi_hba *phba = iscsi_host_priv(shost);
memset(&params, 0, sizeof(struct beiscsi_offload_params));
beiscsi_ep = beiscsi_conn->ep;
if (!beiscsi_ep)
SE_DEBUG(DBG_LVL_1, "In beiscsi_conn_start , no beiscsi_ep\n");
- free_mgmt_sgl_handle(phba, beiscsi_conn->plogin_sgl_handle);
beiscsi_conn->login_in_progress = 0;
beiscsi_set_params_for_offld(beiscsi_conn, &params);
beiscsi_offload_connection(beiscsi_conn, &params);
@@ -498,6 +494,13 @@ beiscsi_ep_connect(struct Scsi_Host *shost, struct sockaddr *dst_addr,
SE_DEBUG(DBG_LVL_1, "shost is NULL \n");
return ERR_PTR(ret);
}
+
+ if (phba->state) {
+ ret = -EBUSY;
+ SE_DEBUG(DBG_LVL_1, "The Adapet state is Not UP \n");
+ return ERR_PTR(ret);
+ }
+
ep = iscsi_create_endpoint(sizeof(struct beiscsi_endpoint));
if (!ep) {
ret = -ENOMEM;
diff --git a/drivers/scsi/be2iscsi/be_main.c b/drivers/scsi/be2iscsi/be_main.c
index 4f1aca346e3..1a557fa7788 100644
--- a/drivers/scsi/be2iscsi/be_main.c
+++ b/drivers/scsi/be2iscsi/be_main.c
@@ -39,7 +39,8 @@
static unsigned int be_iopoll_budget = 10;
static unsigned int be_max_phys_size = 64;
-static unsigned int enable_msix;
+static unsigned int enable_msix = 1;
+static unsigned int ring_mode;
MODULE_DEVICE_TABLE(pci, beiscsi_pci_id_table);
MODULE_DESCRIPTION(DRV_DESC " " BUILD_STR);
@@ -58,6 +59,17 @@ static int beiscsi_slave_configure(struct scsi_device *sdev)
return 0;
}
+/*------------------- PCI Driver operations and data ----------------- */
+static DEFINE_PCI_DEVICE_TABLE(beiscsi_pci_id_table) = {
+ { PCI_DEVICE(BE_VENDOR_ID, BE_DEVICE_ID1) },
+ { PCI_DEVICE(BE_VENDOR_ID, OC_DEVICE_ID1) },
+ { PCI_DEVICE(BE_VENDOR_ID, OC_DEVICE_ID2) },
+ { PCI_DEVICE(BE_VENDOR_ID, OC_DEVICE_ID3) },
+ { PCI_DEVICE(BE_VENDOR_ID, OC_DEVICE_ID4) },
+ { 0 }
+};
+MODULE_DEVICE_TABLE(pci, beiscsi_pci_id_table);
+
static struct scsi_host_template beiscsi_sht = {
.module = THIS_MODULE,
.name = "ServerEngines 10Gbe open-iscsi Initiator Driver",
@@ -76,16 +88,8 @@ static struct scsi_host_template beiscsi_sht = {
.cmd_per_lun = BEISCSI_CMD_PER_LUN,
.use_clustering = ENABLE_CLUSTERING,
};
-static struct scsi_transport_template *beiscsi_scsi_transport;
-/*------------------- PCI Driver operations and data ----------------- */
-static DEFINE_PCI_DEVICE_TABLE(beiscsi_pci_id_table) = {
- { PCI_DEVICE(BE_VENDOR_ID, BE_DEVICE_ID1) },
- { PCI_DEVICE(BE_VENDOR_ID, OC_DEVICE_ID1) },
- { PCI_DEVICE(BE_VENDOR_ID, OC_DEVICE_ID2) },
- { 0 }
-};
-MODULE_DEVICE_TABLE(pci, beiscsi_pci_id_table);
+static struct scsi_transport_template *beiscsi_scsi_transport;
static struct beiscsi_hba *beiscsi_hba_alloc(struct pci_dev *pcidev)
{
@@ -104,7 +108,6 @@ static struct beiscsi_hba *beiscsi_hba_alloc(struct pci_dev *pcidev)
shost->max_cmd_len = BEISCSI_MAX_CMD_LEN;
shost->max_lun = BEISCSI_NUM_MAX_LUN;
shost->transportt = beiscsi_scsi_transport;
-
phba = iscsi_host_priv(shost);
memset(phba, 0, sizeof(*phba));
phba->shost = shost;
@@ -181,6 +184,7 @@ static int beiscsi_enable_pci(struct pci_dev *pcidev)
return ret;
}
+ pci_set_master(pcidev);
if (pci_set_consistent_dma_mask(pcidev, DMA_BIT_MASK(64))) {
ret = pci_set_consistent_dma_mask(pcidev, DMA_BIT_MASK(32));
if (ret) {
@@ -203,7 +207,6 @@ static int be_ctrl_init(struct beiscsi_hba *phba, struct pci_dev *pdev)
status = beiscsi_map_pci_bars(phba, pdev);
if (status)
return status;
-
mbox_mem_alloc->size = sizeof(struct be_mcc_mailbox) + 16;
mbox_mem_alloc->va = pci_alloc_consistent(pdev,
mbox_mem_alloc->size,
@@ -219,6 +222,9 @@ static int be_ctrl_init(struct beiscsi_hba *phba, struct pci_dev *pdev)
mbox_mem_align->dma = PTR_ALIGN(mbox_mem_alloc->dma, 16);
memset(mbox_mem_align->va, 0, sizeof(struct be_mcc_mailbox));
spin_lock_init(&ctrl->mbox_lock);
+ spin_lock_init(&phba->ctrl.mcc_lock);
+ spin_lock_init(&phba->ctrl.mcc_cq_lock);
+
return status;
}
@@ -268,6 +274,113 @@ static void hwi_ring_eq_db(struct beiscsi_hba *phba,
}
/**
+ * be_isr_mcc - The isr routine of the driver.
+ * @irq: Not used
+ * @dev_id: Pointer to host adapter structure
+ */
+static irqreturn_t be_isr_mcc(int irq, void *dev_id)
+{
+ struct beiscsi_hba *phba;
+ struct be_eq_entry *eqe = NULL;
+ struct be_queue_info *eq;
+ struct be_queue_info *mcc;
+ unsigned int num_eq_processed;
+ struct be_eq_obj *pbe_eq;
+ unsigned long flags;
+
+ pbe_eq = dev_id;
+ eq = &pbe_eq->q;
+ phba = pbe_eq->phba;
+ mcc = &phba->ctrl.mcc_obj.cq;
+ eqe = queue_tail_node(eq);
+ if (!eqe)
+ SE_DEBUG(DBG_LVL_1, "eqe is NULL\n");
+
+ num_eq_processed = 0;
+
+ while (eqe->dw[offsetof(struct amap_eq_entry, valid) / 32]
+ & EQE_VALID_MASK) {
+ if (((eqe->dw[offsetof(struct amap_eq_entry,
+ resource_id) / 32] &
+ EQE_RESID_MASK) >> 16) == mcc->id) {
+ spin_lock_irqsave(&phba->isr_lock, flags);
+ phba->todo_mcc_cq = 1;
+ spin_unlock_irqrestore(&phba->isr_lock, flags);
+ }
+ AMAP_SET_BITS(struct amap_eq_entry, valid, eqe, 0);
+ queue_tail_inc(eq);
+ eqe = queue_tail_node(eq);
+ num_eq_processed++;
+ }
+ if (phba->todo_mcc_cq)
+ queue_work(phba->wq, &phba->work_cqs);
+ if (num_eq_processed)
+ hwi_ring_eq_db(phba, eq->id, 1, num_eq_processed, 1, 1);
+
+ return IRQ_HANDLED;
+}
+
+/**
+ * be_isr_msix - The isr routine of the driver.
+ * @irq: Not used
+ * @dev_id: Pointer to host adapter structure
+ */
+static irqreturn_t be_isr_msix(int irq, void *dev_id)
+{
+ struct beiscsi_hba *phba;
+ struct be_eq_entry *eqe = NULL;
+ struct be_queue_info *eq;
+ struct be_queue_info *cq;
+ unsigned int num_eq_processed;
+ struct be_eq_obj *pbe_eq;
+ unsigned long flags;
+
+ pbe_eq = dev_id;
+ eq = &pbe_eq->q;
+ cq = pbe_eq->cq;
+ eqe = queue_tail_node(eq);
+ if (!eqe)
+ SE_DEBUG(DBG_LVL_1, "eqe is NULL\n");
+
+ phba = pbe_eq->phba;
+ num_eq_processed = 0;
+ if (blk_iopoll_enabled) {
+ while (eqe->dw[offsetof(struct amap_eq_entry, valid) / 32]
+ & EQE_VALID_MASK) {
+ if (!blk_iopoll_sched_prep(&pbe_eq->iopoll))
+ blk_iopoll_sched(&pbe_eq->iopoll);
+
+ AMAP_SET_BITS(struct amap_eq_entry, valid, eqe, 0);
+ queue_tail_inc(eq);
+ eqe = queue_tail_node(eq);
+ num_eq_processed++;
+ }
+ if (num_eq_processed)
+ hwi_ring_eq_db(phba, eq->id, 1, num_eq_processed, 0, 1);
+
+ return IRQ_HANDLED;
+ } else {
+ while (eqe->dw[offsetof(struct amap_eq_entry, valid) / 32]
+ & EQE_VALID_MASK) {
+ spin_lock_irqsave(&phba->isr_lock, flags);
+ phba->todo_cq = 1;
+ spin_unlock_irqrestore(&phba->isr_lock, flags);
+ AMAP_SET_BITS(struct amap_eq_entry, valid, eqe, 0);
+ queue_tail_inc(eq);
+ eqe = queue_tail_node(eq);
+ num_eq_processed++;
+ }
+ if (phba->todo_cq)
+ queue_work(phba->wq, &phba->work_cqs);
+
+ if (num_eq_processed)
+ hwi_ring_eq_db(phba, eq->id, 1, num_eq_processed, 1, 1);
+
+ return IRQ_HANDLED;
+ }
+}
+
+/**
* be_isr - The isr routine of the driver.
* @irq: Not used
* @dev_id: Pointer to host adapter structure
@@ -280,48 +393,70 @@ static irqreturn_t be_isr(int irq, void *dev_id)
struct be_eq_entry *eqe = NULL;
struct be_queue_info *eq;
struct be_queue_info *cq;
+ struct be_queue_info *mcc;
unsigned long flags, index;
- unsigned int num_eq_processed;
+ unsigned int num_mcceq_processed, num_ioeq_processed;
struct be_ctrl_info *ctrl;
+ struct be_eq_obj *pbe_eq;
int isr;
phba = dev_id;
- if (!enable_msix) {
- ctrl = &phba->ctrl;;
- isr = ioread32(ctrl->csr + CEV_ISR0_OFFSET +
- (PCI_FUNC(ctrl->pdev->devfn) * CEV_ISR_SIZE));
- if (!isr)
- return IRQ_NONE;
- }
+ ctrl = &phba->ctrl;;
+ isr = ioread32(ctrl->csr + CEV_ISR0_OFFSET +
+ (PCI_FUNC(ctrl->pdev->devfn) * CEV_ISR_SIZE));
+ if (!isr)
+ return IRQ_NONE;
phwi_ctrlr = phba->phwi_ctrlr;
phwi_context = phwi_ctrlr->phwi_ctxt;
- eq = &phwi_context->be_eq.q;
- cq = &phwi_context->be_cq;
+ pbe_eq = &phwi_context->be_eq[0];
+
+ eq = &phwi_context->be_eq[0].q;
+ mcc = &phba->ctrl.mcc_obj.cq;
index = 0;
eqe = queue_tail_node(eq);
if (!eqe)
SE_DEBUG(DBG_LVL_1, "eqe is NULL\n");
- num_eq_processed = 0;
+ num_ioeq_processed = 0;
+ num_mcceq_processed = 0;
if (blk_iopoll_enabled) {
while (eqe->dw[offsetof(struct amap_eq_entry, valid) / 32]
& EQE_VALID_MASK) {
- if (!blk_iopoll_sched_prep(&phba->iopoll))
- blk_iopoll_sched(&phba->iopoll);
-
+ if (((eqe->dw[offsetof(struct amap_eq_entry,
+ resource_id) / 32] &
+ EQE_RESID_MASK) >> 16) == mcc->id) {
+ spin_lock_irqsave(&phba->isr_lock, flags);
+ phba->todo_mcc_cq = 1;
+ spin_unlock_irqrestore(&phba->isr_lock, flags);
+ num_mcceq_processed++;
+ } else {
+ if (!blk_iopoll_sched_prep(&pbe_eq->iopoll))
+ blk_iopoll_sched(&pbe_eq->iopoll);
+ num_ioeq_processed++;
+ }
AMAP_SET_BITS(struct amap_eq_entry, valid, eqe, 0);
queue_tail_inc(eq);
eqe = queue_tail_node(eq);
- num_eq_processed++;
- SE_DEBUG(DBG_LVL_8, "Valid EQE\n");
}
- if (num_eq_processed) {
- hwi_ring_eq_db(phba, eq->id, 0, num_eq_processed, 0, 1);
+ if (num_ioeq_processed || num_mcceq_processed) {
+ if (phba->todo_mcc_cq)
+ queue_work(phba->wq, &phba->work_cqs);
+
+ if ((num_mcceq_processed) && (!num_ioeq_processed))
+ hwi_ring_eq_db(phba, eq->id, 0,
+ (num_ioeq_processed +
+ num_mcceq_processed) , 1, 1);
+ else
+ hwi_ring_eq_db(phba, eq->id, 0,
+ (num_ioeq_processed +
+ num_mcceq_processed), 0, 1);
+
return IRQ_HANDLED;
} else
return IRQ_NONE;
} else {
+ cq = &phwi_context->be_cq[0];
while (eqe->dw[offsetof(struct amap_eq_entry, valid) / 32]
& EQE_VALID_MASK) {
@@ -339,13 +474,14 @@ static irqreturn_t be_isr(int irq, void *dev_id)
AMAP_SET_BITS(struct amap_eq_entry, valid, eqe, 0);
queue_tail_inc(eq);
eqe = queue_tail_node(eq);
- num_eq_processed++;
+ num_ioeq_processed++;
}
if (phba->todo_cq || phba->todo_mcc_cq)
queue_work(phba->wq, &phba->work_cqs);
- if (num_eq_processed) {
- hwi_ring_eq_db(phba, eq->id, 0, num_eq_processed, 1, 1);
+ if (num_ioeq_processed) {
+ hwi_ring_eq_db(phba, eq->id, 0,
+ num_ioeq_processed, 1, 1);
return IRQ_HANDLED;
} else
return IRQ_NONE;
@@ -355,13 +491,32 @@ static irqreturn_t be_isr(int irq, void *dev_id)
static int beiscsi_init_irqs(struct beiscsi_hba *phba)
{
struct pci_dev *pcidev = phba->pcidev;
- int ret;
+ struct hwi_controller *phwi_ctrlr;
+ struct hwi_context_memory *phwi_context;
+ int ret, msix_vec, i = 0;
+ char desc[32];
- ret = request_irq(pcidev->irq, be_isr, IRQF_SHARED, "beiscsi", phba);
- if (ret) {
- shost_printk(KERN_ERR, phba->shost, "beiscsi_init_irqs-"
- "Failed to register irq\\n");
- return ret;
+ phwi_ctrlr = phba->phwi_ctrlr;
+ phwi_context = phwi_ctrlr->phwi_ctxt;
+
+ if (phba->msix_enabled) {
+ for (i = 0; i < phba->num_cpus; i++) {
+ sprintf(desc, "beiscsi_msix_%04x", i);
+ msix_vec = phba->msix_entries[i].vector;
+ ret = request_irq(msix_vec, be_isr_msix, 0, desc,
+ &phwi_context->be_eq[i]);
+ }
+ msix_vec = phba->msix_entries[i].vector;
+ ret = request_irq(msix_vec, be_isr_mcc, 0, "beiscsi_msix_mcc",
+ &phwi_context->be_eq[i]);
+ } else {
+ ret = request_irq(pcidev->irq, be_isr, IRQF_SHARED,
+ "beiscsi", phba);
+ if (ret) {
+ shost_printk(KERN_ERR, phba->shost, "beiscsi_init_irqs-"
+ "Failed to register irq\\n");
+ return ret;
+ }
}
return 0;
}
@@ -378,15 +533,6 @@ static void hwi_ring_cq_db(struct beiscsi_hba *phba,
iowrite32(val, phba->db_va + DB_CQ_OFFSET);
}
-/*
- * async pdus include
- * a. unsolicited NOP-In (target initiated NOP-In)
- * b. Async Messages
- * c. Reject PDU
- * d. Login response
- * These headers arrive unprocessed by the EP firmware and iSCSI layer
- * process them
- */
static unsigned int
beiscsi_process_async_pdu(struct beiscsi_conn *beiscsi_conn,
struct beiscsi_hba *phba,
@@ -397,6 +543,9 @@ beiscsi_process_async_pdu(struct beiscsi_conn *beiscsi_conn,
{
struct iscsi_conn *conn = beiscsi_conn->conn;
struct iscsi_session *session = conn->session;
+ struct iscsi_task *task;
+ struct beiscsi_io_task *io_task;
+ struct iscsi_hdr *login_hdr;
switch (ppdu->dw[offsetof(struct amap_pdu_base, opcode) / 32] &
PDUBASE_OPCODE_MASK) {
@@ -412,6 +561,10 @@ beiscsi_process_async_pdu(struct beiscsi_conn *beiscsi_conn,
SE_DEBUG(DBG_LVL_1, "In ISCSI_OP_REJECT\n");
break;
case ISCSI_OP_LOGIN_RSP:
+ task = conn->login_task;
+ io_task = task->dd_data;
+ login_hdr = (struct iscsi_hdr *)ppdu;
+ login_hdr->itt = io_task->libiscsi_itt;
break;
default:
shost_printk(KERN_WARNING, phba->shost,
@@ -440,7 +593,8 @@ static struct sgl_handle *alloc_io_sgl_handle(struct beiscsi_hba *phba)
io_sgl_alloc_index];
phba->io_sgl_hndl_base[phba->io_sgl_alloc_index] = NULL;
phba->io_sgl_hndl_avbl--;
- if (phba->io_sgl_alloc_index == (phba->params.ios_per_ctrl - 1))
+ if (phba->io_sgl_alloc_index == (phba->params.
+ ios_per_ctrl - 1))
phba->io_sgl_alloc_index = 0;
else
phba->io_sgl_alloc_index++;
@@ -490,9 +644,18 @@ struct wrb_handle *alloc_wrb_handle(struct beiscsi_hba *phba, unsigned int cid,
phwi_ctrlr = phba->phwi_ctrlr;
pwrb_context = &phwi_ctrlr->wrb_context[cid];
- pwrb_handle = pwrb_context->pwrb_handle_base[index];
- pwrb_handle->wrb_index = index;
- pwrb_handle->nxt_wrb_index = index;
+ if (pwrb_context->wrb_handles_available) {
+ pwrb_handle = pwrb_context->pwrb_handle_base[
+ pwrb_context->alloc_index];
+ pwrb_context->wrb_handles_available--;
+ pwrb_handle->nxt_wrb_index = pwrb_handle->wrb_index;
+ if (pwrb_context->alloc_index ==
+ (phba->params.wrbs_per_cxn - 1))
+ pwrb_context->alloc_index = 0;
+ else
+ pwrb_context->alloc_index++;
+ } else
+ pwrb_handle = NULL;
return pwrb_handle;
}
@@ -508,11 +671,20 @@ static void
free_wrb_handle(struct beiscsi_hba *phba, struct hwi_wrb_context *pwrb_context,
struct wrb_handle *pwrb_handle)
{
+ if (!ring_mode)
+ pwrb_context->pwrb_handle_base[pwrb_context->free_index] =
+ pwrb_handle;
+ pwrb_context->wrb_handles_available++;
+ if (pwrb_context->free_index == (phba->params.wrbs_per_cxn - 1))
+ pwrb_context->free_index = 0;
+ else
+ pwrb_context->free_index++;
+
SE_DEBUG(DBG_LVL_8,
- "FREE WRB: pwrb_handle=%p free_index=%d=0x%x"
+ "FREE WRB: pwrb_handle=%p free_index=0x%x"
"wrb_handles_available=%d \n",
pwrb_handle, pwrb_context->free_index,
- pwrb_context->free_index, pwrb_context->wrb_handles_available);
+ pwrb_context->wrb_handles_available);
}
static struct sgl_handle *alloc_mgmt_sgl_handle(struct beiscsi_hba *phba)
@@ -540,6 +712,8 @@ void
free_mgmt_sgl_handle(struct beiscsi_hba *phba, struct sgl_handle *psgl_handle)
{
+ SE_DEBUG(DBG_LVL_8, "In free_mgmt_sgl_handle,eh_sgl_free_index=%d \n",
+ phba->eh_sgl_free_index);
if (phba->eh_sgl_hndl_base[phba->eh_sgl_free_index]) {
/*
* this can happen if clean_task is called on a task that
@@ -572,10 +746,10 @@ be_complete_io(struct beiscsi_conn *beiscsi_conn,
u32 resid = 0, exp_cmdsn, max_cmdsn;
u8 rsp, status, flags;
- exp_cmdsn = be32_to_cpu(psol->
+ exp_cmdsn = (psol->
dw[offsetof(struct amap_sol_cqe, i_exp_cmd_sn) / 32]
& SOL_EXP_CMD_SN_MASK);
- max_cmdsn = be32_to_cpu((psol->
+ max_cmdsn = ((psol->
dw[offsetof(struct amap_sol_cqe, i_exp_cmd_sn) / 32]
& SOL_EXP_CMD_SN_MASK) +
((psol->dw[offsetof(struct amap_sol_cqe, i_cmd_wnd)
@@ -610,9 +784,9 @@ be_complete_io(struct beiscsi_conn *beiscsi_conn,
}
if (status == SAM_STAT_CHECK_CONDITION) {
+ unsigned short *slen = (unsigned short *)sts_bhs->sense_info;
sense = sts_bhs->sense_info + sizeof(unsigned short);
- sense_len =
- cpu_to_be16((unsigned short)(sts_bhs->sense_info[0]));
+ sense_len = cpu_to_be16(*slen);
memcpy(task->sc->sense_buffer, sense,
min_t(u16, sense_len, SCSI_SENSE_BUFFERSIZE));
}
@@ -620,8 +794,8 @@ be_complete_io(struct beiscsi_conn *beiscsi_conn,
if (psol->dw[offsetof(struct amap_sol_cqe, i_res_cnt) / 32]
& SOL_RES_CNT_MASK)
conn->rxdata_octets += (psol->
- dw[offsetof(struct amap_sol_cqe, i_res_cnt) / 32]
- & SOL_RES_CNT_MASK);
+ dw[offsetof(struct amap_sol_cqe, i_res_cnt) / 32]
+ & SOL_RES_CNT_MASK);
}
unmap:
scsi_dma_unmap(io_task->scsi_cmnd);
@@ -633,6 +807,7 @@ be_complete_logout(struct beiscsi_conn *beiscsi_conn,
struct iscsi_task *task, struct sol_cqe *psol)
{
struct iscsi_logout_rsp *hdr;
+ struct beiscsi_io_task *io_task = task->dd_data;
struct iscsi_conn *conn = beiscsi_conn->conn;
hdr = (struct iscsi_logout_rsp *)task->hdr;
@@ -651,7 +826,7 @@ be_complete_logout(struct beiscsi_conn *beiscsi_conn,
((psol->dw[offsetof(struct amap_sol_cqe, i_cmd_wnd)
/ 32] & SOL_CMD_WND_MASK) >> 24) - 1);
hdr->hlength = 0;
-
+ hdr->itt = io_task->libiscsi_itt;
__iscsi_complete_pdu(conn, (struct iscsi_hdr *)hdr, NULL, 0);
}
@@ -661,6 +836,7 @@ be_complete_tmf(struct beiscsi_conn *beiscsi_conn,
{
struct iscsi_tm_rsp *hdr;
struct iscsi_conn *conn = beiscsi_conn->conn;
+ struct beiscsi_io_task *io_task = task->dd_data;
hdr = (struct iscsi_tm_rsp *)task->hdr;
hdr->flags = ((psol->dw[offsetof(struct amap_sol_cqe, i_flags) / 32]
@@ -668,11 +844,12 @@ be_complete_tmf(struct beiscsi_conn *beiscsi_conn,
hdr->response = (psol->dw[offsetof(struct amap_sol_cqe, i_resp) /
32] & SOL_RESP_MASK);
hdr->exp_cmdsn = cpu_to_be32(psol->dw[offsetof(struct amap_sol_cqe,
- i_exp_cmd_sn) / 32] & SOL_EXP_CMD_SN_MASK);
+ i_exp_cmd_sn) / 32] & SOL_EXP_CMD_SN_MASK);
hdr->max_cmdsn = be32_to_cpu((psol->dw[offsetof(struct amap_sol_cqe,
i_exp_cmd_sn) / 32] & SOL_EXP_CMD_SN_MASK) +
((psol->dw[offsetof(struct amap_sol_cqe, i_cmd_wnd)
/ 32] & SOL_CMD_WND_MASK) >> 24) - 1);
+ hdr->itt = io_task->libiscsi_itt;
__iscsi_complete_pdu(conn, (struct iscsi_hdr *)hdr, NULL, 0);
}
@@ -681,18 +858,36 @@ hwi_complete_drvr_msgs(struct beiscsi_conn *beiscsi_conn,
struct beiscsi_hba *phba, struct sol_cqe *psol)
{
struct hwi_wrb_context *pwrb_context;
- struct wrb_handle *pwrb_handle;
+ struct wrb_handle *pwrb_handle = NULL;
+ struct sgl_handle *psgl_handle = NULL;
struct hwi_controller *phwi_ctrlr;
+ struct iscsi_task *task;
+ struct beiscsi_io_task *io_task;
struct iscsi_conn *conn = beiscsi_conn->conn;
struct iscsi_session *session = conn->session;
phwi_ctrlr = phba->phwi_ctrlr;
- pwrb_context = &phwi_ctrlr->wrb_context[((psol->
+ if (ring_mode) {
+ psgl_handle = phba->sgl_hndl_array[((psol->
+ dw[offsetof(struct amap_sol_cqe_ring, icd_index) /
+ 32] & SOL_ICD_INDEX_MASK) >> 6)];
+ pwrb_context = &phwi_ctrlr->wrb_context[psgl_handle->cid];
+ task = psgl_handle->task;
+ pwrb_handle = NULL;
+ } else {
+ pwrb_context = &phwi_ctrlr->wrb_context[((psol->
dw[offsetof(struct amap_sol_cqe, cid) / 32] &
SOL_CID_MASK) >> 6)];
- pwrb_handle = pwrb_context->pwrb_handle_basestd[((psol->
+ pwrb_handle = pwrb_context->pwrb_handle_basestd[((psol->
dw[offsetof(struct amap_sol_cqe, wrb_index) /
32] & SOL_WRB_INDEX_MASK) >> 16)];
+ task = pwrb_handle->pio_handle;
+ }
+
+ io_task = task->dd_data;
+ spin_lock(&phba->mgmt_sgl_lock);
+ free_mgmt_sgl_handle(phba, io_task->psgl_handle);
+ spin_unlock(&phba->mgmt_sgl_lock);
spin_lock_bh(&session->lock);
free_wrb_handle(phba, pwrb_context, pwrb_handle);
spin_unlock_bh(&session->lock);
@@ -704,6 +899,7 @@ be_complete_nopin_resp(struct beiscsi_conn *beiscsi_conn,
{
struct iscsi_nopin *hdr;
struct iscsi_conn *conn = beiscsi_conn->conn;
+ struct beiscsi_io_task *io_task = task->dd_data;
hdr = (struct iscsi_nopin *)task->hdr;
hdr->flags = ((psol->dw[offsetof(struct amap_sol_cqe, i_flags) / 32]
@@ -715,6 +911,7 @@ be_complete_nopin_resp(struct beiscsi_conn *beiscsi_conn,
((psol->dw[offsetof(struct amap_sol_cqe, i_cmd_wnd)
/ 32] & SOL_CMD_WND_MASK) >> 24) - 1);
hdr->opcode = ISCSI_OP_NOOP_IN;
+ hdr->itt = io_task->libiscsi_itt;
__iscsi_complete_pdu(conn, (struct iscsi_hdr *)hdr, NULL, 0);
}
@@ -726,25 +923,33 @@ static void hwi_complete_cmd(struct beiscsi_conn *beiscsi_conn,
struct iscsi_wrb *pwrb = NULL;
struct hwi_controller *phwi_ctrlr;
struct iscsi_task *task;
- struct beiscsi_io_task *io_task;
+ struct sgl_handle *psgl_handle = NULL;
+ unsigned int type;
struct iscsi_conn *conn = beiscsi_conn->conn;
struct iscsi_session *session = conn->session;
phwi_ctrlr = phba->phwi_ctrlr;
-
- pwrb_context = &phwi_ctrlr->
- wrb_context[((psol->dw[offsetof(struct amap_sol_cqe, cid) / 32]
- & SOL_CID_MASK) >> 6)];
- pwrb_handle = pwrb_context->pwrb_handle_basestd[((psol->
+ if (ring_mode) {
+ psgl_handle = phba->sgl_hndl_array[((psol->
+ dw[offsetof(struct amap_sol_cqe_ring, icd_index) /
+ 32] & SOL_ICD_INDEX_MASK) >> 6)];
+ task = psgl_handle->task;
+ type = psgl_handle->type;
+ } else {
+ pwrb_context = &phwi_ctrlr->
+ wrb_context[((psol->dw[offsetof
+ (struct amap_sol_cqe, cid) / 32]
+ & SOL_CID_MASK) >> 6)];
+ pwrb_handle = pwrb_context->pwrb_handle_basestd[((psol->
dw[offsetof(struct amap_sol_cqe, wrb_index) /
32] & SOL_WRB_INDEX_MASK) >> 16)];
-
- task = pwrb_handle->pio_handle;
- io_task = task->dd_data;
+ task = pwrb_handle->pio_handle;
+ pwrb = pwrb_handle->pwrb;
+ type = (pwrb->dw[offsetof(struct amap_iscsi_wrb, type) / 32] &
+ WRB_TYPE_MASK) >> 28;
+ }
spin_lock_bh(&session->lock);
- pwrb = pwrb_handle->pwrb;
- switch ((pwrb->dw[offsetof(struct amap_iscsi_wrb, type) / 32] &
- WRB_TYPE_MASK) >> 28) {
+ switch (type) {
case HWH_TYPE_IO:
case HWH_TYPE_IO_RD:
if ((task->hdr->opcode & ISCSI_OPCODE_MASK) ==
@@ -773,12 +978,21 @@ static void hwi_complete_cmd(struct beiscsi_conn *beiscsi_conn,
break;
default:
- shost_printk(KERN_WARNING, phba->shost,
- "wrb_index 0x%x CID 0x%x\n",
- ((psol->dw[offsetof(struct amap_iscsi_wrb, type) /
- 32] & SOL_WRB_INDEX_MASK) >> 16),
- ((psol->dw[offsetof(struct amap_sol_cqe, cid) / 32]
- & SOL_CID_MASK) >> 6));
+ if (ring_mode)
+ shost_printk(KERN_WARNING, phba->shost,
+ "In hwi_complete_cmd, unknown type = %d"
+ "icd_index 0x%x CID 0x%x\n", type,
+ ((psol->dw[offsetof(struct amap_sol_cqe_ring,
+ icd_index) / 32] & SOL_ICD_INDEX_MASK) >> 6),
+ psgl_handle->cid);
+ else
+ shost_printk(KERN_WARNING, phba->shost,
+ "In hwi_complete_cmd, unknown type = %d"
+ "wrb_index 0x%x CID 0x%x\n", type,
+ ((psol->dw[offsetof(struct amap_iscsi_wrb,
+ type) / 32] & SOL_WRB_INDEX_MASK) >> 16),
+ ((psol->dw[offsetof(struct amap_sol_cqe,
+ cid) / 32] & SOL_CID_MASK) >> 6));
break;
}
@@ -1208,40 +1422,55 @@ static void hwi_process_default_pdu_ring(struct beiscsi_conn *beiscsi_conn,
hwi_post_async_buffers(phba, pasync_handle->is_header);
}
-static unsigned int beiscsi_process_cq(struct beiscsi_hba *phba)
+
+static unsigned int beiscsi_process_cq(struct be_eq_obj *pbe_eq)
{
- struct hwi_controller *phwi_ctrlr;
- struct hwi_context_memory *phwi_context;
struct be_queue_info *cq;
struct sol_cqe *sol;
struct dmsg_cqe *dmsg;
unsigned int num_processed = 0;
unsigned int tot_nump = 0;
struct beiscsi_conn *beiscsi_conn;
+ struct sgl_handle *psgl_handle = NULL;
+ struct beiscsi_hba *phba;
- phwi_ctrlr = phba->phwi_ctrlr;
- phwi_context = phwi_ctrlr->phwi_ctxt;
- cq = &phwi_context->be_cq;
+ cq = pbe_eq->cq;
sol = queue_tail_node(cq);
+ phba = pbe_eq->phba;
while (sol->dw[offsetof(struct amap_sol_cqe, valid) / 32] &
CQE_VALID_MASK) {
be_dws_le_to_cpu(sol, sizeof(struct sol_cqe));
- beiscsi_conn = phba->conn_table[(u32) (sol->
+ if (ring_mode) {
+ psgl_handle = phba->sgl_hndl_array[((sol->
+ dw[offsetof(struct amap_sol_cqe_ring,
+ icd_index) / 32] & SOL_ICD_INDEX_MASK)
+ >> 6)];
+ beiscsi_conn = phba->conn_table[psgl_handle->cid];
+ if (!beiscsi_conn || !beiscsi_conn->ep) {
+ shost_printk(KERN_WARNING, phba->shost,
+ "Connection table empty for cid = %d\n",
+ psgl_handle->cid);
+ return 0;
+ }
+
+ } else {
+ beiscsi_conn = phba->conn_table[(u32) (sol->
dw[offsetof(struct amap_sol_cqe, cid) / 32] &
SOL_CID_MASK) >> 6];
- if (!beiscsi_conn || !beiscsi_conn->ep) {
- shost_printk(KERN_WARNING, phba->shost,
+ if (!beiscsi_conn || !beiscsi_conn->ep) {
+ shost_printk(KERN_WARNING, phba->shost,
"Connection table empty for cid = %d\n",
(u32)(sol->dw[offsetof(struct amap_sol_cqe,
cid) / 32] & SOL_CID_MASK) >> 6);
- return 0;
+ return 0;
+ }
}
if (num_processed >= 32) {
- hwi_ring_cq_db(phba, phwi_context->be_cq.id,
+ hwi_ring_cq_db(phba, cq->id,
num_processed, 0, 0);
tot_nump += num_processed;
num_processed = 0;
@@ -1258,8 +1487,12 @@ static unsigned int beiscsi_process_cq(struct beiscsi_hba *phba)
hwi_complete_drvr_msgs(beiscsi_conn, phba, sol);
break;
case UNSOL_HDR_NOTIFY:
+ SE_DEBUG(DBG_LVL_8, "Received UNSOL_HDR_ NOTIFY\n");
+ hwi_process_default_pdu_ring(beiscsi_conn, phba,
+ (struct i_t_dpdu_cqe *)sol);
+ break;
case UNSOL_DATA_NOTIFY:
- SE_DEBUG(DBG_LVL_8, "Received UNSOL_HDR/DATA_NOTIFY\n");
+ SE_DEBUG(DBG_LVL_8, "Received UNSOL_DATA_NOTIFY\n");
hwi_process_default_pdu_ring(beiscsi_conn, phba,
(struct i_t_dpdu_cqe *)sol);
break;
@@ -1278,13 +1511,21 @@ static unsigned int beiscsi_process_cq(struct beiscsi_hba *phba)
case CMD_CXN_KILLED_ITT_INVALID:
case CMD_CXN_KILLED_SEQ_OUTOFORDER:
case CMD_CXN_KILLED_INVALID_DATASN_RCVD:
- SE_DEBUG(DBG_LVL_1,
+ if (ring_mode) {
+ SE_DEBUG(DBG_LVL_1,
+ "CQ Error notification for cmd.. "
+ "code %d cid 0x%x\n",
+ sol->dw[offsetof(struct amap_sol_cqe, code) /
+ 32] & CQE_CODE_MASK, psgl_handle->cid);
+ } else {
+ SE_DEBUG(DBG_LVL_1,
"CQ Error notification for cmd.. "
"code %d cid 0x%x\n",
sol->dw[offsetof(struct amap_sol_cqe, code) /
32] & CQE_CODE_MASK,
(sol->dw[offsetof(struct amap_sol_cqe, cid) /
32] & SOL_CID_MASK));
+ }
break;
case UNSOL_DATA_DIGEST_ERROR_NOTIFY:
SE_DEBUG(DBG_LVL_1,
@@ -1306,23 +1547,37 @@ static unsigned int beiscsi_process_cq(struct beiscsi_hba *phba)
case CXN_KILLED_OVER_RUN_RESIDUAL:
case CXN_KILLED_UNDER_RUN_RESIDUAL:
case CXN_KILLED_CMND_DATA_NOT_ON_SAME_CONN:
- SE_DEBUG(DBG_LVL_1, "CQ Error %d, resetting CID "
+ if (ring_mode) {
+ SE_DEBUG(DBG_LVL_1, "CQ Error %d, reset CID "
+ "0x%x...\n",
+ sol->dw[offsetof(struct amap_sol_cqe, code) /
+ 32] & CQE_CODE_MASK, psgl_handle->cid);
+ } else {
+ SE_DEBUG(DBG_LVL_1, "CQ Error %d, reset CID "
"0x%x...\n",
sol->dw[offsetof(struct amap_sol_cqe, code) /
32] & CQE_CODE_MASK,
sol->dw[offsetof(struct amap_sol_cqe, cid) /
32] & CQE_CID_MASK);
+ }
iscsi_conn_failure(beiscsi_conn->conn,
ISCSI_ERR_CONN_FAILED);
break;
case CXN_KILLED_RST_SENT:
case CXN_KILLED_RST_RCVD:
- SE_DEBUG(DBG_LVL_1, "CQ Error %d, reset received/sent "
- "on CID 0x%x...\n",
+ if (ring_mode) {
+ SE_DEBUG(DBG_LVL_1, "CQ Error %d, reset"
+ "received/sent on CID 0x%x...\n",
+ sol->dw[offsetof(struct amap_sol_cqe, code) /
+ 32] & CQE_CODE_MASK, psgl_handle->cid);
+ } else {
+ SE_DEBUG(DBG_LVL_1, "CQ Error %d, reset"
+ "received/sent on CID 0x%x...\n",
sol->dw[offsetof(struct amap_sol_cqe, code) /
32] & CQE_CODE_MASK,
sol->dw[offsetof(struct amap_sol_cqe, cid) /
32] & CQE_CID_MASK);
+ }
iscsi_conn_failure(beiscsi_conn->conn,
ISCSI_ERR_CONN_FAILED);
break;
@@ -1344,8 +1599,7 @@ static unsigned int beiscsi_process_cq(struct beiscsi_hba *phba)
if (num_processed > 0) {
tot_nump += num_processed;
- hwi_ring_cq_db(phba, phwi_context->be_cq.id, num_processed,
- 1, 0);
+ hwi_ring_cq_db(phba, cq->id, num_processed, 1, 0);
}
return tot_nump;
}
@@ -1353,21 +1607,30 @@ static unsigned int beiscsi_process_cq(struct beiscsi_hba *phba)
static void beiscsi_process_all_cqs(struct work_struct *work)
{
unsigned long flags;
+ struct hwi_controller *phwi_ctrlr;
+ struct hwi_context_memory *phwi_context;
+ struct be_eq_obj *pbe_eq;
struct beiscsi_hba *phba =
container_of(work, struct beiscsi_hba, work_cqs);
+ phwi_ctrlr = phba->phwi_ctrlr;
+ phwi_context = phwi_ctrlr->phwi_ctxt;
+ if (phba->msix_enabled)
+ pbe_eq = &phwi_context->be_eq[phba->num_cpus];
+ else
+ pbe_eq = &phwi_context->be_eq[0];
+
if (phba->todo_mcc_cq) {
spin_lock_irqsave(&phba->isr_lock, flags);
phba->todo_mcc_cq = 0;
spin_unlock_irqrestore(&phba->isr_lock, flags);
- SE_DEBUG(DBG_LVL_1, "MCC Interrupt Not expected \n");
}
if (phba->todo_cq) {
spin_lock_irqsave(&phba->isr_lock, flags);
phba->todo_cq = 0;
spin_unlock_irqrestore(&phba->isr_lock, flags);
- beiscsi_process_cq(phba);
+ beiscsi_process_cq(pbe_eq);
}
}
@@ -1375,19 +1638,15 @@ static int be_iopoll(struct blk_iopoll *iop, int budget)
{
static unsigned int ret;
struct beiscsi_hba *phba;
+ struct be_eq_obj *pbe_eq;
- phba = container_of(iop, struct beiscsi_hba, iopoll);
-
- ret = beiscsi_process_cq(phba);
+ pbe_eq = container_of(iop, struct be_eq_obj, iopoll);
+ ret = beiscsi_process_cq(pbe_eq);
if (ret < budget) {
- struct hwi_controller *phwi_ctrlr;
- struct hwi_context_memory *phwi_context;
-
- phwi_ctrlr = phba->phwi_ctrlr;
- phwi_context = phwi_ctrlr->phwi_ctxt;
+ phba = pbe_eq->phba;
blk_iopoll_complete(iop);
- hwi_ring_eq_db(phba, phwi_context->be_eq.q.id, 0,
- 0, 1, 1);
+ SE_DEBUG(DBG_LVL_8, "rearm pbe_eq->q.id =%d\n", pbe_eq->q.id);
+ hwi_ring_eq_db(phba, pbe_eq->q.id, 0, 0, 1, 1);
}
return ret;
}
@@ -1537,14 +1796,12 @@ static void hwi_write_buffer(struct iscsi_wrb *pwrb, struct iscsi_task *task)
static void beiscsi_find_mem_req(struct beiscsi_hba *phba)
{
- unsigned int num_cq_pages, num_eq_pages, num_async_pdu_buf_pages;
+ unsigned int num_cq_pages, num_async_pdu_buf_pages;
unsigned int num_async_pdu_data_pages, wrb_sz_per_cxn;
unsigned int num_async_pdu_buf_sgl_pages, num_async_pdu_data_sgl_pages;
num_cq_pages = PAGES_REQUIRED(phba->params.num_cq_entries * \
sizeof(struct sol_cqe));
- num_eq_pages = PAGES_REQUIRED(phba->params.num_eq_entries * \
- sizeof(struct be_eq_entry));
num_async_pdu_buf_pages =
PAGES_REQUIRED(phba->params.asyncpdus_per_ctrl * \
phba->params.defpdu_hdr_sz);
@@ -1565,8 +1822,6 @@ static void beiscsi_find_mem_req(struct beiscsi_hba *phba)
phba->mem_req[HWI_MEM_ADDN_CONTEXT] =
sizeof(struct hwi_context_memory);
- phba->mem_req[HWI_MEM_CQ] = num_cq_pages * PAGE_SIZE;
- phba->mem_req[HWI_MEM_EQ] = num_eq_pages * PAGE_SIZE;
phba->mem_req[HWI_MEM_WRB] = sizeof(struct iscsi_wrb)
* (phba->params.wrbs_per_cxn)
@@ -1751,8 +2006,6 @@ static void beiscsi_init_wrb_handle(struct beiscsi_hba *phba)
for (index = 0; index < phba->params.cxns_per_ctrl * 2; index += 2) {
pwrb_context = &phwi_ctrlr->wrb_context[index];
- SE_DEBUG(DBG_LVL_8, "cid=%d pwrb_context=%p \n", index,
- pwrb_context);
pwrb_context->pwrb_handle_base =
kzalloc(sizeof(struct wrb_handle *) *
phba->params.wrbs_per_cxn, GFP_KERNEL);
@@ -1767,6 +2020,7 @@ static void beiscsi_init_wrb_handle(struct beiscsi_hba *phba)
pwrb_context->pwrb_handle_basestd[j] =
pwrb_handle;
pwrb_context->wrb_handles_available++;
+ pwrb_handle->wrb_index = j;
pwrb_handle++;
}
pwrb_context->free_index = 0;
@@ -1785,6 +2039,7 @@ static void beiscsi_init_wrb_handle(struct beiscsi_hba *phba)
pwrb_context->pwrb_handle_basestd[j] =
pwrb_handle;
pwrb_context->wrb_handles_available++;
+ pwrb_handle->wrb_index = j;
pwrb_handle++;
}
pwrb_context->free_index = 0;
@@ -2042,79 +2297,126 @@ static int be_fill_queue(struct be_queue_info *q,
return 0;
}
-static int beiscsi_create_eq(struct beiscsi_hba *phba,
+static int beiscsi_create_eqs(struct beiscsi_hba *phba,
struct hwi_context_memory *phwi_context)
{
- unsigned int idx;
- int ret;
+ unsigned int i, num_eq_pages;
+ int ret, eq_for_mcc;
struct be_queue_info *eq;
struct be_dma_mem *mem;
- struct be_mem_descriptor *mem_descr;
void *eq_vaddress;
+ dma_addr_t paddr;
- idx = 0;
- eq = &phwi_context->be_eq.q;
- mem = &eq->dma_mem;
- mem_descr = phba->init_mem;
- mem_descr += HWI_MEM_EQ;
- eq_vaddress = mem_descr->mem_array[idx].virtual_address;
-
- ret = be_fill_queue(eq, phba->params.num_eq_entries,
- sizeof(struct be_eq_entry), eq_vaddress);
- if (ret) {
- shost_printk(KERN_ERR, phba->shost,
- "be_fill_queue Failed for EQ \n");
- return ret;
- }
+ num_eq_pages = PAGES_REQUIRED(phba->params.num_eq_entries * \
+ sizeof(struct be_eq_entry));
- mem->dma = mem_descr->mem_array[idx].bus_address.u.a64.address;
+ if (phba->msix_enabled)
+ eq_for_mcc = 1;
+ else
+ eq_for_mcc = 0;
+ for (i = 0; i < (phba->num_cpus + eq_for_mcc); i++) {
+ eq = &phwi_context->be_eq[i].q;
+ mem = &eq->dma_mem;
+ phwi_context->be_eq[i].phba = phba;
+ eq_vaddress = pci_alloc_consistent(phba->pcidev,
+ num_eq_pages * PAGE_SIZE,
+ &paddr);
+ if (!eq_vaddress)
+ goto create_eq_error;
+
+ mem->va = eq_vaddress;
+ ret = be_fill_queue(eq, phba->params.num_eq_entries,
+ sizeof(struct be_eq_entry), eq_vaddress);
+ if (ret) {
+ shost_printk(KERN_ERR, phba->shost,
+ "be_fill_queue Failed for EQ \n");
+ goto create_eq_error;
+ }
- ret = beiscsi_cmd_eq_create(&phba->ctrl, eq,
- phwi_context->be_eq.cur_eqd);
- if (ret) {
- shost_printk(KERN_ERR, phba->shost, "beiscsi_cmd_eq_create"
- "Failedfor EQ \n");
- return ret;
+ mem->dma = paddr;
+ ret = beiscsi_cmd_eq_create(&phba->ctrl, eq,
+ phwi_context->cur_eqd);
+ if (ret) {
+ shost_printk(KERN_ERR, phba->shost,
+ "beiscsi_cmd_eq_create"
+ "Failedfor EQ \n");
+ goto create_eq_error;
+ }
+ SE_DEBUG(DBG_LVL_8, "eqid = %d\n", phwi_context->be_eq[i].q.id);
}
- SE_DEBUG(DBG_LVL_8, "eq id is %d\n", phwi_context->be_eq.q.id);
return 0;
+create_eq_error:
+ for (i = 0; i < (phba->num_cpus + 1); i++) {
+ eq = &phwi_context->be_eq[i].q;
+ mem = &eq->dma_mem;
+ if (mem->va)
+ pci_free_consistent(phba->pcidev, num_eq_pages
+ * PAGE_SIZE,
+ mem->va, mem->dma);
+ }
+ return ret;
}
-static int beiscsi_create_cq(struct beiscsi_hba *phba,
+static int beiscsi_create_cqs(struct beiscsi_hba *phba,
struct hwi_context_memory *phwi_context)
{
- unsigned int idx;
+ unsigned int i, num_cq_pages;
int ret;
struct be_queue_info *cq, *eq;
struct be_dma_mem *mem;
- struct be_mem_descriptor *mem_descr;
+ struct be_eq_obj *pbe_eq;
void *cq_vaddress;
+ dma_addr_t paddr;
- idx = 0;
- cq = &phwi_context->be_cq;
- eq = &phwi_context->be_eq.q;
- mem = &cq->dma_mem;
- mem_descr = phba->init_mem;
- mem_descr += HWI_MEM_CQ;
- cq_vaddress = mem_descr->mem_array[idx].virtual_address;
- ret = be_fill_queue(cq, phba->params.icds_per_ctrl / 2,
- sizeof(struct sol_cqe), cq_vaddress);
- if (ret) {
- shost_printk(KERN_ERR, phba->shost,
- "be_fill_queue Failed for ISCSI CQ \n");
- return ret;
- }
+ num_cq_pages = PAGES_REQUIRED(phba->params.num_cq_entries * \
+ sizeof(struct sol_cqe));
- mem->dma = mem_descr->mem_array[idx].bus_address.u.a64.address;
- ret = beiscsi_cmd_cq_create(&phba->ctrl, cq, eq, false, false, 0);
- if (ret) {
- shost_printk(KERN_ERR, phba->shost,
- "beiscsi_cmd_eq_create Failed for ISCSI CQ \n");
- return ret;
+ for (i = 0; i < phba->num_cpus; i++) {
+ cq = &phwi_context->be_cq[i];
+ eq = &phwi_context->be_eq[i].q;
+ pbe_eq = &phwi_context->be_eq[i];
+ pbe_eq->cq = cq;
+ pbe_eq->phba = phba;
+ mem = &cq->dma_mem;
+ cq_vaddress = pci_alloc_consistent(phba->pcidev,
+ num_cq_pages * PAGE_SIZE,
+ &paddr);
+ if (!cq_vaddress)
+ goto create_cq_error;
+ ret = be_fill_queue(cq, phba->params.icds_per_ctrl / 2,
+ sizeof(struct sol_cqe), cq_vaddress);
+ if (ret) {
+ shost_printk(KERN_ERR, phba->shost,
+ "be_fill_queue Failed for ISCSI CQ \n");
+ goto create_cq_error;
+ }
+
+ mem->dma = paddr;
+ ret = beiscsi_cmd_cq_create(&phba->ctrl, cq, eq, false,
+ false, 0);
+ if (ret) {
+ shost_printk(KERN_ERR, phba->shost,
+ "beiscsi_cmd_eq_create"
+ "Failed for ISCSI CQ \n");
+ goto create_cq_error;
+ }
+ SE_DEBUG(DBG_LVL_8, "iscsi cq_id is %d for eq_id %d\n",
+ cq->id, eq->id);
+ SE_DEBUG(DBG_LVL_8, "ISCSI CQ CREATED\n");
}
- SE_DEBUG(DBG_LVL_8, "iscsi cq id is %d\n", phwi_context->be_cq.id);
- SE_DEBUG(DBG_LVL_8, "ISCSI CQ CREATED\n");
return 0;
+
+create_cq_error:
+ for (i = 0; i < phba->num_cpus; i++) {
+ cq = &phwi_context->be_cq[i];
+ mem = &cq->dma_mem;
+ if (mem->va)
+ pci_free_consistent(phba->pcidev, num_cq_pages
+ * PAGE_SIZE,
+ mem->va, mem->dma);
+ }
+ return ret;
+
}
static int
@@ -2132,7 +2434,7 @@ beiscsi_create_def_hdr(struct beiscsi_hba *phba,
idx = 0;
dq = &phwi_context->be_def_hdrq;
- cq = &phwi_context->be_cq;
+ cq = &phwi_context->be_cq[0];
mem = &dq->dma_mem;
mem_descr = phba->init_mem;
mem_descr += HWI_MEM_ASYNC_HEADER_RING;
@@ -2176,7 +2478,7 @@ beiscsi_create_def_data(struct beiscsi_hba *phba,
idx = 0;
dataq = &phwi_context->be_def_dataq;
- cq = &phwi_context->be_cq;
+ cq = &phwi_context->be_cq[0];
mem = &dataq->dma_mem;
mem_descr = phba->init_mem;
mem_descr += HWI_MEM_ASYNC_DATA_RING;
@@ -2239,6 +2541,30 @@ beiscsi_post_pages(struct beiscsi_hba *phba)
return 0;
}
+static void be_queue_free(struct beiscsi_hba *phba, struct be_queue_info *q)
+{
+ struct be_dma_mem *mem = &q->dma_mem;
+ if (mem->va)
+ pci_free_consistent(phba->pcidev, mem->size,
+ mem->va, mem->dma);
+}
+
+static int be_queue_alloc(struct beiscsi_hba *phba, struct be_queue_info *q,
+ u16 len, u16 entry_size)
+{
+ struct be_dma_mem *mem = &q->dma_mem;
+
+ memset(q, 0, sizeof(*q));
+ q->len = len;
+ q->entry_size = entry_size;
+ mem->size = len * entry_size;
+ mem->va = pci_alloc_consistent(phba->pcidev, mem->size, &mem->dma);
+ if (!mem->va)
+ return -1;
+ memset(mem->va, 0, mem->size);
+ return 0;
+}
+
static int
beiscsi_create_wrb_rings(struct beiscsi_hba *phba,
struct hwi_context_memory *phwi_context,
@@ -2328,13 +2654,29 @@ static void free_wrb_handles(struct beiscsi_hba *phba)
}
}
+static void be_mcc_queues_destroy(struct beiscsi_hba *phba)
+{
+ struct be_queue_info *q;
+ struct be_ctrl_info *ctrl = &phba->ctrl;
+
+ q = &phba->ctrl.mcc_obj.q;
+ if (q->created)
+ beiscsi_cmd_q_destroy(ctrl, q, QTYPE_MCCQ);
+ be_queue_free(phba, q);
+
+ q = &phba->ctrl.mcc_obj.cq;
+ if (q->created)
+ beiscsi_cmd_q_destroy(ctrl, q, QTYPE_CQ);
+ be_queue_free(phba, q);
+}
+
static void hwi_cleanup(struct beiscsi_hba *phba)
{
struct be_queue_info *q;
struct be_ctrl_info *ctrl = &phba->ctrl;
struct hwi_controller *phwi_ctrlr;
struct hwi_context_memory *phwi_context;
- int i;
+ int i, eq_num;
phwi_ctrlr = phba->phwi_ctrlr;
phwi_context = phwi_ctrlr->phwi_ctxt;
@@ -2343,7 +2685,6 @@ static void hwi_cleanup(struct beiscsi_hba *phba)
if (q->created)
beiscsi_cmd_q_destroy(ctrl, q, QTYPE_WRBQ);
}
-
free_wrb_handles(phba);
q = &phwi_context->be_def_hdrq;
@@ -2356,13 +2697,76 @@ static void hwi_cleanup(struct beiscsi_hba *phba)
beiscsi_cmd_q_destroy(ctrl, NULL, QTYPE_SGL);
- q = &phwi_context->be_cq;
- if (q->created)
- beiscsi_cmd_q_destroy(ctrl, q, QTYPE_CQ);
+ for (i = 0; i < (phba->num_cpus); i++) {
+ q = &phwi_context->be_cq[i];
+ if (q->created)
+ beiscsi_cmd_q_destroy(ctrl, q, QTYPE_CQ);
+ }
+ if (phba->msix_enabled)
+ eq_num = 1;
+ else
+ eq_num = 0;
+ for (i = 0; i < (phba->num_cpus + eq_num); i++) {
+ q = &phwi_context->be_eq[i].q;
+ if (q->created)
+ beiscsi_cmd_q_destroy(ctrl, q, QTYPE_EQ);
+ }
+ be_mcc_queues_destroy(phba);
+}
- q = &phwi_context->be_eq.q;
- if (q->created)
- beiscsi_cmd_q_destroy(ctrl, q, QTYPE_EQ);
+static int be_mcc_queues_create(struct beiscsi_hba *phba,
+ struct hwi_context_memory *phwi_context)
+{
+ struct be_queue_info *q, *cq;
+ struct be_ctrl_info *ctrl = &phba->ctrl;
+
+ /* Alloc MCC compl queue */
+ cq = &phba->ctrl.mcc_obj.cq;
+ if (be_queue_alloc(phba, cq, MCC_CQ_LEN,
+ sizeof(struct be_mcc_compl)))
+ goto err;
+ /* Ask BE to create MCC compl queue; */
+ if (phba->msix_enabled) {
+ if (beiscsi_cmd_cq_create(ctrl, cq, &phwi_context->be_eq
+ [phba->num_cpus].q, false, true, 0))
+ goto mcc_cq_free;
+ } else {
+ if (beiscsi_cmd_cq_create(ctrl, cq, &phwi_context->be_eq[0].q,
+ false, true, 0))
+ goto mcc_cq_free;
+ }
+
+ /* Alloc MCC queue */
+ q = &phba->ctrl.mcc_obj.q;
+ if (be_queue_alloc(phba, q, MCC_Q_LEN, sizeof(struct be_mcc_wrb)))
+ goto mcc_cq_destroy;
+
+ /* Ask BE to create MCC queue */
+ if (beiscsi_cmd_mccq_create(phba, q, cq))
+ goto mcc_q_free;
+
+ return 0;
+
+mcc_q_free:
+ be_queue_free(phba, q);
+mcc_cq_destroy:
+ beiscsi_cmd_q_destroy(ctrl, cq, QTYPE_CQ);
+mcc_cq_free:
+ be_queue_free(phba, cq);
+err:
+ return -1;
+}
+
+static int find_num_cpus(void)
+{
+ int num_cpus = 0;
+
+ num_cpus = num_online_cpus();
+ if (num_cpus >= MAX_CPUS)
+ num_cpus = MAX_CPUS - 1;
+
+ SE_DEBUG(DBG_LVL_8, "num_cpus = %d \n", num_cpus);
+ return num_cpus;
}
static int hwi_init_port(struct beiscsi_hba *phba)
@@ -2376,26 +2780,33 @@ static int hwi_init_port(struct beiscsi_hba *phba)
def_pdu_ring_sz =
phba->params.asyncpdus_per_ctrl * sizeof(struct phys_addr);
phwi_ctrlr = phba->phwi_ctrlr;
-
phwi_context = phwi_ctrlr->phwi_ctxt;
- phwi_context->be_eq.max_eqd = 0;
- phwi_context->be_eq.min_eqd = 0;
- phwi_context->be_eq.cur_eqd = 64;
- phwi_context->be_eq.enable_aic = false;
+ phwi_context->max_eqd = 0;
+ phwi_context->min_eqd = 0;
+ phwi_context->cur_eqd = 64;
be_cmd_fw_initialize(&phba->ctrl);
- status = beiscsi_create_eq(phba, phwi_context);
+
+ status = beiscsi_create_eqs(phba, phwi_context);
if (status != 0) {
shost_printk(KERN_ERR, phba->shost, "EQ not created \n");
goto error;
}
- status = mgmt_check_supported_fw(ctrl);
+ status = be_mcc_queues_create(phba, phwi_context);
+ if (status != 0)
+ goto error;
+
+ status = mgmt_check_supported_fw(ctrl, phba);
if (status != 0) {
shost_printk(KERN_ERR, phba->shost,
"Unsupported fw version \n");
goto error;
}
+ if (phba->fw_config.iscsi_features == 0x1)
+ ring_mode = 1;
+ else
+ ring_mode = 0;
status = mgmt_get_fw_config(ctrl, phba);
if (status != 0) {
shost_printk(KERN_ERR, phba->shost,
@@ -2403,7 +2814,7 @@ static int hwi_init_port(struct beiscsi_hba *phba)
goto error;
}
- status = beiscsi_create_cq(phba, phwi_context);
+ status = beiscsi_create_cqs(phba, phwi_context);
if (status != 0) {
shost_printk(KERN_ERR, phba->shost, "CQ not created\n");
goto error;
@@ -2447,7 +2858,6 @@ error:
return -ENOMEM;
}
-
static int hwi_init_controller(struct beiscsi_hba *phba)
{
struct hwi_controller *phwi_ctrlr;
@@ -2530,6 +2940,18 @@ static int beiscsi_init_sgl_handle(struct beiscsi_hba *phba)
phba->io_sgl_hndl_avbl = 0;
phba->eh_sgl_hndl_avbl = 0;
+
+ if (ring_mode) {
+ phba->sgl_hndl_array = kzalloc(sizeof(struct sgl_handle *) *
+ phba->params.icds_per_ctrl,
+ GFP_KERNEL);
+ if (!phba->sgl_hndl_array) {
+ shost_printk(KERN_ERR, phba->shost,
+ "Mem Alloc Failed. Failing to load\n");
+ return -ENOMEM;
+ }
+ }
+
mem_descr_sglh = phba->init_mem;
mem_descr_sglh += HWI_MEM_SGLH;
if (1 == mem_descr_sglh->num_elements) {
@@ -2537,6 +2959,8 @@ static int beiscsi_init_sgl_handle(struct beiscsi_hba *phba)
phba->params.ios_per_ctrl,
GFP_KERNEL);
if (!phba->io_sgl_hndl_base) {
+ if (ring_mode)
+ kfree(phba->sgl_hndl_array);
shost_printk(KERN_ERR, phba->shost,
"Mem Alloc Failed. Failing to load\n");
return -ENOMEM;
@@ -2656,13 +3080,12 @@ static unsigned char hwi_enable_intr(struct beiscsi_hba *phba)
struct hwi_context_memory *phwi_context;
struct be_queue_info *eq;
u8 __iomem *addr;
- u32 reg;
+ u32 reg, i;
u32 enabled;
phwi_ctrlr = phba->phwi_ctrlr;
phwi_context = phwi_ctrlr->phwi_ctxt;
- eq = &phwi_context->be_eq.q;
addr = (u8 __iomem *) ((u8 __iomem *) ctrl->pcicfg +
PCICFG_MEMBAR_CTRL_INT_CTRL_OFFSET);
reg = ioread32(addr);
@@ -2673,9 +3096,11 @@ static unsigned char hwi_enable_intr(struct beiscsi_hba *phba)
reg |= MEMBAR_CTRL_INT_CTRL_HOSTINTR_MASK;
SE_DEBUG(DBG_LVL_8, "reg =x%08x addr=%p \n", reg, addr);
iowrite32(reg, addr);
- SE_DEBUG(DBG_LVL_8, "eq->id=%d \n", eq->id);
-
- hwi_ring_eq_db(phba, eq->id, 0, 0, 1, 1);
+ for (i = 0; i <= phba->num_cpus; i++) {
+ eq = &phwi_context->be_eq[i].q;
+ SE_DEBUG(DBG_LVL_8, "eq->id=%d \n", eq->id);
+ hwi_ring_eq_db(phba, eq->id, 0, 0, 1, 1);
+ }
} else
shost_printk(KERN_WARNING, phba->shost,
"In hwi_enable_intr, Not Enabled \n");
@@ -2720,6 +3145,8 @@ static int beiscsi_init_port(struct beiscsi_hba *phba)
if (hba_setup_cid_tbls(phba)) {
shost_printk(KERN_ERR, phba->shost,
"Failed in hba_setup_cid_tbls\n");
+ if (ring_mode)
+ kfree(phba->sgl_hndl_array);
kfree(phba->io_sgl_hndl_base);
kfree(phba->eh_sgl_hndl_base);
goto do_cleanup_ctrlr;
@@ -2738,17 +3165,25 @@ static void hwi_purge_eq(struct beiscsi_hba *phba)
struct hwi_context_memory *phwi_context;
struct be_queue_info *eq;
struct be_eq_entry *eqe = NULL;
+ int i, eq_msix;
phwi_ctrlr = phba->phwi_ctrlr;
phwi_context = phwi_ctrlr->phwi_ctxt;
- eq = &phwi_context->be_eq.q;
- eqe = queue_tail_node(eq);
+ if (phba->msix_enabled)
+ eq_msix = 1;
+ else
+ eq_msix = 0;
- while (eqe->dw[offsetof(struct amap_eq_entry, valid) / 32]
- & EQE_VALID_MASK) {
- AMAP_SET_BITS(struct amap_eq_entry, valid, eqe, 0);
- queue_tail_inc(eq);
+ for (i = 0; i < (phba->num_cpus + eq_msix); i++) {
+ eq = &phwi_context->be_eq[i].q;
eqe = queue_tail_node(eq);
+
+ while (eqe->dw[offsetof(struct amap_eq_entry, valid) / 32]
+ & EQE_VALID_MASK) {
+ AMAP_SET_BITS(struct amap_eq_entry, valid, eqe, 0);
+ queue_tail_inc(eq);
+ eqe = queue_tail_node(eq);
+ }
}
}
@@ -2762,6 +3197,8 @@ static void beiscsi_clean_port(struct beiscsi_hba *phba)
"mgmt_epfw_cleanup FAILED \n");
hwi_cleanup(phba);
hwi_purge_eq(phba);
+ if (ring_mode)
+ kfree(phba->sgl_hndl_array);
kfree(phba->io_sgl_hndl_base);
kfree(phba->eh_sgl_hndl_base);
kfree(phba->cid_array);
@@ -2846,8 +3283,9 @@ beiscsi_offload_connection(struct beiscsi_conn *beiscsi_conn,
be_dws_le_to_cpu(pwrb, sizeof(struct iscsi_target_context_update_wrb));
doorbell |= beiscsi_conn->beiscsi_conn_cid & DB_WRB_POST_CID_MASK;
- doorbell |= (pwrb_handle->wrb_index & DB_DEF_PDU_WRB_INDEX_MASK) <<
- DB_DEF_PDU_WRB_INDEX_SHIFT;
+ if (!ring_mode)
+ doorbell |= (pwrb_handle->wrb_index & DB_DEF_PDU_WRB_INDEX_MASK)
+ << DB_DEF_PDU_WRB_INDEX_SHIFT;
doorbell |= 1 << DB_DEF_PDU_NUM_POSTED_SHIFT;
iowrite32(doorbell, phba->db_va + DB_TXULP0_OFFSET);
@@ -2856,7 +3294,7 @@ beiscsi_offload_connection(struct beiscsi_conn *beiscsi_conn,
static void beiscsi_parse_pdu(struct iscsi_conn *conn, itt_t itt,
int *index, int *age)
{
- *index = be32_to_cpu(itt) >> 16;
+ *index = (int)itt;
if (age)
*age = conn->session->age;
}
@@ -2885,15 +3323,13 @@ static int beiscsi_alloc_pdu(struct iscsi_task *task, uint8_t opcode)
io_task->cmd_bhs = pci_pool_alloc(beiscsi_sess->bhs_pool,
GFP_KERNEL, &paddr);
-
if (!io_task->cmd_bhs)
return -ENOMEM;
-
io_task->bhs_pa.u.a64.address = paddr;
+ io_task->libiscsi_itt = (itt_t)task->itt;
io_task->pwrb_handle = alloc_wrb_handle(phba,
beiscsi_conn->beiscsi_conn_cid,
task->itt);
- io_task->pwrb_handle->pio_handle = task;
io_task->conn = beiscsi_conn;
task->hdr = (struct iscsi_hdr *)&io_task->cmd_bhs->iscsi_hdr;
@@ -2905,7 +3341,6 @@ static int beiscsi_alloc_pdu(struct iscsi_task *task, uint8_t opcode)
spin_unlock(&phba->io_sgl_lock);
if (!io_task->psgl_handle)
goto free_hndls;
-
} else {
io_task->scsi_cmnd = NULL;
if ((task->hdr->opcode & ISCSI_OPCODE_MASK) == ISCSI_OP_LOGIN) {
@@ -2932,8 +3367,18 @@ static int beiscsi_alloc_pdu(struct iscsi_task *task, uint8_t opcode)
goto free_hndls;
}
}
- itt = (itt_t) cpu_to_be32(((unsigned int)task->itt << 16) |
- (unsigned int)(io_task->psgl_handle->sgl_index));
+ itt = (itt_t) cpu_to_be32(((unsigned int)io_task->pwrb_handle->
+ wrb_index << 16) | (unsigned int)
+ (io_task->psgl_handle->sgl_index));
+ if (ring_mode) {
+ phba->sgl_hndl_array[io_task->psgl_handle->sgl_index -
+ phba->fw_config.iscsi_cid_start] =
+ io_task->psgl_handle;
+ io_task->psgl_handle->task = task;
+ io_task->psgl_handle->cid = beiscsi_conn->beiscsi_conn_cid;
+ } else
+ io_task->pwrb_handle->pio_handle = task;
+
io_task->cmd_bhs->iscsi_hdr.itt = itt;
return 0;
@@ -3006,7 +3451,6 @@ static int beiscsi_iotask(struct iscsi_task *task, struct scatterlist *sg,
io_task->bhs_len = sizeof(struct be_cmd_bhs);
if (writedir) {
- SE_DEBUG(DBG_LVL_4, " WRITE Command \t");
memset(&io_task->cmd_bhs->iscsi_data_pdu, 0, 48);
AMAP_SET_BITS(struct amap_pdu_data_out, itt,
&io_task->cmd_bhs->iscsi_data_pdu,
@@ -3016,11 +3460,18 @@ static int beiscsi_iotask(struct iscsi_task *task, struct scatterlist *sg,
ISCSI_OPCODE_SCSI_DATA_OUT);
AMAP_SET_BITS(struct amap_pdu_data_out, final_bit,
&io_task->cmd_bhs->iscsi_data_pdu, 1);
- AMAP_SET_BITS(struct amap_iscsi_wrb, type, pwrb, INI_WR_CMD);
+ if (ring_mode)
+ io_task->psgl_handle->type = INI_WR_CMD;
+ else
+ AMAP_SET_BITS(struct amap_iscsi_wrb, type, pwrb,
+ INI_WR_CMD);
AMAP_SET_BITS(struct amap_iscsi_wrb, dsp, pwrb, 1);
} else {
- SE_DEBUG(DBG_LVL_4, "READ Command \t");
- AMAP_SET_BITS(struct amap_iscsi_wrb, type, pwrb, INI_RD_CMD);
+ if (ring_mode)
+ io_task->psgl_handle->type = INI_RD_CMD;
+ else
+ AMAP_SET_BITS(struct amap_iscsi_wrb, type, pwrb,
+ INI_RD_CMD);
AMAP_SET_BITS(struct amap_iscsi_wrb, dsp, pwrb, 0);
}
memcpy(&io_task->cmd_bhs->iscsi_data_pdu.
@@ -3045,7 +3496,8 @@ static int beiscsi_iotask(struct iscsi_task *task, struct scatterlist *sg,
be_dws_le_to_cpu(pwrb, sizeof(struct iscsi_wrb));
doorbell |= beiscsi_conn->beiscsi_conn_cid & DB_WRB_POST_CID_MASK;
- doorbell |= (io_task->pwrb_handle->wrb_index &
+ if (!ring_mode)
+ doorbell |= (io_task->pwrb_handle->wrb_index &
DB_DEF_PDU_WRB_INDEX_MASK) << DB_DEF_PDU_WRB_INDEX_SHIFT;
doorbell |= 1 << DB_DEF_PDU_NUM_POSTED_SHIFT;
@@ -3059,10 +3511,16 @@ static int beiscsi_mtask(struct iscsi_task *task)
struct iscsi_conn *conn = task->conn;
struct beiscsi_conn *beiscsi_conn = conn->dd_data;
struct beiscsi_hba *phba = beiscsi_conn->phba;
+ struct iscsi_session *session;
struct iscsi_wrb *pwrb = NULL;
+ struct hwi_controller *phwi_ctrlr;
+ struct hwi_wrb_context *pwrb_context;
+ struct wrb_handle *pwrb_handle;
unsigned int doorbell = 0;
+ unsigned int i, cid;
struct iscsi_task *aborted_task;
+ cid = beiscsi_conn->beiscsi_conn_cid;
pwrb = io_task->pwrb_handle->pwrb;
AMAP_SET_BITS(struct amap_iscsi_wrb, cmdsn_itt, pwrb,
be32_to_cpu(task->cmdsn));
@@ -3073,38 +3531,63 @@ static int beiscsi_mtask(struct iscsi_task *task)
switch (task->hdr->opcode & ISCSI_OPCODE_MASK) {
case ISCSI_OP_LOGIN:
- AMAP_SET_BITS(struct amap_iscsi_wrb, type, pwrb, TGT_DM_CMD);
+ if (ring_mode)
+ io_task->psgl_handle->type = TGT_DM_CMD;
+ else
+ AMAP_SET_BITS(struct amap_iscsi_wrb, type, pwrb,
+ TGT_DM_CMD);
AMAP_SET_BITS(struct amap_iscsi_wrb, dmsg, pwrb, 0);
AMAP_SET_BITS(struct amap_iscsi_wrb, cmdsn_itt, pwrb, 1);
hwi_write_buffer(pwrb, task);
break;
case ISCSI_OP_NOOP_OUT:
- AMAP_SET_BITS(struct amap_iscsi_wrb, type, pwrb, INI_RD_CMD);
+ if (ring_mode)
+ io_task->psgl_handle->type = INI_RD_CMD;
+ else
+ AMAP_SET_BITS(struct amap_iscsi_wrb, type, pwrb,
+ INI_RD_CMD);
hwi_write_buffer(pwrb, task);
break;
case ISCSI_OP_TEXT:
- AMAP_SET_BITS(struct amap_iscsi_wrb, type, pwrb, INI_WR_CMD);
+ if (ring_mode)
+ io_task->psgl_handle->type = INI_WR_CMD;
+ else
+ AMAP_SET_BITS(struct amap_iscsi_wrb, type, pwrb,
+ INI_WR_CMD);
AMAP_SET_BITS(struct amap_iscsi_wrb, dsp, pwrb, 1);
hwi_write_buffer(pwrb, task);
break;
case ISCSI_OP_SCSI_TMFUNC:
- aborted_task = iscsi_itt_to_task(conn,
- ((struct iscsi_tm *)task->hdr)->rtt);
+ session = conn->session;
+ i = ((struct iscsi_tm *)task->hdr)->rtt;
+ phwi_ctrlr = phba->phwi_ctrlr;
+ pwrb_context = &phwi_ctrlr->wrb_context[cid];
+ pwrb_handle = pwrb_context->pwrb_handle_basestd[be32_to_cpu(i)
+ >> 16];
+ aborted_task = pwrb_handle->pio_handle;
if (!aborted_task)
return 0;
+
aborted_io_task = aborted_task->dd_data;
if (!aborted_io_task->scsi_cmnd)
return 0;
mgmt_invalidate_icds(phba,
aborted_io_task->psgl_handle->sgl_index,
- beiscsi_conn->beiscsi_conn_cid);
- AMAP_SET_BITS(struct amap_iscsi_wrb, type, pwrb, INI_TMF_CMD);
+ cid);
+ if (ring_mode)
+ io_task->psgl_handle->type = INI_TMF_CMD;
+ else
+ AMAP_SET_BITS(struct amap_iscsi_wrb, type, pwrb,
+ INI_TMF_CMD);
AMAP_SET_BITS(struct amap_iscsi_wrb, dmsg, pwrb, 0);
hwi_write_buffer(pwrb, task);
break;
case ISCSI_OP_LOGOUT:
AMAP_SET_BITS(struct amap_iscsi_wrb, dmsg, pwrb, 0);
+ if (ring_mode)
+ io_task->psgl_handle->type = HWH_TYPE_LOGOUT;
+ else
AMAP_SET_BITS(struct amap_iscsi_wrb, type, pwrb,
HWH_TYPE_LOGOUT);
hwi_write_buffer(pwrb, task);
@@ -3122,8 +3605,9 @@ static int beiscsi_mtask(struct iscsi_task *task)
io_task->pwrb_handle->nxt_wrb_index);
be_dws_le_to_cpu(pwrb, sizeof(struct iscsi_wrb));
- doorbell |= beiscsi_conn->beiscsi_conn_cid & DB_WRB_POST_CID_MASK;
- doorbell |= (io_task->pwrb_handle->wrb_index &
+ doorbell |= cid & DB_WRB_POST_CID_MASK;
+ if (!ring_mode)
+ doorbell |= (io_task->pwrb_handle->wrb_index &
DB_DEF_PDU_WRB_INDEX_MASK) << DB_DEF_PDU_WRB_INDEX_SHIFT;
doorbell |= 1 << DB_DEF_PDU_NUM_POSTED_SHIFT;
iowrite32(doorbell, phba->db_va + DB_TXULP0_OFFSET);
@@ -3165,9 +3649,14 @@ static int beiscsi_task_xmit(struct iscsi_task *task)
return beiscsi_iotask(task, sg, num_sg, xferlen, writedir);
}
+
static void beiscsi_remove(struct pci_dev *pcidev)
{
struct beiscsi_hba *phba = NULL;
+ struct hwi_controller *phwi_ctrlr;
+ struct hwi_context_memory *phwi_context;
+ struct be_eq_obj *pbe_eq;
+ unsigned int i, msix_vec;
phba = (struct beiscsi_hba *)pci_get_drvdata(pcidev);
if (!phba) {
@@ -3175,12 +3664,24 @@ static void beiscsi_remove(struct pci_dev *pcidev)
return;
}
+ phwi_ctrlr = phba->phwi_ctrlr;
+ phwi_context = phwi_ctrlr->phwi_ctxt;
hwi_disable_intr(phba);
- if (phba->pcidev->irq)
- free_irq(phba->pcidev->irq, phba);
+ if (phba->msix_enabled) {
+ for (i = 0; i <= phba->num_cpus; i++) {
+ msix_vec = phba->msix_entries[i].vector;
+ free_irq(msix_vec, &phwi_context->be_eq[i]);
+ }
+ } else
+ if (phba->pcidev->irq)
+ free_irq(phba->pcidev->irq, phba);
+ pci_disable_msix(phba->pcidev);
destroy_workqueue(phba->wq);
if (blk_iopoll_enabled)
- blk_iopoll_disable(&phba->iopoll);
+ for (i = 0; i < phba->num_cpus; i++) {
+ pbe_eq = &phwi_context->be_eq[i];
+ blk_iopoll_disable(&pbe_eq->iopoll);
+ }
beiscsi_clean_port(phba);
beiscsi_free_mem(phba);
@@ -3194,11 +3695,29 @@ static void beiscsi_remove(struct pci_dev *pcidev)
iscsi_host_free(phba->shost);
}
+static void beiscsi_msix_enable(struct beiscsi_hba *phba)
+{
+ int i, status;
+
+ for (i = 0; i <= phba->num_cpus; i++)
+ phba->msix_entries[i].entry = i;
+
+ status = pci_enable_msix(phba->pcidev, phba->msix_entries,
+ (phba->num_cpus + 1));
+ if (!status)
+ phba->msix_enabled = true;
+
+ return;
+}
+
static int __devinit beiscsi_dev_probe(struct pci_dev *pcidev,
const struct pci_device_id *id)
{
struct beiscsi_hba *phba = NULL;
- int ret;
+ struct hwi_controller *phwi_ctrlr;
+ struct hwi_context_memory *phwi_context;
+ struct be_eq_obj *pbe_eq;
+ int ret, msix_vec, num_cpus, i;
ret = beiscsi_enable_pci(pcidev);
if (ret < 0) {
@@ -3213,8 +3732,18 @@ static int __devinit beiscsi_dev_probe(struct pci_dev *pcidev,
" Failed in beiscsi_hba_alloc \n");
goto disable_pci;
}
+ SE_DEBUG(DBG_LVL_8, " phba = %p \n", phba);
pci_set_drvdata(pcidev, phba);
+ if (enable_msix)
+ num_cpus = find_num_cpus();
+ else
+ num_cpus = 1;
+ phba->num_cpus = num_cpus;
+ SE_DEBUG(DBG_LVL_8, "num_cpus = %d \n", phba->num_cpus);
+
+ if (enable_msix)
+ beiscsi_msix_enable(phba);
ret = be_ctrl_init(phba, pcidev);
if (ret) {
shost_printk(KERN_ERR, phba->shost, "beiscsi_dev_probe-"
@@ -3235,7 +3764,7 @@ static int __devinit beiscsi_dev_probe(struct pci_dev *pcidev,
snprintf(phba->wq_name, sizeof(phba->wq_name), "beiscsi_q_irq%u",
phba->shost->host_no);
- phba->wq = create_singlethread_workqueue(phba->wq_name);
+ phba->wq = create_workqueue(phba->wq_name);
if (!phba->wq) {
shost_printk(KERN_ERR, phba->shost, "beiscsi_dev_probe-"
"Failed to allocate work queue\n");
@@ -3244,11 +3773,16 @@ static int __devinit beiscsi_dev_probe(struct pci_dev *pcidev,
INIT_WORK(&phba->work_cqs, beiscsi_process_all_cqs);
+ phwi_ctrlr = phba->phwi_ctrlr;
+ phwi_context = phwi_ctrlr->phwi_ctxt;
if (blk_iopoll_enabled) {
- blk_iopoll_init(&phba->iopoll, be_iopoll_budget, be_iopoll);
- blk_iopoll_enable(&phba->iopoll);
+ for (i = 0; i < phba->num_cpus; i++) {
+ pbe_eq = &phwi_context->be_eq[i];
+ blk_iopoll_init(&pbe_eq->iopoll, be_iopoll_budget,
+ be_iopoll);
+ blk_iopoll_enable(&pbe_eq->iopoll);
+ }
}
-
ret = beiscsi_init_irqs(phba);
if (ret < 0) {
shost_printk(KERN_ERR, phba->shost, "beiscsi_dev_probe-"
@@ -3261,17 +3795,26 @@ static int __devinit beiscsi_dev_probe(struct pci_dev *pcidev,
"Failed to hwi_enable_intr\n");
goto free_ctrlr;
}
-
SE_DEBUG(DBG_LVL_8, "\n\n\n SUCCESS - DRIVER LOADED \n\n\n");
return 0;
free_ctrlr:
- if (phba->pcidev->irq)
- free_irq(phba->pcidev->irq, phba);
+ if (phba->msix_enabled) {
+ for (i = 0; i <= phba->num_cpus; i++) {
+ msix_vec = phba->msix_entries[i].vector;
+ free_irq(msix_vec, &phwi_context->be_eq[i]);
+ }
+ } else
+ if (phba->pcidev->irq)
+ free_irq(phba->pcidev->irq, phba);
+ pci_disable_msix(phba->pcidev);
free_blkenbld:
destroy_workqueue(phba->wq);
if (blk_iopoll_enabled)
- blk_iopoll_disable(&phba->iopoll);
+ for (i = 0; i < phba->num_cpus; i++) {
+ pbe_eq = &phwi_context->be_eq[i];
+ blk_iopoll_disable(&pbe_eq->iopoll);
+ }
free_twq:
beiscsi_clean_port(phba);
beiscsi_free_mem(phba);
@@ -3316,7 +3859,7 @@ struct iscsi_transport beiscsi_iscsi_transport = {
ISCSI_USERNAME | ISCSI_PASSWORD |
ISCSI_USERNAME_IN | ISCSI_PASSWORD_IN |
ISCSI_FAST_ABORT | ISCSI_ABORT_TMO |
- ISCSI_LU_RESET_TMO |
+ ISCSI_LU_RESET_TMO | ISCSI_TGT_RESET_TMO |
ISCSI_PING_TMO | ISCSI_RECV_TMO |
ISCSI_IFACE_NAME | ISCSI_INITIATOR_NAME,
.host_param_mask = ISCSI_HOST_HWADDRESS | ISCSI_HOST_IPADDRESS |
@@ -3351,6 +3894,7 @@ static struct pci_driver beiscsi_pci_driver = {
.id_table = beiscsi_pci_id_table
};
+
static int __init beiscsi_module_init(void)
{
int ret;
@@ -3373,6 +3917,7 @@ static int __init beiscsi_module_init(void)
"beiscsi pci driver.\n");
goto unregister_iscsi_transport;
}
+ ring_mode = 0;
return 0;
unregister_iscsi_transport:
diff --git a/drivers/scsi/be2iscsi/be_main.h b/drivers/scsi/be2iscsi/be_main.h
index 53c9b70ac7a..25e6b208b77 100644
--- a/drivers/scsi/be2iscsi/be_main.h
+++ b/drivers/scsi/be2iscsi/be_main.h
@@ -21,11 +21,9 @@
#ifndef _BEISCSI_MAIN_
#define _BEISCSI_MAIN_
-
#include <linux/kernel.h>
#include <linux/pci.h>
#include <linux/in.h>
-#include <linux/blk-iopoll.h>
#include <scsi/scsi.h>
#include <scsi/scsi_cmnd.h>
#include <scsi/scsi_device.h>
@@ -35,12 +33,8 @@
#include <scsi/scsi_transport_iscsi.h>
#include "be.h"
-
-
-
#define DRV_NAME "be2iscsi"
#define BUILD_STR "2.0.527.0"
-
#define BE_NAME "ServerEngines BladeEngine2" \
"Linux iSCSI Driver version" BUILD_STR
#define DRV_DESC BE_NAME " " "Driver"
@@ -49,6 +43,8 @@
#define BE_DEVICE_ID1 0x212
#define OC_DEVICE_ID1 0x702
#define OC_DEVICE_ID2 0x703
+#define OC_DEVICE_ID3 0x712
+#define OC_DEVICE_ID4 0x222
#define BE2_MAX_SESSIONS 64
#define BE2_CMDS_PER_CXN 128
@@ -63,6 +59,7 @@
#define BE2_IO_DEPTH \
(BE2_MAX_ICDS / 2 - (BE2_LOGOUTS + BE2_TMFS + BE2_NOPOUT_REQ))
+#define MAX_CPUS 31
#define BEISCSI_SGLIST_ELEMENTS BE2_SGE
#define BEISCSI_MAX_CMNDS 1024 /* Max IO's per Ctrlr sht->can_queue */
@@ -79,7 +76,7 @@
#define BE_SENSE_INFO_SIZE 258
#define BE_ISCSI_PDU_HEADER_SIZE 64
#define BE_MIN_MEM_SIZE 16384
-
+#define MAX_CMD_SZ 65536
#define IIOC_SCSI_DATA 0x05 /* Write Operation */
#define DBG_LVL 0x00000001
@@ -100,6 +97,8 @@ do { \
} \
} while (0);
+#define BE_ADAPTER_UP 0x00000000
+#define BE_ADAPTER_LINK_DOWN 0x00000001
/**
* hardware needs the async PDU buffers to be posted in multiples of 8
* So have atleast 8 of them by default
@@ -160,21 +159,19 @@ do { \
enum be_mem_enum {
HWI_MEM_ADDN_CONTEXT,
- HWI_MEM_CQ,
- HWI_MEM_EQ,
HWI_MEM_WRB,
HWI_MEM_WRBH,
- HWI_MEM_SGLH, /* 5 */
+ HWI_MEM_SGLH,
HWI_MEM_SGE,
- HWI_MEM_ASYNC_HEADER_BUF,
+ HWI_MEM_ASYNC_HEADER_BUF, /* 5 */
HWI_MEM_ASYNC_DATA_BUF,
HWI_MEM_ASYNC_HEADER_RING,
- HWI_MEM_ASYNC_DATA_RING, /* 10 */
+ HWI_MEM_ASYNC_DATA_RING,
HWI_MEM_ASYNC_HEADER_HANDLE,
- HWI_MEM_ASYNC_DATA_HANDLE,
+ HWI_MEM_ASYNC_DATA_HANDLE, /* 10 */
HWI_MEM_ASYNC_PDU_CONTEXT,
ISCSI_MEM_GLOBAL_HEADER,
- SE_MEM_MAX /* 15 */
+ SE_MEM_MAX
};
struct be_bus_address32 {
@@ -212,6 +209,9 @@ struct be_mem_descriptor {
struct sgl_handle {
unsigned int sgl_index;
+ unsigned int type;
+ unsigned int cid;
+ struct iscsi_task *task;
struct iscsi_sge *pfrag;
};
@@ -274,13 +274,17 @@ struct beiscsi_hba {
struct pci_dev *pcidev;
unsigned int state;
unsigned short asic_revision;
- struct blk_iopoll iopoll;
+ unsigned int num_cpus;
+ unsigned int nxt_cqid;
+ struct msix_entry msix_entries[MAX_CPUS + 1];
+ bool msix_enabled;
struct be_mem_descriptor *init_mem;
unsigned short io_sgl_alloc_index;
unsigned short io_sgl_free_index;
unsigned short io_sgl_hndl_avbl;
struct sgl_handle **io_sgl_hndl_base;
+ struct sgl_handle **sgl_hndl_array;
unsigned short eh_sgl_alloc_index;
unsigned short eh_sgl_free_index;
@@ -315,6 +319,7 @@ struct beiscsi_hba {
unsigned short cid_alloc;
unsigned short cid_free;
unsigned short avlbl_cids;
+ unsigned short iscsi_features;
spinlock_t cid_lock;
} fw_config;
@@ -343,6 +348,7 @@ struct beiscsi_conn {
unsigned short login_in_progress;
struct sgl_handle *plogin_sgl_handle;
struct beiscsi_session *beiscsi_sess;
+ struct iscsi_task *task;
};
/* This structure is used by the chip */
@@ -390,7 +396,7 @@ struct beiscsi_io_task {
unsigned int flags;
unsigned short cid;
unsigned short header_len;
-
+ itt_t libiscsi_itt;
struct be_cmd_bhs *cmd_bhs;
struct be_bus_address bhs_pa;
unsigned short bhs_len;
@@ -599,7 +605,6 @@ struct amap_cq_db {
void beiscsi_process_eq(struct beiscsi_hba *phba);
-
struct iscsi_wrb {
u32 dw[16];
} __packed;
@@ -820,10 +825,12 @@ struct wrb_handle {
};
struct hwi_context_memory {
- struct be_eq_obj be_eq;
- struct be_queue_info be_cq;
- struct be_queue_info be_mcc_cq;
- struct be_queue_info be_mcc;
+ /* Adaptive interrupt coalescing (AIC) info */
+ u16 min_eqd; /* in usecs */
+ u16 max_eqd; /* in usecs */
+ u16 cur_eqd; /* in usecs */
+ struct be_eq_obj be_eq[MAX_CPUS];
+ struct be_queue_info be_cq[MAX_CPUS];
struct be_queue_info be_def_hdrq;
struct be_queue_info be_def_dataq;
diff --git a/drivers/scsi/be2iscsi/be_mgmt.c b/drivers/scsi/be2iscsi/be_mgmt.c
index 12e644fc746..79c2bd525a8 100644
--- a/drivers/scsi/be2iscsi/be_mgmt.c
+++ b/drivers/scsi/be2iscsi/be_mgmt.c
@@ -35,7 +35,6 @@ unsigned char mgmt_get_fw_config(struct be_ctrl_info *ctrl,
be_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_COMMON,
OPCODE_COMMON_QUERY_FIRMWARE_CONFIG, sizeof(*req));
-
status = be_mbox_notify(ctrl);
if (!status) {
struct be_fw_cfg *pfw_cfg;
@@ -58,7 +57,8 @@ unsigned char mgmt_get_fw_config(struct be_ctrl_info *ctrl,
return status;
}
-unsigned char mgmt_check_supported_fw(struct be_ctrl_info *ctrl)
+unsigned char mgmt_check_supported_fw(struct be_ctrl_info *ctrl,
+ struct beiscsi_hba *phba)
{
struct be_dma_mem nonemb_cmd;
struct be_mcc_wrb *wrb = wrb_from_mbox(&ctrl->mbox_mem);
@@ -85,7 +85,6 @@ unsigned char mgmt_check_supported_fw(struct be_ctrl_info *ctrl)
sge->pa_hi = cpu_to_le32(upper_32_bits(nonemb_cmd.dma));
sge->pa_lo = cpu_to_le32(nonemb_cmd.dma & 0xFFFFFFFF);
sge->len = cpu_to_le32(nonemb_cmd.size);
-
status = be_mbox_notify(ctrl);
if (!status) {
struct be_mgmt_controller_attributes_resp *resp = nonemb_cmd.va;
@@ -95,21 +94,25 @@ unsigned char mgmt_check_supported_fw(struct be_ctrl_info *ctrl)
resp->params.hba_attribs.firmware_version_string);
SE_DEBUG(DBG_LVL_8,
"Developer Build, not performing version check...\n");
-
+ phba->fw_config.iscsi_features =
+ resp->params.hba_attribs.iscsi_features;
+ SE_DEBUG(DBG_LVL_8, " phba->fw_config.iscsi_features = %d\n",
+ phba->fw_config.iscsi_features);
} else
SE_DEBUG(DBG_LVL_1, " Failed in mgmt_check_supported_fw\n");
+ spin_unlock(&ctrl->mbox_lock);
if (nonemb_cmd.va)
pci_free_consistent(ctrl->pdev, nonemb_cmd.size,
nonemb_cmd.va, nonemb_cmd.dma);
- spin_unlock(&ctrl->mbox_lock);
return status;
}
+
unsigned char mgmt_epfw_cleanup(struct beiscsi_hba *phba, unsigned short chute)
{
struct be_ctrl_info *ctrl = &phba->ctrl;
- struct be_mcc_wrb *wrb = wrb_from_mbox(&ctrl->mbox_mem);
+ struct be_mcc_wrb *wrb = wrb_from_mccq(phba);
struct iscsi_cleanup_req *req = embedded_payload(wrb);
int status = 0;
@@ -124,7 +127,7 @@ unsigned char mgmt_epfw_cleanup(struct beiscsi_hba *phba, unsigned short chute)
req->hdr_ring_id = 0;
req->data_ring_id = 0;
- status = be_mbox_notify(ctrl);
+ status = be_mcc_notify_wait(phba);
if (status)
shost_printk(KERN_WARNING, phba->shost,
" mgmt_epfw_cleanup , FAILED\n");
@@ -137,7 +140,7 @@ unsigned char mgmt_invalidate_icds(struct beiscsi_hba *phba,
{
struct be_dma_mem nonemb_cmd;
struct be_ctrl_info *ctrl = &phba->ctrl;
- struct be_mcc_wrb *wrb = wrb_from_mbox(&ctrl->mbox_mem);
+ struct be_mcc_wrb *wrb = wrb_from_mccq(phba);
struct be_sge *sge = nonembedded_sgl(wrb);
struct invalidate_commands_params_in *req;
int status = 0;
@@ -169,7 +172,7 @@ unsigned char mgmt_invalidate_icds(struct beiscsi_hba *phba,
sge->pa_lo = cpu_to_le32(nonemb_cmd.dma & 0xFFFFFFFF);
sge->len = cpu_to_le32(nonemb_cmd.size);
- status = be_mbox_notify(ctrl);
+ status = be_mcc_notify_wait(phba);
if (status)
SE_DEBUG(DBG_LVL_1, "ICDS Invalidation Failed\n");
spin_unlock(&ctrl->mbox_lock);
@@ -186,7 +189,7 @@ unsigned char mgmt_invalidate_connection(struct beiscsi_hba *phba,
unsigned short savecfg_flag)
{
struct be_ctrl_info *ctrl = &phba->ctrl;
- struct be_mcc_wrb *wrb = wrb_from_mbox(&ctrl->mbox_mem);
+ struct be_mcc_wrb *wrb = wrb_from_mccq(phba);
struct iscsi_invalidate_connection_params_in *req =
embedded_payload(wrb);
int status = 0;
@@ -205,7 +208,7 @@ unsigned char mgmt_invalidate_connection(struct beiscsi_hba *phba,
else
req->cleanup_type = CMD_ISCSI_CONNECTION_INVALIDATE;
req->save_cfg = savecfg_flag;
- status = be_mbox_notify(ctrl);
+ status = be_mcc_notify_wait(phba);
if (status)
SE_DEBUG(DBG_LVL_1, "Invalidation Failed\n");
@@ -217,7 +220,7 @@ unsigned char mgmt_upload_connection(struct beiscsi_hba *phba,
unsigned short cid, unsigned int upload_flag)
{
struct be_ctrl_info *ctrl = &phba->ctrl;
- struct be_mcc_wrb *wrb = wrb_from_mbox(&ctrl->mbox_mem);
+ struct be_mcc_wrb *wrb = wrb_from_mccq(phba);
struct tcp_upload_params_in *req = embedded_payload(wrb);
int status = 0;
@@ -229,7 +232,7 @@ unsigned char mgmt_upload_connection(struct beiscsi_hba *phba,
OPCODE_COMMON_TCP_UPLOAD, sizeof(*req));
req->id = (unsigned short)cid;
req->upload_type = (unsigned char)upload_flag;
- status = be_mbox_notify(ctrl);
+ status = be_mcc_notify_wait(phba);
if (status)
SE_DEBUG(DBG_LVL_1, "mgmt_upload_connection Failed\n");
spin_unlock(&ctrl->mbox_lock);
@@ -245,13 +248,14 @@ int mgmt_open_connection(struct beiscsi_hba *phba,
struct sockaddr_in *daddr_in = (struct sockaddr_in *)dst_addr;
struct sockaddr_in6 *daddr_in6 = (struct sockaddr_in6 *)dst_addr;
struct be_ctrl_info *ctrl = &phba->ctrl;
- struct be_mcc_wrb *wrb = wrb_from_mbox(&ctrl->mbox_mem);
+ struct be_mcc_wrb *wrb = wrb_from_mccq(phba);
struct tcp_connect_and_offload_in *req = embedded_payload(wrb);
unsigned short def_hdr_id;
unsigned short def_data_id;
struct phys_addr template_address = { 0, 0 };
struct phys_addr *ptemplate_address;
int status = 0;
+ unsigned int i;
unsigned short cid = beiscsi_ep->ep_cid;
phwi_ctrlr = phba->phwi_ctrlr;
@@ -296,14 +300,18 @@ int mgmt_open_connection(struct beiscsi_hba *phba,
}
req->cid = cid;
- req->cq_id = phwi_context->be_cq.id;
+ i = phba->nxt_cqid++;
+ if (phba->nxt_cqid == phba->num_cpus)
+ phba->nxt_cqid = 0;
+ req->cq_id = phwi_context->be_cq[i].id;
+ SE_DEBUG(DBG_LVL_8, "i=%d cq_id=%d \n", i, req->cq_id);
req->defq_id = def_hdr_id;
req->hdr_ring_id = def_hdr_id;
req->data_ring_id = def_data_id;
req->do_offload = 1;
req->dataout_template_pa.lo = ptemplate_address->lo;
req->dataout_template_pa.hi = ptemplate_address->hi;
- status = be_mbox_notify(ctrl);
+ status = be_mcc_notify_wait(phba);
if (!status) {
struct iscsi_endpoint *ep;
struct tcp_connect_and_offload_out *ptcpcnct_out =
@@ -311,7 +319,7 @@ int mgmt_open_connection(struct beiscsi_hba *phba,
ep = phba->ep_array[ptcpcnct_out->cid];
beiscsi_ep = ep->dd_data;
- beiscsi_ep->fw_handle = 0;
+ beiscsi_ep->fw_handle = ptcpcnct_out->connection_handle;
beiscsi_ep->cid_vld = 1;
SE_DEBUG(DBG_LVL_8, "mgmt_open_connection Success\n");
} else
@@ -319,3 +327,30 @@ int mgmt_open_connection(struct beiscsi_hba *phba,
spin_unlock(&ctrl->mbox_lock);
return status;
}
+
+int be_cmd_get_mac_addr(struct beiscsi_hba *phba, u8 *mac_addr)
+{
+ struct be_ctrl_info *ctrl = &phba->ctrl;
+ struct be_mcc_wrb *wrb = wrb_from_mccq(phba);
+ struct be_cmd_req_get_mac_addr *req = embedded_payload(wrb);
+ int status;
+
+ SE_DEBUG(DBG_LVL_8, "In be_cmd_get_mac_addr\n");
+ spin_lock(&ctrl->mbox_lock);
+ memset(wrb, 0, sizeof(*wrb));
+ be_wrb_hdr_prepare(wrb, sizeof(*req), true, 0);
+ be_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_ISCSI,
+ OPCODE_COMMON_ISCSI_NTWK_GET_NIC_CONFIG,
+ sizeof(*req));
+
+ status = be_mcc_notify_wait(phba);
+ if (!status) {
+ struct be_cmd_resp_get_mac_addr *resp = embedded_payload(wrb);
+
+ memcpy(mac_addr, resp->mac_address, ETH_ALEN);
+ }
+
+ spin_unlock(&ctrl->mbox_lock);
+ return status;
+}
+
diff --git a/drivers/scsi/be2iscsi/be_mgmt.h b/drivers/scsi/be2iscsi/be_mgmt.h
index 00e816ee807..24eaff923f8 100644
--- a/drivers/scsi/be2iscsi/be_mgmt.h
+++ b/drivers/scsi/be2iscsi/be_mgmt.h
@@ -175,7 +175,9 @@ struct mgmt_hba_attributes {
u8 phy_port;
u32 firmware_post_status;
u32 hba_mtu[8];
- u32 future_u32[4];
+ u8 iscsi_features;
+ u8 future_u8[3];
+ u32 future_u32[3];
} __packed;
struct mgmt_controller_attributes {
@@ -246,4 +248,8 @@ unsigned char mgmt_invalidate_connection(struct beiscsi_hba *phba,
unsigned short cid,
unsigned short issue_reset,
unsigned short savecfg_flag);
+
+unsigned char mgmt_fw_cmd(struct be_ctrl_info *ctrl,
+ struct beiscsi_hba *phba,
+ char *buf, unsigned int len);
#endif
diff --git a/drivers/scsi/bfa/bfa_cb_ioim_macros.h b/drivers/scsi/bfa/bfa_cb_ioim_macros.h
index 0050c838c35..961fe439daa 100644
--- a/drivers/scsi/bfa/bfa_cb_ioim_macros.h
+++ b/drivers/scsi/bfa/bfa_cb_ioim_macros.h
@@ -51,7 +51,7 @@ bfad_int_to_lun(u32 luno)
lun.bfa_lun = 0;
lun.scsi_lun[0] = bfa_os_htons(luno);
- return (lun.bfa_lun);
+ return lun.bfa_lun;
}
/**
@@ -68,7 +68,7 @@ bfa_cb_ioim_get_cdb(struct bfad_ioim_s *dio)
{
struct scsi_cmnd *cmnd = (struct scsi_cmnd *)dio;
- return ((u8 *) cmnd->cmnd);
+ return (u8 *) cmnd->cmnd;
}
/**
@@ -97,7 +97,7 @@ bfa_cb_ioim_get_size(struct bfad_ioim_s *dio)
{
struct scsi_cmnd *cmnd = (struct scsi_cmnd *)dio;
- return (scsi_bufflen(cmnd));
+ return scsi_bufflen(cmnd);
}
/**
@@ -129,7 +129,7 @@ bfa_cb_ioim_get_sgaddr(struct bfad_ioim_s *dio, int sgeid)
sge = (struct scatterlist *)scsi_sglist(cmnd) + sgeid;
addr = (u64) sg_dma_address(sge);
- return (*(union bfi_addr_u *) &addr);
+ return *((union bfi_addr_u *) &addr);
}
static inline u32
@@ -197,7 +197,7 @@ bfa_cb_ioim_get_cdblen(struct bfad_ioim_s *dio)
{
struct scsi_cmnd *cmnd = (struct scsi_cmnd *)dio;
- return (cmnd->cmd_len);
+ return cmnd->cmd_len;
}
diff --git a/drivers/scsi/bfa/bfa_cee.c b/drivers/scsi/bfa/bfa_cee.c
index 7a959c34e78..2b917792c6b 100644
--- a/drivers/scsi/bfa/bfa_cee.c
+++ b/drivers/scsi/bfa/bfa_cee.c
@@ -228,7 +228,7 @@ bfa_cee_reset_stats_isr(struct bfa_cee_s *cee, bfa_status_t status)
u32
bfa_cee_meminfo(void)
{
- return (bfa_cee_attr_meminfo() + bfa_cee_stats_meminfo());
+ return bfa_cee_attr_meminfo() + bfa_cee_stats_meminfo();
}
/**
diff --git a/drivers/scsi/bfa/bfa_csdebug.c b/drivers/scsi/bfa/bfa_csdebug.c
index 1b71d349451..caeb1143a4e 100644
--- a/drivers/scsi/bfa/bfa_csdebug.c
+++ b/drivers/scsi/bfa/bfa_csdebug.c
@@ -47,12 +47,12 @@ bfa_q_is_on_q_func(struct list_head *q, struct list_head *qe)
tqe = bfa_q_next(q);
while (tqe != q) {
if (tqe == qe)
- return (1);
+ return 1;
tqe = bfa_q_next(tqe);
if (tqe == NULL)
break;
}
- return (0);
+ return 0;
}
diff --git a/drivers/scsi/bfa/bfa_fcpim.c b/drivers/scsi/bfa/bfa_fcpim.c
index 401babe3494..790c945aeae 100644
--- a/drivers/scsi/bfa/bfa_fcpim.c
+++ b/drivers/scsi/bfa/bfa_fcpim.c
@@ -131,7 +131,7 @@ bfa_fcpim_path_tov_get(struct bfa_s *bfa)
{
struct bfa_fcpim_mod_s *fcpim = BFA_FCPIM_MOD(bfa);
- return (fcpim->path_tov / 1000);
+ return fcpim->path_tov / 1000;
}
bfa_status_t
@@ -169,7 +169,7 @@ bfa_fcpim_qdepth_get(struct bfa_s *bfa)
{
struct bfa_fcpim_mod_s *fcpim = BFA_FCPIM_MOD(bfa);
- return (fcpim->q_depth);
+ return fcpim->q_depth;
}
diff --git a/drivers/scsi/bfa/bfa_fcpim_priv.h b/drivers/scsi/bfa/bfa_fcpim_priv.h
index 153206cfb37..5cf418460f7 100644
--- a/drivers/scsi/bfa/bfa_fcpim_priv.h
+++ b/drivers/scsi/bfa/bfa_fcpim_priv.h
@@ -35,7 +35,7 @@
#define BFA_FCPIM_PATHTOV_MAX (90 * 1000) /* in millisecs */
#define bfa_fcpim_stats(__fcpim, __stats) \
- (__fcpim)->stats.__stats ++
+ ((__fcpim)->stats.__stats++)
struct bfa_fcpim_mod_s {
struct bfa_s *bfa;
@@ -143,7 +143,7 @@ struct bfa_itnim_s {
struct bfa_itnim_hal_stats_s stats;
};
-#define bfa_itnim_is_online(_itnim) (_itnim)->is_online
+#define bfa_itnim_is_online(_itnim) ((_itnim)->is_online)
#define BFA_FCPIM_MOD(_hal) (&(_hal)->modules.fcpim_mod)
#define BFA_IOIM_FROM_TAG(_fcpim, _iotag) \
(&fcpim->ioim_arr[_iotag])
diff --git a/drivers/scsi/bfa/bfa_fcport.c b/drivers/scsi/bfa/bfa_fcport.c
index 992435987de..aef648b55df 100644
--- a/drivers/scsi/bfa/bfa_fcport.c
+++ b/drivers/scsi/bfa/bfa_fcport.c
@@ -388,32 +388,29 @@ bfa_pport_sm_linkup(struct bfa_pport_s *pport, enum bfa_pport_sm_event event)
bfa_pport_callback(pport, BFA_PPORT_LINKDOWN);
bfa_plog_str(pport->bfa->plog, BFA_PL_MID_HAL,
BFA_PL_EID_PORT_ST_CHANGE, 0, "Port Linkdown");
- if (BFA_PORT_IS_DISABLED(pport->bfa)) {
+ if (BFA_PORT_IS_DISABLED(pport->bfa))
bfa_pport_aen_post(pport, BFA_PORT_AEN_OFFLINE);
- } else {
+ else
bfa_pport_aen_post(pport, BFA_PORT_AEN_DISCONNECT);
- }
break;
case BFA_PPORT_SM_STOP:
bfa_sm_set_state(pport, bfa_pport_sm_stopped);
bfa_pport_reset_linkinfo(pport);
- if (BFA_PORT_IS_DISABLED(pport->bfa)) {
+ if (BFA_PORT_IS_DISABLED(pport->bfa))
bfa_pport_aen_post(pport, BFA_PORT_AEN_OFFLINE);
- } else {
+ else
bfa_pport_aen_post(pport, BFA_PORT_AEN_DISCONNECT);
- }
break;
case BFA_PPORT_SM_HWFAIL:
bfa_sm_set_state(pport, bfa_pport_sm_iocdown);
bfa_pport_reset_linkinfo(pport);
bfa_pport_callback(pport, BFA_PPORT_LINKDOWN);
- if (BFA_PORT_IS_DISABLED(pport->bfa)) {
+ if (BFA_PORT_IS_DISABLED(pport->bfa))
bfa_pport_aen_post(pport, BFA_PORT_AEN_OFFLINE);
- } else {
+ else
bfa_pport_aen_post(pport, BFA_PORT_AEN_DISCONNECT);
- }
break;
default:
@@ -999,10 +996,10 @@ bfa_pport_enable(struct bfa_s *bfa)
struct bfa_pport_s *pport = BFA_PORT_MOD(bfa);
if (pport->diag_busy)
- return (BFA_STATUS_DIAG_BUSY);
+ return BFA_STATUS_DIAG_BUSY;
else if (bfa_sm_cmp_state
(BFA_PORT_MOD(bfa), bfa_pport_sm_disabling_qwait))
- return (BFA_STATUS_DEVBUSY);
+ return BFA_STATUS_DEVBUSY;
bfa_sm_send_event(BFA_PORT_MOD(bfa), BFA_PPORT_SM_ENABLE);
return BFA_STATUS_OK;
@@ -1032,7 +1029,7 @@ bfa_pport_cfg_speed(struct bfa_s *bfa, enum bfa_pport_speed speed)
pport->cfg.speed = speed;
- return (BFA_STATUS_OK);
+ return BFA_STATUS_OK;
}
/**
@@ -1068,7 +1065,7 @@ bfa_pport_cfg_topology(struct bfa_s *bfa, enum bfa_pport_topology topology)
}
pport->cfg.topology = topology;
- return (BFA_STATUS_OK);
+ return BFA_STATUS_OK;
}
/**
@@ -1094,7 +1091,7 @@ bfa_pport_cfg_hardalpa(struct bfa_s *bfa, u8 alpa)
pport->cfg.cfg_hardalpa = BFA_TRUE;
pport->cfg.hardalpa = alpa;
- return (BFA_STATUS_OK);
+ return BFA_STATUS_OK;
}
bfa_status_t
@@ -1106,7 +1103,7 @@ bfa_pport_clr_hardalpa(struct bfa_s *bfa)
bfa_trc(bfa, pport->cfg.hardalpa);
pport->cfg.cfg_hardalpa = BFA_FALSE;
- return (BFA_STATUS_OK);
+ return BFA_STATUS_OK;
}
bfa_boolean_t
@@ -1138,16 +1135,16 @@ bfa_pport_cfg_maxfrsize(struct bfa_s *bfa, u16 maxfrsize)
* with in range
*/
if ((maxfrsize > FC_MAX_PDUSZ) || (maxfrsize < FC_MIN_PDUSZ))
- return (BFA_STATUS_INVLD_DFSZ);
+ return BFA_STATUS_INVLD_DFSZ;
/*
* power of 2, if not the max frame size of 2112
*/
if ((maxfrsize != FC_MAX_PDUSZ) && (maxfrsize & (maxfrsize - 1)))
- return (BFA_STATUS_INVLD_DFSZ);
+ return BFA_STATUS_INVLD_DFSZ;
pport->cfg.maxfrsize = maxfrsize;
- return (BFA_STATUS_OK);
+ return BFA_STATUS_OK;
}
u16
@@ -1415,7 +1412,7 @@ bfa_pport_get_stats(struct bfa_s *bfa, union bfa_pport_stats_u *stats,
if (port->stats_busy) {
bfa_trc(bfa, port->stats_busy);
- return (BFA_STATUS_DEVBUSY);
+ return BFA_STATUS_DEVBUSY;
}
port->stats_busy = BFA_TRUE;
@@ -1427,7 +1424,7 @@ bfa_pport_get_stats(struct bfa_s *bfa, union bfa_pport_stats_u *stats,
bfa_timer_start(bfa, &port->timer, bfa_port_stats_timeout, port,
BFA_PORT_STATS_TOV);
- return (BFA_STATUS_OK);
+ return BFA_STATUS_OK;
}
bfa_status_t
@@ -1437,7 +1434,7 @@ bfa_pport_clear_stats(struct bfa_s *bfa, bfa_cb_pport_t cbfn, void *cbarg)
if (port->stats_busy) {
bfa_trc(bfa, port->stats_busy);
- return (BFA_STATUS_DEVBUSY);
+ return BFA_STATUS_DEVBUSY;
}
port->stats_busy = BFA_TRUE;
@@ -1448,7 +1445,7 @@ bfa_pport_clear_stats(struct bfa_s *bfa, bfa_cb_pport_t cbfn, void *cbarg)
bfa_timer_start(bfa, &port->timer, bfa_port_stats_clr_timeout, port,
BFA_PORT_STATS_TOV);
- return (BFA_STATUS_OK);
+ return BFA_STATUS_OK;
}
bfa_status_t
@@ -1515,7 +1512,7 @@ bfa_pport_get_qos_stats(struct bfa_s *bfa, union bfa_pport_stats_u *stats,
/*
* QoS stats is embedded in port stats
*/
- return (bfa_pport_get_stats(bfa, stats, cbfn, cbarg));
+ return bfa_pport_get_stats(bfa, stats, cbfn, cbarg);
}
bfa_status_t
@@ -1525,7 +1522,7 @@ bfa_pport_clear_qos_stats(struct bfa_s *bfa, bfa_cb_pport_t cbfn, void *cbarg)
if (port->stats_busy) {
bfa_trc(bfa, port->stats_busy);
- return (BFA_STATUS_DEVBUSY);
+ return BFA_STATUS_DEVBUSY;
}
port->stats_busy = BFA_TRUE;
@@ -1536,7 +1533,7 @@ bfa_pport_clear_qos_stats(struct bfa_s *bfa, bfa_cb_pport_t cbfn, void *cbarg)
bfa_timer_start(bfa, &port->timer, bfa_port_stats_clr_timeout, port,
BFA_PORT_STATS_TOV);
- return (BFA_STATUS_OK);
+ return BFA_STATUS_OK;
}
/**
@@ -1545,7 +1542,7 @@ bfa_pport_clear_qos_stats(struct bfa_s *bfa, bfa_cb_pport_t cbfn, void *cbarg)
bfa_status_t
bfa_pport_trunk_disable(struct bfa_s *bfa)
{
- return (BFA_STATUS_OK);
+ return BFA_STATUS_OK;
}
bfa_boolean_t
@@ -1562,8 +1559,8 @@ bfa_pport_is_disabled(struct bfa_s *bfa)
{
struct bfa_pport_s *port = BFA_PORT_MOD(bfa);
- return (bfa_sm_to_state(hal_pport_sm_table, port->sm) ==
- BFA_PPORT_ST_DISABLED);
+ return bfa_sm_to_state(hal_pport_sm_table, port->sm) ==
+ BFA_PPORT_ST_DISABLED;
}
@@ -1572,7 +1569,7 @@ bfa_pport_is_ratelim(struct bfa_s *bfa)
{
struct bfa_pport_s *pport = BFA_PORT_MOD(bfa);
-return (pport->cfg.ratelimit ? BFA_TRUE : BFA_FALSE);
+ return pport->cfg.ratelimit ? BFA_TRUE : BFA_FALSE;
}
@@ -1620,7 +1617,7 @@ bfa_pport_cfg_ratelim_speed(struct bfa_s *bfa, enum bfa_pport_speed speed)
pport->cfg.trl_def_speed = speed;
- return (BFA_STATUS_OK);
+ return BFA_STATUS_OK;
}
/**
@@ -1632,7 +1629,7 @@ bfa_pport_get_ratelim_speed(struct bfa_s *bfa)
struct bfa_pport_s *pport = BFA_PORT_MOD(bfa);
bfa_trc(bfa, pport->cfg.trl_def_speed);
- return (pport->cfg.trl_def_speed);
+ return pport->cfg.trl_def_speed;
}
diff --git a/drivers/scsi/bfa/bfa_fcs_lport.c b/drivers/scsi/bfa/bfa_fcs_lport.c
index 8975ed041dc..c7ab257f10a 100644
--- a/drivers/scsi/bfa/bfa_fcs_lport.c
+++ b/drivers/scsi/bfa/bfa_fcs_lport.c
@@ -568,11 +568,10 @@ bfa_fcs_port_offline_actions(struct bfa_fcs_port_s *port)
__port_action[port->fabric->fab_type].offline(port);
- if (bfa_fcs_fabric_is_online(port->fabric) == BFA_TRUE) {
+ if (bfa_fcs_fabric_is_online(port->fabric) == BFA_TRUE)
bfa_fcs_port_aen_post(port, BFA_LPORT_AEN_DISCONNECT);
- } else {
+ else
bfa_fcs_port_aen_post(port, BFA_LPORT_AEN_OFFLINE);
- }
bfa_fcb_port_offline(port->fcs->bfad, port->port_cfg.roles,
port->fabric->vf_drv,
(port->vport == NULL) ? NULL : port->vport->vport_drv);
@@ -777,7 +776,7 @@ bfa_fcs_port_get_rport_by_pwwn(struct bfa_fcs_port_s *port, wwn_t pwwn)
}
bfa_trc(port->fcs, pwwn);
- return (NULL);
+ return NULL;
}
/**
@@ -796,7 +795,7 @@ bfa_fcs_port_get_rport_by_nwwn(struct bfa_fcs_port_s *port, wwn_t nwwn)
}
bfa_trc(port->fcs, nwwn);
- return (NULL);
+ return NULL;
}
/**
@@ -870,7 +869,7 @@ bfa_fcs_port_lip(struct bfa_fcs_port_s *port)
bfa_boolean_t
bfa_fcs_port_is_online(struct bfa_fcs_port_s *port)
{
- return (bfa_sm_cmp_state(port, bfa_fcs_port_sm_online));
+ return bfa_sm_cmp_state(port, bfa_fcs_port_sm_online);
}
/**
diff --git a/drivers/scsi/bfa/bfa_fcxp.c b/drivers/scsi/bfa/bfa_fcxp.c
index 4754a0e9006..cf0ad678268 100644
--- a/drivers/scsi/bfa/bfa_fcxp.c
+++ b/drivers/scsi/bfa/bfa_fcxp.c
@@ -199,7 +199,7 @@ bfa_fcxp_get(struct bfa_fcxp_mod_s *fm)
if (fcxp)
list_add_tail(&fcxp->qe, &fm->fcxp_active_q);
- return (fcxp);
+ return fcxp;
}
static void
@@ -503,7 +503,7 @@ bfa_fcxp_alloc(void *caller, struct bfa_s *bfa, int nreq_sgles,
fcxp = bfa_fcxp_get(BFA_FCXP_MOD(bfa));
if (fcxp == NULL)
- return (NULL);
+ return NULL;
bfa_trc(bfa, fcxp->fcxp_tag);
@@ -568,7 +568,7 @@ bfa_fcxp_alloc(void *caller, struct bfa_s *bfa, int nreq_sgles,
}
}
- return (fcxp);
+ return fcxp;
}
/**
@@ -709,7 +709,7 @@ bfa_status_t
bfa_fcxp_abort(struct bfa_fcxp_s *fcxp)
{
bfa_assert(0);
- return (BFA_STATUS_OK);
+ return BFA_STATUS_OK;
}
void
diff --git a/drivers/scsi/bfa/bfa_intr.c b/drivers/scsi/bfa/bfa_intr.c
index 0ca125712a0..b36540e4ed7 100644
--- a/drivers/scsi/bfa/bfa_intr.c
+++ b/drivers/scsi/bfa/bfa_intr.c
@@ -59,7 +59,7 @@ bfa_intx(struct bfa_s *bfa)
qintr = intr & __HFN_INT_RME_MASK;
bfa_reg_write(bfa->iocfc.bfa_regs.intr_status, qintr);
- for (queue = 0; queue < BFI_IOC_MAX_CQS_ASIC; queue ++) {
+ for (queue = 0; queue < BFI_IOC_MAX_CQS_ASIC; queue++) {
if (intr & (__HFN_INT_RME_Q0 << queue))
bfa_msix_rspq(bfa, queue & (BFI_IOC_MAX_CQS - 1));
}
diff --git a/drivers/scsi/bfa/bfa_intr_priv.h b/drivers/scsi/bfa/bfa_intr_priv.h
index 8ce6e6b105c..5fc301cf4d1 100644
--- a/drivers/scsi/bfa/bfa_intr_priv.h
+++ b/drivers/scsi/bfa/bfa_intr_priv.h
@@ -26,9 +26,9 @@ void bfa_isr_unhandled(struct bfa_s *bfa, struct bfi_msg_s *m);
void bfa_isr_bind(enum bfi_mclass mc, bfa_isr_func_t isr_func);
-#define bfa_reqq_pi(__bfa, __reqq) (__bfa)->iocfc.req_cq_pi[__reqq]
+#define bfa_reqq_pi(__bfa, __reqq) ((__bfa)->iocfc.req_cq_pi[__reqq])
#define bfa_reqq_ci(__bfa, __reqq) \
- *(u32 *)((__bfa)->iocfc.req_cq_shadow_ci[__reqq].kva)
+ (*(u32 *)((__bfa)->iocfc.req_cq_shadow_ci[__reqq].kva))
#define bfa_reqq_full(__bfa, __reqq) \
(((bfa_reqq_pi(__bfa, __reqq) + 1) & \
@@ -50,14 +50,16 @@ void bfa_isr_bind(enum bfi_mclass mc, bfa_isr_func_t isr_func);
} while (0)
#define bfa_rspq_pi(__bfa, __rspq) \
- *(u32 *)((__bfa)->iocfc.rsp_cq_shadow_pi[__rspq].kva)
+ (*(u32 *)((__bfa)->iocfc.rsp_cq_shadow_pi[__rspq].kva))
-#define bfa_rspq_ci(__bfa, __rspq) (__bfa)->iocfc.rsp_cq_ci[__rspq]
+#define bfa_rspq_ci(__bfa, __rspq) ((__bfa)->iocfc.rsp_cq_ci[__rspq])
#define bfa_rspq_elem(__bfa, __rspq, __ci) \
- &((struct bfi_msg_s *)((__bfa)->iocfc.rsp_cq_ba[__rspq].kva))[__ci]
+ (&((struct bfi_msg_s *)((__bfa)->iocfc.rsp_cq_ba[__rspq].kva))[__ci])
-#define CQ_INCR(__index, __size) \
- (__index)++; (__index) &= ((__size) - 1)
+#define CQ_INCR(__index, __size) do { \
+ (__index)++; \
+ (__index) &= ((__size) - 1); \
+} while (0)
/**
* Queue element to wait for room in request queue. FIFO order is
@@ -94,7 +96,7 @@ bfa_reqq_winit(struct bfa_reqq_wait_s *wqe, void (*qresume) (void *cbarg),
wqe->cbarg = cbarg;
}
-#define bfa_reqq(__bfa, __reqq) &(__bfa)->reqq_waitq[__reqq]
+#define bfa_reqq(__bfa, __reqq) (&(__bfa)->reqq_waitq[__reqq])
/**
* static inline void
diff --git a/drivers/scsi/bfa/bfa_ioc.c b/drivers/scsi/bfa/bfa_ioc.c
index 149348934ce..397d7e9eade 100644
--- a/drivers/scsi/bfa/bfa_ioc.c
+++ b/drivers/scsi/bfa/bfa_ioc.c
@@ -51,7 +51,7 @@ BFA_TRC_FILE(HAL, IOC);
(sizeof(struct bfa_trc_mod_s) - \
BFA_TRC_MAX * sizeof(struct bfa_trc_s)))
#define BFA_DBG_FWTRC_OFF(_fn) (BFI_IOC_TRC_OFF + BFA_DBG_FWTRC_LEN * (_fn))
-#define bfa_ioc_stats(_ioc, _stats) (_ioc)->stats._stats ++
+#define bfa_ioc_stats(_ioc, _stats) ((_ioc)->stats._stats++)
#define BFA_FLASH_CHUNK_NO(off) (off / BFI_FLASH_CHUNK_SZ_WORDS)
#define BFA_FLASH_OFFSET_IN_CHUNK(off) (off % BFI_FLASH_CHUNK_SZ_WORDS)
@@ -1953,8 +1953,8 @@ bfa_ioc_error_isr(struct bfa_ioc_s *ioc)
bfa_boolean_t
bfa_ioc_is_disabled(struct bfa_ioc_s *ioc)
{
- return (bfa_fsm_cmp_state(ioc, bfa_ioc_sm_disabling)
- || bfa_fsm_cmp_state(ioc, bfa_ioc_sm_disabled));
+ return bfa_fsm_cmp_state(ioc, bfa_ioc_sm_disabling)
+ || bfa_fsm_cmp_state(ioc, bfa_ioc_sm_disabled);
}
/**
@@ -1963,9 +1963,9 @@ bfa_ioc_is_disabled(struct bfa_ioc_s *ioc)
bfa_boolean_t
bfa_ioc_fw_mismatch(struct bfa_ioc_s *ioc)
{
- return (bfa_fsm_cmp_state(ioc, bfa_ioc_sm_reset)
+ return bfa_fsm_cmp_state(ioc, bfa_ioc_sm_reset)
|| bfa_fsm_cmp_state(ioc, bfa_ioc_sm_fwcheck)
- || bfa_fsm_cmp_state(ioc, bfa_ioc_sm_mismatch));
+ || bfa_fsm_cmp_state(ioc, bfa_ioc_sm_mismatch);
}
#define bfa_ioc_state_disabled(__sm) \
diff --git a/drivers/scsi/bfa/bfa_ioc.h b/drivers/scsi/bfa/bfa_ioc.h
index 58efd4b1314..7c30f05ab13 100644
--- a/drivers/scsi/bfa/bfa_ioc.h
+++ b/drivers/scsi/bfa/bfa_ioc.h
@@ -179,16 +179,16 @@ struct bfa_ioc_s {
struct bfa_ioc_mbox_mod_s mbox_mod;
};
-#define bfa_ioc_pcifn(__ioc) (__ioc)->pcidev.pci_func
-#define bfa_ioc_devid(__ioc) (__ioc)->pcidev.device_id
-#define bfa_ioc_bar0(__ioc) (__ioc)->pcidev.pci_bar_kva
+#define bfa_ioc_pcifn(__ioc) ((__ioc)->pcidev.pci_func)
+#define bfa_ioc_devid(__ioc) ((__ioc)->pcidev.device_id)
+#define bfa_ioc_bar0(__ioc) ((__ioc)->pcidev.pci_bar_kva)
#define bfa_ioc_portid(__ioc) ((__ioc)->port_id)
#define bfa_ioc_fetch_stats(__ioc, __stats) \
- ((__stats)->drv_stats) = (__ioc)->stats
+ (((__stats)->drv_stats) = (__ioc)->stats)
#define bfa_ioc_clr_stats(__ioc) \
bfa_os_memset(&(__ioc)->stats, 0, sizeof((__ioc)->stats))
-#define bfa_ioc_maxfrsize(__ioc) (__ioc)->attr->maxfrsize
-#define bfa_ioc_rx_bbcredit(__ioc) (__ioc)->attr->rx_bbcredit
+#define bfa_ioc_maxfrsize(__ioc) ((__ioc)->attr->maxfrsize)
+#define bfa_ioc_rx_bbcredit(__ioc) ((__ioc)->attr->rx_bbcredit)
#define bfa_ioc_speed_sup(__ioc) \
BFI_ADAPTER_GETP(SPEED, (__ioc)->attr->adapter_prop)
diff --git a/drivers/scsi/bfa/bfa_iocfc.c b/drivers/scsi/bfa/bfa_iocfc.c
index 12350b022d6..d7ab792a9e5 100644
--- a/drivers/scsi/bfa/bfa_iocfc.c
+++ b/drivers/scsi/bfa/bfa_iocfc.c
@@ -794,7 +794,7 @@ bfa_iocfc_get_stats(struct bfa_s *bfa, struct bfa_iocfc_stats_s *stats,
if (iocfc->stats_busy) {
bfa_trc(bfa, iocfc->stats_busy);
- return (BFA_STATUS_DEVBUSY);
+ return BFA_STATUS_DEVBUSY;
}
iocfc->stats_busy = BFA_TRUE;
@@ -804,7 +804,7 @@ bfa_iocfc_get_stats(struct bfa_s *bfa, struct bfa_iocfc_stats_s *stats,
bfa_iocfc_stats_query(bfa);
- return (BFA_STATUS_OK);
+ return BFA_STATUS_OK;
}
bfa_status_t
@@ -814,7 +814,7 @@ bfa_iocfc_clear_stats(struct bfa_s *bfa, bfa_cb_ioc_t cbfn, void *cbarg)
if (iocfc->stats_busy) {
bfa_trc(bfa, iocfc->stats_busy);
- return (BFA_STATUS_DEVBUSY);
+ return BFA_STATUS_DEVBUSY;
}
iocfc->stats_busy = BFA_TRUE;
@@ -822,7 +822,7 @@ bfa_iocfc_clear_stats(struct bfa_s *bfa, bfa_cb_ioc_t cbfn, void *cbarg)
iocfc->stats_cbarg = cbarg;
bfa_iocfc_stats_clear(bfa);
- return (BFA_STATUS_OK);
+ return BFA_STATUS_OK;
}
/**
diff --git a/drivers/scsi/bfa/bfa_iocfc.h b/drivers/scsi/bfa/bfa_iocfc.h
index 7ad177ed4cf..ce9a830a420 100644
--- a/drivers/scsi/bfa/bfa_iocfc.h
+++ b/drivers/scsi/bfa/bfa_iocfc.h
@@ -107,13 +107,13 @@ struct bfa_iocfc_s {
#define bfa_lpuid(__bfa) bfa_ioc_portid(&(__bfa)->ioc)
#define bfa_msix_init(__bfa, __nvecs) \
- (__bfa)->iocfc.hwif.hw_msix_init(__bfa, __nvecs)
+ ((__bfa)->iocfc.hwif.hw_msix_init(__bfa, __nvecs))
#define bfa_msix_install(__bfa) \
- (__bfa)->iocfc.hwif.hw_msix_install(__bfa)
+ ((__bfa)->iocfc.hwif.hw_msix_install(__bfa))
#define bfa_msix_uninstall(__bfa) \
- (__bfa)->iocfc.hwif.hw_msix_uninstall(__bfa)
+ ((__bfa)->iocfc.hwif.hw_msix_uninstall(__bfa))
#define bfa_isr_mode_set(__bfa, __msix) \
- (__bfa)->iocfc.hwif.hw_isr_mode_set(__bfa, __msix)
+ ((__bfa)->iocfc.hwif.hw_isr_mode_set(__bfa, __msix))
#define bfa_msix_getvecs(__bfa, __vecmap, __nvecs, __maxvec) \
(__bfa)->iocfc.hwif.hw_msix_getvecs(__bfa, __vecmap, __nvecs, __maxvec)
diff --git a/drivers/scsi/bfa/bfa_ioim.c b/drivers/scsi/bfa/bfa_ioim.c
index 7ae2552e1e1..f81d359b708 100644
--- a/drivers/scsi/bfa/bfa_ioim.c
+++ b/drivers/scsi/bfa/bfa_ioim.c
@@ -105,13 +105,13 @@ bfa_ioim_sm_uninit(struct bfa_ioim_s *ioim, enum bfa_ioim_event event)
bfa_sm_set_state(ioim, bfa_ioim_sm_hcb);
list_del(&ioim->qe);
list_add_tail(&ioim->qe,
- &ioim->fcpim->ioim_comp_q);
+ &ioim->fcpim->ioim_comp_q);
bfa_cb_queue(ioim->bfa, &ioim->hcb_qe,
__bfa_cb_ioim_pathtov, ioim);
} else {
list_del(&ioim->qe);
list_add_tail(&ioim->qe,
- &ioim->itnim->pending_q);
+ &ioim->itnim->pending_q);
}
break;
}
diff --git a/drivers/scsi/bfa/bfa_itnim.c b/drivers/scsi/bfa/bfa_itnim.c
index 4d5c61a4f85..eabf7d38bd0 100644
--- a/drivers/scsi/bfa/bfa_itnim.c
+++ b/drivers/scsi/bfa/bfa_itnim.c
@@ -1029,7 +1029,7 @@ bfa_itnim_create(struct bfa_s *bfa, struct bfa_rport_s *rport, void *ditn)
bfa_stats(itnim, creates);
bfa_sm_send_event(itnim, BFA_ITNIM_SM_CREATE);
- return (itnim);
+ return itnim;
}
void
@@ -1061,7 +1061,7 @@ bfa_itnim_offline(struct bfa_itnim_s *itnim)
bfa_boolean_t
bfa_itnim_hold_io(struct bfa_itnim_s *itnim)
{
- return (
+ return
itnim->fcpim->path_tov && itnim->iotov_active &&
(bfa_sm_cmp_state(itnim, bfa_itnim_sm_fwcreate) ||
bfa_sm_cmp_state(itnim, bfa_itnim_sm_sler) ||
@@ -1069,7 +1069,7 @@ bfa_itnim_hold_io(struct bfa_itnim_s *itnim)
bfa_sm_cmp_state(itnim, bfa_itnim_sm_fwdelete) ||
bfa_sm_cmp_state(itnim, bfa_itnim_sm_offline) ||
bfa_sm_cmp_state(itnim, bfa_itnim_sm_iocdisable))
-);
+ ;
}
void
diff --git a/drivers/scsi/bfa/bfa_log.c b/drivers/scsi/bfa/bfa_log.c
index c2735e55cf0..e7514016c9c 100644
--- a/drivers/scsi/bfa/bfa_log.c
+++ b/drivers/scsi/bfa/bfa_log.c
@@ -231,9 +231,9 @@ bfa_log_get_level(struct bfa_log_mod_s *log_mod, int mod_id)
return BFA_LOG_INVALID;
if (log_mod)
- return (log_mod->log_level[mod_id]);
+ return log_mod->log_level[mod_id];
else
- return (bfa_log_info[mod_id].level);
+ return bfa_log_info[mod_id].level;
}
enum bfa_log_severity
diff --git a/drivers/scsi/bfa/bfa_port_priv.h b/drivers/scsi/bfa/bfa_port_priv.h
index 4b97e275990..51f698a06b6 100644
--- a/drivers/scsi/bfa/bfa_port_priv.h
+++ b/drivers/scsi/bfa/bfa_port_priv.h
@@ -59,8 +59,8 @@ struct bfa_pport_s {
u8 *stats_kva;
u64 stats_pa;
union bfa_pport_stats_u *stats; /* pport stats */
- u32 mypid : 24;
- u32 rsvd_b : 8;
+ u32 mypid:24;
+ u32 rsvd_b:8;
struct bfa_timer_s timer; /* timer */
union bfa_pport_stats_u *stats_ret;
/* driver stats location */
diff --git a/drivers/scsi/bfa/bfa_rport.c b/drivers/scsi/bfa/bfa_rport.c
index 16da77a8db2..3e1990a7425 100644
--- a/drivers/scsi/bfa/bfa_rport.c
+++ b/drivers/scsi/bfa/bfa_rport.c
@@ -677,7 +677,7 @@ bfa_rport_alloc(struct bfa_rport_mod_s *mod)
if (rport)
list_add_tail(&rport->qe, &mod->rp_active_q);
- return (rport);
+ return rport;
}
static void
@@ -834,7 +834,7 @@ bfa_rport_create(struct bfa_s *bfa, void *rport_drv)
rp = bfa_rport_alloc(BFA_RPORT_MOD(bfa));
if (rp == NULL)
- return (NULL);
+ return NULL;
rp->bfa = bfa;
rp->rport_drv = rport_drv;
@@ -843,7 +843,7 @@ bfa_rport_create(struct bfa_s *bfa, void *rport_drv)
bfa_assert(bfa_sm_cmp_state(rp, bfa_rport_sm_uninit));
bfa_sm_send_event(rp, BFA_RPORT_SM_CREATE);
- return (rp);
+ return rp;
}
void
diff --git a/drivers/scsi/bfa/bfa_tskim.c b/drivers/scsi/bfa/bfa_tskim.c
index 010d40d1e5d..ff7a4dc0bf3 100644
--- a/drivers/scsi/bfa/bfa_tskim.c
+++ b/drivers/scsi/bfa/bfa_tskim.c
@@ -23,13 +23,14 @@ BFA_TRC_FILE(HAL, TSKIM);
/**
* task management completion handling
*/
-#define bfa_tskim_qcomp(__tskim, __cbfn) do { \
- bfa_cb_queue((__tskim)->bfa, &(__tskim)->hcb_qe, __cbfn, (__tskim)); \
+#define bfa_tskim_qcomp(__tskim, __cbfn) do { \
+ bfa_cb_queue((__tskim)->bfa, &(__tskim)->hcb_qe, \
+ __cbfn, (__tskim)); \
bfa_tskim_notify_comp(__tskim); \
} while (0)
-#define bfa_tskim_notify_comp(__tskim) do { \
- if ((__tskim)->notify) \
+#define bfa_tskim_notify_comp(__tskim) do { \
+ if ((__tskim)->notify) \
bfa_itnim_tskdone((__tskim)->itnim); \
} while (0)
diff --git a/drivers/scsi/bfa/bfa_uf.c b/drivers/scsi/bfa/bfa_uf.c
index ff5f9deb1b2..4b3c2417d18 100644
--- a/drivers/scsi/bfa/bfa_uf.c
+++ b/drivers/scsi/bfa/bfa_uf.c
@@ -185,7 +185,7 @@ bfa_uf_get(struct bfa_uf_mod_s *uf_mod)
struct bfa_uf_s *uf;
bfa_q_deq(&uf_mod->uf_free_q, &uf);
- return (uf);
+ return uf;
}
static void
diff --git a/drivers/scsi/bfa/bfad.c b/drivers/scsi/bfa/bfad.c
index 6f2be5abf56..b52b773d49d 100644
--- a/drivers/scsi/bfa/bfad.c
+++ b/drivers/scsi/bfa/bfad.c
@@ -188,8 +188,8 @@ static struct bfad_port_s *
bfad_get_drv_port(struct bfad_s *bfad, struct bfad_vf_s *vf_drv,
struct bfad_vport_s *vp_drv)
{
- return ((vp_drv) ? (&(vp_drv)->drv_port)
- : ((vf_drv) ? (&(vf_drv)->base_port) : (&(bfad)->pport)));
+ return (vp_drv) ? (&(vp_drv)->drv_port)
+ : ((vf_drv) ? (&(vf_drv)->base_port) : (&(bfad)->pport));
}
struct bfad_port_s *
@@ -716,7 +716,7 @@ bfad_drv_init(struct bfad_s *bfad)
if ((bfad->bfad_flags & BFAD_MSIX_ON)
&& bfad_install_msix_handler(bfad)) {
printk(KERN_WARNING "%s: install_msix failed, bfad%d\n",
- __FUNCTION__, bfad->inst_no);
+ __func__, bfad->inst_no);
}
bfad_init_timer(bfad);
diff --git a/drivers/scsi/bfa/bfad_fwimg.c b/drivers/scsi/bfa/bfad_fwimg.c
index bd34b0db2d6..2ad65f275a9 100644
--- a/drivers/scsi/bfa/bfad_fwimg.c
+++ b/drivers/scsi/bfa/bfad_fwimg.c
@@ -65,10 +65,10 @@ bfad_read_firmware(struct pci_dev *pdev, u32 **bfi_image,
memcpy(*bfi_image, fw->data, fw->size);
*bfi_image_size = fw->size/sizeof(u32);
- return(*bfi_image);
+ return *bfi_image;
error:
- return(NULL);
+ return NULL;
}
u32 *
@@ -78,12 +78,12 @@ bfad_get_firmware_buf(struct pci_dev *pdev)
if (bfi_image_ct_size == 0)
bfad_read_firmware(pdev, &bfi_image_ct,
&bfi_image_ct_size, BFAD_FW_FILE_CT);
- return(bfi_image_ct);
+ return bfi_image_ct;
} else {
if (bfi_image_cb_size == 0)
bfad_read_firmware(pdev, &bfi_image_cb,
&bfi_image_cb_size, BFAD_FW_FILE_CB);
- return(bfi_image_cb);
+ return bfi_image_cb;
}
}
diff --git a/drivers/scsi/bfa/bfad_im.c b/drivers/scsi/bfa/bfad_im.c
index 55d012a9a66..f788c2a0ab0 100644
--- a/drivers/scsi/bfa/bfad_im.c
+++ b/drivers/scsi/bfa/bfad_im.c
@@ -1050,7 +1050,7 @@ bfad_im_itnim_work_handler(struct work_struct *work)
} else {
printk(KERN_WARNING
"%s: itnim %llx is already in online state\n",
- __FUNCTION__,
+ __func__,
bfa_fcs_itnim_get_pwwn(&itnim->fcs_itnim));
}
diff --git a/drivers/scsi/bfa/bfad_im_compat.h b/drivers/scsi/bfa/bfad_im_compat.h
index 1d3e74ec338..b36be15044a 100644
--- a/drivers/scsi/bfa/bfad_im_compat.h
+++ b/drivers/scsi/bfa/bfad_im_compat.h
@@ -31,7 +31,7 @@ u32 *bfad_read_firmware(struct pci_dev *pdev, u32 **bfi_image,
static inline u32 *
bfad_load_fwimg(struct pci_dev *pdev)
{
- return(bfad_get_firmware_buf(pdev));
+ return bfad_get_firmware_buf(pdev);
}
static inline void
diff --git a/drivers/scsi/bfa/bfad_intr.c b/drivers/scsi/bfa/bfad_intr.c
index f104e029cac..7de8832f6fe 100644
--- a/drivers/scsi/bfa/bfad_intr.c
+++ b/drivers/scsi/bfa/bfad_intr.c
@@ -23,13 +23,12 @@ BFA_TRC_FILE(LDRV, INTR);
/**
* bfa_isr BFA driver interrupt functions
*/
-irqreturn_t bfad_intx(int irq, void *dev_id);
static int msix_disable;
module_param(msix_disable, int, S_IRUGO | S_IWUSR);
/**
* Line based interrupt handler.
*/
-irqreturn_t
+static irqreturn_t
bfad_intx(int irq, void *dev_id)
{
struct bfad_s *bfad = dev_id;
diff --git a/drivers/scsi/bfa/fabric.c b/drivers/scsi/bfa/fabric.c
index a8b14c47b00..a4b5dd44957 100644
--- a/drivers/scsi/bfa/fabric.c
+++ b/drivers/scsi/bfa/fabric.c
@@ -36,12 +36,12 @@ BFA_TRC_FILE(FCS, FABRIC);
#define BFA_FCS_FABRIC_RETRY_DELAY (2000) /* Milliseconds */
#define BFA_FCS_FABRIC_CLEANUP_DELAY (10000) /* Milliseconds */
-#define bfa_fcs_fabric_set_opertype(__fabric) do { \
- if (bfa_pport_get_topology((__fabric)->fcs->bfa) \
- == BFA_PPORT_TOPOLOGY_P2P) \
- (__fabric)->oper_type = BFA_PPORT_TYPE_NPORT; \
- else \
- (__fabric)->oper_type = BFA_PPORT_TYPE_NLPORT; \
+#define bfa_fcs_fabric_set_opertype(__fabric) do { \
+ if (bfa_pport_get_topology((__fabric)->fcs->bfa) \
+ == BFA_PPORT_TOPOLOGY_P2P) \
+ (__fabric)->oper_type = BFA_PPORT_TYPE_NPORT; \
+ else \
+ (__fabric)->oper_type = BFA_PPORT_TYPE_NLPORT; \
} while (0)
/*
@@ -887,7 +887,7 @@ bfa_fcs_fabric_modsusp(struct bfa_fcs_s *fcs)
bfa_boolean_t
bfa_fcs_fabric_is_loopback(struct bfa_fcs_fabric_s *fabric)
{
- return (bfa_sm_cmp_state(fabric, bfa_fcs_fabric_sm_loopback));
+ return bfa_sm_cmp_state(fabric, bfa_fcs_fabric_sm_loopback);
}
enum bfa_pport_type
@@ -974,7 +974,7 @@ bfa_fcs_fabric_port_delete_comp(struct bfa_fcs_fabric_s *fabric)
int
bfa_fcs_fabric_is_online(struct bfa_fcs_fabric_s *fabric)
{
- return (bfa_sm_cmp_state(fabric, bfa_fcs_fabric_sm_online));
+ return bfa_sm_cmp_state(fabric, bfa_fcs_fabric_sm_online);
}
@@ -1015,7 +1015,7 @@ bfa_fcs_fabric_vport_lookup(struct bfa_fcs_fabric_s *fabric, wwn_t pwwn)
u16
bfa_fcs_fabric_vport_count(struct bfa_fcs_fabric_s *fabric)
{
- return (fabric->num_vports);
+ return fabric->num_vports;
}
/**
diff --git a/drivers/scsi/bfa/fcbuild.c b/drivers/scsi/bfa/fcbuild.c
index d174706b9ca..fee5456451c 100644
--- a/drivers/scsi/bfa/fcbuild.c
+++ b/drivers/scsi/bfa/fcbuild.c
@@ -188,14 +188,14 @@ fc_els_rsp_parse(struct fchs_s *fchs, int len)
switch (els_cmd->els_code) {
case FC_ELS_LS_RJT:
if (ls_rjt->reason_code == FC_LS_RJT_RSN_LOGICAL_BUSY)
- return (FC_PARSE_BUSY);
+ return FC_PARSE_BUSY;
else
- return (FC_PARSE_FAILURE);
+ return FC_PARSE_FAILURE;
case FC_ELS_ACC:
- return (FC_PARSE_OK);
+ return FC_PARSE_OK;
}
- return (FC_PARSE_OK);
+ return FC_PARSE_OK;
}
static void
@@ -228,7 +228,7 @@ fc_plogi_x_build(struct fchs_s *fchs, void *pld, u32 d_id, u32 s_id,
bfa_os_memcpy(&plogi->port_name, &port_name, sizeof(wwn_t));
bfa_os_memcpy(&plogi->node_name, &node_name, sizeof(wwn_t));
- return (sizeof(struct fc_logi_s));
+ return sizeof(struct fc_logi_s);
}
u16
@@ -267,7 +267,7 @@ fc_flogi_build(struct fchs_s *fchs, struct fc_logi_s *flogi, u32 s_id,
flogi->csp.npiv_supp = 1; /* @todo. field name is not correct */
vvl_info[0] = bfa_os_htonl(FLOGI_VVL_BRCD);
- return (sizeof(struct fc_logi_s));
+ return sizeof(struct fc_logi_s);
}
u16
@@ -287,7 +287,7 @@ fc_flogi_acc_build(struct fchs_s *fchs, struct fc_logi_s *flogi, u32 s_id,
flogi->csp.bbcred = bfa_os_htons(local_bb_credits);
- return (sizeof(struct fc_logi_s));
+ return sizeof(struct fc_logi_s);
}
u16
@@ -306,7 +306,7 @@ fc_fdisc_build(struct fchs_s *fchs, struct fc_logi_s *flogi, u32 s_id,
flogi->port_name = port_name;
flogi->node_name = node_name;
- return (sizeof(struct fc_logi_s));
+ return sizeof(struct fc_logi_s);
}
u16
@@ -338,26 +338,26 @@ fc_plogi_rsp_parse(struct fchs_s *fchs, int len, wwn_t port_name)
case FC_ELS_LS_RJT:
ls_rjt = (struct fc_ls_rjt_s *) (fchs + 1);
if (ls_rjt->reason_code == FC_LS_RJT_RSN_LOGICAL_BUSY)
- return (FC_PARSE_BUSY);
+ return FC_PARSE_BUSY;
else
- return (FC_PARSE_FAILURE);
+ return FC_PARSE_FAILURE;
case FC_ELS_ACC:
plogi = (struct fc_logi_s *) (fchs + 1);
if (len < sizeof(struct fc_logi_s))
- return (FC_PARSE_FAILURE);
+ return FC_PARSE_FAILURE;
if (!wwn_is_equal(plogi->port_name, port_name))
- return (FC_PARSE_FAILURE);
+ return FC_PARSE_FAILURE;
if (!plogi->class3.class_valid)
- return (FC_PARSE_FAILURE);
+ return FC_PARSE_FAILURE;
if (bfa_os_ntohs(plogi->class3.rxsz) < (FC_MIN_PDUSZ))
- return (FC_PARSE_FAILURE);
+ return FC_PARSE_FAILURE;
- return (FC_PARSE_OK);
+ return FC_PARSE_OK;
default:
- return (FC_PARSE_FAILURE);
+ return FC_PARSE_FAILURE;
}
}
@@ -372,7 +372,7 @@ fc_plogi_parse(struct fchs_s *fchs)
if ((bfa_os_ntohs(plogi->class3.rxsz) < FC_MIN_PDUSZ)
|| (bfa_os_ntohs(plogi->class3.rxsz) > FC_MAX_PDUSZ)
|| (plogi->class3.rxsz == 0))
- return (FC_PARSE_FAILURE);
+ return FC_PARSE_FAILURE;
return FC_PARSE_OK;
}
@@ -393,7 +393,7 @@ fc_prli_build(struct fchs_s *fchs, void *pld, u32 d_id, u32 s_id,
prli->parampage.servparams.task_retry_id = 0;
prli->parampage.servparams.confirm = 1;
- return (sizeof(struct fc_prli_s));
+ return sizeof(struct fc_prli_s);
}
u16
@@ -414,41 +414,41 @@ fc_prli_acc_build(struct fchs_s *fchs, void *pld, u32 d_id, u32 s_id,
prli->parampage.rspcode = FC_PRLI_ACC_XQTD;
- return (sizeof(struct fc_prli_s));
+ return sizeof(struct fc_prli_s);
}
enum fc_parse_status
fc_prli_rsp_parse(struct fc_prli_s *prli, int len)
{
if (len < sizeof(struct fc_prli_s))
- return (FC_PARSE_FAILURE);
+ return FC_PARSE_FAILURE;
if (prli->command != FC_ELS_ACC)
- return (FC_PARSE_FAILURE);
+ return FC_PARSE_FAILURE;
if ((prli->parampage.rspcode != FC_PRLI_ACC_XQTD)
&& (prli->parampage.rspcode != FC_PRLI_ACC_PREDEF_IMG))
- return (FC_PARSE_FAILURE);
+ return FC_PARSE_FAILURE;
if (prli->parampage.servparams.target != 1)
- return (FC_PARSE_FAILURE);
+ return FC_PARSE_FAILURE;
- return (FC_PARSE_OK);
+ return FC_PARSE_OK;
}
enum fc_parse_status
fc_prli_parse(struct fc_prli_s *prli)
{
if (prli->parampage.type != FC_TYPE_FCP)
- return (FC_PARSE_FAILURE);
+ return FC_PARSE_FAILURE;
if (!prli->parampage.imagepair)
- return (FC_PARSE_FAILURE);
+ return FC_PARSE_FAILURE;
if (!prli->parampage.servparams.initiator)
- return (FC_PARSE_FAILURE);
+ return FC_PARSE_FAILURE;
- return (FC_PARSE_OK);
+ return FC_PARSE_OK;
}
u16
@@ -462,7 +462,7 @@ fc_logo_build(struct fchs_s *fchs, struct fc_logo_s *logo, u32 d_id,
logo->nport_id = (s_id);
logo->orig_port_name = port_name;
- return (sizeof(struct fc_logo_s));
+ return sizeof(struct fc_logo_s);
}
static u16
@@ -484,7 +484,7 @@ fc_adisc_x_build(struct fchs_s *fchs, struct fc_adisc_s *adisc, u32 d_id,
adisc->orig_node_name = node_name;
adisc->nport_id = (s_id);
- return (sizeof(struct fc_adisc_s));
+ return sizeof(struct fc_adisc_s);
}
u16
@@ -511,15 +511,15 @@ fc_adisc_rsp_parse(struct fc_adisc_s *adisc, int len, wwn_t port_name,
{
if (len < sizeof(struct fc_adisc_s))
- return (FC_PARSE_FAILURE);
+ return FC_PARSE_FAILURE;
if (adisc->els_cmd.els_code != FC_ELS_ACC)
- return (FC_PARSE_FAILURE);
+ return FC_PARSE_FAILURE;
if (!wwn_is_equal(adisc->orig_port_name, port_name))
- return (FC_PARSE_FAILURE);
+ return FC_PARSE_FAILURE;
- return (FC_PARSE_OK);
+ return FC_PARSE_OK;
}
enum fc_parse_status
@@ -529,14 +529,14 @@ fc_adisc_parse(struct fchs_s *fchs, void *pld, u32 host_dap,
struct fc_adisc_s *adisc = (struct fc_adisc_s *) pld;
if (adisc->els_cmd.els_code != FC_ELS_ACC)
- return (FC_PARSE_FAILURE);
+ return FC_PARSE_FAILURE;
if ((adisc->nport_id == (host_dap))
&& wwn_is_equal(adisc->orig_port_name, port_name)
&& wwn_is_equal(adisc->orig_node_name, node_name))
- return (FC_PARSE_OK);
+ return FC_PARSE_OK;
- return (FC_PARSE_FAILURE);
+ return FC_PARSE_FAILURE;
}
enum fc_parse_status
@@ -550,13 +550,13 @@ fc_pdisc_parse(struct fchs_s *fchs, wwn_t node_name, wwn_t port_name)
if ((bfa_os_ntohs(pdisc->class3.rxsz) <
(FC_MIN_PDUSZ - sizeof(struct fchs_s)))
|| (pdisc->class3.rxsz == 0))
- return (FC_PARSE_FAILURE);
+ return FC_PARSE_FAILURE;
if (!wwn_is_equal(pdisc->port_name, port_name))
- return (FC_PARSE_FAILURE);
+ return FC_PARSE_FAILURE;
if (!wwn_is_equal(pdisc->node_name, node_name))
- return (FC_PARSE_FAILURE);
+ return FC_PARSE_FAILURE;
return FC_PARSE_OK;
}
@@ -570,7 +570,7 @@ fc_abts_build(struct fchs_s *fchs, u32 d_id, u32 s_id, u16 ox_id)
fchs->s_id = (s_id);
fchs->ox_id = bfa_os_htons(ox_id);
- return (sizeof(struct fchs_s));
+ return sizeof(struct fchs_s);
}
enum fc_parse_status
@@ -578,9 +578,9 @@ fc_abts_rsp_parse(struct fchs_s *fchs, int len)
{
if ((fchs->cat_info == FC_CAT_BA_ACC)
|| (fchs->cat_info == FC_CAT_BA_RJT))
- return (FC_PARSE_OK);
+ return FC_PARSE_OK;
- return (FC_PARSE_FAILURE);
+ return FC_PARSE_FAILURE;
}
u16
@@ -597,7 +597,7 @@ fc_rrq_build(struct fchs_s *fchs, struct fc_rrq_s *rrq, u32 d_id,
rrq->ox_id = bfa_os_htons(rrq_oxid);
rrq->rx_id = FC_RXID_ANY;
- return (sizeof(struct fc_rrq_s));
+ return sizeof(struct fc_rrq_s);
}
u16
@@ -611,7 +611,7 @@ fc_logo_acc_build(struct fchs_s *fchs, void *pld, u32 d_id, u32 s_id,
memset(acc, 0, sizeof(struct fc_els_cmd_s));
acc->els_code = FC_ELS_ACC;
- return (sizeof(struct fc_els_cmd_s));
+ return sizeof(struct fc_els_cmd_s);
}
u16
@@ -627,7 +627,7 @@ fc_ls_rjt_build(struct fchs_s *fchs, struct fc_ls_rjt_s *ls_rjt, u32 d_id,
ls_rjt->reason_code_expl = reason_code_expl;
ls_rjt->vendor_unique = 0x00;
- return (sizeof(struct fc_ls_rjt_s));
+ return sizeof(struct fc_ls_rjt_s);
}
u16
@@ -643,7 +643,7 @@ fc_ba_acc_build(struct fchs_s *fchs, struct fc_ba_acc_s *ba_acc, u32 d_id,
ba_acc->ox_id = fchs->ox_id;
ba_acc->rx_id = fchs->rx_id;
- return (sizeof(struct fc_ba_acc_s));
+ return sizeof(struct fc_ba_acc_s);
}
u16
@@ -654,7 +654,7 @@ fc_ls_acc_build(struct fchs_s *fchs, struct fc_els_cmd_s *els_cmd,
memset(els_cmd, 0, sizeof(struct fc_els_cmd_s));
els_cmd->els_code = FC_ELS_ACC;
- return (sizeof(struct fc_els_cmd_s));
+ return sizeof(struct fc_els_cmd_s);
}
int
@@ -696,7 +696,7 @@ fc_tprlo_acc_build(struct fchs_s *fchs, struct fc_tprlo_acc_s *tprlo_acc,
tprlo_acc->tprlo_acc_params[page].orig_process_assc = 0;
tprlo_acc->tprlo_acc_params[page].resp_process_assc = 0;
}
- return (bfa_os_ntohs(tprlo_acc->payload_len));
+ return bfa_os_ntohs(tprlo_acc->payload_len);
}
u16
@@ -721,7 +721,7 @@ fc_prlo_acc_build(struct fchs_s *fchs, struct fc_prlo_acc_s *prlo_acc,
prlo_acc->prlo_acc_params[page].resp_process_assc = 0;
}
- return (bfa_os_ntohs(prlo_acc->payload_len));
+ return bfa_os_ntohs(prlo_acc->payload_len);
}
u16
@@ -735,7 +735,7 @@ fc_rnid_build(struct fchs_s *fchs, struct fc_rnid_cmd_s *rnid, u32 d_id,
rnid->els_cmd.els_code = FC_ELS_RNID;
rnid->node_id_data_format = data_format;
- return (sizeof(struct fc_rnid_cmd_s));
+ return sizeof(struct fc_rnid_cmd_s);
}
u16
@@ -759,10 +759,10 @@ fc_rnid_acc_build(struct fchs_s *fchs, struct fc_rnid_acc_s *rnid_acc,
rnid_acc->specific_id_data_length =
sizeof(struct fc_rnid_general_topology_data_s);
bfa_os_assign(rnid_acc->gen_topology_data, *gen_topo_data);
- return (sizeof(struct fc_rnid_acc_s));
+ return sizeof(struct fc_rnid_acc_s);
} else {
- return (sizeof(struct fc_rnid_acc_s) -
- sizeof(struct fc_rnid_general_topology_data_s));
+ return sizeof(struct fc_rnid_acc_s) -
+ sizeof(struct fc_rnid_general_topology_data_s);
}
}
@@ -776,7 +776,7 @@ fc_rpsc_build(struct fchs_s *fchs, struct fc_rpsc_cmd_s *rpsc, u32 d_id,
memset(rpsc, 0, sizeof(struct fc_rpsc_cmd_s));
rpsc->els_cmd.els_code = FC_ELS_RPSC;
- return (sizeof(struct fc_rpsc_cmd_s));
+ return sizeof(struct fc_rpsc_cmd_s);
}
u16
@@ -797,8 +797,8 @@ fc_rpsc2_build(struct fchs_s *fchs, struct fc_rpsc2_cmd_s *rpsc2,
for (i = 0; i < npids; i++)
rpsc2->pid_list[i].pid = pid_list[i];
- return (sizeof(struct fc_rpsc2_cmd_s) + ((npids - 1) *
- (sizeof(u32))));
+ return sizeof(struct fc_rpsc2_cmd_s) + ((npids - 1) *
+ (sizeof(u32)));
}
u16
@@ -819,7 +819,7 @@ fc_rpsc_acc_build(struct fchs_s *fchs, struct fc_rpsc_acc_s *rpsc_acc,
rpsc_acc->speed_info[0].port_op_speed =
bfa_os_htons(oper_speed->port_op_speed);
- return (sizeof(struct fc_rpsc_acc_s));
+ return sizeof(struct fc_rpsc_acc_s);
}
@@ -856,7 +856,7 @@ fc_pdisc_build(struct fchs_s *fchs, u32 d_id, u32 s_id,
pdisc->port_name = port_name;
pdisc->node_name = node_name;
- return (sizeof(struct fc_logi_s));
+ return sizeof(struct fc_logi_s);
}
u16
@@ -865,21 +865,21 @@ fc_pdisc_rsp_parse(struct fchs_s *fchs, int len, wwn_t port_name)
struct fc_logi_s *pdisc = (struct fc_logi_s *) (fchs + 1);
if (len < sizeof(struct fc_logi_s))
- return (FC_PARSE_LEN_INVAL);
+ return FC_PARSE_LEN_INVAL;
if (pdisc->els_cmd.els_code != FC_ELS_ACC)
- return (FC_PARSE_ACC_INVAL);
+ return FC_PARSE_ACC_INVAL;
if (!wwn_is_equal(pdisc->port_name, port_name))
- return (FC_PARSE_PWWN_NOT_EQUAL);
+ return FC_PARSE_PWWN_NOT_EQUAL;
if (!pdisc->class3.class_valid)
- return (FC_PARSE_NWWN_NOT_EQUAL);
+ return FC_PARSE_NWWN_NOT_EQUAL;
if (bfa_os_ntohs(pdisc->class3.rxsz) < (FC_MIN_PDUSZ))
- return (FC_PARSE_RXSZ_INVAL);
+ return FC_PARSE_RXSZ_INVAL;
- return (FC_PARSE_OK);
+ return FC_PARSE_OK;
}
u16
@@ -903,7 +903,7 @@ fc_prlo_build(struct fchs_s *fchs, u32 d_id, u32 s_id, u16 ox_id,
prlo->prlo_params[page].resp_process_assc = 0;
}
- return (bfa_os_ntohs(prlo->payload_len));
+ return bfa_os_ntohs(prlo->payload_len);
}
u16
@@ -916,7 +916,7 @@ fc_prlo_rsp_parse(struct fchs_s *fchs, int len)
len = len;
if (prlo->command != FC_ELS_ACC)
- return (FC_PARSE_FAILURE);
+ return FC_PARSE_FAILURE;
num_pages = ((bfa_os_ntohs(prlo->payload_len)) - 4) / 16;
@@ -936,7 +936,7 @@ fc_prlo_rsp_parse(struct fchs_s *fchs, int len)
if (prlo->prlo_acc_params[page].resp_process_assc != 0)
return FC_PARSE_FAILURE;
}
- return (FC_PARSE_OK);
+ return FC_PARSE_OK;
}
@@ -968,7 +968,7 @@ fc_tprlo_build(struct fchs_s *fchs, u32 d_id, u32 s_id,
}
}
- return (bfa_os_ntohs(tprlo->payload_len));
+ return bfa_os_ntohs(tprlo->payload_len);
}
u16
@@ -981,23 +981,23 @@ fc_tprlo_rsp_parse(struct fchs_s *fchs, int len)
len = len;
if (tprlo->command != FC_ELS_ACC)
- return (FC_PARSE_ACC_INVAL);
+ return FC_PARSE_ACC_INVAL;
num_pages = (bfa_os_ntohs(tprlo->payload_len) - 4) / 16;
for (page = 0; page < num_pages; page++) {
if (tprlo->tprlo_acc_params[page].type != FC_TYPE_FCP)
- return (FC_PARSE_NOT_FCP);
+ return FC_PARSE_NOT_FCP;
if (tprlo->tprlo_acc_params[page].opa_valid != 0)
- return (FC_PARSE_OPAFLAG_INVAL);
+ return FC_PARSE_OPAFLAG_INVAL;
if (tprlo->tprlo_acc_params[page].rpa_valid != 0)
- return (FC_PARSE_RPAFLAG_INVAL);
+ return FC_PARSE_RPAFLAG_INVAL;
if (tprlo->tprlo_acc_params[page].orig_process_assc != 0)
- return (FC_PARSE_OPA_INVAL);
+ return FC_PARSE_OPA_INVAL;
if (tprlo->tprlo_acc_params[page].resp_process_assc != 0)
- return (FC_PARSE_RPA_INVAL);
+ return FC_PARSE_RPA_INVAL;
}
- return (FC_PARSE_OK);
+ return FC_PARSE_OK;
}
enum fc_parse_status
@@ -1024,7 +1024,7 @@ fc_ba_rjt_build(struct fchs_s *fchs, u32 d_id, u32 s_id,
fchs->cat_info = FC_CAT_BA_RJT;
ba_rjt->reason_code = reason_code;
ba_rjt->reason_expl = reason_expl;
- return (sizeof(struct fc_ba_rjt_s));
+ return sizeof(struct fc_ba_rjt_s);
}
static void
@@ -1073,7 +1073,7 @@ fc_gidpn_build(struct fchs_s *fchs, void *pyld, u32 s_id, u16 ox_id,
bfa_os_memset(gidpn, 0, sizeof(struct fcgs_gidpn_req_s));
gidpn->port_name = port_name;
- return (sizeof(struct fcgs_gidpn_req_s) + sizeof(struct ct_hdr_s));
+ return sizeof(struct fcgs_gidpn_req_s) + sizeof(struct ct_hdr_s);
}
u16
@@ -1090,7 +1090,7 @@ fc_gpnid_build(struct fchs_s *fchs, void *pyld, u32 s_id, u16 ox_id,
bfa_os_memset(gpnid, 0, sizeof(fcgs_gpnid_req_t));
gpnid->dap = port_id;
- return (sizeof(fcgs_gpnid_req_t) + sizeof(struct ct_hdr_s));
+ return sizeof(fcgs_gpnid_req_t) + sizeof(struct ct_hdr_s);
}
u16
@@ -1107,7 +1107,7 @@ fc_gnnid_build(struct fchs_s *fchs, void *pyld, u32 s_id, u16 ox_id,
bfa_os_memset(gnnid, 0, sizeof(fcgs_gnnid_req_t));
gnnid->dap = port_id;
- return (sizeof(fcgs_gnnid_req_t) + sizeof(struct ct_hdr_s));
+ return sizeof(fcgs_gnnid_req_t) + sizeof(struct ct_hdr_s);
}
u16
@@ -1137,7 +1137,7 @@ fc_scr_build(struct fchs_s *fchs, struct fc_scr_s *scr, u8 set_br_reg,
if (set_br_reg)
scr->vu_reg_func = FC_VU_SCR_REG_FUNC_FABRIC_NAME_CHANGE;
- return (sizeof(struct fc_scr_s));
+ return sizeof(struct fc_scr_s);
}
u16
@@ -1157,7 +1157,7 @@ fc_rscn_build(struct fchs_s *fchs, struct fc_rscn_pl_s *rscn, u32 s_id,
rscn->event[0].format = FC_RSCN_FORMAT_PORTID;
rscn->event[0].portid = s_id;
- return (sizeof(struct fc_rscn_pl_s));
+ return sizeof(struct fc_rscn_pl_s);
}
u16
@@ -1188,7 +1188,7 @@ fc_rftid_build(struct fchs_s *fchs, void *pyld, u32 s_id, u16 ox_id,
rftid->fc4_type[index] |= bfa_os_htonl(type_value);
}
- return (sizeof(struct fcgs_rftid_req_s) + sizeof(struct ct_hdr_s));
+ return sizeof(struct fcgs_rftid_req_s) + sizeof(struct ct_hdr_s);
}
u16
@@ -1210,7 +1210,7 @@ fc_rftid_build_sol(struct fchs_s *fchs, void *pyld, u32 s_id,
bfa_os_memcpy((void *)rftid->fc4_type, (void *)fc4_bitmap,
(bitmap_size < 32 ? bitmap_size : 32));
- return (sizeof(struct fcgs_rftid_req_s) + sizeof(struct ct_hdr_s));
+ return sizeof(struct fcgs_rftid_req_s) + sizeof(struct ct_hdr_s);
}
u16
@@ -1231,7 +1231,7 @@ fc_rffid_build(struct fchs_s *fchs, void *pyld, u32 s_id, u16 ox_id,
rffid->fc4ftr_bits = fc4_ftrs;
rffid->fc4_type = fc4_type;
- return (sizeof(struct fcgs_rffid_req_s) + sizeof(struct ct_hdr_s));
+ return sizeof(struct fcgs_rffid_req_s) + sizeof(struct ct_hdr_s);
}
u16
@@ -1253,7 +1253,7 @@ fc_rspnid_build(struct fchs_s *fchs, void *pyld, u32 s_id, u16 ox_id,
rspnid->spn_len = (u8) strlen((char *)name);
strncpy((char *)rspnid->spn, (char *)name, rspnid->spn_len);
- return (sizeof(struct fcgs_rspnid_req_s) + sizeof(struct ct_hdr_s));
+ return sizeof(struct fcgs_rspnid_req_s) + sizeof(struct ct_hdr_s);
}
u16
@@ -1275,7 +1275,7 @@ fc_gid_ft_build(struct fchs_s *fchs, void *pyld, u32 s_id,
gidft->domain_id = 0;
gidft->area_id = 0;
- return (sizeof(struct fcgs_gidft_req_s) + sizeof(struct ct_hdr_s));
+ return sizeof(struct fcgs_gidft_req_s) + sizeof(struct ct_hdr_s);
}
u16
@@ -1294,7 +1294,7 @@ fc_rpnid_build(struct fchs_s *fchs, void *pyld, u32 s_id, u32 port_id,
rpnid->port_id = port_id;
rpnid->port_name = port_name;
- return (sizeof(struct fcgs_rpnid_req_s) + sizeof(struct ct_hdr_s));
+ return sizeof(struct fcgs_rpnid_req_s) + sizeof(struct ct_hdr_s);
}
u16
@@ -1313,7 +1313,7 @@ fc_rnnid_build(struct fchs_s *fchs, void *pyld, u32 s_id, u32 port_id,
rnnid->port_id = port_id;
rnnid->node_name = node_name;
- return (sizeof(struct fcgs_rnnid_req_s) + sizeof(struct ct_hdr_s));
+ return sizeof(struct fcgs_rnnid_req_s) + sizeof(struct ct_hdr_s);
}
u16
@@ -1332,7 +1332,7 @@ fc_rcsid_build(struct fchs_s *fchs, void *pyld, u32 s_id, u32 port_id,
rcsid->port_id = port_id;
rcsid->cos = cos;
- return (sizeof(struct fcgs_rcsid_req_s) + sizeof(struct ct_hdr_s));
+ return sizeof(struct fcgs_rcsid_req_s) + sizeof(struct ct_hdr_s);
}
u16
@@ -1351,7 +1351,7 @@ fc_rptid_build(struct fchs_s *fchs, void *pyld, u32 s_id, u32 port_id,
rptid->port_id = port_id;
rptid->port_type = port_type;
- return (sizeof(struct fcgs_rptid_req_s) + sizeof(struct ct_hdr_s));
+ return sizeof(struct fcgs_rptid_req_s) + sizeof(struct ct_hdr_s);
}
u16
@@ -1368,7 +1368,7 @@ fc_ganxt_build(struct fchs_s *fchs, void *pyld, u32 s_id, u32 port_id)
bfa_os_memset(ganxt, 0, sizeof(struct fcgs_ganxt_req_s));
ganxt->port_id = port_id;
- return (sizeof(struct ct_hdr_s) + sizeof(struct fcgs_ganxt_req_s));
+ return sizeof(struct ct_hdr_s) + sizeof(struct fcgs_ganxt_req_s);
}
/*
@@ -1385,7 +1385,7 @@ fc_fdmi_reqhdr_build(struct fchs_s *fchs, void *pyld, u32 s_id,
fc_gs_fchdr_build(fchs, d_id, s_id, 0);
fc_gs_fdmi_cthdr_build(cthdr, s_id, cmd_code);
- return (sizeof(struct ct_hdr_s));
+ return sizeof(struct ct_hdr_s);
}
/*
@@ -1425,7 +1425,7 @@ fc_gmal_req_build(struct fchs_s *fchs, void *pyld, u32 s_id, wwn_t wwn)
bfa_os_memset(gmal, 0, sizeof(fcgs_gmal_req_t));
gmal->wwn = wwn;
- return (sizeof(struct ct_hdr_s) + sizeof(fcgs_gmal_req_t));
+ return sizeof(struct ct_hdr_s) + sizeof(fcgs_gmal_req_t);
}
/*
@@ -1445,5 +1445,5 @@ fc_gfn_req_build(struct fchs_s *fchs, void *pyld, u32 s_id, wwn_t wwn)
bfa_os_memset(gfn, 0, sizeof(fcgs_gfn_req_t));
gfn->wwn = wwn;
- return (sizeof(struct ct_hdr_s) + sizeof(fcgs_gfn_req_t));
+ return sizeof(struct ct_hdr_s) + sizeof(fcgs_gfn_req_t);
}
diff --git a/drivers/scsi/bfa/fcbuild.h b/drivers/scsi/bfa/fcbuild.h
index 4d248424f7b..8fa7f270ef7 100644
--- a/drivers/scsi/bfa/fcbuild.h
+++ b/drivers/scsi/bfa/fcbuild.h
@@ -32,8 +32,8 @@
* Utility Macros/functions
*/
-#define fcif_sof_set(_ifhdr, _sof) (_ifhdr)->sof = FC_ ## _sof
-#define fcif_eof_set(_ifhdr, _eof) (_ifhdr)->eof = FC_ ## _eof
+#define fcif_sof_set(_ifhdr, _sof) ((_ifhdr)->sof = FC_ ## _sof)
+#define fcif_eof_set(_ifhdr, _eof) ((_ifhdr)->eof = FC_ ## _eof)
#define wwn_is_equal(_wwn1, _wwn2) \
(memcmp(&(_wwn1), &(_wwn2), sizeof(wwn_t)) == 0)
@@ -49,7 +49,7 @@
static inline u32
fc_get_ctresp_pyld_len(u32 resp_len)
{
- return (resp_len - sizeof(struct ct_hdr_s));
+ return resp_len - sizeof(struct ct_hdr_s);
}
/*
diff --git a/drivers/scsi/bfa/fcpim.c b/drivers/scsi/bfa/fcpim.c
index 8ce5d893467..1f3c06efaa9 100644
--- a/drivers/scsi/bfa/fcpim.c
+++ b/drivers/scsi/bfa/fcpim.c
@@ -286,11 +286,10 @@ bfa_fcs_itnim_sm_online(struct bfa_fcs_itnim_s *itnim,
bfa_sm_set_state(itnim, bfa_fcs_itnim_sm_hcb_offline);
bfa_fcb_itnim_offline(itnim->itnim_drv);
bfa_itnim_offline(itnim->bfa_itnim);
- if (bfa_fcs_port_is_online(itnim->rport->port) == BFA_TRUE) {
+ if (bfa_fcs_port_is_online(itnim->rport->port) == BFA_TRUE)
bfa_fcs_itnim_aen_post(itnim, BFA_ITNIM_AEN_DISCONNECT);
- } else {
+ else
bfa_fcs_itnim_aen_post(itnim, BFA_ITNIM_AEN_OFFLINE);
- }
break;
case BFA_FCS_ITNIM_SM_DELETE:
@@ -732,7 +731,7 @@ bfa_fcs_itnim_lookup(struct bfa_fcs_port_s *port, wwn_t rpwwn)
return NULL;
bfa_assert(rport->itnim != NULL);
- return (rport->itnim);
+ return rport->itnim;
}
bfa_status_t
diff --git a/drivers/scsi/bfa/fcs.h b/drivers/scsi/bfa/fcs.h
index deee685e847..8d08230e629 100644
--- a/drivers/scsi/bfa/fcs.h
+++ b/drivers/scsi/bfa/fcs.h
@@ -23,7 +23,7 @@
#ifndef __FCS_H__
#define __FCS_H__
-#define __fcs_min_cfg(__fcs) (__fcs)->min_cfg
+#define __fcs_min_cfg(__fcs) ((__fcs)->min_cfg)
void bfa_fcs_modexit_comp(struct bfa_fcs_s *fcs);
diff --git a/drivers/scsi/bfa/fdmi.c b/drivers/scsi/bfa/fdmi.c
index b845eb272c7..df2a1e54e16 100644
--- a/drivers/scsi/bfa/fdmi.c
+++ b/drivers/scsi/bfa/fdmi.c
@@ -72,9 +72,9 @@ static u16 bfa_fcs_port_fdmi_build_rpa_pyld(
struct bfa_fcs_port_fdmi_s *fdmi, u8 *pyld);
static u16 bfa_fcs_port_fdmi_build_portattr_block(
struct bfa_fcs_port_fdmi_s *fdmi, u8 *pyld);
-void bfa_fcs_fdmi_get_hbaattr(struct bfa_fcs_port_fdmi_s *fdmi,
+static void bfa_fcs_fdmi_get_hbaattr(struct bfa_fcs_port_fdmi_s *fdmi,
struct bfa_fcs_fdmi_hba_attr_s *hba_attr);
-void bfa_fcs_fdmi_get_portattr(struct bfa_fcs_port_fdmi_s *fdmi,
+static void bfa_fcs_fdmi_get_portattr(struct bfa_fcs_port_fdmi_s *fdmi,
struct bfa_fcs_fdmi_port_attr_s *port_attr);
/**
* fcs_fdmi_sm FCS FDMI state machine
@@ -1091,7 +1091,7 @@ bfa_fcs_port_fdmi_timeout(void *arg)
bfa_sm_send_event(fdmi, FDMISM_EVENT_TIMEOUT);
}
-void
+static void
bfa_fcs_fdmi_get_hbaattr(struct bfa_fcs_port_fdmi_s *fdmi,
struct bfa_fcs_fdmi_hba_attr_s *hba_attr)
{
@@ -1145,7 +1145,7 @@ bfa_fcs_fdmi_get_hbaattr(struct bfa_fcs_port_fdmi_s *fdmi,
}
-void
+static void
bfa_fcs_fdmi_get_portattr(struct bfa_fcs_port_fdmi_s *fdmi,
struct bfa_fcs_fdmi_port_attr_s *port_attr)
{
diff --git a/drivers/scsi/bfa/include/aen/bfa_aen.h b/drivers/scsi/bfa/include/aen/bfa_aen.h
index da8cac093d3..d9cbc2a783d 100644
--- a/drivers/scsi/bfa/include/aen/bfa_aen.h
+++ b/drivers/scsi/bfa/include/aen/bfa_aen.h
@@ -54,7 +54,7 @@ bfa_aen_get_max_cfg_entry(void)
static inline s32
bfa_aen_get_meminfo(void)
{
- return (sizeof(struct bfa_aen_entry_s) * bfa_aen_get_max_cfg_entry());
+ return sizeof(struct bfa_aen_entry_s) * bfa_aen_get_max_cfg_entry();
}
static inline s32
diff --git a/drivers/scsi/bfa/include/bfa.h b/drivers/scsi/bfa/include/bfa.h
index 64c1412c570..d4bc0d9fa42 100644
--- a/drivers/scsi/bfa/include/bfa.h
+++ b/drivers/scsi/bfa/include/bfa.h
@@ -76,11 +76,11 @@ struct bfa_meminfo_s {
struct bfa_mem_elem_s meminfo[BFA_MEM_TYPE_MAX];
};
#define bfa_meminfo_kva(_m) \
- (_m)->meminfo[BFA_MEM_TYPE_KVA - 1].kva_curp
+ ((_m)->meminfo[BFA_MEM_TYPE_KVA - 1].kva_curp)
#define bfa_meminfo_dma_virt(_m) \
- (_m)->meminfo[BFA_MEM_TYPE_DMA - 1].kva_curp
+ ((_m)->meminfo[BFA_MEM_TYPE_DMA - 1].kva_curp)
#define bfa_meminfo_dma_phys(_m) \
- (_m)->meminfo[BFA_MEM_TYPE_DMA - 1].dma_curp
+ ((_m)->meminfo[BFA_MEM_TYPE_DMA - 1].dma_curp)
/**
* Generic Scatter Gather Element used by driver
@@ -100,7 +100,7 @@ struct bfa_sge_s {
/*
* bfa stats interfaces
*/
-#define bfa_stats(_mod, _stats) (_mod)->stats._stats ++
+#define bfa_stats(_mod, _stats) ((_mod)->stats._stats++)
#define bfa_ioc_get_stats(__bfa, __ioc_stats) \
bfa_ioc_fetch_stats(&(__bfa)->ioc, __ioc_stats)
@@ -136,7 +136,7 @@ void bfa_isr_enable(struct bfa_s *bfa);
void bfa_isr_disable(struct bfa_s *bfa);
void bfa_msix_getvecs(struct bfa_s *bfa, u32 *msix_vecs_bmap,
u32 *num_vecs, u32 *max_vec_bit);
-#define bfa_msix(__bfa, __vec) (__bfa)->msix.handler[__vec](__bfa, __vec)
+#define bfa_msix(__bfa, __vec) ((__bfa)->msix.handler[__vec](__bfa, __vec))
void bfa_comp_deq(struct bfa_s *bfa, struct list_head *comp_q);
void bfa_comp_process(struct bfa_s *bfa, struct list_head *comp_q);
diff --git a/drivers/scsi/bfa/include/bfa_svc.h b/drivers/scsi/bfa/include/bfa_svc.h
index 0c80b74f72e..268d956bad8 100644
--- a/drivers/scsi/bfa/include/bfa_svc.h
+++ b/drivers/scsi/bfa/include/bfa_svc.h
@@ -34,10 +34,10 @@ struct bfa_fcxp_s;
*/
struct bfa_rport_info_s {
u16 max_frmsz; /* max rcv pdu size */
- u32 pid : 24, /* remote port ID */
- lp_tag : 8;
- u32 local_pid : 24, /* local port ID */
- cisc : 8; /* CIRO supported */
+ u32 pid:24, /* remote port ID */
+ lp_tag:8;
+ u32 local_pid:24, /* local port ID */
+ cisc:8; /* CIRO supported */
u8 fc_class; /* supported FC classes. enum fc_cos */
u8 vf_en; /* virtual fabric enable */
u16 vf_id; /* virtual fabric ID */
diff --git a/drivers/scsi/bfa/include/bfi/bfi.h b/drivers/scsi/bfa/include/bfi/bfi.h
index 6cadfe0d4ba..7042c18e542 100644
--- a/drivers/scsi/bfa/include/bfi/bfi.h
+++ b/drivers/scsi/bfa/include/bfi/bfi.h
@@ -93,13 +93,13 @@ union bfi_addr_u {
*/
struct bfi_sge_s {
#ifdef __BIGENDIAN
- u32 flags : 2,
- rsvd : 2,
- sg_len : 28;
+ u32 flags:2,
+ rsvd:2,
+ sg_len:28;
#else
- u32 sg_len : 28,
- rsvd : 2,
- flags : 2;
+ u32 sg_len:28,
+ rsvd:2,
+ flags:2;
#endif
union bfi_addr_u sga;
};
diff --git a/drivers/scsi/bfa/include/bfi/bfi_ioc.h b/drivers/scsi/bfa/include/bfi/bfi_ioc.h
index 026e9c06ae9..96ef0567065 100644
--- a/drivers/scsi/bfa/include/bfi/bfi_ioc.h
+++ b/drivers/scsi/bfa/include/bfi/bfi_ioc.h
@@ -142,7 +142,7 @@ enum {
BFI_ADAPTER_UNSUPP = 0x400000, /* unknown adapter type */
};
-#define BFI_ADAPTER_GETP(__prop,__adap_prop) \
+#define BFI_ADAPTER_GETP(__prop, __adap_prop) \
(((__adap_prop) & BFI_ADAPTER_ ## __prop ## _MK) >> \
BFI_ADAPTER_ ## __prop ## _SH)
#define BFI_ADAPTER_SETP(__prop, __val) \
diff --git a/drivers/scsi/bfa/include/bfi/bfi_lps.h b/drivers/scsi/bfa/include/bfi/bfi_lps.h
index 414b0e30f6e..c59d47badb4 100644
--- a/drivers/scsi/bfa/include/bfi/bfi_lps.h
+++ b/drivers/scsi/bfa/include/bfi/bfi_lps.h
@@ -55,8 +55,8 @@ struct bfi_lps_login_rsp_s {
u16 bb_credit;
u8 f_port;
u8 npiv_en;
- u32 lp_pid : 24;
- u32 auth_req : 8;
+ u32 lp_pid:24;
+ u32 auth_req:8;
mac_t lp_mac;
mac_t fcf_mac;
u8 ext_status;
diff --git a/drivers/scsi/bfa/include/bfi/bfi_rport.h b/drivers/scsi/bfa/include/bfi/bfi_rport.h
index 3520f55f09d..e1cd83b56ec 100644
--- a/drivers/scsi/bfa/include/bfi/bfi_rport.h
+++ b/drivers/scsi/bfa/include/bfi/bfi_rport.h
@@ -38,10 +38,10 @@ struct bfi_rport_create_req_s {
struct bfi_mhdr_s mh; /* common msg header */
u16 bfa_handle; /* host rport handle */
u16 max_frmsz; /* max rcv pdu size */
- u32 pid : 24, /* remote port ID */
- lp_tag : 8; /* local port tag */
- u32 local_pid : 24, /* local port ID */
- cisc : 8;
+ u32 pid:24, /* remote port ID */
+ lp_tag:8; /* local port tag */
+ u32 local_pid:24, /* local port ID */
+ cisc:8;
u8 fc_class; /* supported FC classes */
u8 vf_en; /* virtual fabric enable */
u16 vf_id; /* virtual fabric ID */
diff --git a/drivers/scsi/bfa/include/cs/bfa_checksum.h b/drivers/scsi/bfa/include/cs/bfa_checksum.h
index af8c1d533ba..650f8d0aaff 100644
--- a/drivers/scsi/bfa/include/cs/bfa_checksum.h
+++ b/drivers/scsi/bfa/include/cs/bfa_checksum.h
@@ -31,7 +31,7 @@ bfa_checksum_u32(u32 *buf, int sz)
for (i = 0; i < m; i++)
sum ^= buf[i];
- return (sum);
+ return sum;
}
static inline u16
@@ -43,7 +43,7 @@ bfa_checksum_u16(u16 *buf, int sz)
for (i = 0; i < m; i++)
sum ^= buf[i];
- return (sum);
+ return sum;
}
static inline u8
@@ -55,6 +55,6 @@ bfa_checksum_u8(u8 *buf, int sz)
for (i = 0; i < sz; i++)
sum ^= buf[i];
- return (sum);
+ return sum;
}
#endif
diff --git a/drivers/scsi/bfa/include/cs/bfa_sm.h b/drivers/scsi/bfa/include/cs/bfa_sm.h
index 9877066680a..b0a92baf665 100644
--- a/drivers/scsi/bfa/include/cs/bfa_sm.h
+++ b/drivers/scsi/bfa/include/cs/bfa_sm.h
@@ -24,8 +24,8 @@
typedef void (*bfa_sm_t)(void *sm, int event);
-#define bfa_sm_set_state(_sm, _state) (_sm)->sm = (bfa_sm_t)(_state)
-#define bfa_sm_send_event(_sm, _event) (_sm)->sm((_sm), (_event))
+#define bfa_sm_set_state(_sm, _state) ((_sm)->sm = (bfa_sm_t)(_state))
+#define bfa_sm_send_event(_sm, _event) ((_sm)->sm((_sm), (_event)))
#define bfa_sm_get_state(_sm) ((_sm)->sm)
#define bfa_sm_cmp_state(_sm, _state) ((_sm)->sm == (bfa_sm_t)(_state))
@@ -62,7 +62,7 @@ typedef void (*bfa_fsm_t)(void *fsm, int event);
} while (0)
#define bfa_fsm_send_event(_fsm, _event) \
- (_fsm)->fsm((_fsm), (_event))
+ ((_fsm)->fsm((_fsm), (_event)))
#define bfa_fsm_cmp_state(_fsm, _state) \
((_fsm)->fsm == (bfa_fsm_t)(_state))
diff --git a/drivers/scsi/bfa/include/cs/bfa_trc.h b/drivers/scsi/bfa/include/cs/bfa_trc.h
index 3e743928c74..310771c888e 100644
--- a/drivers/scsi/bfa/include/cs/bfa_trc.h
+++ b/drivers/scsi/bfa/include/cs/bfa_trc.h
@@ -24,7 +24,7 @@
#endif
#ifndef BFA_TRC_TS
-#define BFA_TRC_TS(_trcm) ((_trcm)->ticks ++)
+#define BFA_TRC_TS(_trcm) ((_trcm)->ticks++)
#endif
struct bfa_trc_s {
diff --git a/drivers/scsi/bfa/include/fcs/bfa_fcs_fabric.h b/drivers/scsi/bfa/include/fcs/bfa_fcs_fabric.h
index 4ffd2242d3d..08b79d5e46f 100644
--- a/drivers/scsi/bfa/include/fcs/bfa_fcs_fabric.h
+++ b/drivers/scsi/bfa/include/fcs/bfa_fcs_fabric.h
@@ -75,7 +75,7 @@ struct bfa_fcs_fabric_s {
*/
};
-#define bfa_fcs_fabric_npiv_capable(__f) (__f)->is_npiv
+#define bfa_fcs_fabric_npiv_capable(__f) ((__f)->is_npiv)
#define bfa_fcs_fabric_is_switched(__f) \
((__f)->fab_type == BFA_FCS_FABRIC_SWITCHED)
diff --git a/drivers/scsi/bfa/include/fcs/bfa_fcs_lport.h b/drivers/scsi/bfa/include/fcs/bfa_fcs_lport.h
index b85cba884b9..967ceb0eb07 100644
--- a/drivers/scsi/bfa/include/fcs/bfa_fcs_lport.h
+++ b/drivers/scsi/bfa/include/fcs/bfa_fcs_lport.h
@@ -125,12 +125,12 @@ union bfa_fcs_port_topo_u {
struct bfa_fcs_port_s {
struct list_head qe; /* used by port/vport */
bfa_sm_t sm; /* state machine */
- struct bfa_fcs_fabric_s *fabric; /* parent fabric */
- struct bfa_port_cfg_s port_cfg; /* port configuration */
+ struct bfa_fcs_fabric_s *fabric;/* parent fabric */
+ struct bfa_port_cfg_s port_cfg;/* port configuration */
struct bfa_timer_s link_timer; /* timer for link offline */
- u32 pid : 24; /* FC address */
- u8 lp_tag; /* lport tag */
- u16 num_rports; /* Num of r-ports */
+ u32 pid:24; /* FC address */
+ u8 lp_tag; /* lport tag */
+ u16 num_rports; /* Num of r-ports */
struct list_head rport_q; /* queue of discovered r-ports */
struct bfa_fcs_s *fcs; /* FCS instance */
union bfa_fcs_port_topo_u port_topo; /* fabric/loop/n2n details */
@@ -188,13 +188,14 @@ bfa_fcs_port_get_drvport(struct bfa_fcs_port_s *port)
}
-#define bfa_fcs_port_get_opertype(_lport) (_lport)->fabric->oper_type
+#define bfa_fcs_port_get_opertype(_lport) ((_lport)->fabric->oper_type)
-#define bfa_fcs_port_get_fabric_name(_lport) (_lport)->fabric->fabric_name
+#define bfa_fcs_port_get_fabric_name(_lport) ((_lport)->fabric->fabric_name)
-#define bfa_fcs_port_get_fabric_ipaddr(_lport) (_lport)->fabric->fabric_ip_addr
+#define bfa_fcs_port_get_fabric_ipaddr(_lport) \
+ ((_lport)->fabric->fabric_ip_addr)
/**
* bfa fcs port public functions
diff --git a/drivers/scsi/bfa/include/protocol/ct.h b/drivers/scsi/bfa/include/protocol/ct.h
index 79d16a9ab28..b82540a230c 100644
--- a/drivers/scsi/bfa/include/protocol/ct.h
+++ b/drivers/scsi/bfa/include/protocol/ct.h
@@ -193,11 +193,11 @@ struct fcgs_rftid_req_s {
#define FC_GS_FCP_FC4_FEATURE_TARGET 0x01
struct fcgs_rffid_req_s{
- u32 rsvd :8;
- u32 dap :24; /* port identifier */
- u32 rsvd1 :16;
- u32 fc4ftr_bits :8; /* fc4 feature bits */
- u32 fc4_type :8; /* corresponding FC4 Type */
+ u32 rsvd:8;
+ u32 dap:24; /* port identifier */
+ u32 rsvd1:16;
+ u32 fc4ftr_bits:8; /* fc4 feature bits */
+ u32 fc4_type:8; /* corresponding FC4 Type */
};
/**
diff --git a/drivers/scsi/bfa/include/protocol/fc.h b/drivers/scsi/bfa/include/protocol/fc.h
index 3e39ba58cfb..14969eecf6a 100644
--- a/drivers/scsi/bfa/include/protocol/fc.h
+++ b/drivers/scsi/bfa/include/protocol/fc.h
@@ -486,14 +486,14 @@ struct fc_rsi_s {
* see FC-PH-X table 113 & 115 for explanation also FCP table 8
*/
struct fc_prli_params_s{
- u32 reserved: 16;
+ u32 reserved:16;
#ifdef __BIGENDIAN
- u32 reserved1: 5;
- u32 rec_support : 1;
- u32 task_retry_id : 1;
- u32 retry : 1;
+ u32 reserved1:5;
+ u32 rec_support:1;
+ u32 task_retry_id:1;
+ u32 retry:1;
- u32 confirm : 1;
+ u32 confirm:1;
u32 doverlay:1;
u32 initiator:1;
u32 target:1;
@@ -502,10 +502,10 @@ struct fc_prli_params_s{
u32 rxrdisab:1;
u32 wxrdisab:1;
#else
- u32 retry : 1;
- u32 task_retry_id : 1;
- u32 rec_support : 1;
- u32 reserved1: 5;
+ u32 retry:1;
+ u32 task_retry_id:1;
+ u32 rec_support:1;
+ u32 reserved1:5;
u32 wxrdisab:1;
u32 rxrdisab:1;
@@ -514,7 +514,7 @@ struct fc_prli_params_s{
u32 target:1;
u32 initiator:1;
u32 doverlay:1;
- u32 confirm : 1;
+ u32 confirm:1;
#endif
};
diff --git a/drivers/scsi/bfa/loop.c b/drivers/scsi/bfa/loop.c
index a418dedebe9..f7c7f4f3c64 100644
--- a/drivers/scsi/bfa/loop.c
+++ b/drivers/scsi/bfa/loop.c
@@ -58,49 +58,16 @@ static const u8 port_loop_alpa_map[] = {
/*
* Local Functions
*/
-bfa_status_t bfa_fcs_port_loop_send_plogi(struct bfa_fcs_port_s *port,
- u8 alpa);
-
-void bfa_fcs_port_loop_plogi_response(void *fcsarg,
- struct bfa_fcxp_s *fcxp,
- void *cbarg,
- bfa_status_t req_status,
- u32 rsp_len,
- u32 resid_len,
- struct fchs_s *rsp_fchs);
-
-bfa_status_t bfa_fcs_port_loop_send_adisc(struct bfa_fcs_port_s *port,
- u8 alpa);
-
-void bfa_fcs_port_loop_adisc_response(void *fcsarg,
- struct bfa_fcxp_s *fcxp,
- void *cbarg,
- bfa_status_t req_status,
- u32 rsp_len,
- u32 resid_len,
- struct fchs_s *rsp_fchs);
-
-bfa_status_t bfa_fcs_port_loop_send_plogi_acc(struct bfa_fcs_port_s *port,
- u8 alpa);
-
-void bfa_fcs_port_loop_plogi_acc_response(void *fcsarg,
- struct bfa_fcxp_s *fcxp,
- void *cbarg,
- bfa_status_t req_status,
- u32 rsp_len,
- u32 resid_len,
- struct fchs_s *rsp_fchs);
-
-bfa_status_t bfa_fcs_port_loop_send_adisc_acc(struct bfa_fcs_port_s *port,
- u8 alpa);
-
-void bfa_fcs_port_loop_adisc_acc_response(void *fcsarg,
- struct bfa_fcxp_s *fcxp,
- void *cbarg,
- bfa_status_t req_status,
- u32 rsp_len,
- u32 resid_len,
- struct fchs_s *rsp_fchs);
+static bfa_status_t bfa_fcs_port_loop_send_plogi(struct bfa_fcs_port_s *port,
+ u8 alpa);
+
+static void bfa_fcs_port_loop_plogi_response(void *fcsarg,
+ struct bfa_fcxp_s *fcxp,
+ void *cbarg,
+ bfa_status_t req_status,
+ u32 rsp_len,
+ u32 resid_len,
+ struct fchs_s *rsp_fchs);
/**
* Called by port to initializar in provate LOOP topology.
*/
@@ -179,7 +146,7 @@ bfa_fcs_port_loop_lip(struct bfa_fcs_port_s *port)
/**
* Local Functions.
*/
-bfa_status_t
+static bfa_status_t
bfa_fcs_port_loop_send_plogi(struct bfa_fcs_port_s *port, u8 alpa)
{
struct fchs_s fchs;
@@ -208,7 +175,7 @@ bfa_fcs_port_loop_send_plogi(struct bfa_fcs_port_s *port, u8 alpa)
/**
* Called by fcxp to notify the Plogi response
*/
-void
+static void
bfa_fcs_port_loop_plogi_response(void *fcsarg, struct bfa_fcxp_s *fcxp,
void *cbarg, bfa_status_t req_status,
u32 rsp_len, u32 resid_len,
@@ -244,179 +211,3 @@ bfa_fcs_port_loop_plogi_response(void *fcsarg, struct bfa_fcxp_s *fcxp,
bfa_assert(0);
}
}
-
-bfa_status_t
-bfa_fcs_port_loop_send_plogi_acc(struct bfa_fcs_port_s *port, u8 alpa)
-{
- struct fchs_s fchs;
- struct bfa_fcxp_s *fcxp;
- int len;
-
- bfa_trc(port->fcs, alpa);
-
- fcxp = bfa_fcxp_alloc(NULL, port->fcs->bfa, 0, 0, NULL, NULL, NULL,
- NULL);
- bfa_assert(fcxp);
-
- len = fc_plogi_acc_build(&fchs, bfa_fcxp_get_reqbuf(fcxp), alpa,
- bfa_fcs_port_get_fcid(port), 0,
- port->port_cfg.pwwn, port->port_cfg.nwwn,
- bfa_pport_get_maxfrsize(port->fcs->bfa));
-
- bfa_fcxp_send(fcxp, NULL, port->fabric->vf_id, port->lp_tag, BFA_FALSE,
- FC_CLASS_3, len, &fchs,
- bfa_fcs_port_loop_plogi_acc_response,
- (void *)port, FC_MAX_PDUSZ, 0); /* No response
- * expected
- */
-
- return BFA_STATUS_OK;
-}
-
-/*
- * Plogi Acc Response
- * We donot do any processing here.
- */
-void
-bfa_fcs_port_loop_plogi_acc_response(void *fcsarg, struct bfa_fcxp_s *fcxp,
- void *cbarg, bfa_status_t req_status,
- u32 rsp_len, u32 resid_len,
- struct fchs_s *rsp_fchs)
-{
-
- struct bfa_fcs_port_s *port = (struct bfa_fcs_port_s *) cbarg;
-
- bfa_trc(port->fcs, port->pid);
-
- /*
- * Sanity Checks
- */
- if (req_status != BFA_STATUS_OK) {
- bfa_trc(port->fcs, req_status);
- return;
- }
-}
-
-bfa_status_t
-bfa_fcs_port_loop_send_adisc(struct bfa_fcs_port_s *port, u8 alpa)
-{
- struct fchs_s fchs;
- struct bfa_fcxp_s *fcxp;
- int len;
-
- bfa_trc(port->fcs, alpa);
-
- fcxp = bfa_fcxp_alloc(NULL, port->fcs->bfa, 0, 0, NULL, NULL, NULL,
- NULL);
- bfa_assert(fcxp);
-
- len = fc_adisc_build(&fchs, bfa_fcxp_get_reqbuf(fcxp), alpa,
- bfa_fcs_port_get_fcid(port), 0,
- port->port_cfg.pwwn, port->port_cfg.nwwn);
-
- bfa_fcxp_send(fcxp, NULL, port->fabric->vf_id, port->lp_tag, BFA_FALSE,
- FC_CLASS_3, len, &fchs,
- bfa_fcs_port_loop_adisc_response, (void *)port,
- FC_MAX_PDUSZ, FC_RA_TOV);
-
- return BFA_STATUS_OK;
-}
-
-/**
- * Called by fcxp to notify the ADISC response
- */
-void
-bfa_fcs_port_loop_adisc_response(void *fcsarg, struct bfa_fcxp_s *fcxp,
- void *cbarg, bfa_status_t req_status,
- u32 rsp_len, u32 resid_len,
- struct fchs_s *rsp_fchs)
-{
- struct bfa_fcs_port_s *port = (struct bfa_fcs_port_s *) cbarg;
- struct bfa_fcs_rport_s *rport;
- struct fc_adisc_s *adisc_resp;
- struct fc_els_cmd_s *els_cmd;
- u32 pid = rsp_fchs->s_id;
-
- bfa_trc(port->fcs, req_status);
-
- /*
- * Sanity Checks
- */
- if (req_status != BFA_STATUS_OK) {
- /*
- * TBD : we may need to retry certain requests
- */
- bfa_fcxp_free(fcxp);
- return;
- }
-
- els_cmd = (struct fc_els_cmd_s *) BFA_FCXP_RSP_PLD(fcxp);
- adisc_resp = (struct fc_adisc_s *) els_cmd;
-
- if (els_cmd->els_code == FC_ELS_ACC) {
- } else {
- bfa_trc(port->fcs, adisc_resp->els_cmd.els_code);
-
- /*
- * TBD: we may need to check for reject codes and retry
- */
- rport = bfa_fcs_port_get_rport_by_pid(port, pid);
- if (rport) {
- list_del(&rport->qe);
- bfa_fcs_rport_delete(rport);
- }
-
- }
- return;
-}
-
-bfa_status_t
-bfa_fcs_port_loop_send_adisc_acc(struct bfa_fcs_port_s *port, u8 alpa)
-{
- struct fchs_s fchs;
- struct bfa_fcxp_s *fcxp;
- int len;
-
- bfa_trc(port->fcs, alpa);
-
- fcxp = bfa_fcxp_alloc(NULL, port->fcs->bfa, 0, 0, NULL, NULL, NULL,
- NULL);
- bfa_assert(fcxp);
-
- len = fc_adisc_acc_build(&fchs, bfa_fcxp_get_reqbuf(fcxp), alpa,
- bfa_fcs_port_get_fcid(port), 0,
- port->port_cfg.pwwn, port->port_cfg.nwwn);
-
- bfa_fcxp_send(fcxp, NULL, port->fabric->vf_id, port->lp_tag, BFA_FALSE,
- FC_CLASS_3, len, &fchs,
- bfa_fcs_port_loop_adisc_acc_response,
- (void *)port, FC_MAX_PDUSZ, 0); /* no reponse
- * expected
- */
-
- return BFA_STATUS_OK;
-}
-
-/*
- * Adisc Acc Response
- * We donot do any processing here.
- */
-void
-bfa_fcs_port_loop_adisc_acc_response(void *fcsarg, struct bfa_fcxp_s *fcxp,
- void *cbarg, bfa_status_t req_status,
- u32 rsp_len, u32 resid_len,
- struct fchs_s *rsp_fchs)
-{
-
- struct bfa_fcs_port_s *port = (struct bfa_fcs_port_s *) cbarg;
-
- bfa_trc(port->fcs, port->pid);
-
- /*
- * Sanity Checks
- */
- if (req_status != BFA_STATUS_OK) {
- bfa_trc(port->fcs, req_status);
- return;
- }
-}
diff --git a/drivers/scsi/bfa/lport_api.c b/drivers/scsi/bfa/lport_api.c
index 8f51a83f183..1e06792cd4c 100644
--- a/drivers/scsi/bfa/lport_api.c
+++ b/drivers/scsi/bfa/lport_api.c
@@ -43,7 +43,7 @@ bfa_fcs_cfg_base_port(struct bfa_fcs_s *fcs, struct bfa_port_cfg_s *port_cfg)
struct bfa_fcs_port_s *
bfa_fcs_get_base_port(struct bfa_fcs_s *fcs)
{
- return (&fcs->fabric.bport);
+ return &fcs->fabric.bport;
}
wwn_t
@@ -88,11 +88,10 @@ bfa_fcs_port_get_rport(struct bfa_fcs_port_s *port, wwn_t wwn, int index,
}
bfa_trc(fcs, i);
- if (rport) {
+ if (rport)
return rport->pwwn;
- } else {
+ else
return (wwn_t) 0;
- }
}
void
@@ -198,17 +197,17 @@ bfa_fcs_lookup_port(struct bfa_fcs_s *fcs, u16 vf_id, wwn_t lpwwn)
vf = bfa_fcs_vf_lookup(fcs, vf_id);
if (vf == NULL) {
bfa_trc(fcs, vf_id);
- return (NULL);
+ return NULL;
}
if (!lpwwn || (vf->bport.port_cfg.pwwn == lpwwn))
- return (&vf->bport);
+ return &vf->bport;
vport = bfa_fcs_fabric_vport_lookup(vf, lpwwn);
if (vport)
- return (&vport->lport);
+ return &vport->lport;
- return (NULL);
+ return NULL;
}
/*
diff --git a/drivers/scsi/bfa/ns.c b/drivers/scsi/bfa/ns.c
index 59fea99d67a..2f8b880060b 100644
--- a/drivers/scsi/bfa/ns.c
+++ b/drivers/scsi/bfa/ns.c
@@ -932,11 +932,10 @@ bfa_fcs_port_ns_send_rff_id(void *ns_cbarg, struct bfa_fcxp_s *fcxp_alloced)
}
ns->fcxp = fcxp;
- if (BFA_FCS_VPORT_IS_INITIATOR_MODE(ns->port)) {
+ if (BFA_FCS_VPORT_IS_INITIATOR_MODE(ns->port))
fc4_ftrs = FC_GS_FCP_FC4_FEATURE_INITIATOR;
- } else if (BFA_FCS_VPORT_IS_TARGET_MODE(ns->port)) {
+ else if (BFA_FCS_VPORT_IS_TARGET_MODE(ns->port))
fc4_ftrs = FC_GS_FCP_FC4_FEATURE_TARGET;
- }
len = fc_rffid_build(&fchs, bfa_fcxp_get_reqbuf(fcxp),
bfa_fcs_port_get_fcid(port), 0, FC_TYPE_FCP,
diff --git a/drivers/scsi/bfa/plog.c b/drivers/scsi/bfa/plog.c
index 86af818d17b..fcb8864d327 100644
--- a/drivers/scsi/bfa/plog.c
+++ b/drivers/scsi/bfa/plog.c
@@ -180,5 +180,5 @@ bfa_plog_disable(struct bfa_plog_s *plog)
bfa_boolean_t
bfa_plog_get_setting(struct bfa_plog_s *plog)
{
- return((bfa_boolean_t)plog->plog_enabled);
+ return (bfa_boolean_t)plog->plog_enabled;
}
diff --git a/drivers/scsi/bfa/rport_ftrs.c b/drivers/scsi/bfa/rport_ftrs.c
index 8a1f59d596c..e1932c885ac 100644
--- a/drivers/scsi/bfa/rport_ftrs.c
+++ b/drivers/scsi/bfa/rport_ftrs.c
@@ -79,7 +79,7 @@ bfa_fcs_rpf_sm_uninit(struct bfa_fcs_rpf_s *rpf, enum rpf_event event)
bfa_trc(rport->fcs, event);
switch (event) {
- case RPFSM_EVENT_RPORT_ONLINE :
+ case RPFSM_EVENT_RPORT_ONLINE:
if (!BFA_FCS_PID_IS_WKA(rport->pid)) {
bfa_sm_set_state(rpf, bfa_fcs_rpf_sm_rpsc_sending);
rpf->rpsc_retries = 0;
@@ -87,7 +87,7 @@ bfa_fcs_rpf_sm_uninit(struct bfa_fcs_rpf_s *rpf, enum rpf_event event)
break;
};
- case RPFSM_EVENT_RPORT_OFFLINE :
+ case RPFSM_EVENT_RPORT_OFFLINE:
break;
default:
@@ -107,7 +107,7 @@ bfa_fcs_rpf_sm_rpsc_sending(struct bfa_fcs_rpf_s *rpf, enum rpf_event event)
bfa_sm_set_state(rpf, bfa_fcs_rpf_sm_rpsc);
break;
- case RPFSM_EVENT_RPORT_OFFLINE :
+ case RPFSM_EVENT_RPORT_OFFLINE:
bfa_sm_set_state(rpf, bfa_fcs_rpf_sm_offline);
bfa_fcxp_walloc_cancel(rport->fcs->bfa, &rpf->fcxp_wqe);
rpf->rpsc_retries = 0;
@@ -130,11 +130,10 @@ bfa_fcs_rpf_sm_rpsc(struct bfa_fcs_rpf_s *rpf, enum rpf_event event)
case RPFSM_EVENT_RPSC_COMP:
bfa_sm_set_state(rpf, bfa_fcs_rpf_sm_online);
/* Update speed info in f/w via BFA */
- if (rpf->rpsc_speed != BFA_PPORT_SPEED_UNKNOWN) {
+ if (rpf->rpsc_speed != BFA_PPORT_SPEED_UNKNOWN)
bfa_rport_speed(rport->bfa_rport, rpf->rpsc_speed);
- } else if (rpf->assigned_speed != BFA_PPORT_SPEED_UNKNOWN) {
+ else if (rpf->assigned_speed != BFA_PPORT_SPEED_UNKNOWN)
bfa_rport_speed(rport->bfa_rport, rpf->assigned_speed);
- }
break;
case RPFSM_EVENT_RPSC_FAIL:
@@ -154,7 +153,7 @@ bfa_fcs_rpf_sm_rpsc(struct bfa_fcs_rpf_s *rpf, enum rpf_event event)
}
break;
- case RPFSM_EVENT_RPORT_OFFLINE :
+ case RPFSM_EVENT_RPORT_OFFLINE:
bfa_sm_set_state(rpf, bfa_fcs_rpf_sm_offline);
bfa_fcxp_discard(rpf->fcxp);
rpf->rpsc_retries = 0;
@@ -174,13 +173,13 @@ bfa_fcs_rpf_sm_rpsc_retry(struct bfa_fcs_rpf_s *rpf, enum rpf_event event)
bfa_trc(rport->fcs, event);
switch (event) {
- case RPFSM_EVENT_TIMEOUT :
+ case RPFSM_EVENT_TIMEOUT:
/* re-send the RPSC */
bfa_sm_set_state(rpf, bfa_fcs_rpf_sm_rpsc_sending);
bfa_fcs_rpf_send_rpsc2(rpf, NULL);
break;
- case RPFSM_EVENT_RPORT_OFFLINE :
+ case RPFSM_EVENT_RPORT_OFFLINE:
bfa_timer_stop(&rpf->timer);
bfa_sm_set_state(rpf, bfa_fcs_rpf_sm_offline);
rpf->rpsc_retries = 0;
@@ -201,7 +200,7 @@ bfa_fcs_rpf_sm_online(struct bfa_fcs_rpf_s *rpf, enum rpf_event event)
bfa_trc(rport->fcs, event);
switch (event) {
- case RPFSM_EVENT_RPORT_OFFLINE :
+ case RPFSM_EVENT_RPORT_OFFLINE:
bfa_sm_set_state(rpf, bfa_fcs_rpf_sm_offline);
rpf->rpsc_retries = 0;
break;
@@ -221,12 +220,12 @@ bfa_fcs_rpf_sm_offline(struct bfa_fcs_rpf_s *rpf, enum rpf_event event)
bfa_trc(rport->fcs, event);
switch (event) {
- case RPFSM_EVENT_RPORT_ONLINE :
+ case RPFSM_EVENT_RPORT_ONLINE:
bfa_sm_set_state(rpf, bfa_fcs_rpf_sm_rpsc_sending);
bfa_fcs_rpf_send_rpsc2(rpf, NULL);
break;
- case RPFSM_EVENT_RPORT_OFFLINE :
+ case RPFSM_EVENT_RPORT_OFFLINE:
break;
default:
@@ -366,10 +365,9 @@ bfa_fcs_rpf_rpsc2_response(void *fcsarg, struct bfa_fcxp_s *fcxp, void *cbarg,
bfa_trc(rport->fcs, ls_rjt->reason_code);
bfa_trc(rport->fcs, ls_rjt->reason_code_expl);
rport->stats.rpsc_rejects++;
- if (ls_rjt->reason_code == FC_LS_RJT_RSN_CMD_NOT_SUPP) {
+ if (ls_rjt->reason_code == FC_LS_RJT_RSN_CMD_NOT_SUPP)
bfa_sm_send_event(rpf, RPFSM_EVENT_RPSC_FAIL);
- } else {
+ else
bfa_sm_send_event(rpf, RPFSM_EVENT_RPSC_ERROR);
- }
}
}
diff --git a/drivers/scsi/bfa/vfapi.c b/drivers/scsi/bfa/vfapi.c
index 31d81fe2fc4..391a4790beb 100644
--- a/drivers/scsi/bfa/vfapi.c
+++ b/drivers/scsi/bfa/vfapi.c
@@ -189,7 +189,7 @@ bfa_fcs_vf_lookup(struct bfa_fcs_s *fcs, u16 vf_id)
{
bfa_trc(fcs, vf_id);
if (vf_id == FC_VF_ID_NULL)
- return (&fcs->fabric);
+ return &fcs->fabric;
/**
* @todo vf support
diff --git a/drivers/scsi/bfa/vport.c b/drivers/scsi/bfa/vport.c
index c10af06c571..e90f1e38c32 100644
--- a/drivers/scsi/bfa/vport.c
+++ b/drivers/scsi/bfa/vport.c
@@ -31,13 +31,13 @@
BFA_TRC_FILE(FCS, VPORT);
-#define __vport_fcs(__vp) (__vp)->lport.fcs
-#define __vport_pwwn(__vp) (__vp)->lport.port_cfg.pwwn
-#define __vport_nwwn(__vp) (__vp)->lport.port_cfg.nwwn
-#define __vport_bfa(__vp) (__vp)->lport.fcs->bfa
-#define __vport_fcid(__vp) (__vp)->lport.pid
-#define __vport_fabric(__vp) (__vp)->lport.fabric
-#define __vport_vfid(__vp) (__vp)->lport.fabric->vf_id
+#define __vport_fcs(__vp) ((__vp)->lport.fcs)
+#define __vport_pwwn(__vp) ((__vp)->lport.port_cfg.pwwn)
+#define __vport_nwwn(__vp) ((__vp)->lport.port_cfg.nwwn)
+#define __vport_bfa(__vp) ((__vp)->lport.fcs->bfa)
+#define __vport_fcid(__vp) ((__vp)->lport.pid)
+#define __vport_fabric(__vp) ((__vp)->lport.fabric)
+#define __vport_vfid(__vp) ((__vp)->lport.fabric->vf_id)
#define BFA_FCS_VPORT_MAX_RETRIES 5
/*
@@ -641,9 +641,9 @@ bfa_fcs_vport_get_max(struct bfa_fcs_s *fcs)
bfa_get_attr(fcs->bfa, &ioc_attr);
if (ioc_attr.pci_attr.device_id == BFA_PCI_DEVICE_ID_CT)
- return (BFA_FCS_MAX_VPORTS_SUPP_CT);
+ return BFA_FCS_MAX_VPORTS_SUPP_CT;
else
- return (BFA_FCS_MAX_VPORTS_SUPP_CB);
+ return BFA_FCS_MAX_VPORTS_SUPP_CB;
}
@@ -675,7 +675,7 @@ bfa_fcs_vport_create(struct bfa_fcs_vport_s *vport, struct bfa_fcs_s *fcs,
struct bfad_vport_s *vport_drv)
{
if (vport_cfg->pwwn == 0)
- return (BFA_STATUS_INVALID_WWN);
+ return BFA_STATUS_INVALID_WWN;
if (bfa_fcs_port_get_pwwn(&fcs->fabric.bport) == vport_cfg->pwwn)
return BFA_STATUS_VPORT_WWN_BP;
diff --git a/drivers/scsi/bnx2i/bnx2i.h b/drivers/scsi/bnx2i/bnx2i.h
index 5edde1a8c04..2b973f3c2eb 100644
--- a/drivers/scsi/bnx2i/bnx2i.h
+++ b/drivers/scsi/bnx2i/bnx2i.h
@@ -232,7 +232,6 @@ struct bnx2i_conn {
struct iscsi_cls_conn *cls_conn;
struct bnx2i_hba *hba;
struct completion cmd_cleanup_cmpl;
- int is_bound;
u32 iscsi_conn_cid;
#define BNX2I_CID_RESERVED 0x5AFF
diff --git a/drivers/scsi/bnx2i/bnx2i_iscsi.c b/drivers/scsi/bnx2i/bnx2i_iscsi.c
index 10110be7c0f..132898c88d5 100644
--- a/drivers/scsi/bnx2i/bnx2i_iscsi.c
+++ b/drivers/scsi/bnx2i/bnx2i_iscsi.c
@@ -1161,9 +1161,6 @@ static int bnx2i_task_xmit(struct iscsi_task *task)
struct bnx2i_cmd *cmd = task->dd_data;
struct iscsi_cmd *hdr = (struct iscsi_cmd *) task->hdr;
- if (!bnx2i_conn->is_bound)
- return -ENOTCONN;
-
/*
* If there is no scsi_cmnd this must be a mgmt task
*/
@@ -1371,7 +1368,6 @@ static int bnx2i_conn_bind(struct iscsi_cls_session *cls_session,
bnx2i_conn->ep = bnx2i_ep;
bnx2i_conn->iscsi_conn_cid = bnx2i_ep->ep_iscsi_cid;
bnx2i_conn->fw_cid = bnx2i_ep->ep_cid;
- bnx2i_conn->is_bound = 1;
ret_code = bnx2i_bind_conn_to_iscsi_cid(hba, bnx2i_conn,
bnx2i_ep->ep_iscsi_cid);
@@ -1896,9 +1892,7 @@ static void bnx2i_ep_disconnect(struct iscsi_endpoint *ep)
conn = bnx2i_conn->cls_conn->dd_data;
session = conn->session;
- spin_lock_bh(&session->lock);
- bnx2i_conn->is_bound = 0;
- spin_unlock_bh(&session->lock);
+ iscsi_suspend_queue(conn);
}
hba = bnx2i_ep->hba;
@@ -2034,7 +2028,7 @@ struct iscsi_transport bnx2i_iscsi_transport = {
ISCSI_USERNAME | ISCSI_PASSWORD |
ISCSI_USERNAME_IN | ISCSI_PASSWORD_IN |
ISCSI_FAST_ABORT | ISCSI_ABORT_TMO |
- ISCSI_LU_RESET_TMO |
+ ISCSI_LU_RESET_TMO | ISCSI_TGT_RESET_TMO |
ISCSI_PING_TMO | ISCSI_RECV_TMO |
ISCSI_IFACE_NAME | ISCSI_INITIATOR_NAME,
.host_param_mask = ISCSI_HOST_HWADDRESS | ISCSI_HOST_NETDEV_NAME,
diff --git a/drivers/scsi/constants.c b/drivers/scsi/constants.c
index 63abb06c4ed..9129bcf117c 100644
--- a/drivers/scsi/constants.c
+++ b/drivers/scsi/constants.c
@@ -141,6 +141,7 @@ static const struct value_name_pair serv_out12_arr[] = {
static const struct value_name_pair serv_in16_arr[] = {
{0x10, "Read capacity(16)"},
{0x11, "Read long(16)"},
+ {0x12, "Get LBA status"},
};
#define SERV_IN16_SZ ARRAY_SIZE(serv_in16_arr)
diff --git a/drivers/scsi/cxgb3i/cxgb3i_iscsi.c b/drivers/scsi/cxgb3i/cxgb3i_iscsi.c
index 2631bddd255..969c83162cc 100644
--- a/drivers/scsi/cxgb3i/cxgb3i_iscsi.c
+++ b/drivers/scsi/cxgb3i/cxgb3i_iscsi.c
@@ -937,7 +937,7 @@ static struct iscsi_transport cxgb3i_iscsi_transport = {
ISCSI_USERNAME | ISCSI_PASSWORD |
ISCSI_USERNAME_IN | ISCSI_PASSWORD_IN |
ISCSI_FAST_ABORT | ISCSI_ABORT_TMO |
- ISCSI_LU_RESET_TMO |
+ ISCSI_LU_RESET_TMO | ISCSI_TGT_RESET_TMO |
ISCSI_PING_TMO | ISCSI_RECV_TMO |
ISCSI_IFACE_NAME | ISCSI_INITIATOR_NAME,
.host_param_mask = ISCSI_HOST_HWADDRESS | ISCSI_HOST_IPADDRESS |
diff --git a/drivers/scsi/device_handler/scsi_dh.c b/drivers/scsi/device_handler/scsi_dh.c
index 3ee1cbc8947..e19a1a55270 100644
--- a/drivers/scsi/device_handler/scsi_dh.c
+++ b/drivers/scsi/device_handler/scsi_dh.c
@@ -226,7 +226,7 @@ store_dh_state(struct device *dev, struct device_attribute *attr,
* Activate a device handler
*/
if (scsi_dh->activate)
- err = scsi_dh->activate(sdev);
+ err = scsi_dh->activate(sdev, NULL, NULL);
else
err = 0;
}
@@ -304,18 +304,15 @@ static int scsi_dh_notifier(struct notifier_block *nb,
sdev = to_scsi_device(dev);
if (action == BUS_NOTIFY_ADD_DEVICE) {
+ err = device_create_file(dev, &scsi_dh_state_attr);
+ /* don't care about err */
devinfo = device_handler_match(NULL, sdev);
- if (!devinfo)
- goto out;
-
- err = scsi_dh_handler_attach(sdev, devinfo);
- if (!err)
- err = device_create_file(dev, &scsi_dh_state_attr);
+ if (devinfo)
+ err = scsi_dh_handler_attach(sdev, devinfo);
} else if (action == BUS_NOTIFY_DEL_DEVICE) {
device_remove_file(dev, &scsi_dh_state_attr);
scsi_dh_handler_detach(sdev, NULL);
}
-out:
return err;
}
@@ -423,10 +420,17 @@ EXPORT_SYMBOL_GPL(scsi_unregister_device_handler);
/*
* scsi_dh_activate - activate the path associated with the scsi_device
* corresponding to the given request queue.
- * @q - Request queue that is associated with the scsi_device to be
- * activated.
+ * Returns immediately without waiting for activation to be completed.
+ * @q - Request queue that is associated with the scsi_device to be
+ * activated.
+ * @fn - Function to be called upon completion of the activation.
+ * Function fn is called with data (below) and the error code.
+ * Function fn may be called from the same calling context. So,
+ * do not hold the lock in the caller which may be needed in fn.
+ * @data - data passed to the function fn upon completion.
+ *
*/
-int scsi_dh_activate(struct request_queue *q)
+int scsi_dh_activate(struct request_queue *q, activate_complete fn, void *data)
{
int err = 0;
unsigned long flags;
@@ -445,7 +449,7 @@ int scsi_dh_activate(struct request_queue *q)
return err;
if (scsi_dh->activate)
- err = scsi_dh->activate(sdev);
+ err = scsi_dh->activate(sdev, fn, data);
put_device(&sdev->sdev_gendev);
return err;
}
diff --git a/drivers/scsi/device_handler/scsi_dh_alua.c b/drivers/scsi/device_handler/scsi_dh_alua.c
index b5cdefaf260..4f0d0138f48 100644
--- a/drivers/scsi/device_handler/scsi_dh_alua.c
+++ b/drivers/scsi/device_handler/scsi_dh_alua.c
@@ -60,11 +60,17 @@ struct alua_dh_data {
int bufflen;
unsigned char sense[SCSI_SENSE_BUFFERSIZE];
int senselen;
+ struct scsi_device *sdev;
+ activate_complete callback_fn;
+ void *callback_data;
};
#define ALUA_POLICY_SWITCH_CURRENT 0
#define ALUA_POLICY_SWITCH_ALL 1
+static char print_alua_state(int);
+static int alua_check_sense(struct scsi_device *, struct scsi_sense_hdr *);
+
static inline struct alua_dh_data *get_alua_data(struct scsi_device *sdev)
{
struct scsi_dh_data *scsi_dh_data = sdev->scsi_dh_data;
@@ -231,18 +237,71 @@ done:
}
/*
+ * alua_stpg - Evaluate SET TARGET GROUP STATES
+ * @sdev: the device to be evaluated
+ * @state: the new target group state
+ *
+ * Send a SET TARGET GROUP STATES command to the device.
+ * We only have to test here if we should resubmit the command;
+ * any other error is assumed as a failure.
+ */
+static void stpg_endio(struct request *req, int error)
+{
+ struct alua_dh_data *h = req->end_io_data;
+ struct scsi_sense_hdr sense_hdr;
+ unsigned err = SCSI_DH_IO;
+
+ if (error || host_byte(req->errors) != DID_OK ||
+ msg_byte(req->errors) != COMMAND_COMPLETE)
+ goto done;
+
+ if (err == SCSI_DH_IO && h->senselen > 0) {
+ err = scsi_normalize_sense(h->sense, SCSI_SENSE_BUFFERSIZE,
+ &sense_hdr);
+ if (!err) {
+ err = SCSI_DH_IO;
+ goto done;
+ }
+ err = alua_check_sense(h->sdev, &sense_hdr);
+ if (err == ADD_TO_MLQUEUE) {
+ err = SCSI_DH_RETRY;
+ goto done;
+ }
+ sdev_printk(KERN_INFO, h->sdev,
+ "%s: stpg sense code: %02x/%02x/%02x\n",
+ ALUA_DH_NAME, sense_hdr.sense_key,
+ sense_hdr.asc, sense_hdr.ascq);
+ err = SCSI_DH_IO;
+ }
+ if (err == SCSI_DH_OK) {
+ h->state = TPGS_STATE_OPTIMIZED;
+ sdev_printk(KERN_INFO, h->sdev,
+ "%s: port group %02x switched to state %c\n",
+ ALUA_DH_NAME, h->group_id,
+ print_alua_state(h->state));
+ }
+done:
+ blk_put_request(req);
+ if (h->callback_fn) {
+ h->callback_fn(h->callback_data, err);
+ h->callback_fn = h->callback_data = NULL;
+ }
+ return;
+}
+
+/*
* submit_stpg - Issue a SET TARGET GROUP STATES command
- * @sdev: sdev the command should be sent to
*
* Currently we're only setting the current target port group state
* to 'active/optimized' and let the array firmware figure out
* the states of the remaining groups.
*/
-static unsigned submit_stpg(struct scsi_device *sdev, struct alua_dh_data *h)
+static unsigned submit_stpg(struct alua_dh_data *h)
{
struct request *rq;
int err = SCSI_DH_RES_TEMP_UNAVAIL;
int stpg_len = 8;
+ struct scsi_device *sdev = h->sdev;
/* Prepare the data buffer */
memset(h->buff, 0, stpg_len);
@@ -252,7 +311,7 @@ static unsigned submit_stpg(struct scsi_device *sdev, struct alua_dh_data *h)
rq = get_alua_req(sdev, h->buff, stpg_len, WRITE);
if (!rq)
- goto done;
+ return SCSI_DH_RES_TEMP_UNAVAIL;
/* Prepare the command. */
rq->cmd[0] = MAINTENANCE_OUT;
@@ -266,17 +325,9 @@ static unsigned submit_stpg(struct scsi_device *sdev, struct alua_dh_data *h)
rq->sense = h->sense;
memset(rq->sense, 0, SCSI_SENSE_BUFFERSIZE);
rq->sense_len = h->senselen = 0;
+ rq->end_io_data = h;
- err = blk_execute_rq(rq->q, NULL, rq, 1);
- if (err == -EIO) {
- sdev_printk(KERN_INFO, sdev,
- "%s: stpg failed with %x\n",
- ALUA_DH_NAME, rq->errors);
- h->senselen = rq->sense_len;
- err = SCSI_DH_IO;
- }
- blk_put_request(rq);
-done:
+ blk_execute_rq_nowait(rq->q, NULL, rq, 1, stpg_endio);
return err;
}
@@ -477,50 +528,6 @@ static int alua_check_sense(struct scsi_device *sdev,
}
/*
- * alua_stpg - Evaluate SET TARGET GROUP STATES
- * @sdev: the device to be evaluated
- * @state: the new target group state
- *
- * Send a SET TARGET GROUP STATES command to the device.
- * We only have to test here if we should resubmit the command;
- * any other error is assumed as a failure.
- */
-static int alua_stpg(struct scsi_device *sdev, int state,
- struct alua_dh_data *h)
-{
- struct scsi_sense_hdr sense_hdr;
- unsigned err;
- int retry = ALUA_FAILOVER_RETRIES;
-
- retry:
- err = submit_stpg(sdev, h);
- if (err == SCSI_DH_IO && h->senselen > 0) {
- err = scsi_normalize_sense(h->sense, SCSI_SENSE_BUFFERSIZE,
- &sense_hdr);
- if (!err)
- return SCSI_DH_IO;
- err = alua_check_sense(sdev, &sense_hdr);
- if (retry > 0 && err == ADD_TO_MLQUEUE) {
- retry--;
- goto retry;
- }
- sdev_printk(KERN_INFO, sdev,
- "%s: stpg sense code: %02x/%02x/%02x\n",
- ALUA_DH_NAME, sense_hdr.sense_key,
- sense_hdr.asc, sense_hdr.ascq);
- err = SCSI_DH_IO;
- }
- if (err == SCSI_DH_OK) {
- h->state = state;
- sdev_printk(KERN_INFO, sdev,
- "%s: port group %02x switched to state %c\n",
- ALUA_DH_NAME, h->group_id,
- print_alua_state(h->state) );
- }
- return err;
-}
-
-/*
* alua_rtpg - Evaluate REPORT TARGET GROUP STATES
* @sdev: the device to be evaluated.
*
@@ -652,7 +659,8 @@ out:
* based on a certain policy. But until we actually encounter them it
* should be okay.
*/
-static int alua_activate(struct scsi_device *sdev)
+static int alua_activate(struct scsi_device *sdev,
+ activate_complete fn, void *data)
{
struct alua_dh_data *h = get_alua_data(sdev);
int err = SCSI_DH_OK;
@@ -663,11 +671,19 @@ static int alua_activate(struct scsi_device *sdev)
goto out;
}
- if (h->tpgs & TPGS_MODE_EXPLICIT && h->state != TPGS_STATE_OPTIMIZED)
- err = alua_stpg(sdev, TPGS_STATE_OPTIMIZED, h);
+ if (h->tpgs & TPGS_MODE_EXPLICIT && h->state != TPGS_STATE_OPTIMIZED) {
+ h->callback_fn = fn;
+ h->callback_data = data;
+ err = submit_stpg(h);
+ if (err == SCSI_DH_OK)
+ return 0;
+ h->callback_fn = h->callback_data = NULL;
+ }
out:
- return err;
+ if (fn)
+ fn(data, err);
+ return 0;
}
/*
@@ -745,6 +761,7 @@ static int alua_bus_attach(struct scsi_device *sdev)
h->rel_port = -1;
h->buff = h->inq;
h->bufflen = ALUA_INQUIRY_SIZE;
+ h->sdev = sdev;
err = alua_initialize(sdev, h);
if (err != SCSI_DH_OK)
diff --git a/drivers/scsi/device_handler/scsi_dh_emc.c b/drivers/scsi/device_handler/scsi_dh_emc.c
index 0cffe84976f..61966750bd6 100644
--- a/drivers/scsi/device_handler/scsi_dh_emc.c
+++ b/drivers/scsi/device_handler/scsi_dh_emc.c
@@ -528,7 +528,8 @@ retry:
return err;
}
-static int clariion_activate(struct scsi_device *sdev)
+static int clariion_activate(struct scsi_device *sdev,
+ activate_complete fn, void *data)
{
struct clariion_dh_data *csdev = get_clariion_data(sdev);
int result;
@@ -559,7 +560,9 @@ done:
csdev->port, lun_state[csdev->lun_state],
csdev->default_sp + 'A');
- return result;
+ if (fn)
+ fn(data, result);
+ return 0;
}
/*
* params - parameters in the following format
diff --git a/drivers/scsi/device_handler/scsi_dh_hp_sw.c b/drivers/scsi/device_handler/scsi_dh_hp_sw.c
index f7da7530875..857fdd6032b 100644
--- a/drivers/scsi/device_handler/scsi_dh_hp_sw.c
+++ b/drivers/scsi/device_handler/scsi_dh_hp_sw.c
@@ -39,8 +39,14 @@ struct hp_sw_dh_data {
unsigned char sense[SCSI_SENSE_BUFFERSIZE];
int path_state;
int retries;
+ int retry_cnt;
+ struct scsi_device *sdev;
+ activate_complete callback_fn;
+ void *callback_data;
};
+static int hp_sw_start_stop(struct hp_sw_dh_data *);
+
static inline struct hp_sw_dh_data *get_hp_sw_data(struct scsi_device *sdev)
{
struct scsi_dh_data *scsi_dh_data = sdev->scsi_dh_data;
@@ -191,19 +197,53 @@ static int start_done(struct scsi_device *sdev, unsigned char *sense)
return rc;
}
+static void start_stop_endio(struct request *req, int error)
+{
+ struct hp_sw_dh_data *h = req->end_io_data;
+ unsigned err = SCSI_DH_OK;
+
+ if (error || host_byte(req->errors) != DID_OK ||
+ msg_byte(req->errors) != COMMAND_COMPLETE) {
+ sdev_printk(KERN_WARNING, h->sdev,
+ "%s: sending start_stop_unit failed with %x\n",
+ HP_SW_NAME, req->errors);
+ err = SCSI_DH_IO;
+ goto done;
+ }
+
+ if (req->sense_len > 0) {
+ err = start_done(h->sdev, h->sense);
+ if (err == SCSI_DH_RETRY) {
+ err = SCSI_DH_IO;
+ if (--h->retry_cnt) {
+ blk_put_request(req);
+ err = hp_sw_start_stop(h);
+ if (err == SCSI_DH_OK)
+ return;
+ }
+ }
+ }
+done:
+ blk_put_request(req);
+ if (h->callback_fn) {
+ h->callback_fn(h->callback_data, err);
+ h->callback_fn = h->callback_data = NULL;
+ }
+ return;
+
+}
+
/*
* hp_sw_start_stop - Send START STOP UNIT command
* @sdev: sdev command should be sent to
*
* Sending START STOP UNIT activates the SP.
*/
-static int hp_sw_start_stop(struct scsi_device *sdev, struct hp_sw_dh_data *h)
+static int hp_sw_start_stop(struct hp_sw_dh_data *h)
{
struct request *req;
- int ret, retry;
-retry:
- req = blk_get_request(sdev->request_queue, WRITE, GFP_NOIO);
+ req = blk_get_request(h->sdev->request_queue, WRITE, GFP_ATOMIC);
if (!req)
return SCSI_DH_RES_TEMP_UNAVAIL;
@@ -217,32 +257,10 @@ retry:
req->sense = h->sense;
memset(req->sense, 0, SCSI_SENSE_BUFFERSIZE);
req->sense_len = 0;
- retry = h->retries;
-
- ret = blk_execute_rq(req->q, NULL, req, 1);
- if (ret == -EIO) {
- if (req->sense_len > 0) {
- ret = start_done(sdev, h->sense);
- } else {
- sdev_printk(KERN_WARNING, sdev,
- "%s: sending start_stop_unit failed with %x\n",
- HP_SW_NAME, req->errors);
- ret = SCSI_DH_IO;
- }
- } else
- ret = SCSI_DH_OK;
+ req->end_io_data = h;
- if (ret == SCSI_DH_RETRY) {
- if (--retry) {
- blk_put_request(req);
- goto retry;
- }
- ret = SCSI_DH_IO;
- }
-
- blk_put_request(req);
-
- return ret;
+ blk_execute_rq_nowait(req->q, NULL, req, 1, start_stop_endio);
+ return SCSI_DH_OK;
}
static int hp_sw_prep_fn(struct scsi_device *sdev, struct request *req)
@@ -268,7 +286,8 @@ static int hp_sw_prep_fn(struct scsi_device *sdev, struct request *req)
* activate the passive path (and deactivate the
* previously active one).
*/
-static int hp_sw_activate(struct scsi_device *sdev)
+static int hp_sw_activate(struct scsi_device *sdev,
+ activate_complete fn, void *data)
{
int ret = SCSI_DH_OK;
struct hp_sw_dh_data *h = get_hp_sw_data(sdev);
@@ -276,14 +295,18 @@ static int hp_sw_activate(struct scsi_device *sdev)
ret = hp_sw_tur(sdev, h);
if (ret == SCSI_DH_OK && h->path_state == HP_SW_PATH_PASSIVE) {
- ret = hp_sw_start_stop(sdev, h);
+ h->retry_cnt = h->retries;
+ h->callback_fn = fn;
+ h->callback_data = data;
+ ret = hp_sw_start_stop(h);
if (ret == SCSI_DH_OK)
- sdev_printk(KERN_INFO, sdev,
- "%s: activated path\n",
- HP_SW_NAME);
+ return 0;
+ h->callback_fn = h->callback_data = NULL;
}
- return ret;
+ if (fn)
+ fn(data, ret);
+ return 0;
}
static const struct scsi_dh_devlist hp_sw_dh_data_list[] = {
@@ -326,6 +349,7 @@ static int hp_sw_bus_attach(struct scsi_device *sdev)
h = (struct hp_sw_dh_data *) scsi_dh_data->buf;
h->path_state = HP_SW_PATH_UNINITIALIZED;
h->retries = HP_SW_RETRIES;
+ h->sdev = sdev;
ret = hp_sw_tur(sdev, h);
if (ret != SCSI_DH_OK || h->path_state == HP_SW_PATH_UNINITIALIZED)
diff --git a/drivers/scsi/device_handler/scsi_dh_rdac.c b/drivers/scsi/device_handler/scsi_dh_rdac.c
index 268189d31d9..47cfe1c49c3 100644
--- a/drivers/scsi/device_handler/scsi_dh_rdac.c
+++ b/drivers/scsi/device_handler/scsi_dh_rdac.c
@@ -22,6 +22,7 @@
#include <scsi/scsi.h>
#include <scsi/scsi_eh.h>
#include <scsi/scsi_dh.h>
+#include <linux/workqueue.h>
#define RDAC_NAME "rdac"
#define RDAC_RETRY_COUNT 5
@@ -138,7 +139,13 @@ struct rdac_controller {
} mode_select;
u8 index;
u8 array_name[ARRAY_LABEL_LEN];
+ spinlock_t ms_lock;
+ int ms_queued;
+ struct work_struct ms_work;
+ struct scsi_device *ms_sdev;
+ struct list_head ms_head;
};
+
struct c8_inquiry {
u8 peripheral_info;
u8 page_code; /* 0xC8 */
@@ -198,8 +205,17 @@ static const char *lun_state[] =
"owned (AVT mode)",
};
+struct rdac_queue_data {
+ struct list_head entry;
+ struct rdac_dh_data *h;
+ activate_complete callback_fn;
+ void *callback_data;
+};
+
static LIST_HEAD(ctlr_list);
static DEFINE_SPINLOCK(list_lock);
+static struct workqueue_struct *kmpath_rdacd;
+static void send_mode_select(struct work_struct *work);
/*
* module parameter to enable rdac debug logging.
@@ -281,7 +297,6 @@ static struct request *rdac_failover_get(struct scsi_device *sdev,
rdac_pg->subpage_code = 0x1;
rdac_pg->page_len[0] = 0x01;
rdac_pg->page_len[1] = 0x28;
- rdac_pg->lun_table[h->lun] = 0x81;
} else {
struct rdac_pg_legacy *rdac_pg;
@@ -291,7 +306,6 @@ static struct request *rdac_failover_get(struct scsi_device *sdev,
common = &rdac_pg->common;
rdac_pg->page_code = RDAC_PAGE_CODE_REDUNDANT_CONTROLLER;
rdac_pg->page_len = 0x68;
- rdac_pg->lun_table[h->lun] = 0x81;
}
common->rdac_mode[1] = RDAC_MODE_TRANSFER_SPECIFIED_LUNS;
common->quiescence_timeout = RDAC_QUIESCENCE_TIME;
@@ -325,6 +339,7 @@ static void release_controller(struct kref *kref)
struct rdac_controller *ctlr;
ctlr = container_of(kref, struct rdac_controller, kref);
+ flush_workqueue(kmpath_rdacd);
spin_lock(&list_lock);
list_del(&ctlr->node);
spin_unlock(&list_lock);
@@ -363,6 +378,11 @@ static struct rdac_controller *get_controller(u8 *subsys_id, u8 *slot_id,
kref_init(&ctlr->kref);
ctlr->use_ms10 = -1;
+ ctlr->ms_queued = 0;
+ ctlr->ms_sdev = NULL;
+ spin_lock_init(&ctlr->ms_lock);
+ INIT_WORK(&ctlr->ms_work, send_mode_select);
+ INIT_LIST_HEAD(&ctlr->ms_head);
list_add(&ctlr->node, &ctlr_list);
done:
spin_unlock(&list_lock);
@@ -490,7 +510,7 @@ static int set_mode_select(struct scsi_device *sdev, struct rdac_dh_data *h)
}
static int mode_select_handle_sense(struct scsi_device *sdev,
- unsigned char *sensebuf)
+ unsigned char *sensebuf)
{
struct scsi_sense_hdr sense_hdr;
int err = SCSI_DH_IO, ret;
@@ -533,11 +553,29 @@ done:
return err;
}
-static int send_mode_select(struct scsi_device *sdev, struct rdac_dh_data *h)
+static void send_mode_select(struct work_struct *work)
{
+ struct rdac_controller *ctlr =
+ container_of(work, struct rdac_controller, ms_work);
struct request *rq;
+ struct scsi_device *sdev = ctlr->ms_sdev;
+ struct rdac_dh_data *h = get_rdac_data(sdev);
struct request_queue *q = sdev->request_queue;
int err, retry_cnt = RDAC_RETRY_COUNT;
+ struct rdac_queue_data *tmp, *qdata;
+ LIST_HEAD(list);
+ u8 *lun_table;
+
+ spin_lock(&ctlr->ms_lock);
+ list_splice_init(&ctlr->ms_head, &list);
+ ctlr->ms_queued = 0;
+ ctlr->ms_sdev = NULL;
+ spin_unlock(&ctlr->ms_lock);
+
+ if (ctlr->use_ms10)
+ lun_table = ctlr->mode_select.expanded.lun_table;
+ else
+ lun_table = ctlr->mode_select.legacy.lun_table;
retry:
err = SCSI_DH_RES_TEMP_UNAVAIL;
@@ -545,6 +583,10 @@ retry:
if (!rq)
goto done;
+ list_for_each_entry(qdata, &list, entry) {
+ lun_table[qdata->h->lun] = 0x81;
+ }
+
RDAC_LOG(RDAC_LOG_FAILOVER, sdev, "array %s, ctlr %d, "
"%s MODE_SELECT command",
(char *) h->ctlr->array_name, h->ctlr->index,
@@ -565,10 +607,45 @@ retry:
}
done:
- return err;
+ list_for_each_entry_safe(qdata, tmp, &list, entry) {
+ list_del(&qdata->entry);
+ if (err == SCSI_DH_OK)
+ qdata->h->state = RDAC_STATE_ACTIVE;
+ if (qdata->callback_fn)
+ qdata->callback_fn(qdata->callback_data, err);
+ kfree(qdata);
+ }
+ return;
+}
+
+static int queue_mode_select(struct scsi_device *sdev,
+ activate_complete fn, void *data)
+{
+ struct rdac_queue_data *qdata;
+ struct rdac_controller *ctlr;
+
+ qdata = kzalloc(sizeof(*qdata), GFP_KERNEL);
+ if (!qdata)
+ return SCSI_DH_RETRY;
+
+ qdata->h = get_rdac_data(sdev);
+ qdata->callback_fn = fn;
+ qdata->callback_data = data;
+
+ ctlr = qdata->h->ctlr;
+ spin_lock(&ctlr->ms_lock);
+ list_add_tail(&qdata->entry, &ctlr->ms_head);
+ if (!ctlr->ms_queued) {
+ ctlr->ms_queued = 1;
+ ctlr->ms_sdev = sdev;
+ queue_work(kmpath_rdacd, &ctlr->ms_work);
+ }
+ spin_unlock(&ctlr->ms_lock);
+ return SCSI_DH_OK;
}
-static int rdac_activate(struct scsi_device *sdev)
+static int rdac_activate(struct scsi_device *sdev,
+ activate_complete fn, void *data)
{
struct rdac_dh_data *h = get_rdac_data(sdev);
int err = SCSI_DH_OK;
@@ -577,10 +654,15 @@ static int rdac_activate(struct scsi_device *sdev)
if (err != SCSI_DH_OK)
goto done;
- if (h->lun_state == RDAC_LUN_UNOWNED)
- err = send_mode_select(sdev, h);
+ if (h->lun_state == RDAC_LUN_UNOWNED) {
+ err = queue_mode_select(sdev, fn, data);
+ if (err == SCSI_DH_OK)
+ return 0;
+ }
done:
- return err;
+ if (fn)
+ fn(data, err);
+ return 0;
}
static int rdac_prep_fn(struct scsi_device *sdev, struct request *req)
@@ -790,13 +872,26 @@ static int __init rdac_init(void)
int r;
r = scsi_register_device_handler(&rdac_dh);
- if (r != 0)
+ if (r != 0) {
printk(KERN_ERR "Failed to register scsi device handler.");
+ goto done;
+ }
+
+ /*
+ * Create workqueue to handle mode selects for rdac
+ */
+ kmpath_rdacd = create_singlethread_workqueue("kmpath_rdacd");
+ if (!kmpath_rdacd) {
+ scsi_unregister_device_handler(&rdac_dh);
+ printk(KERN_ERR "kmpath_rdacd creation failed.\n");
+ }
+done:
return r;
}
static void __exit rdac_exit(void)
{
+ destroy_workqueue(kmpath_rdacd);
scsi_unregister_device_handler(&rdac_dh);
}
diff --git a/drivers/scsi/fcoe/fcoe.c b/drivers/scsi/fcoe/fcoe.c
index 704b8e03494..a30ffaa1222 100644
--- a/drivers/scsi/fcoe/fcoe.c
+++ b/drivers/scsi/fcoe/fcoe.c
@@ -66,14 +66,14 @@ LIST_HEAD(fcoe_hostlist);
DEFINE_PER_CPU(struct fcoe_percpu_s, fcoe_percpu);
/* Function Prototypes */
-static int fcoe_reset(struct Scsi_Host *shost);
+static int fcoe_reset(struct Scsi_Host *);
static int fcoe_xmit(struct fc_lport *, struct fc_frame *);
static int fcoe_rcv(struct sk_buff *, struct net_device *,
struct packet_type *, struct net_device *);
-static int fcoe_percpu_receive_thread(void *arg);
-static void fcoe_clean_pending_queue(struct fc_lport *lp);
-static void fcoe_percpu_clean(struct fc_lport *lp);
-static int fcoe_link_ok(struct fc_lport *lp);
+static int fcoe_percpu_receive_thread(void *);
+static void fcoe_clean_pending_queue(struct fc_lport *);
+static void fcoe_percpu_clean(struct fc_lport *);
+static int fcoe_link_ok(struct fc_lport *);
static struct fc_lport *fcoe_hostlist_lookup(const struct net_device *);
static int fcoe_hostlist_add(const struct fc_lport *);
@@ -82,15 +82,69 @@ static void fcoe_check_wait_queue(struct fc_lport *, struct sk_buff *);
static int fcoe_device_notification(struct notifier_block *, ulong, void *);
static void fcoe_dev_setup(void);
static void fcoe_dev_cleanup(void);
-static struct fcoe_interface *
- fcoe_hostlist_lookup_port(const struct net_device *dev);
+static struct fcoe_interface
+*fcoe_hostlist_lookup_port(const struct net_device *);
+
+static int fcoe_fip_recv(struct sk_buff *, struct net_device *,
+ struct packet_type *, struct net_device *);
+
+static void fcoe_fip_send(struct fcoe_ctlr *, struct sk_buff *);
+static void fcoe_update_src_mac(struct fc_lport *, u8 *);
+static u8 *fcoe_get_src_mac(struct fc_lport *);
+static void fcoe_destroy_work(struct work_struct *);
+
+static int fcoe_ddp_setup(struct fc_lport *, u16, struct scatterlist *,
+ unsigned int);
+static int fcoe_ddp_done(struct fc_lport *, u16);
+
+static int fcoe_cpu_callback(struct notifier_block *, unsigned long, void *);
+
+static int fcoe_create(const char *, struct kernel_param *);
+static int fcoe_destroy(const char *, struct kernel_param *);
+
+static struct fc_seq *fcoe_elsct_send(struct fc_lport *,
+ u32 did, struct fc_frame *,
+ unsigned int op,
+ void (*resp)(struct fc_seq *,
+ struct fc_frame *,
+ void *),
+ void *, u32 timeout);
+static void fcoe_recv_frame(struct sk_buff *skb);
+
+static void fcoe_get_lesb(struct fc_lport *, struct fc_els_lesb *);
+
+module_param_call(create, fcoe_create, NULL, NULL, S_IWUSR);
+__MODULE_PARM_TYPE(create, "string");
+MODULE_PARM_DESC(create, "Create fcoe fcoe using net device passed in.");
+module_param_call(destroy, fcoe_destroy, NULL, NULL, S_IWUSR);
+__MODULE_PARM_TYPE(destroy, "string");
+MODULE_PARM_DESC(destroy, "Destroy fcoe fcoe");
-/* notification function from net device */
+/* notification function for packets from net device */
static struct notifier_block fcoe_notifier = {
.notifier_call = fcoe_device_notification,
};
-static struct scsi_transport_template *scsi_transport_fcoe_sw;
+/* notification function for CPU hotplug events */
+static struct notifier_block fcoe_cpu_notifier = {
+ .notifier_call = fcoe_cpu_callback,
+};
+
+static struct scsi_transport_template *fcoe_transport_template;
+static struct scsi_transport_template *fcoe_vport_transport_template;
+
+static int fcoe_vport_destroy(struct fc_vport *);
+static int fcoe_vport_create(struct fc_vport *, bool disabled);
+static int fcoe_vport_disable(struct fc_vport *, bool disable);
+static void fcoe_set_vport_symbolic_name(struct fc_vport *);
+
+static struct libfc_function_template fcoe_libfc_fcn_templ = {
+ .frame_send = fcoe_xmit,
+ .ddp_setup = fcoe_ddp_setup,
+ .ddp_done = fcoe_ddp_done,
+ .elsct_send = fcoe_elsct_send,
+ .get_lesb = fcoe_get_lesb,
+};
struct fc_function_template fcoe_transport_function = {
.show_host_node_name = 1,
@@ -123,6 +177,48 @@ struct fc_function_template fcoe_transport_function = {
.issue_fc_host_lip = fcoe_reset,
.terminate_rport_io = fc_rport_terminate_io,
+
+ .vport_create = fcoe_vport_create,
+ .vport_delete = fcoe_vport_destroy,
+ .vport_disable = fcoe_vport_disable,
+ .set_vport_symbolic_name = fcoe_set_vport_symbolic_name,
+
+ .bsg_request = fc_lport_bsg_request,
+};
+
+struct fc_function_template fcoe_vport_transport_function = {
+ .show_host_node_name = 1,
+ .show_host_port_name = 1,
+ .show_host_supported_classes = 1,
+ .show_host_supported_fc4s = 1,
+ .show_host_active_fc4s = 1,
+ .show_host_maxframe_size = 1,
+
+ .show_host_port_id = 1,
+ .show_host_supported_speeds = 1,
+ .get_host_speed = fc_get_host_speed,
+ .show_host_speed = 1,
+ .show_host_port_type = 1,
+ .get_host_port_state = fc_get_host_port_state,
+ .show_host_port_state = 1,
+ .show_host_symbolic_name = 1,
+
+ .dd_fcrport_size = sizeof(struct fc_rport_libfc_priv),
+ .show_rport_maxframe_size = 1,
+ .show_rport_supported_classes = 1,
+
+ .show_host_fabric_name = 1,
+ .show_starget_node_name = 1,
+ .show_starget_port_name = 1,
+ .show_starget_port_id = 1,
+ .set_rport_dev_loss_tmo = fc_set_rport_loss_tmo,
+ .show_rport_dev_loss_tmo = 1,
+ .get_fc_host_stats = fc_get_host_stats,
+ .issue_fc_host_lip = fcoe_reset,
+
+ .terminate_rport_io = fc_rport_terminate_io,
+
+ .bsg_request = fc_lport_bsg_request,
};
static struct scsi_host_template fcoe_shost_template = {
@@ -137,20 +233,17 @@ static struct scsi_host_template fcoe_shost_template = {
.change_queue_depth = fc_change_queue_depth,
.change_queue_type = fc_change_queue_type,
.this_id = -1,
- .cmd_per_lun = 32,
+ .cmd_per_lun = 3,
.can_queue = FCOE_MAX_OUTSTANDING_COMMANDS,
.use_clustering = ENABLE_CLUSTERING,
.sg_tablesize = SG_ALL,
.max_sectors = 0xffff,
};
-static int fcoe_fip_recv(struct sk_buff *skb, struct net_device *dev,
- struct packet_type *ptype,
- struct net_device *orig_dev);
/**
- * fcoe_interface_setup()
- * @fcoe: new fcoe_interface
- * @netdev : ptr to the associated netdevice struct
+ * fcoe_interface_setup() - Setup a FCoE interface
+ * @fcoe: The new FCoE interface
+ * @netdev: The net device that the fcoe interface is on
*
* Returns : 0 for success
* Locking: must be called with the RTNL mutex held
@@ -160,23 +253,36 @@ static int fcoe_interface_setup(struct fcoe_interface *fcoe,
{
struct fcoe_ctlr *fip = &fcoe->ctlr;
struct netdev_hw_addr *ha;
+ struct net_device *real_dev;
u8 flogi_maddr[ETH_ALEN];
+ const struct net_device_ops *ops;
fcoe->netdev = netdev;
+ /* Let LLD initialize for FCoE */
+ ops = netdev->netdev_ops;
+ if (ops->ndo_fcoe_enable) {
+ if (ops->ndo_fcoe_enable(netdev))
+ FCOE_NETDEV_DBG(netdev, "Failed to enable FCoE"
+ " specific feature for LLD.\n");
+ }
+
/* Do not support for bonding device */
if ((netdev->priv_flags & IFF_MASTER_ALB) ||
(netdev->priv_flags & IFF_SLAVE_INACTIVE) ||
(netdev->priv_flags & IFF_MASTER_8023AD)) {
+ FCOE_NETDEV_DBG(netdev, "Bonded interfaces not supported\n");
return -EOPNOTSUPP;
}
/* look for SAN MAC address, if multiple SAN MACs exist, only
* use the first one for SPMA */
+ real_dev = (netdev->priv_flags & IFF_802_1Q_VLAN) ?
+ vlan_dev_real_dev(netdev) : netdev;
rcu_read_lock();
- for_each_dev_addr(netdev, ha) {
+ for_each_dev_addr(real_dev, ha) {
if ((ha->type == NETDEV_HW_ADDR_T_SAN) &&
- (is_valid_ether_addr(fip->ctl_src_addr))) {
+ (is_valid_ether_addr(ha->addr))) {
memcpy(fip->ctl_src_addr, ha->addr, ETH_ALEN);
fip->spma = 1;
break;
@@ -216,19 +322,16 @@ static int fcoe_interface_setup(struct fcoe_interface *fcoe,
return 0;
}
-static void fcoe_fip_send(struct fcoe_ctlr *fip, struct sk_buff *skb);
-static void fcoe_update_src_mac(struct fcoe_ctlr *fip, u8 *old, u8 *new);
-static void fcoe_destroy_work(struct work_struct *work);
-
/**
- * fcoe_interface_create()
- * @netdev: network interface
+ * fcoe_interface_create() - Create a FCoE interface on a net device
+ * @netdev: The net device to create the FCoE interface on
*
* Returns: pointer to a struct fcoe_interface or NULL on error
*/
static struct fcoe_interface *fcoe_interface_create(struct net_device *netdev)
{
struct fcoe_interface *fcoe;
+ int err;
fcoe = kzalloc(sizeof(*fcoe), GFP_KERNEL);
if (!fcoe) {
@@ -245,15 +348,22 @@ static struct fcoe_interface *fcoe_interface_create(struct net_device *netdev)
fcoe_ctlr_init(&fcoe->ctlr);
fcoe->ctlr.send = fcoe_fip_send;
fcoe->ctlr.update_mac = fcoe_update_src_mac;
+ fcoe->ctlr.get_src_addr = fcoe_get_src_mac;
- fcoe_interface_setup(fcoe, netdev);
+ err = fcoe_interface_setup(fcoe, netdev);
+ if (err) {
+ fcoe_ctlr_destroy(&fcoe->ctlr);
+ kfree(fcoe);
+ dev_put(netdev);
+ return NULL;
+ }
return fcoe;
}
/**
- * fcoe_interface_cleanup() - clean up netdev configurations
- * @fcoe:
+ * fcoe_interface_cleanup() - Clean up a FCoE interface
+ * @fcoe: The FCoE interface to be cleaned up
*
* Caller must be holding the RTNL mutex
*/
@@ -262,6 +372,7 @@ void fcoe_interface_cleanup(struct fcoe_interface *fcoe)
struct net_device *netdev = fcoe->netdev;
struct fcoe_ctlr *fip = &fcoe->ctlr;
u8 flogi_maddr[ETH_ALEN];
+ const struct net_device_ops *ops;
/*
* Don't listen for Ethernet packets anymore.
@@ -276,16 +387,22 @@ void fcoe_interface_cleanup(struct fcoe_interface *fcoe)
/* Delete secondary MAC addresses */
memcpy(flogi_maddr, (u8[6]) FC_FCOE_FLOGI_MAC, ETH_ALEN);
dev_unicast_delete(netdev, flogi_maddr);
- if (!is_zero_ether_addr(fip->data_src_addr))
- dev_unicast_delete(netdev, fip->data_src_addr);
if (fip->spma)
dev_unicast_delete(netdev, fip->ctl_src_addr);
dev_mc_delete(netdev, FIP_ALL_ENODE_MACS, ETH_ALEN, 0);
+
+ /* Tell the LLD we are done w/ FCoE */
+ ops = netdev->netdev_ops;
+ if (ops->ndo_fcoe_disable) {
+ if (ops->ndo_fcoe_disable(netdev))
+ FCOE_NETDEV_DBG(netdev, "Failed to disable FCoE"
+ " specific feature for LLD.\n");
+ }
}
/**
* fcoe_interface_release() - fcoe_port kref release function
- * @kref: embedded reference count in an fcoe_interface struct
+ * @kref: Embedded reference count in an fcoe_interface struct
*/
static void fcoe_interface_release(struct kref *kref)
{
@@ -301,8 +418,8 @@ static void fcoe_interface_release(struct kref *kref)
}
/**
- * fcoe_interface_get()
- * @fcoe:
+ * fcoe_interface_get() - Get a reference to a FCoE interface
+ * @fcoe: The FCoE interface to be held
*/
static inline void fcoe_interface_get(struct fcoe_interface *fcoe)
{
@@ -310,8 +427,8 @@ static inline void fcoe_interface_get(struct fcoe_interface *fcoe)
}
/**
- * fcoe_interface_put()
- * @fcoe:
+ * fcoe_interface_put() - Put a reference to a FCoE interface
+ * @fcoe: The FCoE interface to be released
*/
static inline void fcoe_interface_put(struct fcoe_interface *fcoe)
{
@@ -319,15 +436,16 @@ static inline void fcoe_interface_put(struct fcoe_interface *fcoe)
}
/**
- * fcoe_fip_recv - handle a received FIP frame.
- * @skb: the receive skb
- * @dev: associated &net_device
- * @ptype: the &packet_type structure which was used to register this handler.
- * @orig_dev: original receive &net_device, in case @dev is a bond.
+ * fcoe_fip_recv() - Handler for received FIP frames
+ * @skb: The receive skb
+ * @netdev: The associated net device
+ * @ptype: The packet_type structure which was used to register this handler
+ * @orig_dev: The original net_device the the skb was received on.
+ * (in case dev is a bond)
*
* Returns: 0 for success
*/
-static int fcoe_fip_recv(struct sk_buff *skb, struct net_device *dev,
+static int fcoe_fip_recv(struct sk_buff *skb, struct net_device *netdev,
struct packet_type *ptype,
struct net_device *orig_dev)
{
@@ -339,9 +457,9 @@ static int fcoe_fip_recv(struct sk_buff *skb, struct net_device *dev,
}
/**
- * fcoe_fip_send() - send an Ethernet-encapsulated FIP frame.
- * @fip: FCoE controller.
- * @skb: FIP Packet.
+ * fcoe_fip_send() - Send an Ethernet-encapsulated FIP frame
+ * @fip: The FCoE controller
+ * @skb: The FIP packet to be sent
*/
static void fcoe_fip_send(struct fcoe_ctlr *fip, struct sk_buff *skb)
{
@@ -350,88 +468,101 @@ static void fcoe_fip_send(struct fcoe_ctlr *fip, struct sk_buff *skb)
}
/**
- * fcoe_update_src_mac() - Update Ethernet MAC filters.
- * @fip: FCoE controller.
- * @old: Unicast MAC address to delete if the MAC is non-zero.
- * @new: Unicast MAC address to add.
+ * fcoe_update_src_mac() - Update the Ethernet MAC filters
+ * @lport: The local port to update the source MAC on
+ * @addr: Unicast MAC address to add
*
* Remove any previously-set unicast MAC filter.
* Add secondary FCoE MAC address filter for our OUI.
*/
-static void fcoe_update_src_mac(struct fcoe_ctlr *fip, u8 *old, u8 *new)
+static void fcoe_update_src_mac(struct fc_lport *lport, u8 *addr)
{
- struct fcoe_interface *fcoe;
+ struct fcoe_port *port = lport_priv(lport);
+ struct fcoe_interface *fcoe = port->fcoe;
- fcoe = fcoe_from_ctlr(fip);
rtnl_lock();
- if (!is_zero_ether_addr(old))
- dev_unicast_delete(fcoe->netdev, old);
- dev_unicast_add(fcoe->netdev, new);
+ if (!is_zero_ether_addr(port->data_src_addr))
+ dev_unicast_delete(fcoe->netdev, port->data_src_addr);
+ if (!is_zero_ether_addr(addr))
+ dev_unicast_add(fcoe->netdev, addr);
+ memcpy(port->data_src_addr, addr, ETH_ALEN);
rtnl_unlock();
}
/**
- * fcoe_lport_config() - sets up the fc_lport
- * @lp: ptr to the fc_lport
+ * fcoe_get_src_mac() - return the Ethernet source address for an lport
+ * @lport: libfc lport
+ */
+static u8 *fcoe_get_src_mac(struct fc_lport *lport)
+{
+ struct fcoe_port *port = lport_priv(lport);
+
+ return port->data_src_addr;
+}
+
+/**
+ * fcoe_lport_config() - Set up a local port
+ * @lport: The local port to be setup
*
* Returns: 0 for success
*/
-static int fcoe_lport_config(struct fc_lport *lp)
+static int fcoe_lport_config(struct fc_lport *lport)
{
- lp->link_up = 0;
- lp->qfull = 0;
- lp->max_retry_count = 3;
- lp->max_rport_retry_count = 3;
- lp->e_d_tov = 2 * 1000; /* FC-FS default */
- lp->r_a_tov = 2 * 2 * 1000;
- lp->service_params = (FCP_SPPF_INIT_FCN | FCP_SPPF_RD_XRDY_DIS |
- FCP_SPPF_RETRY | FCP_SPPF_CONF_COMPL);
-
- fc_lport_init_stats(lp);
+ lport->link_up = 0;
+ lport->qfull = 0;
+ lport->max_retry_count = 3;
+ lport->max_rport_retry_count = 3;
+ lport->e_d_tov = 2 * 1000; /* FC-FS default */
+ lport->r_a_tov = 2 * 2 * 1000;
+ lport->service_params = (FCP_SPPF_INIT_FCN | FCP_SPPF_RD_XRDY_DIS |
+ FCP_SPPF_RETRY | FCP_SPPF_CONF_COMPL);
+ lport->does_npiv = 1;
+
+ fc_lport_init_stats(lport);
/* lport fc_lport related configuration */
- fc_lport_config(lp);
+ fc_lport_config(lport);
/* offload related configuration */
- lp->crc_offload = 0;
- lp->seq_offload = 0;
- lp->lro_enabled = 0;
- lp->lro_xid = 0;
- lp->lso_max = 0;
+ lport->crc_offload = 0;
+ lport->seq_offload = 0;
+ lport->lro_enabled = 0;
+ lport->lro_xid = 0;
+ lport->lso_max = 0;
return 0;
}
/**
- * fcoe_queue_timer() - fcoe queue timer
- * @lp: the fc_lport pointer
+ * fcoe_queue_timer() - The fcoe queue timer
+ * @lport: The local port
*
* Calls fcoe_check_wait_queue on timeout
- *
*/
-static void fcoe_queue_timer(ulong lp)
+static void fcoe_queue_timer(ulong lport)
{
- fcoe_check_wait_queue((struct fc_lport *)lp, NULL);
+ fcoe_check_wait_queue((struct fc_lport *)lport, NULL);
}
/**
- * fcoe_netdev_config() - Set up netdev for SW FCoE
- * @lp : ptr to the fc_lport
- * @netdev : ptr to the associated netdevice struct
+ * fcoe_netdev_config() - Set up net devive for SW FCoE
+ * @lport: The local port that is associated with the net device
+ * @netdev: The associated net device
*
- * Must be called after fcoe_lport_config() as it will use lport mutex
+ * Must be called after fcoe_lport_config() as it will use local port mutex
*
- * Returns : 0 for success
+ * Returns: 0 for success
*/
-static int fcoe_netdev_config(struct fc_lport *lp, struct net_device *netdev)
+static int fcoe_netdev_config(struct fc_lport *lport, struct net_device *netdev)
{
u32 mfs;
u64 wwnn, wwpn;
struct fcoe_interface *fcoe;
struct fcoe_port *port;
+ int vid = 0;
/* Setup lport private data to point to fcoe softc */
- port = lport_priv(lp);
+ port = lport_priv(lport);
fcoe = port->fcoe;
/*
@@ -439,86 +570,112 @@ static int fcoe_netdev_config(struct fc_lport *lp, struct net_device *netdev)
* user-configured limit. If the MFS is too low, fcoe_link_ok()
* will return 0, so do this first.
*/
- mfs = netdev->mtu - (sizeof(struct fcoe_hdr) +
- sizeof(struct fcoe_crc_eof));
- if (fc_set_mfs(lp, mfs))
+ mfs = netdev->mtu;
+ if (netdev->features & NETIF_F_FCOE_MTU) {
+ mfs = FCOE_MTU;
+ FCOE_NETDEV_DBG(netdev, "Supports FCOE_MTU of %d bytes\n", mfs);
+ }
+ mfs -= (sizeof(struct fcoe_hdr) + sizeof(struct fcoe_crc_eof));
+ if (fc_set_mfs(lport, mfs))
return -EINVAL;
/* offload features support */
if (netdev->features & NETIF_F_SG)
- lp->sg_supp = 1;
+ lport->sg_supp = 1;
if (netdev->features & NETIF_F_FCOE_CRC) {
- lp->crc_offload = 1;
+ lport->crc_offload = 1;
FCOE_NETDEV_DBG(netdev, "Supports FCCRC offload\n");
}
if (netdev->features & NETIF_F_FSO) {
- lp->seq_offload = 1;
- lp->lso_max = netdev->gso_max_size;
+ lport->seq_offload = 1;
+ lport->lso_max = netdev->gso_max_size;
FCOE_NETDEV_DBG(netdev, "Supports LSO for max len 0x%x\n",
- lp->lso_max);
+ lport->lso_max);
}
if (netdev->fcoe_ddp_xid) {
- lp->lro_enabled = 1;
- lp->lro_xid = netdev->fcoe_ddp_xid;
+ lport->lro_enabled = 1;
+ lport->lro_xid = netdev->fcoe_ddp_xid;
FCOE_NETDEV_DBG(netdev, "Supports LRO for max xid 0x%x\n",
- lp->lro_xid);
+ lport->lro_xid);
}
skb_queue_head_init(&port->fcoe_pending_queue);
port->fcoe_pending_queue_active = 0;
- setup_timer(&port->timer, fcoe_queue_timer, (unsigned long)lp);
+ setup_timer(&port->timer, fcoe_queue_timer, (unsigned long)lport);
- wwnn = fcoe_wwn_from_mac(netdev->dev_addr, 1, 0);
- fc_set_wwnn(lp, wwnn);
- /* XXX - 3rd arg needs to be vlan id */
- wwpn = fcoe_wwn_from_mac(netdev->dev_addr, 2, 0);
- fc_set_wwpn(lp, wwpn);
+ if (!lport->vport) {
+ /*
+ * Use NAA 1&2 (FC-FS Rev. 2.0, Sec. 15) to generate WWNN/WWPN:
+ * For WWNN, we use NAA 1 w/ bit 27-16 of word 0 as 0.
+ * For WWPN, we use NAA 2 w/ bit 27-16 of word 0 from VLAN ID
+ */
+ if (netdev->priv_flags & IFF_802_1Q_VLAN)
+ vid = vlan_dev_vlan_id(netdev);
+ wwnn = fcoe_wwn_from_mac(fcoe->ctlr.ctl_src_addr, 1, 0);
+ fc_set_wwnn(lport, wwnn);
+ wwpn = fcoe_wwn_from_mac(fcoe->ctlr.ctl_src_addr, 2, vid);
+ fc_set_wwpn(lport, wwpn);
+ }
return 0;
}
/**
- * fcoe_shost_config() - Sets up fc_lport->host
- * @lp : ptr to the fc_lport
- * @shost : ptr to the associated scsi host
- * @dev : device associated to scsi host
+ * fcoe_shost_config() - Set up the SCSI host associated with a local port
+ * @lport: The local port
+ * @shost: The SCSI host to associate with the local port
+ * @dev: The device associated with the SCSI host
*
* Must be called after fcoe_lport_config() and fcoe_netdev_config()
*
- * Returns : 0 for success
+ * Returns: 0 for success
*/
-static int fcoe_shost_config(struct fc_lport *lp, struct Scsi_Host *shost,
- struct device *dev)
+static int fcoe_shost_config(struct fc_lport *lport, struct Scsi_Host *shost,
+ struct device *dev)
{
int rc = 0;
/* lport scsi host config */
- lp->host = shost;
-
- lp->host->max_lun = FCOE_MAX_LUN;
- lp->host->max_id = FCOE_MAX_FCP_TARGET;
- lp->host->max_channel = 0;
- lp->host->transportt = scsi_transport_fcoe_sw;
+ lport->host->max_lun = FCOE_MAX_LUN;
+ lport->host->max_id = FCOE_MAX_FCP_TARGET;
+ lport->host->max_channel = 0;
+ if (lport->vport)
+ lport->host->transportt = fcoe_vport_transport_template;
+ else
+ lport->host->transportt = fcoe_transport_template;
/* add the new host to the SCSI-ml */
- rc = scsi_add_host(lp->host, dev);
+ rc = scsi_add_host(lport->host, dev);
if (rc) {
- FCOE_NETDEV_DBG(fcoe_netdev(lp), "fcoe_shost_config: "
+ FCOE_NETDEV_DBG(fcoe_netdev(lport), "fcoe_shost_config: "
"error on scsi_add_host\n");
return rc;
}
- sprintf(fc_host_symbolic_name(lp->host), "%s v%s over %s",
- FCOE_NAME, FCOE_VERSION,
- fcoe_netdev(lp)->name);
+
+ if (!lport->vport)
+ fc_host_max_npiv_vports(lport->host) = USHORT_MAX;
+
+ snprintf(fc_host_symbolic_name(lport->host), FC_SYMBOLIC_NAME_SIZE,
+ "%s v%s over %s", FCOE_NAME, FCOE_VERSION,
+ fcoe_netdev(lport)->name);
return 0;
}
-/*
- * fcoe_oem_match() - match for read types IO
- * @fp: the fc_frame for new IO.
+/**
+ * fcoe_oem_match() - The match routine for the offloaded exchange manager
+ * @fp: The I/O frame
+ *
+ * This routine will be associated with an exchange manager (EM). When
+ * the libfc exchange handling code is looking for an EM to use it will
+ * call this routine and pass it the frame that it wishes to send. This
+ * routine will return True if the associated EM is to be used and False
+ * if the echange code should continue looking for an EM.
*
- * Returns : true for read types IO, otherwise returns false.
+ * The offload EM that this routine is associated with will handle any
+ * packets that are for SCSI read requests.
+ *
+ * Returns: True for read types I/O, otherwise returns false.
*/
bool fcoe_oem_match(struct fc_frame *fp)
{
@@ -527,14 +684,14 @@ bool fcoe_oem_match(struct fc_frame *fp)
}
/**
- * fcoe_em_config() - allocates em for this lport
- * @lp: the fcoe that em is to allocated for
+ * fcoe_em_config() - Allocate and configure an exchange manager
+ * @lport: The local port that the new EM will be associated with
*
- * Returns : 0 on success
+ * Returns: 0 on success
*/
-static inline int fcoe_em_config(struct fc_lport *lp)
+static inline int fcoe_em_config(struct fc_lport *lport)
{
- struct fcoe_port *port = lport_priv(lp);
+ struct fcoe_port *port = lport_priv(lport);
struct fcoe_interface *fcoe = port->fcoe;
struct fcoe_interface *oldfcoe = NULL;
struct net_device *old_real_dev, *cur_real_dev;
@@ -545,8 +702,9 @@ static inline int fcoe_em_config(struct fc_lport *lp)
* Check if need to allocate an em instance for
* offload exchange ids to be shared across all VN_PORTs/lport.
*/
- if (!lp->lro_enabled || !lp->lro_xid || (lp->lro_xid >= max_xid)) {
- lp->lro_xid = 0;
+ if (!lport->lro_enabled || !lport->lro_xid ||
+ (lport->lro_xid >= max_xid)) {
+ lport->lro_xid = 0;
goto skip_oem;
}
@@ -572,16 +730,16 @@ static inline int fcoe_em_config(struct fc_lport *lp)
}
if (fcoe->oem) {
- if (!fc_exch_mgr_add(lp, fcoe->oem, fcoe_oem_match)) {
+ if (!fc_exch_mgr_add(lport, fcoe->oem, fcoe_oem_match)) {
printk(KERN_ERR "fcoe_em_config: failed to add "
"offload em:%p on interface:%s\n",
fcoe->oem, fcoe->netdev->name);
return -ENOMEM;
}
} else {
- fcoe->oem = fc_exch_mgr_alloc(lp, FC_CLASS_3,
- FCOE_MIN_XID, lp->lro_xid,
- fcoe_oem_match);
+ fcoe->oem = fc_exch_mgr_alloc(lport, FC_CLASS_3,
+ FCOE_MIN_XID, lport->lro_xid,
+ fcoe_oem_match);
if (!fcoe->oem) {
printk(KERN_ERR "fcoe_em_config: failed to allocate "
"em for offload exches on interface:%s\n",
@@ -593,10 +751,10 @@ static inline int fcoe_em_config(struct fc_lport *lp)
/*
* Exclude offload EM xid range from next EM xid range.
*/
- min_xid += lp->lro_xid + 1;
+ min_xid += lport->lro_xid + 1;
skip_oem:
- if (!fc_exch_mgr_alloc(lp, FC_CLASS_3, min_xid, max_xid, NULL)) {
+ if (!fc_exch_mgr_alloc(lport, FC_CLASS_3, min_xid, max_xid, NULL)) {
printk(KERN_ERR "fcoe_em_config: failed to "
"allocate em on interface %s\n", fcoe->netdev->name);
return -ENOMEM;
@@ -606,8 +764,8 @@ skip_oem:
}
/**
- * fcoe_if_destroy() - FCoE software HBA tear-down function
- * @lport: fc_lport to destroy
+ * fcoe_if_destroy() - Tear down a SW FCoE instance
+ * @lport: The local port to be destroyed
*/
static void fcoe_if_destroy(struct fc_lport *lport)
{
@@ -630,6 +788,11 @@ static void fcoe_if_destroy(struct fc_lport *lport)
/* Free existing transmit skbs */
fcoe_clean_pending_queue(lport);
+ rtnl_lock();
+ if (!is_zero_ether_addr(port->data_src_addr))
+ dev_unicast_delete(netdev, port->data_src_addr);
+ rtnl_unlock();
+
/* receives may not be stopped until after this */
fcoe_interface_put(fcoe);
@@ -650,82 +813,89 @@ static void fcoe_if_destroy(struct fc_lport *lport)
scsi_host_put(lport->host);
}
-/*
- * fcoe_ddp_setup - calls LLD's ddp_setup through net_device
- * @lp: the corresponding fc_lport
- * @xid: the exchange id for this ddp transfer
- * @sgl: the scatterlist describing this transfer
- * @sgc: number of sg items
+/**
+ * fcoe_ddp_setup() - Call a LLD's ddp_setup through the net device
+ * @lport: The local port to setup DDP for
+ * @xid: The exchange ID for this DDP transfer
+ * @sgl: The scatterlist describing this transfer
+ * @sgc: The number of sg items
*
- * Returns : 0 no ddp
+ * Returns: 0 if the DDP context was not configured
*/
-static int fcoe_ddp_setup(struct fc_lport *lp, u16 xid,
- struct scatterlist *sgl, unsigned int sgc)
+static int fcoe_ddp_setup(struct fc_lport *lport, u16 xid,
+ struct scatterlist *sgl, unsigned int sgc)
{
- struct net_device *n = fcoe_netdev(lp);
+ struct net_device *netdev = fcoe_netdev(lport);
- if (n->netdev_ops && n->netdev_ops->ndo_fcoe_ddp_setup)
- return n->netdev_ops->ndo_fcoe_ddp_setup(n, xid, sgl, sgc);
+ if (netdev->netdev_ops->ndo_fcoe_ddp_setup)
+ return netdev->netdev_ops->ndo_fcoe_ddp_setup(netdev,
+ xid, sgl,
+ sgc);
return 0;
}
-/*
- * fcoe_ddp_done - calls LLD's ddp_done through net_device
- * @lp: the corresponding fc_lport
- * @xid: the exchange id for this ddp transfer
+/**
+ * fcoe_ddp_done() - Call a LLD's ddp_done through the net device
+ * @lport: The local port to complete DDP on
+ * @xid: The exchange ID for this DDP transfer
*
- * Returns : the length of data that have been completed by ddp
+ * Returns: the length of data that have been completed by DDP
*/
-static int fcoe_ddp_done(struct fc_lport *lp, u16 xid)
+static int fcoe_ddp_done(struct fc_lport *lport, u16 xid)
{
- struct net_device *n = fcoe_netdev(lp);
+ struct net_device *netdev = fcoe_netdev(lport);
- if (n->netdev_ops && n->netdev_ops->ndo_fcoe_ddp_done)
- return n->netdev_ops->ndo_fcoe_ddp_done(n, xid);
+ if (netdev->netdev_ops->ndo_fcoe_ddp_done)
+ return netdev->netdev_ops->ndo_fcoe_ddp_done(netdev, xid);
return 0;
}
-static struct libfc_function_template fcoe_libfc_fcn_templ = {
- .frame_send = fcoe_xmit,
- .ddp_setup = fcoe_ddp_setup,
- .ddp_done = fcoe_ddp_done,
-};
-
/**
- * fcoe_if_create() - this function creates the fcoe port
- * @fcoe: fcoe_interface structure to create an fc_lport instance on
- * @parent: device pointer to be the parent in sysfs for the SCSI host
+ * fcoe_if_create() - Create a FCoE instance on an interface
+ * @fcoe: The FCoE interface to create a local port on
+ * @parent: The device pointer to be the parent in sysfs for the SCSI host
+ * @npiv: Indicates if the port is a vport or not
*
- * Creates fc_lport struct and scsi_host for lport, configures lport.
+ * Creates a fc_lport instance and a Scsi_Host instance and configure them.
*
- * Returns : The allocated fc_lport or an error pointer
+ * Returns: The allocated fc_lport or an error pointer
*/
static struct fc_lport *fcoe_if_create(struct fcoe_interface *fcoe,
- struct device *parent)
+ struct device *parent, int npiv)
{
- int rc;
+ struct net_device *netdev = fcoe->netdev;
struct fc_lport *lport = NULL;
struct fcoe_port *port;
struct Scsi_Host *shost;
- struct net_device *netdev = fcoe->netdev;
+ int rc;
+ /*
+ * parent is only a vport if npiv is 1,
+ * but we'll only use vport in that case so go ahead and set it
+ */
+ struct fc_vport *vport = dev_to_vport(parent);
FCOE_NETDEV_DBG(netdev, "Create Interface\n");
- shost = libfc_host_alloc(&fcoe_shost_template,
- sizeof(struct fcoe_port));
- if (!shost) {
+ if (!npiv) {
+ lport = libfc_host_alloc(&fcoe_shost_template,
+ sizeof(struct fcoe_port));
+ } else {
+ lport = libfc_vport_create(vport,
+ sizeof(struct fcoe_port));
+ }
+ if (!lport) {
FCOE_NETDEV_DBG(netdev, "Could not allocate host structure\n");
rc = -ENOMEM;
goto out;
}
- lport = shost_priv(shost);
+ shost = lport->host;
port = lport_priv(lport);
port->lport = lport;
port->fcoe = fcoe;
INIT_WORK(&port->destroy_work, fcoe_destroy_work);
- /* configure fc_lport, e.g., em */
+ /* configure a fc_lport including the exchange manager */
rc = fcoe_lport_config(lport);
if (rc) {
FCOE_NETDEV_DBG(netdev, "Could not configure lport for the "
@@ -733,6 +903,13 @@ static struct fc_lport *fcoe_if_create(struct fcoe_interface *fcoe,
goto out_host_put;
}
+ if (npiv) {
+ FCOE_NETDEV_DBG(netdev, "Setting vport names, 0x%llX 0x%llX\n",
+ vport->node_name, vport->port_name);
+ fc_set_wwnn(lport, vport->node_name);
+ fc_set_wwpn(lport, vport->port_name);
+ }
+
/* configure lport network properties */
rc = fcoe_netdev_config(lport, netdev);
if (rc) {
@@ -757,21 +934,24 @@ static struct fc_lport *fcoe_if_create(struct fcoe_interface *fcoe,
goto out_lp_destroy;
}
- /*
- * fcoe_em_alloc() and fcoe_hostlist_add() both
- * need to be atomic with respect to other changes to the hostlist
- * since fcoe_em_alloc() looks for an existing EM
- * instance on host list updated by fcoe_hostlist_add().
- *
- * This is currently handled through the fcoe_config_mutex begin held.
- */
+ if (!npiv) {
+ /*
+ * fcoe_em_alloc() and fcoe_hostlist_add() both
+ * need to be atomic with respect to other changes to the
+ * hostlist since fcoe_em_alloc() looks for an existing EM
+ * instance on host list updated by fcoe_hostlist_add().
+ *
+ * This is currently handled through the fcoe_config_mutex
+ * begin held.
+ */
- /* lport exch manager allocation */
- rc = fcoe_em_config(lport);
- if (rc) {
- FCOE_NETDEV_DBG(netdev, "Could not configure the EM for the "
- "interface\n");
- goto out_lp_destroy;
+ /* lport exch manager allocation */
+ rc = fcoe_em_config(lport);
+ if (rc) {
+ FCOE_NETDEV_DBG(netdev, "Could not configure the EM "
+ "for the interface\n");
+ goto out_lp_destroy;
+ }
}
fcoe_interface_get(fcoe);
@@ -786,17 +966,20 @@ out:
}
/**
- * fcoe_if_init() - attach to scsi transport
+ * fcoe_if_init() - Initialization routine for fcoe.ko
+ *
+ * Attaches the SW FCoE transport to the FC transport
*
- * Returns : 0 on success
+ * Returns: 0 on success
*/
static int __init fcoe_if_init(void)
{
/* attach to scsi transport */
- scsi_transport_fcoe_sw =
- fc_attach_transport(&fcoe_transport_function);
+ fcoe_transport_template = fc_attach_transport(&fcoe_transport_function);
+ fcoe_vport_transport_template =
+ fc_attach_transport(&fcoe_vport_transport_function);
- if (!scsi_transport_fcoe_sw) {
+ if (!fcoe_transport_template) {
printk(KERN_ERR "fcoe: Failed to attach to the FC transport\n");
return -ENODEV;
}
@@ -805,20 +988,24 @@ static int __init fcoe_if_init(void)
}
/**
- * fcoe_if_exit() - detach from scsi transport
+ * fcoe_if_exit() - Tear down fcoe.ko
+ *
+ * Detaches the SW FCoE transport from the FC transport
*
- * Returns : 0 on success
+ * Returns: 0 on success
*/
int __exit fcoe_if_exit(void)
{
- fc_release_transport(scsi_transport_fcoe_sw);
- scsi_transport_fcoe_sw = NULL;
+ fc_release_transport(fcoe_transport_template);
+ fc_release_transport(fcoe_vport_transport_template);
+ fcoe_transport_template = NULL;
+ fcoe_vport_transport_template = NULL;
return 0;
}
/**
- * fcoe_percpu_thread_create() - Create a receive thread for an online cpu
- * @cpu: cpu index for the online cpu
+ * fcoe_percpu_thread_create() - Create a receive thread for an online CPU
+ * @cpu: The CPU index of the CPU to create a receive thread for
*/
static void fcoe_percpu_thread_create(unsigned int cpu)
{
@@ -841,8 +1028,8 @@ static void fcoe_percpu_thread_create(unsigned int cpu)
}
/**
- * fcoe_percpu_thread_destroy() - removes the rx thread for the given cpu
- * @cpu: cpu index the rx thread is to be removed
+ * fcoe_percpu_thread_destroy() - Remove the receive thread of a CPU
+ * @cpu: The CPU index of the CPU whose receive thread is to be destroyed
*
* Destroys a per-CPU Rx thread. Any pending skbs are moved to the
* current CPU's Rx thread. If the thread being destroyed is bound to
@@ -890,7 +1077,7 @@ static void fcoe_percpu_thread_destroy(unsigned int cpu)
} else {
/*
* The targeted CPU is not initialized and cannot accept
- * new skbs. Unlock the targeted CPU and drop the skbs
+ * new skbs. Unlock the targeted CPU and drop the skbs
* on the CPU that is going offline.
*/
while ((skb = __skb_dequeue(&p->fcoe_rx_list)) != NULL)
@@ -931,12 +1118,12 @@ static void fcoe_percpu_thread_destroy(unsigned int cpu)
}
/**
- * fcoe_cpu_callback() - fcoe cpu hotplug event callback
- * @nfb: callback data block
- * @action: event triggering the callback
- * @hcpu: index for the cpu of this event
+ * fcoe_cpu_callback() - Handler for CPU hotplug events
+ * @nfb: The callback data block
+ * @action: The event triggering the callback
+ * @hcpu: The index of the CPU that the event is for
*
- * This creates or destroys per cpu data for fcoe
+ * This creates or destroys per-CPU data for fcoe
*
* Returns NOTIFY_OK always.
*/
@@ -962,25 +1149,22 @@ static int fcoe_cpu_callback(struct notifier_block *nfb,
return NOTIFY_OK;
}
-static struct notifier_block fcoe_cpu_notifier = {
- .notifier_call = fcoe_cpu_callback,
-};
-
/**
- * fcoe_rcv() - this is the fcoe receive function called by NET_RX_SOFTIRQ
- * @skb: the receive skb
- * @dev: associated net device
- * @ptype: context
- * @olddev: last device
+ * fcoe_rcv() - Receive packets from a net device
+ * @skb: The received packet
+ * @netdev: The net device that the packet was received on
+ * @ptype: The packet type context
+ * @olddev: The last device net device
*
- * this function will receive the packet and build fc frame and pass it up
+ * This routine is called by NET_RX_SOFTIRQ. It receives a packet, builds a
+ * FC frame and passes the frame to libfc.
*
* Returns: 0 for success
*/
-int fcoe_rcv(struct sk_buff *skb, struct net_device *dev,
+int fcoe_rcv(struct sk_buff *skb, struct net_device *netdev,
struct packet_type *ptype, struct net_device *olddev)
{
- struct fc_lport *lp;
+ struct fc_lport *lport;
struct fcoe_rcv_info *fr;
struct fcoe_interface *fcoe;
struct fc_frame_header *fh;
@@ -988,15 +1172,15 @@ int fcoe_rcv(struct sk_buff *skb, struct net_device *dev,
unsigned int cpu;
fcoe = container_of(ptype, struct fcoe_interface, fcoe_packet_type);
- lp = fcoe->ctlr.lp;
- if (unlikely(lp == NULL)) {
- FCOE_NETDEV_DBG(dev, "Cannot find hba structure");
+ lport = fcoe->ctlr.lp;
+ if (unlikely(!lport)) {
+ FCOE_NETDEV_DBG(netdev, "Cannot find hba structure");
goto err2;
}
- if (!lp->link_up)
+ if (!lport->link_up)
goto err2;
- FCOE_NETDEV_DBG(dev, "skb_info: len:%d data_len:%d head:%p "
+ FCOE_NETDEV_DBG(netdev, "skb_info: len:%d data_len:%d head:%p "
"data:%p tail:%p end:%p sum:%d dev:%s",
skb->len, skb->data_len, skb->head, skb->data,
skb_tail_pointer(skb), skb_end_pointer(skb),
@@ -1004,7 +1188,7 @@ int fcoe_rcv(struct sk_buff *skb, struct net_device *dev,
/* check for FCOE packet type */
if (unlikely(eth_hdr(skb)->h_proto != htons(ETH_P_FCOE))) {
- FCOE_NETDEV_DBG(dev, "Wrong FC type frame");
+ FCOE_NETDEV_DBG(netdev, "Wrong FC type frame");
goto err;
}
@@ -1013,14 +1197,14 @@ int fcoe_rcv(struct sk_buff *skb, struct net_device *dev,
* and FC headers are pulled into the linear data area.
*/
if (unlikely((skb->len < FCOE_MIN_FRAME) ||
- !pskb_may_pull(skb, FCOE_HEADER_LEN)))
+ !pskb_may_pull(skb, FCOE_HEADER_LEN)))
goto err;
skb_set_transport_header(skb, sizeof(struct fcoe_hdr));
fh = (struct fc_frame_header *) skb_transport_header(skb);
fr = fcoe_dev_from_skb(skb);
- fr->fr_dev = lp;
+ fr->fr_dev = lport;
fr->ptype = ptype;
/*
@@ -1042,7 +1226,7 @@ int fcoe_rcv(struct sk_buff *skb, struct net_device *dev,
* the first CPU now. For non-SMP systems this
* will check the same CPU twice.
*/
- FCOE_NETDEV_DBG(dev, "CPU is online, but no receive thread "
+ FCOE_NETDEV_DBG(netdev, "CPU is online, but no receive thread "
"ready for incoming skb- using first online "
"CPU.\n");
@@ -1061,15 +1245,29 @@ int fcoe_rcv(struct sk_buff *skb, struct net_device *dev,
* this skb. We also have this receive thread locked,
* so we're free to queue skbs into it's queue.
*/
- __skb_queue_tail(&fps->fcoe_rx_list, skb);
- if (fps->fcoe_rx_list.qlen == 1)
- wake_up_process(fps->thread);
- spin_unlock_bh(&fps->fcoe_rx_list.lock);
+ /* If this is a SCSI-FCP frame, and this is already executing on the
+ * correct CPU, and the queue for this CPU is empty, then go ahead
+ * and process the frame directly in the softirq context.
+ * This lets us process completions without context switching from the
+ * NET_RX softirq, to our receive processing thread, and then back to
+ * BLOCK softirq context.
+ */
+ if (fh->fh_type == FC_TYPE_FCP &&
+ cpu == smp_processor_id() &&
+ skb_queue_empty(&fps->fcoe_rx_list)) {
+ spin_unlock_bh(&fps->fcoe_rx_list.lock);
+ fcoe_recv_frame(skb);
+ } else {
+ __skb_queue_tail(&fps->fcoe_rx_list, skb);
+ if (fps->fcoe_rx_list.qlen == 1)
+ wake_up_process(fps->thread);
+ spin_unlock_bh(&fps->fcoe_rx_list.lock);
+ }
return 0;
err:
- fc_lport_get_stats(lp)->ErrorFrames++;
+ fc_lport_get_stats(lport)->ErrorFrames++;
err2:
kfree_skb(skb);
@@ -1077,17 +1275,21 @@ err2:
}
/**
- * fcoe_start_io() - pass to netdev to start xmit for fcoe
- * @skb: the skb to be xmitted
+ * fcoe_start_io() - Start FCoE I/O
+ * @skb: The packet to be transmitted
+ *
+ * This routine is called from the net device to start transmitting
+ * FCoE packets.
*
* Returns: 0 for success
*/
static inline int fcoe_start_io(struct sk_buff *skb)
{
+ struct sk_buff *nskb;
int rc;
- skb_get(skb);
- rc = dev_queue_xmit(skb);
+ nskb = skb_clone(skb, GFP_ATOMIC);
+ rc = dev_queue_xmit(nskb);
if (rc != 0)
return rc;
kfree_skb(skb);
@@ -1095,9 +1297,15 @@ static inline int fcoe_start_io(struct sk_buff *skb)
}
/**
- * fcoe_get_paged_crc_eof() - in case we need to alloc a page for crc_eof
- * @skb: the skb to be xmitted
- * @tlen: total len
+ * fcoe_get_paged_crc_eof() - Allocate a page to be used for the trailer CRC
+ * @skb: The packet to be transmitted
+ * @tlen: The total length of the trailer
+ *
+ * This routine allocates a page for frame trailers. The page is re-used if
+ * there is enough room left on it for the current trailer. If there isn't
+ * enough buffer left a new page is allocated for the trailer. Reference to
+ * the page from this function as well as the skbs using the page fragments
+ * ensure that the page is freed at the appropriate time.
*
* Returns: 0 for success
*/
@@ -1136,11 +1344,12 @@ static int fcoe_get_paged_crc_eof(struct sk_buff *skb, int tlen)
}
/**
- * fcoe_fc_crc() - calculates FC CRC in this fcoe skb
- * @fp: the fc_frame containing data to be checksummed
+ * fcoe_fc_crc() - Calculates the CRC for a given frame
+ * @fp: The frame to be checksumed
+ *
+ * This uses crc32() routine to calculate the CRC for a frame
*
- * This uses crc32() to calculate the crc for port frame
- * Return : 32 bit crc
+ * Return: The 32 bit CRC value
*/
u32 fcoe_fc_crc(struct fc_frame *fp)
{
@@ -1171,13 +1380,13 @@ u32 fcoe_fc_crc(struct fc_frame *fp)
}
/**
- * fcoe_xmit() - FCoE frame transmit function
- * @lp: the associated local fcoe
- * @fp: the fc_frame to be transmitted
+ * fcoe_xmit() - Transmit a FCoE frame
+ * @lport: The local port that the frame is to be transmitted for
+ * @fp: The frame to be transmitted
*
- * Return : 0 for success
+ * Return: 0 for success
*/
-int fcoe_xmit(struct fc_lport *lp, struct fc_frame *fp)
+int fcoe_xmit(struct fc_lport *lport, struct fc_frame *fp)
{
int wlen;
u32 crc;
@@ -1189,7 +1398,7 @@ int fcoe_xmit(struct fc_lport *lp, struct fc_frame *fp)
unsigned int hlen; /* header length implies the version */
unsigned int tlen; /* trailer length */
unsigned int elen; /* eth header, may include vlan */
- struct fcoe_port *port = lport_priv(lp);
+ struct fcoe_port *port = lport_priv(lport);
struct fcoe_interface *fcoe = port->fcoe;
u8 sof, eof;
struct fcoe_hdr *hp;
@@ -1200,13 +1409,13 @@ int fcoe_xmit(struct fc_lport *lp, struct fc_frame *fp)
skb = fp_skb(fp);
wlen = skb->len / FCOE_WORD_TO_BYTE;
- if (!lp->link_up) {
+ if (!lport->link_up) {
kfree_skb(skb);
return 0;
}
if (unlikely(fh->fh_r_ctl == FC_RCTL_ELS_REQ) &&
- fcoe_ctlr_els_send(&fcoe->ctlr, skb))
+ fcoe_ctlr_els_send(&fcoe->ctlr, lport, skb))
return 0;
sof = fr_sof(fp);
@@ -1218,7 +1427,7 @@ int fcoe_xmit(struct fc_lport *lp, struct fc_frame *fp)
wlen = (skb->len - tlen + sizeof(crc)) / FCOE_WORD_TO_BYTE;
/* crc offload */
- if (likely(lp->crc_offload)) {
+ if (likely(lport->crc_offload)) {
skb->ip_summed = CHECKSUM_PARTIAL;
skb->csum_start = skb_headroom(skb);
skb->csum_offset = skb->len;
@@ -1271,7 +1480,7 @@ int fcoe_xmit(struct fc_lport *lp, struct fc_frame *fp)
if (unlikely(fcoe->ctlr.flogi_oxid != FC_XID_UNKNOWN))
memcpy(eh->h_source, fcoe->ctlr.ctl_src_addr, ETH_ALEN);
else
- memcpy(eh->h_source, fcoe->ctlr.data_src_addr, ETH_ALEN);
+ memcpy(eh->h_source, port->data_src_addr, ETH_ALEN);
hp = (struct fcoe_hdr *)(eh + 1);
memset(hp, 0, sizeof(*hp));
@@ -1280,7 +1489,7 @@ int fcoe_xmit(struct fc_lport *lp, struct fc_frame *fp)
hp->fcoe_sof = sof;
/* fcoe lso, mss is in max_payload which is non-zero for FCP data */
- if (lp->seq_offload && fr_max_payload(fp)) {
+ if (lport->seq_offload && fr_max_payload(fp)) {
skb_shinfo(skb)->gso_type = SKB_GSO_FCOE;
skb_shinfo(skb)->gso_size = fr_max_payload(fp);
} else {
@@ -1288,23 +1497,23 @@ int fcoe_xmit(struct fc_lport *lp, struct fc_frame *fp)
skb_shinfo(skb)->gso_size = 0;
}
/* update tx stats: regardless if LLD fails */
- stats = fc_lport_get_stats(lp);
+ stats = fc_lport_get_stats(lport);
stats->TxFrames++;
stats->TxWords += wlen;
/* send down to lld */
- fr_dev(fp) = lp;
+ fr_dev(fp) = lport;
if (port->fcoe_pending_queue.qlen)
- fcoe_check_wait_queue(lp, skb);
+ fcoe_check_wait_queue(lport, skb);
else if (fcoe_start_io(skb))
- fcoe_check_wait_queue(lp, skb);
+ fcoe_check_wait_queue(lport, skb);
return 0;
}
/**
- * fcoe_percpu_flush_done() - Indicate percpu queue flush completion.
- * @skb: the skb being completed.
+ * fcoe_percpu_flush_done() - Indicate per-CPU queue flush completion
+ * @skb: The completed skb (argument required by destructor)
*/
static void fcoe_percpu_flush_done(struct sk_buff *skb)
{
@@ -1312,26 +1521,134 @@ static void fcoe_percpu_flush_done(struct sk_buff *skb)
}
/**
- * fcoe_percpu_receive_thread() - recv thread per cpu
- * @arg: ptr to the fcoe per cpu struct
- *
- * Return: 0 for success
+ * fcoe_recv_frame() - process a single received frame
+ * @skb: frame to process
*/
-int fcoe_percpu_receive_thread(void *arg)
+static void fcoe_recv_frame(struct sk_buff *skb)
{
- struct fcoe_percpu_s *p = arg;
u32 fr_len;
- struct fc_lport *lp;
+ struct fc_lport *lport;
struct fcoe_rcv_info *fr;
struct fcoe_dev_stats *stats;
struct fc_frame_header *fh;
- struct sk_buff *skb;
struct fcoe_crc_eof crc_eof;
struct fc_frame *fp;
u8 *mac = NULL;
struct fcoe_port *port;
struct fcoe_hdr *hp;
+ fr = fcoe_dev_from_skb(skb);
+ lport = fr->fr_dev;
+ if (unlikely(!lport)) {
+ if (skb->destructor != fcoe_percpu_flush_done)
+ FCOE_NETDEV_DBG(skb->dev, "NULL lport in skb");
+ kfree_skb(skb);
+ return;
+ }
+
+ FCOE_NETDEV_DBG(skb->dev, "skb_info: len:%d data_len:%d "
+ "head:%p data:%p tail:%p end:%p sum:%d dev:%s",
+ skb->len, skb->data_len,
+ skb->head, skb->data, skb_tail_pointer(skb),
+ skb_end_pointer(skb), skb->csum,
+ skb->dev ? skb->dev->name : "<NULL>");
+
+ /*
+ * Save source MAC address before discarding header.
+ */
+ port = lport_priv(lport);
+ if (skb_is_nonlinear(skb))
+ skb_linearize(skb); /* not ideal */
+ mac = eth_hdr(skb)->h_source;
+
+ /*
+ * Frame length checks and setting up the header pointers
+ * was done in fcoe_rcv already.
+ */
+ hp = (struct fcoe_hdr *) skb_network_header(skb);
+ fh = (struct fc_frame_header *) skb_transport_header(skb);
+
+ stats = fc_lport_get_stats(lport);
+ if (unlikely(FC_FCOE_DECAPS_VER(hp) != FC_FCOE_VER)) {
+ if (stats->ErrorFrames < 5)
+ printk(KERN_WARNING "fcoe: FCoE version "
+ "mismatch: The frame has "
+ "version %x, but the "
+ "initiator supports version "
+ "%x\n", FC_FCOE_DECAPS_VER(hp),
+ FC_FCOE_VER);
+ stats->ErrorFrames++;
+ kfree_skb(skb);
+ return;
+ }
+
+ skb_pull(skb, sizeof(struct fcoe_hdr));
+ fr_len = skb->len - sizeof(struct fcoe_crc_eof);
+
+ stats->RxFrames++;
+ stats->RxWords += fr_len / FCOE_WORD_TO_BYTE;
+
+ fp = (struct fc_frame *)skb;
+ fc_frame_init(fp);
+ fr_dev(fp) = lport;
+ fr_sof(fp) = hp->fcoe_sof;
+
+ /* Copy out the CRC and EOF trailer for access */
+ if (skb_copy_bits(skb, fr_len, &crc_eof, sizeof(crc_eof))) {
+ kfree_skb(skb);
+ return;
+ }
+ fr_eof(fp) = crc_eof.fcoe_eof;
+ fr_crc(fp) = crc_eof.fcoe_crc32;
+ if (pskb_trim(skb, fr_len)) {
+ kfree_skb(skb);
+ return;
+ }
+
+ /*
+ * We only check CRC if no offload is available and if it is
+ * it's solicited data, in which case, the FCP layer would
+ * check it during the copy.
+ */
+ if (lport->crc_offload &&
+ skb->ip_summed == CHECKSUM_UNNECESSARY)
+ fr_flags(fp) &= ~FCPHF_CRC_UNCHECKED;
+ else
+ fr_flags(fp) |= FCPHF_CRC_UNCHECKED;
+
+ fh = fc_frame_header_get(fp);
+ if (fh->fh_r_ctl == FC_RCTL_DD_SOL_DATA &&
+ fh->fh_type == FC_TYPE_FCP) {
+ fc_exch_recv(lport, fp);
+ return;
+ }
+ if (fr_flags(fp) & FCPHF_CRC_UNCHECKED) {
+ if (le32_to_cpu(fr_crc(fp)) !=
+ ~crc32(~0, skb->data, fr_len)) {
+ if (stats->InvalidCRCCount < 5)
+ printk(KERN_WARNING "fcoe: dropping "
+ "frame with CRC error\n");
+ stats->InvalidCRCCount++;
+ stats->ErrorFrames++;
+ fc_frame_free(fp);
+ return;
+ }
+ fr_flags(fp) &= ~FCPHF_CRC_UNCHECKED;
+ }
+ fc_exch_recv(lport, fp);
+}
+
+/**
+ * fcoe_percpu_receive_thread() - The per-CPU packet receive thread
+ * @arg: The per-CPU context
+ *
+ * Return: 0 for success
+ */
+int fcoe_percpu_receive_thread(void *arg)
+{
+ struct fcoe_percpu_s *p = arg;
+ struct sk_buff *skb;
+
set_user_nice(current, -20);
while (!kthread_should_stop()) {
@@ -1347,129 +1664,27 @@ int fcoe_percpu_receive_thread(void *arg)
spin_lock_bh(&p->fcoe_rx_list.lock);
}
spin_unlock_bh(&p->fcoe_rx_list.lock);
- fr = fcoe_dev_from_skb(skb);
- lp = fr->fr_dev;
- if (unlikely(lp == NULL)) {
- if (skb->destructor != fcoe_percpu_flush_done)
- FCOE_NETDEV_DBG(skb->dev, "NULL lport in skb");
- kfree_skb(skb);
- continue;
- }
-
- FCOE_NETDEV_DBG(skb->dev, "skb_info: len:%d data_len:%d "
- "head:%p data:%p tail:%p end:%p sum:%d dev:%s",
- skb->len, skb->data_len,
- skb->head, skb->data, skb_tail_pointer(skb),
- skb_end_pointer(skb), skb->csum,
- skb->dev ? skb->dev->name : "<NULL>");
-
- /*
- * Save source MAC address before discarding header.
- */
- port = lport_priv(lp);
- if (skb_is_nonlinear(skb))
- skb_linearize(skb); /* not ideal */
- mac = eth_hdr(skb)->h_source;
-
- /*
- * Frame length checks and setting up the header pointers
- * was done in fcoe_rcv already.
- */
- hp = (struct fcoe_hdr *) skb_network_header(skb);
- fh = (struct fc_frame_header *) skb_transport_header(skb);
-
- stats = fc_lport_get_stats(lp);
- if (unlikely(FC_FCOE_DECAPS_VER(hp) != FC_FCOE_VER)) {
- if (stats->ErrorFrames < 5)
- printk(KERN_WARNING "fcoe: FCoE version "
- "mismatch: The frame has "
- "version %x, but the "
- "initiator supports version "
- "%x\n", FC_FCOE_DECAPS_VER(hp),
- FC_FCOE_VER);
- stats->ErrorFrames++;
- kfree_skb(skb);
- continue;
- }
-
- skb_pull(skb, sizeof(struct fcoe_hdr));
- fr_len = skb->len - sizeof(struct fcoe_crc_eof);
-
- stats->RxFrames++;
- stats->RxWords += fr_len / FCOE_WORD_TO_BYTE;
-
- fp = (struct fc_frame *)skb;
- fc_frame_init(fp);
- fr_dev(fp) = lp;
- fr_sof(fp) = hp->fcoe_sof;
-
- /* Copy out the CRC and EOF trailer for access */
- if (skb_copy_bits(skb, fr_len, &crc_eof, sizeof(crc_eof))) {
- kfree_skb(skb);
- continue;
- }
- fr_eof(fp) = crc_eof.fcoe_eof;
- fr_crc(fp) = crc_eof.fcoe_crc32;
- if (pskb_trim(skb, fr_len)) {
- kfree_skb(skb);
- continue;
- }
-
- /*
- * We only check CRC if no offload is available and if it is
- * it's solicited data, in which case, the FCP layer would
- * check it during the copy.
- */
- if (lp->crc_offload && skb->ip_summed == CHECKSUM_UNNECESSARY)
- fr_flags(fp) &= ~FCPHF_CRC_UNCHECKED;
- else
- fr_flags(fp) |= FCPHF_CRC_UNCHECKED;
-
- fh = fc_frame_header_get(fp);
- if (fh->fh_r_ctl == FC_RCTL_DD_SOL_DATA &&
- fh->fh_type == FC_TYPE_FCP) {
- fc_exch_recv(lp, fp);
- continue;
- }
- if (fr_flags(fp) & FCPHF_CRC_UNCHECKED) {
- if (le32_to_cpu(fr_crc(fp)) !=
- ~crc32(~0, skb->data, fr_len)) {
- if (stats->InvalidCRCCount < 5)
- printk(KERN_WARNING "fcoe: dropping "
- "frame with CRC error\n");
- stats->InvalidCRCCount++;
- stats->ErrorFrames++;
- fc_frame_free(fp);
- continue;
- }
- fr_flags(fp) &= ~FCPHF_CRC_UNCHECKED;
- }
- if (unlikely(port->fcoe->ctlr.flogi_oxid != FC_XID_UNKNOWN) &&
- fcoe_ctlr_recv_flogi(&port->fcoe->ctlr, fp, mac)) {
- fc_frame_free(fp);
- continue;
- }
- fc_exch_recv(lp, fp);
+ fcoe_recv_frame(skb);
}
return 0;
}
/**
- * fcoe_check_wait_queue() - attempt to clear the transmit backlog
- * @lp: the fc_lport
+ * fcoe_check_wait_queue() - Attempt to clear the transmit backlog
+ * @lport: The local port whose backlog is to be cleared
*
- * This empties the wait_queue, dequeue the head of the wait_queue queue
- * and calls fcoe_start_io() for each packet, if all skb have been
- * transmitted, return qlen or -1 if a error occurs, then restore
- * wait_queue and try again later.
+ * This empties the wait_queue, dequeues the head of the wait_queue queue
+ * and calls fcoe_start_io() for each packet. If all skb have been
+ * transmitted it returns the qlen. If an error occurs it restores
+ * wait_queue (to try again later) and returns -1.
*
- * The wait_queue is used when the skb transmit fails. skb will go
- * in the wait_queue which will be emptied by the timer function or
+ * The wait_queue is used when the skb transmit fails. The failed skb
+ * will go in the wait_queue which will be emptied by the timer function or
* by the next skb transmit.
*/
-static void fcoe_check_wait_queue(struct fc_lport *lp, struct sk_buff *skb)
+static void fcoe_check_wait_queue(struct fc_lport *lport, struct sk_buff *skb)
{
- struct fcoe_port *port = lport_priv(lp);
+ struct fcoe_port *port = lport_priv(lport);
int rc;
spin_lock_bh(&port->fcoe_pending_queue.lock);
@@ -1501,19 +1716,19 @@ static void fcoe_check_wait_queue(struct fc_lport *lp, struct sk_buff *skb)
}
if (port->fcoe_pending_queue.qlen < FCOE_LOW_QUEUE_DEPTH)
- lp->qfull = 0;
+ lport->qfull = 0;
if (port->fcoe_pending_queue.qlen && !timer_pending(&port->timer))
mod_timer(&port->timer, jiffies + 2);
port->fcoe_pending_queue_active = 0;
out:
if (port->fcoe_pending_queue.qlen > FCOE_MAX_QUEUE_DEPTH)
- lp->qfull = 1;
+ lport->qfull = 1;
spin_unlock_bh(&port->fcoe_pending_queue.lock);
return;
}
/**
- * fcoe_dev_setup() - setup link change notification interface
+ * fcoe_dev_setup() - Setup the link change notification interface
*/
static void fcoe_dev_setup(void)
{
@@ -1521,7 +1736,7 @@ static void fcoe_dev_setup(void)
}
/**
- * fcoe_dev_cleanup() - cleanup link change notification interface
+ * fcoe_dev_cleanup() - Cleanup the link change notification interface
*/
static void fcoe_dev_cleanup(void)
{
@@ -1529,19 +1744,19 @@ static void fcoe_dev_cleanup(void)
}
/**
- * fcoe_device_notification() - netdev event notification callback
- * @notifier: context of the notification
- * @event: type of event
- * @ptr: fixed array for output parsed ifname
+ * fcoe_device_notification() - Handler for net device events
+ * @notifier: The context of the notification
+ * @event: The type of event
+ * @ptr: The net device that the event was on
*
- * This function is called by the ethernet driver in case of link change event
+ * This function is called by the Ethernet driver in case of link change event.
*
* Returns: 0 for success
*/
static int fcoe_device_notification(struct notifier_block *notifier,
ulong event, void *ptr)
{
- struct fc_lport *lp = NULL;
+ struct fc_lport *lport = NULL;
struct net_device *netdev = ptr;
struct fcoe_interface *fcoe;
struct fcoe_port *port;
@@ -1552,11 +1767,11 @@ static int fcoe_device_notification(struct notifier_block *notifier,
list_for_each_entry(fcoe, &fcoe_hostlist, list) {
if (fcoe->netdev == netdev) {
- lp = fcoe->ctlr.lp;
+ lport = fcoe->ctlr.lp;
break;
}
}
- if (lp == NULL) {
+ if (!lport) {
rc = NOTIFY_DONE;
goto out;
}
@@ -1570,10 +1785,12 @@ static int fcoe_device_notification(struct notifier_block *notifier,
case NETDEV_CHANGE:
break;
case NETDEV_CHANGEMTU:
+ if (netdev->features & NETIF_F_FCOE_MTU)
+ break;
mfs = netdev->mtu - (sizeof(struct fcoe_hdr) +
sizeof(struct fcoe_crc_eof));
if (mfs >= FC_MIN_MAX_FRAME)
- fc_set_mfs(lp, mfs);
+ fc_set_mfs(lport, mfs);
break;
case NETDEV_REGISTER:
break;
@@ -1588,22 +1805,22 @@ static int fcoe_device_notification(struct notifier_block *notifier,
FCOE_NETDEV_DBG(netdev, "Unknown event %ld "
"from netdev netlink\n", event);
}
- if (link_possible && !fcoe_link_ok(lp))
+ if (link_possible && !fcoe_link_ok(lport))
fcoe_ctlr_link_up(&fcoe->ctlr);
else if (fcoe_ctlr_link_down(&fcoe->ctlr)) {
- stats = fc_lport_get_stats(lp);
+ stats = fc_lport_get_stats(lport);
stats->LinkFailureCount++;
- fcoe_clean_pending_queue(lp);
+ fcoe_clean_pending_queue(lport);
}
out:
return rc;
}
/**
- * fcoe_if_to_netdev() - parse a name buffer to get netdev
- * @buffer: incoming buffer to be copied
+ * fcoe_if_to_netdev() - Parse a name buffer to get a net device
+ * @buffer: The name of the net device
*
- * Returns: NULL or ptr to net_device
+ * Returns: NULL or a ptr to net_device
*/
static struct net_device *fcoe_if_to_netdev(const char *buffer)
{
@@ -1621,9 +1838,11 @@ static struct net_device *fcoe_if_to_netdev(const char *buffer)
}
/**
- * fcoe_destroy() - handles the destroy from sysfs
- * @buffer: expected to be an eth if name
- * @kp: associated kernel param
+ * fcoe_destroy() - Destroy a FCoE interface
+ * @buffer: The name of the Ethernet interface to be destroyed
+ * @kp: The associated kernel parameter
+ *
+ * Called from sysfs.
*
* Returns: 0 for success
*/
@@ -1631,7 +1850,7 @@ static int fcoe_destroy(const char *buffer, struct kernel_param *kp)
{
struct fcoe_interface *fcoe;
struct net_device *netdev;
- int rc;
+ int rc = 0;
mutex_lock(&fcoe_config_mutex);
#ifdef CONFIG_FCOE_MODULE
@@ -1670,6 +1889,10 @@ out_nodev:
return rc;
}
+/**
+ * fcoe_destroy_work() - Destroy a FCoE port in a deferred work context
+ * @work: Handle to the FCoE port to be destroyed
+ */
static void fcoe_destroy_work(struct work_struct *work)
{
struct fcoe_port *port;
@@ -1681,9 +1904,11 @@ static void fcoe_destroy_work(struct work_struct *work)
}
/**
- * fcoe_create() - Handles the create call from sysfs
- * @buffer: expected to be an eth if name
- * @kp: associated kernel param
+ * fcoe_create() - Create a fcoe interface
+ * @buffer: The name of the Ethernet interface to create on
+ * @kp: The associated kernel param
+ *
+ * Called from sysfs.
*
* Returns: 0 for success
*/
@@ -1726,7 +1951,7 @@ static int fcoe_create(const char *buffer, struct kernel_param *kp)
goto out_putdev;
}
- lport = fcoe_if_create(fcoe, &netdev->dev);
+ lport = fcoe_if_create(fcoe, &netdev->dev, 0);
if (IS_ERR(lport)) {
printk(KERN_ERR "fcoe: Failed to create interface (%s)\n",
netdev->name);
@@ -1762,16 +1987,9 @@ out_nodev:
return rc;
}
-module_param_call(create, fcoe_create, NULL, NULL, S_IWUSR);
-__MODULE_PARM_TYPE(create, "string");
-MODULE_PARM_DESC(create, "Create fcoe fcoe using net device passed in.");
-module_param_call(destroy, fcoe_destroy, NULL, NULL, S_IWUSR);
-__MODULE_PARM_TYPE(destroy, "string");
-MODULE_PARM_DESC(destroy, "Destroy fcoe fcoe");
-
/**
- * fcoe_link_ok() - Check if link is ok for the fc_lport
- * @lp: ptr to the fc_lport
+ * fcoe_link_ok() - Check if the link is OK for a local port
+ * @lport: The local port to check link on
*
* Any permanently-disqualifying conditions have been previously checked.
* This also updates the speed setting, which may change with link for 100/1000.
@@ -1783,26 +2001,26 @@ MODULE_PARM_DESC(destroy, "Destroy fcoe fcoe");
* Returns: 0 if link is OK for use by FCoE.
*
*/
-int fcoe_link_ok(struct fc_lport *lp)
+int fcoe_link_ok(struct fc_lport *lport)
{
- struct fcoe_port *port = lport_priv(lp);
- struct net_device *dev = port->fcoe->netdev;
+ struct fcoe_port *port = lport_priv(lport);
+ struct net_device *netdev = port->fcoe->netdev;
struct ethtool_cmd ecmd = { ETHTOOL_GSET };
- if ((dev->flags & IFF_UP) && netif_carrier_ok(dev) &&
- (!dev_ethtool_get_settings(dev, &ecmd))) {
- lp->link_supported_speeds &=
+ if ((netdev->flags & IFF_UP) && netif_carrier_ok(netdev) &&
+ (!dev_ethtool_get_settings(netdev, &ecmd))) {
+ lport->link_supported_speeds &=
~(FC_PORTSPEED_1GBIT | FC_PORTSPEED_10GBIT);
if (ecmd.supported & (SUPPORTED_1000baseT_Half |
SUPPORTED_1000baseT_Full))
- lp->link_supported_speeds |= FC_PORTSPEED_1GBIT;
+ lport->link_supported_speeds |= FC_PORTSPEED_1GBIT;
if (ecmd.supported & SUPPORTED_10000baseT_Full)
- lp->link_supported_speeds |=
+ lport->link_supported_speeds |=
FC_PORTSPEED_10GBIT;
if (ecmd.speed == SPEED_1000)
- lp->link_speed = FC_PORTSPEED_1GBIT;
+ lport->link_speed = FC_PORTSPEED_1GBIT;
if (ecmd.speed == SPEED_10000)
- lp->link_speed = FC_PORTSPEED_10GBIT;
+ lport->link_speed = FC_PORTSPEED_10GBIT;
return 0;
}
@@ -1810,8 +2028,8 @@ int fcoe_link_ok(struct fc_lport *lp)
}
/**
- * fcoe_percpu_clean() - Clear the pending skbs for an lport
- * @lp: the fc_lport
+ * fcoe_percpu_clean() - Clear all pending skbs for an local port
+ * @lport: The local port whose skbs are to be cleared
*
* Must be called with fcoe_create_mutex held to single-thread completion.
*
@@ -1820,7 +2038,7 @@ int fcoe_link_ok(struct fc_lport *lp)
* there no packets that will be handled by the lport, but also that any
* threads already handling packet have returned.
*/
-void fcoe_percpu_clean(struct fc_lport *lp)
+void fcoe_percpu_clean(struct fc_lport *lport)
{
struct fcoe_percpu_s *pp;
struct fcoe_rcv_info *fr;
@@ -1838,7 +2056,7 @@ void fcoe_percpu_clean(struct fc_lport *lp)
skb = next) {
next = skb->next;
fr = fcoe_dev_from_skb(skb);
- if (fr->fr_dev == lp) {
+ if (fr->fr_dev == lport) {
__skb_unlink(skb, list);
kfree_skb(skb);
}
@@ -1867,13 +2085,11 @@ void fcoe_percpu_clean(struct fc_lport *lp)
/**
* fcoe_clean_pending_queue() - Dequeue a skb and free it
- * @lp: the corresponding fc_lport
- *
- * Returns: none
+ * @lport: The local port to dequeue a skb on
*/
-void fcoe_clean_pending_queue(struct fc_lport *lp)
+void fcoe_clean_pending_queue(struct fc_lport *lport)
{
- struct fcoe_port *port = lport_priv(lp);
+ struct fcoe_port *port = lport_priv(lport);
struct sk_buff *skb;
spin_lock_bh(&port->fcoe_pending_queue.lock);
@@ -1886,10 +2102,10 @@ void fcoe_clean_pending_queue(struct fc_lport *lp)
}
/**
- * fcoe_reset() - Resets the fcoe
- * @shost: shost the reset is from
+ * fcoe_reset() - Reset a local port
+ * @shost: The SCSI host associated with the local port to be reset
*
- * Returns: always 0
+ * Returns: Always 0 (return value required by FC transport template)
*/
int fcoe_reset(struct Scsi_Host *shost)
{
@@ -1899,30 +2115,33 @@ int fcoe_reset(struct Scsi_Host *shost)
}
/**
- * fcoe_hostlist_lookup_port() - find the corresponding lport by a given device
- * @dev: this is currently ptr to net_device
+ * fcoe_hostlist_lookup_port() - Find the FCoE interface associated with a net device
+ * @netdev: The net device used as a key
+ *
+ * Locking: Must be called with the RNL mutex held.
*
- * Returns: NULL or the located fcoe_port
- * Locking: must be called with the RNL mutex held
+ * Returns: NULL or the FCoE interface
*/
static struct fcoe_interface *
-fcoe_hostlist_lookup_port(const struct net_device *dev)
+fcoe_hostlist_lookup_port(const struct net_device *netdev)
{
struct fcoe_interface *fcoe;
list_for_each_entry(fcoe, &fcoe_hostlist, list) {
- if (fcoe->netdev == dev)
+ if (fcoe->netdev == netdev)
return fcoe;
}
return NULL;
}
/**
- * fcoe_hostlist_lookup() - Find the corresponding lport by netdev
- * @netdev: ptr to net_device
+ * fcoe_hostlist_lookup() - Find the local port associated with a
+ * given net device
+ * @netdev: The netdevice used as a key
*
- * Returns: 0 for success
- * Locking: must be called with the RTNL mutex held
+ * Locking: Must be called with the RTNL mutex held
+ *
+ * Returns: NULL or the local port
*/
static struct fc_lport *fcoe_hostlist_lookup(const struct net_device *netdev)
{
@@ -1933,11 +2152,13 @@ static struct fc_lport *fcoe_hostlist_lookup(const struct net_device *netdev)
}
/**
- * fcoe_hostlist_add() - Add a lport to lports list
- * @lp: ptr to the fc_lport to be added
+ * fcoe_hostlist_add() - Add the FCoE interface identified by a local
+ * port to the hostlist
+ * @lport: The local port that identifies the FCoE interface to be added
*
- * Returns: 0 for success
* Locking: must be called with the RTNL mutex held
+ *
+ * Returns: 0 for success
*/
static int fcoe_hostlist_add(const struct fc_lport *lport)
{
@@ -1954,15 +2175,15 @@ static int fcoe_hostlist_add(const struct fc_lport *lport)
}
/**
- * fcoe_init() - fcoe module loading initialization
+ * fcoe_init() - Initialize fcoe.ko
*
- * Returns 0 on success, negative on failure
+ * Returns: 0 on success, or a negative value on failure
*/
static int __init fcoe_init(void)
{
+ struct fcoe_percpu_s *p;
unsigned int cpu;
int rc = 0;
- struct fcoe_percpu_s *p;
mutex_lock(&fcoe_config_mutex);
@@ -1999,15 +2220,15 @@ out_free:
module_init(fcoe_init);
/**
- * fcoe_exit() - fcoe module unloading cleanup
+ * fcoe_exit() - Clean up fcoe.ko
*
- * Returns 0 on success, negative on failure
+ * Returns: 0 on success or a negative value on failure
*/
static void __exit fcoe_exit(void)
{
- unsigned int cpu;
struct fcoe_interface *fcoe, *tmp;
struct fcoe_port *port;
+ unsigned int cpu;
mutex_lock(&fcoe_config_mutex);
@@ -2033,9 +2254,238 @@ static void __exit fcoe_exit(void)
/* flush any asyncronous interface destroys,
* this should happen after the netdev notifier is unregistered */
flush_scheduled_work();
+ /* That will flush out all the N_Ports on the hostlist, but now we
+ * may have NPIV VN_Ports scheduled for destruction */
+ flush_scheduled_work();
/* detach from scsi transport
* must happen after all destroys are done, therefor after the flush */
fcoe_if_exit();
}
module_exit(fcoe_exit);
+
+/**
+ * fcoe_flogi_resp() - FCoE specific FLOGI and FDISC response handler
+ * @seq: active sequence in the FLOGI or FDISC exchange
+ * @fp: response frame, or error encoded in a pointer (timeout)
+ * @arg: pointer the the fcoe_ctlr structure
+ *
+ * This handles MAC address managment for FCoE, then passes control on to
+ * the libfc FLOGI response handler.
+ */
+static void fcoe_flogi_resp(struct fc_seq *seq, struct fc_frame *fp, void *arg)
+{
+ struct fcoe_ctlr *fip = arg;
+ struct fc_exch *exch = fc_seq_exch(seq);
+ struct fc_lport *lport = exch->lp;
+ u8 *mac;
+
+ if (IS_ERR(fp))
+ goto done;
+
+ mac = fr_cb(fp)->granted_mac;
+ if (is_zero_ether_addr(mac)) {
+ /* pre-FIP */
+ if (fcoe_ctlr_recv_flogi(fip, lport, fp)) {
+ fc_frame_free(fp);
+ return;
+ }
+ }
+ fcoe_update_src_mac(lport, mac);
+done:
+ fc_lport_flogi_resp(seq, fp, lport);
+}
+
+/**
+ * fcoe_logo_resp() - FCoE specific LOGO response handler
+ * @seq: active sequence in the LOGO exchange
+ * @fp: response frame, or error encoded in a pointer (timeout)
+ * @arg: pointer the the fcoe_ctlr structure
+ *
+ * This handles MAC address managment for FCoE, then passes control on to
+ * the libfc LOGO response handler.
+ */
+static void fcoe_logo_resp(struct fc_seq *seq, struct fc_frame *fp, void *arg)
+{
+ struct fc_lport *lport = arg;
+ static u8 zero_mac[ETH_ALEN] = { 0 };
+
+ if (!IS_ERR(fp))
+ fcoe_update_src_mac(lport, zero_mac);
+ fc_lport_logo_resp(seq, fp, lport);
+}
+
+/**
+ * fcoe_elsct_send - FCoE specific ELS handler
+ *
+ * This does special case handling of FIP encapsualted ELS exchanges for FCoE,
+ * using FCoE specific response handlers and passing the FIP controller as
+ * the argument (the lport is still available from the exchange).
+ *
+ * Most of the work here is just handed off to the libfc routine.
+ */
+static struct fc_seq *fcoe_elsct_send(struct fc_lport *lport, u32 did,
+ struct fc_frame *fp, unsigned int op,
+ void (*resp)(struct fc_seq *,
+ struct fc_frame *,
+ void *),
+ void *arg, u32 timeout)
+{
+ struct fcoe_port *port = lport_priv(lport);
+ struct fcoe_interface *fcoe = port->fcoe;
+ struct fcoe_ctlr *fip = &fcoe->ctlr;
+ struct fc_frame_header *fh = fc_frame_header_get(fp);
+
+ switch (op) {
+ case ELS_FLOGI:
+ case ELS_FDISC:
+ return fc_elsct_send(lport, did, fp, op, fcoe_flogi_resp,
+ fip, timeout);
+ case ELS_LOGO:
+ /* only hook onto fabric logouts, not port logouts */
+ if (ntoh24(fh->fh_d_id) != FC_FID_FLOGI)
+ break;
+ return fc_elsct_send(lport, did, fp, op, fcoe_logo_resp,
+ lport, timeout);
+ }
+ return fc_elsct_send(lport, did, fp, op, resp, arg, timeout);
+}
+
+/**
+ * fcoe_vport_create() - create an fc_host/scsi_host for a vport
+ * @vport: fc_vport object to create a new fc_host for
+ * @disabled: start the new fc_host in a disabled state by default?
+ *
+ * Returns: 0 for success
+ */
+static int fcoe_vport_create(struct fc_vport *vport, bool disabled)
+{
+ struct Scsi_Host *shost = vport_to_shost(vport);
+ struct fc_lport *n_port = shost_priv(shost);
+ struct fcoe_port *port = lport_priv(n_port);
+ struct fcoe_interface *fcoe = port->fcoe;
+ struct net_device *netdev = fcoe->netdev;
+ struct fc_lport *vn_port;
+
+ mutex_lock(&fcoe_config_mutex);
+ vn_port = fcoe_if_create(fcoe, &vport->dev, 1);
+ mutex_unlock(&fcoe_config_mutex);
+
+ if (IS_ERR(vn_port)) {
+ printk(KERN_ERR "fcoe: fcoe_vport_create(%s) failed\n",
+ netdev->name);
+ return -EIO;
+ }
+
+ if (disabled) {
+ fc_vport_set_state(vport, FC_VPORT_DISABLED);
+ } else {
+ vn_port->boot_time = jiffies;
+ fc_fabric_login(vn_port);
+ fc_vport_setlink(vn_port);
+ }
+ return 0;
+}
+
+/**
+ * fcoe_vport_destroy() - destroy the fc_host/scsi_host for a vport
+ * @vport: fc_vport object that is being destroyed
+ *
+ * Returns: 0 for success
+ */
+static int fcoe_vport_destroy(struct fc_vport *vport)
+{
+ struct Scsi_Host *shost = vport_to_shost(vport);
+ struct fc_lport *n_port = shost_priv(shost);
+ struct fc_lport *vn_port = vport->dd_data;
+ struct fcoe_port *port = lport_priv(vn_port);
+
+ mutex_lock(&n_port->lp_mutex);
+ list_del(&vn_port->list);
+ mutex_unlock(&n_port->lp_mutex);
+ schedule_work(&port->destroy_work);
+ return 0;
+}
+
+/**
+ * fcoe_vport_disable() - change vport state
+ * @vport: vport to bring online/offline
+ * @disable: should the vport be disabled?
+ */
+static int fcoe_vport_disable(struct fc_vport *vport, bool disable)
+{
+ struct fc_lport *lport = vport->dd_data;
+
+ if (disable) {
+ fc_vport_set_state(vport, FC_VPORT_DISABLED);
+ fc_fabric_logoff(lport);
+ } else {
+ lport->boot_time = jiffies;
+ fc_fabric_login(lport);
+ fc_vport_setlink(lport);
+ }
+
+ return 0;
+}
+
+/**
+ * fcoe_vport_set_symbolic_name() - append vport string to symbolic name
+ * @vport: fc_vport with a new symbolic name string
+ *
+ * After generating a new symbolic name string, a new RSPN_ID request is
+ * sent to the name server. There is no response handler, so if it fails
+ * for some reason it will not be retried.
+ */
+static void fcoe_set_vport_symbolic_name(struct fc_vport *vport)
+{
+ struct fc_lport *lport = vport->dd_data;
+ struct fc_frame *fp;
+ size_t len;
+
+ snprintf(fc_host_symbolic_name(lport->host), FC_SYMBOLIC_NAME_SIZE,
+ "%s v%s over %s : %s", FCOE_NAME, FCOE_VERSION,
+ fcoe_netdev(lport)->name, vport->symbolic_name);
+
+ if (lport->state != LPORT_ST_READY)
+ return;
+
+ len = strnlen(fc_host_symbolic_name(lport->host), 255);
+ fp = fc_frame_alloc(lport,
+ sizeof(struct fc_ct_hdr) +
+ sizeof(struct fc_ns_rspn) + len);
+ if (!fp)
+ return;
+ lport->tt.elsct_send(lport, FC_FID_DIR_SERV, fp, FC_NS_RSPN_ID,
+ NULL, NULL, 3 * lport->r_a_tov);
+}
+
+/**
+ * fcoe_get_lesb() - Fill the FCoE Link Error Status Block
+ * @lport: the local port
+ * @fc_lesb: the link error status block
+ */
+static void fcoe_get_lesb(struct fc_lport *lport,
+ struct fc_els_lesb *fc_lesb)
+{
+ unsigned int cpu;
+ u32 lfc, vlfc, mdac;
+ struct fcoe_dev_stats *devst;
+ struct fcoe_fc_els_lesb *lesb;
+ struct net_device *netdev = fcoe_netdev(lport);
+
+ lfc = 0;
+ vlfc = 0;
+ mdac = 0;
+ lesb = (struct fcoe_fc_els_lesb *)fc_lesb;
+ memset(lesb, 0, sizeof(*lesb));
+ for_each_possible_cpu(cpu) {
+ devst = per_cpu_ptr(lport->dev_stats, cpu);
+ lfc += devst->LinkFailureCount;
+ vlfc += devst->VLinkFailureCount;
+ mdac += devst->MissDiscAdvCount;
+ }
+ lesb->lesb_link_fail = htonl(lfc);
+ lesb->lesb_vlink_fail = htonl(vlfc);
+ lesb->lesb_miss_fka = htonl(mdac);
+ lesb->lesb_fcs_error = htonl(dev_get_stats(netdev)->rx_crc_errors);
+}
diff --git a/drivers/scsi/fcoe/fcoe.h b/drivers/scsi/fcoe/fcoe.h
index ce7f60fb1bc..c69b2c56c2d 100644
--- a/drivers/scsi/fcoe/fcoe.h
+++ b/drivers/scsi/fcoe/fcoe.h
@@ -32,7 +32,7 @@
#define FCOE_NAME "fcoe"
#define FCOE_VENDOR "Open-FCoE.org"
-#define FCOE_MAX_LUN 255
+#define FCOE_MAX_LUN 0xFFFF
#define FCOE_MAX_FCP_TARGET 256
#define FCOE_MAX_OUTSTANDING_COMMANDS 1024
@@ -40,11 +40,17 @@
#define FCOE_MIN_XID 0x0000 /* the min xid supported by fcoe_sw */
#define FCOE_MAX_XID 0x0FFF /* the max xid supported by fcoe_sw */
+/*
+ * Max MTU for FCoE: 14 (FCoE header) + 24 (FC header) + 2112 (max FC payload)
+ * + 4 (FC CRC) + 4 (FCoE trailer) = 2158 bytes
+ */
+#define FCOE_MTU 2158
+
unsigned int fcoe_debug_logging;
module_param_named(debug_logging, fcoe_debug_logging, int, S_IRUGO|S_IWUSR);
MODULE_PARM_DESC(debug_logging, "a bit mask of logging levels");
-#define FCOE_LOGGING 0x01 /* General logging, not categorized */
+#define FCOE_LOGGING 0x01 /* General logging, not categorized */
#define FCOE_NETDEV_LOGGING 0x02 /* Netdevice logging */
#define FCOE_CHECK_LOGGING(LEVEL, CMD) \
@@ -64,8 +70,13 @@ do { \
printk(KERN_INFO "fcoe: %s: " fmt, \
netdev->name, ##args);)
-/*
- * this percpu struct for fcoe
+/**
+ * struct fcoe_percpu_s - The per-CPU context for FCoE receive threads
+ * @thread: The thread context
+ * @fcoe_rx_list: The queue of pending packets to process
+ * @page: The memory page for calculating frame trailer CRCs
+ * @crc_eof_offset: The offset into the CRC page pointing to available
+ * memory for a new trailer
*/
struct fcoe_percpu_s {
struct task_struct *thread;
@@ -74,37 +85,62 @@ struct fcoe_percpu_s {
int crc_eof_offset;
};
-/*
- * an FCoE interface, 1:1 with netdev
+/**
+ * struct fcoe_interface - A FCoE interface
+ * @list: Handle for a list of FCoE interfaces
+ * @netdev: The associated net device
+ * @fcoe_packet_type: FCoE packet type
+ * @fip_packet_type: FIP packet type
+ * @ctlr: The FCoE controller (for FIP)
+ * @oem: The offload exchange manager for all local port
+ * instances associated with this port
+ * @kref: The kernel reference
+ *
+ * This structure is 1:1 with a net devive.
*/
struct fcoe_interface {
- struct list_head list;
- struct net_device *netdev;
- struct packet_type fcoe_packet_type;
- struct packet_type fip_packet_type;
- struct fcoe_ctlr ctlr;
- struct fc_exch_mgr *oem; /* offload exchange manager */
- struct kref kref;
+ struct list_head list;
+ struct net_device *netdev;
+ struct packet_type fcoe_packet_type;
+ struct packet_type fip_packet_type;
+ struct fcoe_ctlr ctlr;
+ struct fc_exch_mgr *oem;
+ struct kref kref;
};
-/*
- * the FCoE private structure that's allocated along with the
- * Scsi_Host and libfc fc_lport structures
+/**
+ * struct fcoe_port - The FCoE private structure
+ * @fcoe: The associated fcoe interface
+ * @lport: The associated local port
+ * @fcoe_pending_queue: The pending Rx queue of skbs
+ * @fcoe_pending_queue_active: Indicates if the pending queue is active
+ * @timer: The queue timer
+ * @destroy_work: Handle for work context
+ * (to prevent RTNL deadlocks)
+ * @data_srt_addr: Source address for data
+ *
+ * An instance of this structure is to be allocated along with the
+ * Scsi_Host and libfc fc_lport structures.
*/
struct fcoe_port {
struct fcoe_interface *fcoe;
- struct fc_lport *lport;
- struct sk_buff_head fcoe_pending_queue;
- u8 fcoe_pending_queue_active;
- struct timer_list timer; /* queue timer */
- struct work_struct destroy_work; /* to prevent rtnl deadlocks */
+ struct fc_lport *lport;
+ struct sk_buff_head fcoe_pending_queue;
+ u8 fcoe_pending_queue_active;
+ struct timer_list timer;
+ struct work_struct destroy_work;
+ u8 data_src_addr[ETH_ALEN];
};
#define fcoe_from_ctlr(fip) container_of(fip, struct fcoe_interface, ctlr)
-static inline struct net_device *fcoe_netdev(const struct fc_lport *lp)
+/**
+ * fcoe_netdev() - Return the net device associated with a local port
+ * @lport: The local port to get the net device from
+ */
+static inline struct net_device *fcoe_netdev(const struct fc_lport *lport)
{
- return ((struct fcoe_port *)lport_priv(lp))->fcoe->netdev;
+ return ((struct fcoe_port *)lport_priv(lport))->fcoe->netdev;
}
#endif /* _FCOE_H_ */
diff --git a/drivers/scsi/fcoe/libfcoe.c b/drivers/scsi/fcoe/libfcoe.c
index 11ae5c94608..9823291395a 100644
--- a/drivers/scsi/fcoe/libfcoe.c
+++ b/drivers/scsi/fcoe/libfcoe.c
@@ -59,26 +59,30 @@ unsigned int libfcoe_debug_logging;
module_param_named(debug_logging, libfcoe_debug_logging, int, S_IRUGO|S_IWUSR);
MODULE_PARM_DESC(debug_logging, "a bit mask of logging levels");
-#define LIBFCOE_LOGGING 0x01 /* General logging, not categorized */
+#define LIBFCOE_LOGGING 0x01 /* General logging, not categorized */
#define LIBFCOE_FIP_LOGGING 0x02 /* FIP logging */
-#define LIBFCOE_CHECK_LOGGING(LEVEL, CMD) \
-do { \
- if (unlikely(libfcoe_debug_logging & LEVEL)) \
- do { \
- CMD; \
- } while (0); \
+#define LIBFCOE_CHECK_LOGGING(LEVEL, CMD) \
+do { \
+ if (unlikely(libfcoe_debug_logging & LEVEL)) \
+ do { \
+ CMD; \
+ } while (0); \
} while (0)
#define LIBFCOE_DBG(fmt, args...) \
LIBFCOE_CHECK_LOGGING(LIBFCOE_LOGGING, \
printk(KERN_INFO "libfcoe: " fmt, ##args);)
-#define LIBFCOE_FIP_DBG(fmt, args...) \
+#define LIBFCOE_FIP_DBG(fip, fmt, args...) \
LIBFCOE_CHECK_LOGGING(LIBFCOE_FIP_LOGGING, \
- printk(KERN_INFO "fip: " fmt, ##args);)
+ printk(KERN_INFO "host%d: fip: " fmt, \
+ (fip)->lp->host->host_no, ##args);)
-/*
+/**
+ * fcoe_ctlr_mtu_valid() - Check if a FCF's MTU is valid
+ * @fcf: The FCF to check
+ *
* Return non-zero if FCF fcoe_size has been validated.
*/
static inline int fcoe_ctlr_mtu_valid(const struct fcoe_fcf *fcf)
@@ -86,7 +90,10 @@ static inline int fcoe_ctlr_mtu_valid(const struct fcoe_fcf *fcf)
return (fcf->flags & FIP_FL_SOL) != 0;
}
-/*
+/**
+ * fcoe_ctlr_fcf_usable() - Check if a FCF is usable
+ * @fcf: The FCF to check
+ *
* Return non-zero if the FCF is usable.
*/
static inline int fcoe_ctlr_fcf_usable(struct fcoe_fcf *fcf)
@@ -97,12 +104,13 @@ static inline int fcoe_ctlr_fcf_usable(struct fcoe_fcf *fcf)
}
/**
- * fcoe_ctlr_init() - Initialize the FCoE Controller instance.
- * @fip: FCoE controller.
+ * fcoe_ctlr_init() - Initialize the FCoE Controller instance
+ * @fip: The FCoE controller to initialize
*/
void fcoe_ctlr_init(struct fcoe_ctlr *fip)
{
fip->state = FIP_ST_LINK_WAIT;
+ fip->mode = FIP_ST_AUTO;
INIT_LIST_HEAD(&fip->fcfs);
spin_lock_init(&fip->lock);
fip->flogi_oxid = FC_XID_UNKNOWN;
@@ -114,8 +122,8 @@ void fcoe_ctlr_init(struct fcoe_ctlr *fip)
EXPORT_SYMBOL(fcoe_ctlr_init);
/**
- * fcoe_ctlr_reset_fcfs() - Reset and free all FCFs for a controller.
- * @fip: FCoE controller.
+ * fcoe_ctlr_reset_fcfs() - Reset and free all FCFs for a controller
+ * @fip: The FCoE controller whose FCFs are to be reset
*
* Called with &fcoe_ctlr lock held.
*/
@@ -134,8 +142,8 @@ static void fcoe_ctlr_reset_fcfs(struct fcoe_ctlr *fip)
}
/**
- * fcoe_ctlr_destroy() - Disable and tear-down the FCoE controller.
- * @fip: FCoE controller.
+ * fcoe_ctlr_destroy() - Disable and tear down a FCoE controller
+ * @fip: The FCoE controller to tear down
*
* This is called by FCoE drivers before freeing the &fcoe_ctlr.
*
@@ -148,9 +156,7 @@ static void fcoe_ctlr_reset_fcfs(struct fcoe_ctlr *fip)
void fcoe_ctlr_destroy(struct fcoe_ctlr *fip)
{
cancel_work_sync(&fip->recv_work);
- spin_lock_bh(&fip->fip_recv_list.lock);
- __skb_queue_purge(&fip->fip_recv_list);
- spin_unlock_bh(&fip->fip_recv_list.lock);
+ skb_queue_purge(&fip->fip_recv_list);
spin_lock_bh(&fip->lock);
fip->state = FIP_ST_DISABLED;
@@ -162,8 +168,8 @@ void fcoe_ctlr_destroy(struct fcoe_ctlr *fip)
EXPORT_SYMBOL(fcoe_ctlr_destroy);
/**
- * fcoe_ctlr_fcoe_size() - Return the maximum FCoE size required for VN_Port.
- * @fip: FCoE controller.
+ * fcoe_ctlr_fcoe_size() - Return the maximum FCoE size required for VN_Port
+ * @fip: The FCoE controller to get the maximum FCoE size from
*
* Returns the maximum packet size including the FCoE header and trailer,
* but not including any Ethernet or VLAN headers.
@@ -180,9 +186,9 @@ static inline u32 fcoe_ctlr_fcoe_size(struct fcoe_ctlr *fip)
}
/**
- * fcoe_ctlr_solicit() - Send a solicitation.
- * @fip: FCoE controller.
- * @fcf: Destination FCF. If NULL, a multicast solicitation is sent.
+ * fcoe_ctlr_solicit() - Send a FIP solicitation
+ * @fip: The FCoE controller to send the solicitation on
+ * @fcf: The destination FCF (if NULL, a multicast solicitation is sent)
*/
static void fcoe_ctlr_solicit(struct fcoe_ctlr *fip, struct fcoe_fcf *fcf)
{
@@ -241,8 +247,8 @@ static void fcoe_ctlr_solicit(struct fcoe_ctlr *fip, struct fcoe_fcf *fcf)
}
/**
- * fcoe_ctlr_link_up() - Start FCoE controller.
- * @fip: FCoE controller.
+ * fcoe_ctlr_link_up() - Start FCoE controller
+ * @fip: The FCoE controller to start
*
* Called from the LLD when the network link is ready.
*/
@@ -255,11 +261,12 @@ void fcoe_ctlr_link_up(struct fcoe_ctlr *fip)
spin_unlock_bh(&fip->lock);
fc_linkup(fip->lp);
} else if (fip->state == FIP_ST_LINK_WAIT) {
- fip->state = FIP_ST_AUTO;
+ fip->state = fip->mode;
fip->last_link = 1;
fip->link = 1;
spin_unlock_bh(&fip->lock);
- LIBFCOE_FIP_DBG("%s", "setting AUTO mode.\n");
+ if (fip->state == FIP_ST_AUTO)
+ LIBFCOE_FIP_DBG(fip, "%s", "setting AUTO mode.\n");
fc_linkup(fip->lp);
fcoe_ctlr_solicit(fip, NULL);
} else
@@ -268,45 +275,23 @@ void fcoe_ctlr_link_up(struct fcoe_ctlr *fip)
EXPORT_SYMBOL(fcoe_ctlr_link_up);
/**
- * fcoe_ctlr_reset() - Reset FIP.
- * @fip: FCoE controller.
- * @new_state: FIP state to be entered.
- *
- * Returns non-zero if the link was up and now isn't.
+ * fcoe_ctlr_reset() - Reset a FCoE controller
+ * @fip: The FCoE controller to reset
*/
-static int fcoe_ctlr_reset(struct fcoe_ctlr *fip, enum fip_state new_state)
+static void fcoe_ctlr_reset(struct fcoe_ctlr *fip)
{
- struct fc_lport *lp = fip->lp;
- int link_dropped;
-
- spin_lock_bh(&fip->lock);
fcoe_ctlr_reset_fcfs(fip);
del_timer(&fip->timer);
- fip->state = new_state;
fip->ctlr_ka_time = 0;
fip->port_ka_time = 0;
fip->sol_time = 0;
fip->flogi_oxid = FC_XID_UNKNOWN;
fip->map_dest = 0;
- fip->last_link = 0;
- link_dropped = fip->link;
- fip->link = 0;
- spin_unlock_bh(&fip->lock);
-
- if (link_dropped)
- fc_linkdown(lp);
-
- if (new_state == FIP_ST_ENABLED) {
- fcoe_ctlr_solicit(fip, NULL);
- fc_linkup(lp);
- link_dropped = 0;
- }
- return link_dropped;
}
/**
- * fcoe_ctlr_link_down() - Stop FCoE controller.
- * @fip: FCoE controller.
+ * fcoe_ctlr_link_down() - Stop a FCoE controller
+ * @fip: The FCoE controller to be stopped
*
* Returns non-zero if the link was up and now isn't.
*
@@ -315,15 +300,29 @@ static int fcoe_ctlr_reset(struct fcoe_ctlr *fip, enum fip_state new_state)
*/
int fcoe_ctlr_link_down(struct fcoe_ctlr *fip)
{
- return fcoe_ctlr_reset(fip, FIP_ST_LINK_WAIT);
+ int link_dropped;
+
+ LIBFCOE_FIP_DBG(fip, "link down.\n");
+ spin_lock_bh(&fip->lock);
+ fcoe_ctlr_reset(fip);
+ link_dropped = fip->link;
+ fip->link = 0;
+ fip->last_link = 0;
+ fip->state = FIP_ST_LINK_WAIT;
+ spin_unlock_bh(&fip->lock);
+
+ if (link_dropped)
+ fc_linkdown(fip->lp);
+ return link_dropped;
}
EXPORT_SYMBOL(fcoe_ctlr_link_down);
/**
- * fcoe_ctlr_send_keep_alive() - Send a keep-alive to the selected FCF.
- * @fip: FCoE controller.
- * @ports: 0 for controller keep-alive, 1 for port keep-alive.
- * @sa: source MAC address.
+ * fcoe_ctlr_send_keep_alive() - Send a keep-alive to the selected FCF
+ * @fip: The FCoE controller to send the FKA on
+ * @lport: libfc fc_lport to send from
+ * @ports: 0 for controller keep-alive, 1 for port keep-alive
+ * @sa: The source MAC address
*
* A controller keep-alive is sent every fka_period (typically 8 seconds).
* The source MAC is the native MAC address.
@@ -332,7 +331,9 @@ EXPORT_SYMBOL(fcoe_ctlr_link_down);
* The source MAC is the assigned mapped source address.
* The destination is the FCF's F-port.
*/
-static void fcoe_ctlr_send_keep_alive(struct fcoe_ctlr *fip, int ports, u8 *sa)
+static void fcoe_ctlr_send_keep_alive(struct fcoe_ctlr *fip,
+ struct fc_lport *lport,
+ int ports, u8 *sa)
{
struct sk_buff *skb;
struct fip_kal {
@@ -350,8 +351,7 @@ static void fcoe_ctlr_send_keep_alive(struct fcoe_ctlr *fip, int ports, u8 *sa)
if (!fcf || !fc_host_port_id(lp->host))
return;
- len = fcoe_ctlr_fcoe_size(fip) + sizeof(struct ethhdr);
- BUG_ON(len < sizeof(*kal) + sizeof(*vn));
+ len = sizeof(*kal) + ports * sizeof(*vn);
skb = dev_alloc_skb(len);
if (!skb)
return;
@@ -366,7 +366,7 @@ static void fcoe_ctlr_send_keep_alive(struct fcoe_ctlr *fip, int ports, u8 *sa)
kal->fip.fip_op = htons(FIP_OP_CTRL);
kal->fip.fip_subcode = FIP_SC_KEEP_ALIVE;
kal->fip.fip_dl_len = htons((sizeof(kal->mac) +
- ports * sizeof(*vn)) / FIP_BPW);
+ ports * sizeof(*vn)) / FIP_BPW);
kal->fip.fip_flags = htons(FIP_FL_FPMA);
if (fip->spma)
kal->fip.fip_flags |= htons(FIP_FL_SPMA);
@@ -374,16 +374,14 @@ static void fcoe_ctlr_send_keep_alive(struct fcoe_ctlr *fip, int ports, u8 *sa)
kal->mac.fd_desc.fip_dtype = FIP_DT_MAC;
kal->mac.fd_desc.fip_dlen = sizeof(kal->mac) / FIP_BPW;
memcpy(kal->mac.fd_mac, fip->ctl_src_addr, ETH_ALEN);
-
if (ports) {
vn = (struct fip_vn_desc *)(kal + 1);
vn->fd_desc.fip_dtype = FIP_DT_VN_ID;
vn->fd_desc.fip_dlen = sizeof(*vn) / FIP_BPW;
- memcpy(vn->fd_mac, fip->data_src_addr, ETH_ALEN);
+ memcpy(vn->fd_mac, fip->get_src_addr(lport), ETH_ALEN);
hton24(vn->fd_fc_id, fc_host_port_id(lp->host));
put_unaligned_be64(lp->wwpn, &vn->fd_wwpn);
}
-
skb_put(skb, len);
skb->protocol = htons(ETH_P_FIP);
skb_reset_mac_header(skb);
@@ -392,10 +390,10 @@ static void fcoe_ctlr_send_keep_alive(struct fcoe_ctlr *fip, int ports, u8 *sa)
}
/**
- * fcoe_ctlr_encaps() - Encapsulate an ELS frame for FIP, without sending it.
- * @fip: FCoE controller.
- * @dtype: FIP descriptor type for the frame.
- * @skb: FCoE ELS frame including FC header but no FCoE headers.
+ * fcoe_ctlr_encaps() - Encapsulate an ELS frame for FIP, without sending it
+ * @fip: The FCoE controller for the ELS frame
+ * @dtype: The FIP descriptor type for the frame
+ * @skb: The FCoE ELS frame including FC header but no FCoE headers
*
* Returns non-zero error code on failure.
*
@@ -405,7 +403,7 @@ static void fcoe_ctlr_send_keep_alive(struct fcoe_ctlr *fip, int ports, u8 *sa)
* Headroom includes the FIP encapsulation description, FIP header, and
* Ethernet header. The tailroom is for the FIP MAC descriptor.
*/
-static int fcoe_ctlr_encaps(struct fcoe_ctlr *fip,
+static int fcoe_ctlr_encaps(struct fcoe_ctlr *fip, struct fc_lport *lport,
u8 dtype, struct sk_buff *skb)
{
struct fip_encaps_head {
@@ -449,8 +447,8 @@ static int fcoe_ctlr_encaps(struct fcoe_ctlr *fip,
memset(mac, 0, sizeof(mac));
mac->fd_desc.fip_dtype = FIP_DT_MAC;
mac->fd_desc.fip_dlen = sizeof(*mac) / FIP_BPW;
- if (dtype != FIP_DT_FLOGI)
- memcpy(mac->fd_mac, fip->data_src_addr, ETH_ALEN);
+ if (dtype != FIP_DT_FLOGI && dtype != FIP_DT_FDISC)
+ memcpy(mac->fd_mac, fip->get_src_addr(lport), ETH_ALEN);
else if (fip->spma)
memcpy(mac->fd_mac, fip->ctl_src_addr, ETH_ALEN);
@@ -463,6 +461,7 @@ static int fcoe_ctlr_encaps(struct fcoe_ctlr *fip,
/**
* fcoe_ctlr_els_send() - Send an ELS frame encapsulated by FIP if appropriate.
* @fip: FCoE controller.
+ * @lport: libfc fc_lport to send from
* @skb: FCoE ELS frame including FC header but no FCoE headers.
*
* Returns a non-zero error code if the frame should not be sent.
@@ -471,11 +470,13 @@ static int fcoe_ctlr_encaps(struct fcoe_ctlr *fip,
* The caller must check that the length is a multiple of 4.
* The SKB must have enough headroom (28 bytes) and tailroom (8 bytes).
*/
-int fcoe_ctlr_els_send(struct fcoe_ctlr *fip, struct sk_buff *skb)
+int fcoe_ctlr_els_send(struct fcoe_ctlr *fip, struct fc_lport *lport,
+ struct sk_buff *skb)
{
struct fc_frame_header *fh;
u16 old_xid;
u8 op;
+ u8 mac[ETH_ALEN];
fh = (struct fc_frame_header *)skb->data;
op = *(u8 *)(fh + 1);
@@ -498,6 +499,8 @@ int fcoe_ctlr_els_send(struct fcoe_ctlr *fip, struct sk_buff *skb)
if (fip->state == FIP_ST_NON_FIP)
return 0;
+ if (!fip->sel_fcf)
+ goto drop;
switch (op) {
case ELS_FLOGI:
@@ -530,14 +533,15 @@ int fcoe_ctlr_els_send(struct fcoe_ctlr *fip, struct sk_buff *skb)
* FLOGI.
*/
fip->flogi_oxid = FC_XID_UNKNOWN;
- fc_fcoe_set_mac(fip->data_src_addr, fh->fh_s_id);
+ fc_fcoe_set_mac(mac, fh->fh_d_id);
+ fip->update_mac(lport, mac);
return 0;
default:
if (fip->state != FIP_ST_ENABLED)
goto drop;
return 0;
}
- if (fcoe_ctlr_encaps(fip, op, skb))
+ if (fcoe_ctlr_encaps(fip, lport, op, skb))
goto drop;
fip->send(fip, skb);
return -EINPROGRESS;
@@ -547,9 +551,9 @@ drop:
}
EXPORT_SYMBOL(fcoe_ctlr_els_send);
-/*
- * fcoe_ctlr_age_fcfs() - Reset and free all old FCFs for a controller.
- * @fip: FCoE controller.
+/**
+ * fcoe_ctlr_age_fcfs() - Reset and free all old FCFs for a controller
+ * @fip: The FCoE controller to free FCFs on
*
* Called with lock held.
*
@@ -558,14 +562,28 @@ EXPORT_SYMBOL(fcoe_ctlr_els_send);
* times its keep-alive period including fuzz.
*
* In addition, determine the time when an FCF selection can occur.
+ *
+ * Also, increment the MissDiscAdvCount when no advertisement is received
+ * for the corresponding FCF for 1.5 * FKA_ADV_PERIOD (FC-BB-5 LESB).
*/
static void fcoe_ctlr_age_fcfs(struct fcoe_ctlr *fip)
{
struct fcoe_fcf *fcf;
struct fcoe_fcf *next;
unsigned long sel_time = 0;
+ unsigned long mda_time = 0;
list_for_each_entry_safe(fcf, next, &fip->fcfs, list) {
+ mda_time = fcf->fka_period + (fcf->fka_period >> 1);
+ if ((fip->sel_fcf == fcf) &&
+ (time_after(jiffies, fcf->time + mda_time))) {
+ mod_timer(&fip->timer, jiffies + mda_time);
+ fc_lport_get_stats(fip->lp)->MissDiscAdvCount++;
+ printk(KERN_INFO "libfcoe: host%d: Missing Discovery "
+ "Advertisement for fab %llx count %lld\n",
+ fip->lp->host->host_no, fcf->fabric_name,
+ fc_lport_get_stats(fip->lp)->MissDiscAdvCount);
+ }
if (time_after(jiffies, fcf->time + fcf->fka_period * 3 +
msecs_to_jiffies(FIP_FCF_FUZZ * 3))) {
if (fip->sel_fcf == fcf)
@@ -574,6 +592,7 @@ static void fcoe_ctlr_age_fcfs(struct fcoe_ctlr *fip)
WARN_ON(!fip->fcf_count);
fip->fcf_count--;
kfree(fcf);
+ fc_lport_get_stats(fip->lp)->VLinkFailureCount++;
} else if (fcoe_ctlr_mtu_valid(fcf) &&
(!sel_time || time_before(sel_time, fcf->time))) {
sel_time = fcf->time;
@@ -590,14 +609,16 @@ static void fcoe_ctlr_age_fcfs(struct fcoe_ctlr *fip)
}
/**
- * fcoe_ctlr_parse_adv() - Decode a FIP advertisement into a new FCF entry.
- * @skb: received FIP advertisement frame
- * @fcf: resulting FCF entry.
+ * fcoe_ctlr_parse_adv() - Decode a FIP advertisement into a new FCF entry
+ * @fip: The FCoE controller receiving the advertisement
+ * @skb: The received FIP advertisement frame
+ * @fcf: The resulting FCF entry
*
* Returns zero on a valid parsed advertisement,
* otherwise returns non zero value.
*/
-static int fcoe_ctlr_parse_adv(struct sk_buff *skb, struct fcoe_fcf *fcf)
+static int fcoe_ctlr_parse_adv(struct fcoe_ctlr *fip,
+ struct sk_buff *skb, struct fcoe_fcf *fcf)
{
struct fip_header *fiph;
struct fip_desc *desc = NULL;
@@ -636,7 +657,7 @@ static int fcoe_ctlr_parse_adv(struct sk_buff *skb, struct fcoe_fcf *fcf)
((struct fip_mac_desc *)desc)->fd_mac,
ETH_ALEN);
if (!is_valid_ether_addr(fcf->fcf_mac)) {
- LIBFCOE_FIP_DBG("Invalid MAC address "
+ LIBFCOE_FIP_DBG(fip, "Invalid MAC address "
"in FIP adv\n");
return -EINVAL;
}
@@ -659,6 +680,8 @@ static int fcoe_ctlr_parse_adv(struct sk_buff *skb, struct fcoe_fcf *fcf)
if (dlen != sizeof(struct fip_fka_desc))
goto len_err;
fka = (struct fip_fka_desc *)desc;
+ if (fka->fd_flags & FIP_FKA_ADV_D)
+ fcf->fd_flags = 1;
t = ntohl(fka->fd_fka_period);
if (t >= FCOE_CTLR_MIN_FKA)
fcf->fka_period = msecs_to_jiffies(t);
@@ -670,7 +693,7 @@ static int fcoe_ctlr_parse_adv(struct sk_buff *skb, struct fcoe_fcf *fcf)
case FIP_DT_LOGO:
case FIP_DT_ELP:
default:
- LIBFCOE_FIP_DBG("unexpected descriptor type %x "
+ LIBFCOE_FIP_DBG(fip, "unexpected descriptor type %x "
"in FIP adv\n", desc->fip_dtype);
/* standard says ignore unknown descriptors >= 128 */
if (desc->fip_dtype < FIP_DT_VENDOR_BASE)
@@ -687,15 +710,15 @@ static int fcoe_ctlr_parse_adv(struct sk_buff *skb, struct fcoe_fcf *fcf)
return 0;
len_err:
- LIBFCOE_FIP_DBG("FIP length error in descriptor type %x len %zu\n",
+ LIBFCOE_FIP_DBG(fip, "FIP length error in descriptor type %x len %zu\n",
desc->fip_dtype, dlen);
return -EINVAL;
}
/**
- * fcoe_ctlr_recv_adv() - Handle an incoming advertisement.
- * @fip: FCoE controller.
- * @skb: Received FIP packet.
+ * fcoe_ctlr_recv_adv() - Handle an incoming advertisement
+ * @fip: The FCoE controller receiving the advertisement
+ * @skb: The received FIP packet
*/
static void fcoe_ctlr_recv_adv(struct fcoe_ctlr *fip, struct sk_buff *skb)
{
@@ -706,7 +729,7 @@ static void fcoe_ctlr_recv_adv(struct fcoe_ctlr *fip, struct sk_buff *skb)
int first = 0;
int mtu_valid;
- if (fcoe_ctlr_parse_adv(skb, &new))
+ if (fcoe_ctlr_parse_adv(fip, skb, &new))
return;
spin_lock_bh(&fip->lock);
@@ -752,7 +775,7 @@ static void fcoe_ctlr_recv_adv(struct fcoe_ctlr *fip, struct sk_buff *skb)
mtu_valid = fcoe_ctlr_mtu_valid(fcf);
fcf->time = jiffies;
if (!found) {
- LIBFCOE_FIP_DBG("New FCF for fab %llx map %x val %d\n",
+ LIBFCOE_FIP_DBG(fip, "New FCF for fab %llx map %x val %d\n",
fcf->fabric_name, fcf->fc_map, mtu_valid);
}
@@ -778,7 +801,7 @@ static void fcoe_ctlr_recv_adv(struct fcoe_ctlr *fip, struct sk_buff *skb)
*/
if (mtu_valid && !fip->sel_time && fcoe_ctlr_fcf_usable(fcf)) {
fip->sel_time = jiffies +
- msecs_to_jiffies(FCOE_CTLR_START_DELAY);
+ msecs_to_jiffies(FCOE_CTLR_START_DELAY);
if (!timer_pending(&fip->timer) ||
time_before(fip->sel_time, fip->timer.expires))
mod_timer(&fip->timer, fip->sel_time);
@@ -788,15 +811,15 @@ out:
}
/**
- * fcoe_ctlr_recv_els() - Handle an incoming FIP-encapsulated ELS frame.
- * @fip: FCoE controller.
- * @skb: Received FIP packet.
+ * fcoe_ctlr_recv_els() - Handle an incoming FIP encapsulated ELS frame
+ * @fip: The FCoE controller which received the packet
+ * @skb: The received FIP packet
*/
static void fcoe_ctlr_recv_els(struct fcoe_ctlr *fip, struct sk_buff *skb)
{
- struct fc_lport *lp = fip->lp;
+ struct fc_lport *lport = fip->lp;
struct fip_header *fiph;
- struct fc_frame *fp;
+ struct fc_frame *fp = (struct fc_frame *)skb;
struct fc_frame_header *fh = NULL;
struct fip_desc *desc;
struct fip_encaps *els;
@@ -831,10 +854,11 @@ static void fcoe_ctlr_recv_els(struct fcoe_ctlr *fip, struct sk_buff *skb)
((struct fip_mac_desc *)desc)->fd_mac,
ETH_ALEN);
if (!is_valid_ether_addr(granted_mac)) {
- LIBFCOE_FIP_DBG("Invalid MAC address "
+ LIBFCOE_FIP_DBG(fip, "Invalid MAC address "
"in FIP ELS\n");
goto drop;
}
+ memcpy(fr_cb(fp)->granted_mac, granted_mac, ETH_ALEN);
break;
case FIP_DT_FLOGI:
case FIP_DT_FDISC:
@@ -850,7 +874,7 @@ static void fcoe_ctlr_recv_els(struct fcoe_ctlr *fip, struct sk_buff *skb)
els_dtype = desc->fip_dtype;
break;
default:
- LIBFCOE_FIP_DBG("unexpected descriptor type %x "
+ LIBFCOE_FIP_DBG(fip, "unexpected descriptor type %x "
"in FIP adv\n", desc->fip_dtype);
/* standard says ignore unknown descriptors >= 128 */
if (desc->fip_dtype < FIP_DT_VENDOR_BASE)
@@ -867,11 +891,8 @@ static void fcoe_ctlr_recv_els(struct fcoe_ctlr *fip, struct sk_buff *skb)
if (els_dtype == FIP_DT_FLOGI && sub == FIP_SC_REP &&
fip->flogi_oxid == ntohs(fh->fh_ox_id) &&
- els_op == ELS_LS_ACC && is_valid_ether_addr(granted_mac)) {
+ els_op == ELS_LS_ACC && is_valid_ether_addr(granted_mac))
fip->flogi_oxid = FC_XID_UNKNOWN;
- fip->update_mac(fip, fip->data_src_addr, granted_mac);
- memcpy(fip->data_src_addr, granted_mac, ETH_ALEN);
- }
/*
* Convert skb into an fc_frame containing only the ELS.
@@ -882,32 +903,32 @@ static void fcoe_ctlr_recv_els(struct fcoe_ctlr *fip, struct sk_buff *skb)
fc_frame_init(fp);
fr_sof(fp) = FC_SOF_I3;
fr_eof(fp) = FC_EOF_T;
- fr_dev(fp) = lp;
+ fr_dev(fp) = lport;
- stats = fc_lport_get_stats(lp);
+ stats = fc_lport_get_stats(lport);
stats->RxFrames++;
stats->RxWords += skb->len / FIP_BPW;
- fc_exch_recv(lp, fp);
+ fc_exch_recv(lport, fp);
return;
len_err:
- LIBFCOE_FIP_DBG("FIP length error in descriptor type %x len %zu\n",
+ LIBFCOE_FIP_DBG(fip, "FIP length error in descriptor type %x len %zu\n",
desc->fip_dtype, dlen);
drop:
kfree_skb(skb);
}
/**
- * fcoe_ctlr_recv_els() - Handle an incoming link reset frame.
- * @fip: FCoE controller.
- * @fh: Received FIP header.
+ * fcoe_ctlr_recv_els() - Handle an incoming link reset frame
+ * @fip: The FCoE controller that received the frame
+ * @fh: The received FIP header
*
* There may be multiple VN_Port descriptors.
* The overall length has already been checked.
*/
static void fcoe_ctlr_recv_clr_vlink(struct fcoe_ctlr *fip,
- struct fip_header *fh)
+ struct fip_header *fh)
{
struct fip_desc *desc;
struct fip_mac_desc *mp;
@@ -916,13 +937,13 @@ static void fcoe_ctlr_recv_clr_vlink(struct fcoe_ctlr *fip,
size_t rlen;
size_t dlen;
struct fcoe_fcf *fcf = fip->sel_fcf;
- struct fc_lport *lp = fip->lp;
+ struct fc_lport *lport = fip->lp;
u32 desc_mask;
- LIBFCOE_FIP_DBG("Clear Virtual Link received\n");
+ LIBFCOE_FIP_DBG(fip, "Clear Virtual Link received\n");
if (!fcf)
return;
- if (!fcf || !fc_host_port_id(lp->host))
+ if (!fcf || !fc_host_port_id(lport->host))
return;
/*
@@ -958,9 +979,10 @@ static void fcoe_ctlr_recv_clr_vlink(struct fcoe_ctlr *fip,
if (dlen < sizeof(*vp))
return;
if (compare_ether_addr(vp->fd_mac,
- fip->data_src_addr) == 0 &&
- get_unaligned_be64(&vp->fd_wwpn) == lp->wwpn &&
- ntoh24(vp->fd_fc_id) == fc_host_port_id(lp->host))
+ fip->get_src_addr(lport)) == 0 &&
+ get_unaligned_be64(&vp->fd_wwpn) == lport->wwpn &&
+ ntoh24(vp->fd_fc_id) ==
+ fc_host_port_id(lport->host))
desc_mask &= ~BIT(FIP_DT_VN_ID);
break;
default:
@@ -977,33 +999,39 @@ static void fcoe_ctlr_recv_clr_vlink(struct fcoe_ctlr *fip,
* reset only if all required descriptors were present and valid.
*/
if (desc_mask) {
- LIBFCOE_FIP_DBG("missing descriptors mask %x\n", desc_mask);
+ LIBFCOE_FIP_DBG(fip, "missing descriptors mask %x\n",
+ desc_mask);
} else {
- LIBFCOE_FIP_DBG("performing Clear Virtual Link\n");
- fcoe_ctlr_reset(fip, FIP_ST_ENABLED);
+ LIBFCOE_FIP_DBG(fip, "performing Clear Virtual Link\n");
+
+ spin_lock_bh(&fip->lock);
+ fc_lport_get_stats(lport)->VLinkFailureCount++;
+ fcoe_ctlr_reset(fip);
+ spin_unlock_bh(&fip->lock);
+
+ fc_lport_reset(fip->lp);
+ fcoe_ctlr_solicit(fip, NULL);
}
}
/**
- * fcoe_ctlr_recv() - Receive a FIP frame.
- * @fip: FCoE controller.
- * @skb: Received FIP packet.
+ * fcoe_ctlr_recv() - Receive a FIP packet
+ * @fip: The FCoE controller that received the packet
+ * @skb: The received FIP packet
*
- * This is called from NET_RX_SOFTIRQ.
+ * This may be called from either NET_RX_SOFTIRQ or IRQ.
*/
void fcoe_ctlr_recv(struct fcoe_ctlr *fip, struct sk_buff *skb)
{
- spin_lock_bh(&fip->fip_recv_list.lock);
- __skb_queue_tail(&fip->fip_recv_list, skb);
- spin_unlock_bh(&fip->fip_recv_list.lock);
+ skb_queue_tail(&fip->fip_recv_list, skb);
schedule_work(&fip->recv_work);
}
EXPORT_SYMBOL(fcoe_ctlr_recv);
/**
- * fcoe_ctlr_recv_handler() - Receive a FIP frame.
- * @fip: FCoE controller.
- * @skb: Received FIP packet.
+ * fcoe_ctlr_recv_handler() - Receive a FIP frame
+ * @fip: The FCoE controller that received the frame
+ * @skb: The received FIP frame
*
* Returns non-zero if the frame is dropped.
*/
@@ -1038,7 +1066,7 @@ static int fcoe_ctlr_recv_handler(struct fcoe_ctlr *fip, struct sk_buff *skb)
fip->map_dest = 0;
fip->state = FIP_ST_ENABLED;
state = FIP_ST_ENABLED;
- LIBFCOE_FIP_DBG("Using FIP mode\n");
+ LIBFCOE_FIP_DBG(fip, "Using FIP mode\n");
}
spin_unlock_bh(&fip->lock);
if (state != FIP_ST_ENABLED)
@@ -1060,8 +1088,8 @@ drop:
}
/**
- * fcoe_ctlr_select() - Select the best FCF, if possible.
- * @fip: FCoE controller.
+ * fcoe_ctlr_select() - Select the best FCF (if possible)
+ * @fip: The FCoE controller
*
* If there are conflicting advertisements, no FCF can be chosen.
*
@@ -1073,11 +1101,11 @@ static void fcoe_ctlr_select(struct fcoe_ctlr *fip)
struct fcoe_fcf *best = NULL;
list_for_each_entry(fcf, &fip->fcfs, list) {
- LIBFCOE_FIP_DBG("consider FCF for fab %llx VFID %d map %x "
+ LIBFCOE_FIP_DBG(fip, "consider FCF for fab %llx VFID %d map %x "
"val %d\n", fcf->fabric_name, fcf->vfid,
fcf->fc_map, fcoe_ctlr_mtu_valid(fcf));
if (!fcoe_ctlr_fcf_usable(fcf)) {
- LIBFCOE_FIP_DBG("FCF for fab %llx map %x %svalid "
+ LIBFCOE_FIP_DBG(fip, "FCF for fab %llx map %x %svalid "
"%savailable\n", fcf->fabric_name,
fcf->fc_map, (fcf->flags & FIP_FL_SOL)
? "" : "in", (fcf->flags & FIP_FL_AVAIL)
@@ -1091,7 +1119,7 @@ static void fcoe_ctlr_select(struct fcoe_ctlr *fip)
if (fcf->fabric_name != best->fabric_name ||
fcf->vfid != best->vfid ||
fcf->fc_map != best->fc_map) {
- LIBFCOE_FIP_DBG("Conflicting fabric, VFID, "
+ LIBFCOE_FIP_DBG(fip, "Conflicting fabric, VFID, "
"or FC-MAP\n");
return;
}
@@ -1102,8 +1130,8 @@ static void fcoe_ctlr_select(struct fcoe_ctlr *fip)
}
/**
- * fcoe_ctlr_timeout() - FIP timer function.
- * @arg: &fcoe_ctlr pointer.
+ * fcoe_ctlr_timeout() - FIP timeout handler
+ * @arg: The FCoE controller that timed out
*
* Ages FCFs. Triggers FCF selection if possible. Sends keep-alives.
*/
@@ -1113,8 +1141,6 @@ static void fcoe_ctlr_timeout(unsigned long arg)
struct fcoe_fcf *sel;
struct fcoe_fcf *fcf;
unsigned long next_timer = jiffies + msecs_to_jiffies(FIP_VN_KA_PERIOD);
- u8 send_ctlr_ka;
- u8 send_port_ka;
spin_lock_bh(&fip->lock);
if (fip->state == FIP_ST_DISABLED) {
@@ -1140,53 +1166,47 @@ static void fcoe_ctlr_timeout(unsigned long arg)
fip->lp->host->host_no, sel->fcf_mac);
memcpy(fip->dest_addr, sel->fcf_mac, ETH_ALEN);
fip->port_ka_time = jiffies +
- msecs_to_jiffies(FIP_VN_KA_PERIOD);
+ msecs_to_jiffies(FIP_VN_KA_PERIOD);
fip->ctlr_ka_time = jiffies + sel->fka_period;
- fip->link = 1;
} else {
printk(KERN_NOTICE "libfcoe: host%d: "
- "FIP Fibre-Channel Forwarder timed out. "
+ "FIP Fibre-Channel Forwarder timed out. "
"Starting FCF discovery.\n",
fip->lp->host->host_no);
- fip->link = 0;
+ fip->reset_req = 1;
+ schedule_work(&fip->link_work);
}
- schedule_work(&fip->link_work);
}
- send_ctlr_ka = 0;
- send_port_ka = 0;
- if (sel) {
+ if (sel && !sel->fd_flags) {
if (time_after_eq(jiffies, fip->ctlr_ka_time)) {
fip->ctlr_ka_time = jiffies + sel->fka_period;
- send_ctlr_ka = 1;
+ fip->send_ctlr_ka = 1;
}
if (time_after(next_timer, fip->ctlr_ka_time))
next_timer = fip->ctlr_ka_time;
if (time_after_eq(jiffies, fip->port_ka_time)) {
fip->port_ka_time += jiffies +
- msecs_to_jiffies(FIP_VN_KA_PERIOD);
- send_port_ka = 1;
+ msecs_to_jiffies(FIP_VN_KA_PERIOD);
+ fip->send_port_ka = 1;
}
if (time_after(next_timer, fip->port_ka_time))
next_timer = fip->port_ka_time;
mod_timer(&fip->timer, next_timer);
} else if (fip->sel_time) {
next_timer = fip->sel_time +
- msecs_to_jiffies(FCOE_CTLR_START_DELAY);
+ msecs_to_jiffies(FCOE_CTLR_START_DELAY);
mod_timer(&fip->timer, next_timer);
}
+ if (fip->send_ctlr_ka || fip->send_port_ka)
+ schedule_work(&fip->link_work);
spin_unlock_bh(&fip->lock);
-
- if (send_ctlr_ka)
- fcoe_ctlr_send_keep_alive(fip, 0, fip->ctl_src_addr);
- if (send_port_ka)
- fcoe_ctlr_send_keep_alive(fip, 1, fip->data_src_addr);
}
/**
- * fcoe_ctlr_link_work() - worker thread function for link changes.
- * @work: pointer to link_work member inside &fcoe_ctlr.
+ * fcoe_ctlr_link_work() - Worker thread function for link changes
+ * @work: Handle to a FCoE controller
*
* See if the link status has changed and if so, report it.
*
@@ -1196,27 +1216,49 @@ static void fcoe_ctlr_timeout(unsigned long arg)
static void fcoe_ctlr_link_work(struct work_struct *work)
{
struct fcoe_ctlr *fip;
+ struct fc_lport *vport;
+ u8 *mac;
int link;
int last_link;
+ int reset;
fip = container_of(work, struct fcoe_ctlr, link_work);
spin_lock_bh(&fip->lock);
last_link = fip->last_link;
link = fip->link;
fip->last_link = link;
+ reset = fip->reset_req;
+ fip->reset_req = 0;
spin_unlock_bh(&fip->lock);
if (last_link != link) {
if (link)
fc_linkup(fip->lp);
else
- fcoe_ctlr_reset(fip, FIP_ST_LINK_WAIT);
+ fc_linkdown(fip->lp);
+ } else if (reset && link)
+ fc_lport_reset(fip->lp);
+
+ if (fip->send_ctlr_ka) {
+ fip->send_ctlr_ka = 0;
+ fcoe_ctlr_send_keep_alive(fip, NULL, 0, fip->ctl_src_addr);
+ }
+ if (fip->send_port_ka) {
+ fip->send_port_ka = 0;
+ mutex_lock(&fip->lp->lp_mutex);
+ mac = fip->get_src_addr(fip->lp);
+ fcoe_ctlr_send_keep_alive(fip, fip->lp, 1, mac);
+ list_for_each_entry(vport, &fip->lp->vports, list) {
+ mac = fip->get_src_addr(vport);
+ fcoe_ctlr_send_keep_alive(fip, vport, 1, mac);
+ }
+ mutex_unlock(&fip->lp->lp_mutex);
}
}
/**
- * fcoe_ctlr_recv_work() - Worker thread function for receiving FIP frames.
- * @recv_work: pointer to recv_work member inside &fcoe_ctlr.
+ * fcoe_ctlr_recv_work() - Worker thread function for receiving FIP frames
+ * @recv_work: Handle to a FCoE controller
*/
static void fcoe_ctlr_recv_work(struct work_struct *recv_work)
{
@@ -1224,20 +1266,14 @@ static void fcoe_ctlr_recv_work(struct work_struct *recv_work)
struct sk_buff *skb;
fip = container_of(recv_work, struct fcoe_ctlr, recv_work);
- spin_lock_bh(&fip->fip_recv_list.lock);
- while ((skb = __skb_dequeue(&fip->fip_recv_list))) {
- spin_unlock_bh(&fip->fip_recv_list.lock);
+ while ((skb = skb_dequeue(&fip->fip_recv_list)))
fcoe_ctlr_recv_handler(fip, skb);
- spin_lock_bh(&fip->fip_recv_list.lock);
- }
- spin_unlock_bh(&fip->fip_recv_list.lock);
}
/**
- * fcoe_ctlr_recv_flogi() - snoop Pre-FIP receipt of FLOGI response or request.
- * @fip: FCoE controller.
- * @fp: FC frame.
- * @sa: Ethernet source MAC address from received FCoE frame.
+ * fcoe_ctlr_recv_flogi() - Snoop pre-FIP receipt of FLOGI response
+ * @fip: The FCoE controller
+ * @fp: The FC frame to snoop
*
* Snoop potential response to FLOGI or even incoming FLOGI.
*
@@ -1245,15 +1281,18 @@ static void fcoe_ctlr_recv_work(struct work_struct *recv_work)
* by fip->flogi_oxid != FC_XID_UNKNOWN.
*
* The caller is responsible for freeing the frame.
+ * Fill in the granted_mac address.
*
* Return non-zero if the frame should not be delivered to libfc.
*/
-int fcoe_ctlr_recv_flogi(struct fcoe_ctlr *fip, struct fc_frame *fp, u8 *sa)
+int fcoe_ctlr_recv_flogi(struct fcoe_ctlr *fip, struct fc_lport *lport,
+ struct fc_frame *fp)
{
struct fc_frame_header *fh;
u8 op;
- u8 mac[ETH_ALEN];
+ u8 *sa;
+ sa = eth_hdr(&fp->skb)->h_source;
fh = fc_frame_header_get(fp);
if (fh->fh_type != FC_TYPE_ELS)
return 0;
@@ -1268,7 +1307,8 @@ int fcoe_ctlr_recv_flogi(struct fcoe_ctlr *fip, struct fc_frame *fp, u8 *sa)
return -EINVAL;
}
fip->state = FIP_ST_NON_FIP;
- LIBFCOE_FIP_DBG("received FLOGI LS_ACC using non-FIP mode\n");
+ LIBFCOE_FIP_DBG(fip,
+ "received FLOGI LS_ACC using non-FIP mode\n");
/*
* FLOGI accepted.
@@ -1283,11 +1323,8 @@ int fcoe_ctlr_recv_flogi(struct fcoe_ctlr *fip, struct fc_frame *fp, u8 *sa)
fip->map_dest = 0;
}
fip->flogi_oxid = FC_XID_UNKNOWN;
- memcpy(mac, fip->data_src_addr, ETH_ALEN);
- fc_fcoe_set_mac(fip->data_src_addr, fh->fh_d_id);
spin_unlock_bh(&fip->lock);
-
- fip->update_mac(fip, mac, fip->data_src_addr);
+ fc_fcoe_set_mac(fr_cb(fp)->granted_mac, fh->fh_d_id);
} else if (op == ELS_FLOGI && fh->fh_r_ctl == FC_RCTL_ELS_REQ && sa) {
/*
* Save source MAC for point-to-point responses.
@@ -1297,7 +1334,7 @@ int fcoe_ctlr_recv_flogi(struct fcoe_ctlr *fip, struct fc_frame *fp, u8 *sa)
memcpy(fip->dest_addr, sa, ETH_ALEN);
fip->map_dest = 0;
if (fip->state == FIP_ST_NON_FIP)
- LIBFCOE_FIP_DBG("received FLOGI REQ, "
+ LIBFCOE_FIP_DBG(fip, "received FLOGI REQ, "
"using non-FIP mode\n");
fip->state = FIP_ST_NON_FIP;
}
@@ -1308,10 +1345,10 @@ int fcoe_ctlr_recv_flogi(struct fcoe_ctlr *fip, struct fc_frame *fp, u8 *sa)
EXPORT_SYMBOL(fcoe_ctlr_recv_flogi);
/**
- * fcoe_wwn_from_mac() - Converts 48-bit IEEE MAC address to 64-bit FC WWN.
- * @mac: mac address
- * @scheme: check port
- * @port: port indicator for converting
+ * fcoe_wwn_from_mac() - Converts a 48-bit IEEE MAC address to a 64-bit FC WWN
+ * @mac: The MAC address to convert
+ * @scheme: The scheme to use when converting
+ * @port: The port indicator for converting
*
* Returns: u64 fc world wide name
*/
@@ -1349,24 +1386,26 @@ u64 fcoe_wwn_from_mac(unsigned char mac[MAX_ADDR_LEN],
EXPORT_SYMBOL_GPL(fcoe_wwn_from_mac);
/**
- * fcoe_libfc_config() - sets up libfc related properties for lport
- * @lp: ptr to the fc_lport
- * @tt: libfc function template
+ * fcoe_libfc_config() - Sets up libfc related properties for local port
+ * @lp: The local port to configure libfc for
+ * @tt: The libfc function template
*
* Returns : 0 for success
*/
-int fcoe_libfc_config(struct fc_lport *lp, struct libfc_function_template *tt)
+int fcoe_libfc_config(struct fc_lport *lport,
+ struct libfc_function_template *tt)
{
/* Set the function pointers set by the LLDD */
- memcpy(&lp->tt, tt, sizeof(*tt));
- if (fc_fcp_init(lp))
+ memcpy(&lport->tt, tt, sizeof(*tt));
+ if (fc_fcp_init(lport))
return -ENOMEM;
- fc_exch_init(lp);
- fc_elsct_init(lp);
- fc_lport_init(lp);
- fc_rport_init(lp);
- fc_disc_init(lp);
+ fc_exch_init(lport);
+ fc_elsct_init(lport);
+ fc_lport_init(lport);
+ fc_rport_init(lport);
+ fc_disc_init(lport);
return 0;
}
EXPORT_SYMBOL_GPL(fcoe_libfc_config);
+
diff --git a/drivers/scsi/fnic/fnic.h b/drivers/scsi/fnic/fnic.h
index e4c0a3d7d87..bb208a6091e 100644
--- a/drivers/scsi/fnic/fnic.h
+++ b/drivers/scsi/fnic/fnic.h
@@ -22,6 +22,7 @@
#include <linux/netdevice.h>
#include <linux/workqueue.h>
#include <scsi/libfc.h>
+#include <scsi/libfcoe.h>
#include "fnic_io.h"
#include "fnic_res.h"
#include "vnic_dev.h"
@@ -44,7 +45,7 @@
#define FNIC_IO_LOCKS 64 /* IO locks: power of 2 */
#define FNIC_DFLT_QUEUE_DEPTH 32
#define FNIC_STATS_RATE_LIMIT 4 /* limit rate at which stats are pulled up */
-
+#define FNIC_MAX_CMD_LEN 16 /* Supported CDB length */
/*
* Tag bits used for special requests.
*/
@@ -145,6 +146,7 @@ struct mempool;
/* Per-instance private data structure */
struct fnic {
struct fc_lport *lport;
+ struct fcoe_ctlr ctlr; /* FIP FCoE controller structure */
struct vnic_dev_bar bar0;
struct msix_entry msix_entry[FNIC_MSIX_INTR_MAX];
@@ -162,23 +164,16 @@ struct fnic {
unsigned int wq_count;
unsigned int cq_count;
- u32 fcoui_mode:1; /* use fcoui address*/
u32 vlan_hw_insert:1; /* let hw insert the tag */
u32 in_remove:1; /* fnic device in removal */
u32 stop_rx_link_events:1; /* stop proc. rx frames, link events */
struct completion *remove_wait; /* device remove thread blocks */
- struct fc_frame *flogi;
- struct fc_frame *flogi_resp;
- u16 flogi_oxid;
- unsigned long s_id;
enum fnic_state state;
spinlock_t fnic_lock;
u16 vlan_id; /* VLAN tag including priority */
- u8 mac_addr[ETH_ALEN];
- u8 dest_addr[ETH_ALEN];
u8 data_src_addr[ETH_ALEN];
u64 fcp_input_bytes; /* internal statistic */
u64 fcp_output_bytes; /* internal statistic */
@@ -205,6 +200,7 @@ struct fnic {
struct work_struct link_work;
struct work_struct frame_work;
struct sk_buff_head frame_queue;
+ struct sk_buff_head tx_queue;
/* copy work queue cache line section */
____cacheline_aligned struct vnic_wq_copy wq_copy[FNIC_WQ_COPY_MAX];
@@ -224,6 +220,11 @@ struct fnic {
____cacheline_aligned struct vnic_intr intr[FNIC_MSIX_INTR_MAX];
};
+static inline struct fnic *fnic_from_ctlr(struct fcoe_ctlr *fip)
+{
+ return container_of(fip, struct fnic, ctlr);
+}
+
extern struct workqueue_struct *fnic_event_queue;
extern struct device_attribute *fnic_attrs[];
@@ -239,7 +240,11 @@ void fnic_handle_link(struct work_struct *work);
int fnic_rq_cmpl_handler(struct fnic *fnic, int);
int fnic_alloc_rq_frame(struct vnic_rq *rq);
void fnic_free_rq_buf(struct vnic_rq *rq, struct vnic_rq_buf *buf);
-int fnic_send_frame(struct fnic *fnic, struct fc_frame *fp);
+void fnic_flush_tx(struct fnic *);
+void fnic_eth_send(struct fcoe_ctlr *, struct sk_buff *skb);
+void fnic_set_port_id(struct fc_lport *, u32, struct fc_frame *);
+void fnic_update_mac(struct fc_lport *, u8 *new);
+void fnic_update_mac_locked(struct fnic *, u8 *new);
int fnic_queuecommand(struct scsi_cmnd *, void (*done)(struct scsi_cmnd *));
int fnic_abort_cmd(struct scsi_cmnd *);
@@ -252,7 +257,7 @@ void fnic_empty_scsi_cleanup(struct fc_lport *);
void fnic_exch_mgr_reset(struct fc_lport *, u32, u32);
int fnic_wq_copy_cmpl_handler(struct fnic *fnic, int);
int fnic_wq_cmpl_handler(struct fnic *fnic, int);
-int fnic_flogi_reg_handler(struct fnic *fnic);
+int fnic_flogi_reg_handler(struct fnic *fnic, u32);
void fnic_wq_copy_cleanup_handler(struct vnic_wq_copy *wq,
struct fcpio_host_req *desc);
int fnic_fw_reset_handler(struct fnic *fnic);
diff --git a/drivers/scsi/fnic/fnic_fcs.c b/drivers/scsi/fnic/fnic_fcs.c
index 50db3e36a61..54f8d0e5407 100644
--- a/drivers/scsi/fnic/fnic_fcs.c
+++ b/drivers/scsi/fnic/fnic_fcs.c
@@ -23,6 +23,7 @@
#include <linux/if_ether.h>
#include <linux/if_vlan.h>
#include <linux/workqueue.h>
+#include <scsi/fc/fc_fip.h>
#include <scsi/fc/fc_els.h>
#include <scsi/fc/fc_fcoe.h>
#include <scsi/fc_frame.h>
@@ -34,6 +35,8 @@
struct workqueue_struct *fnic_event_queue;
+static void fnic_set_eth_mode(struct fnic *);
+
void fnic_handle_link(struct work_struct *work)
{
struct fnic *fnic = container_of(work, struct fnic, link_work);
@@ -64,10 +67,10 @@ void fnic_handle_link(struct work_struct *work)
spin_unlock_irqrestore(&fnic->fnic_lock, flags);
FNIC_FCS_DBG(KERN_DEBUG, fnic->lport->host,
"link down\n");
- fc_linkdown(fnic->lport);
+ fcoe_ctlr_link_down(&fnic->ctlr);
FNIC_FCS_DBG(KERN_DEBUG, fnic->lport->host,
"link up\n");
- fc_linkup(fnic->lport);
+ fcoe_ctlr_link_up(&fnic->ctlr);
} else
/* UP -> UP */
spin_unlock_irqrestore(&fnic->fnic_lock, flags);
@@ -76,13 +79,13 @@ void fnic_handle_link(struct work_struct *work)
/* DOWN -> UP */
spin_unlock_irqrestore(&fnic->fnic_lock, flags);
FNIC_FCS_DBG(KERN_DEBUG, fnic->lport->host, "link up\n");
- fc_linkup(fnic->lport);
+ fcoe_ctlr_link_up(&fnic->ctlr);
} else {
/* UP -> DOWN */
fnic->lport->host_stats.link_failure_count++;
spin_unlock_irqrestore(&fnic->fnic_lock, flags);
FNIC_FCS_DBG(KERN_DEBUG, fnic->lport->host, "link down\n");
- fc_linkdown(fnic->lport);
+ fcoe_ctlr_link_down(&fnic->ctlr);
}
}
@@ -107,197 +110,179 @@ void fnic_handle_frame(struct work_struct *work)
return;
}
fp = (struct fc_frame *)skb;
- /* if Flogi resp frame, register the address */
- if (fr_flags(fp)) {
- vnic_dev_add_addr(fnic->vdev,
- fnic->data_src_addr);
- fr_flags(fp) = 0;
+
+ /*
+ * If we're in a transitional state, just re-queue and return.
+ * The queue will be serviced when we get to a stable state.
+ */
+ if (fnic->state != FNIC_IN_FC_MODE &&
+ fnic->state != FNIC_IN_ETH_MODE) {
+ skb_queue_head(&fnic->frame_queue, skb);
+ spin_unlock_irqrestore(&fnic->fnic_lock, flags);
+ return;
}
spin_unlock_irqrestore(&fnic->fnic_lock, flags);
fc_exch_recv(lp, fp);
}
-
-}
-
-static inline void fnic_import_rq_fc_frame(struct sk_buff *skb,
- u32 len, u8 sof, u8 eof)
-{
- struct fc_frame *fp = (struct fc_frame *)skb;
-
- skb_trim(skb, len);
- fr_eof(fp) = eof;
- fr_sof(fp) = sof;
}
-
-static inline int fnic_import_rq_eth_pkt(struct sk_buff *skb, u32 len)
+/**
+ * fnic_import_rq_eth_pkt() - handle received FCoE or FIP frame.
+ * @fnic: fnic instance.
+ * @skb: Ethernet Frame.
+ */
+static inline int fnic_import_rq_eth_pkt(struct fnic *fnic, struct sk_buff *skb)
{
struct fc_frame *fp;
struct ethhdr *eh;
- struct vlan_ethhdr *vh;
struct fcoe_hdr *fcoe_hdr;
struct fcoe_crc_eof *ft;
- u32 transport_len = 0;
+ /*
+ * Undo VLAN encapsulation if present.
+ */
eh = (struct ethhdr *)skb->data;
- vh = (struct vlan_ethhdr *)skb->data;
- if (vh->h_vlan_proto == htons(ETH_P_8021Q) &&
- vh->h_vlan_encapsulated_proto == htons(ETH_P_FCOE)) {
- skb_pull(skb, sizeof(struct vlan_ethhdr));
- transport_len += sizeof(struct vlan_ethhdr);
- } else if (eh->h_proto == htons(ETH_P_FCOE)) {
- transport_len += sizeof(struct ethhdr);
- skb_pull(skb, sizeof(struct ethhdr));
- } else
- return -1;
+ if (eh->h_proto == htons(ETH_P_8021Q)) {
+ memmove((u8 *)eh + VLAN_HLEN, eh, ETH_ALEN * 2);
+ eh = (struct ethhdr *)skb_pull(skb, VLAN_HLEN);
+ skb_reset_mac_header(skb);
+ }
+ if (eh->h_proto == htons(ETH_P_FIP)) {
+ skb_pull(skb, sizeof(*eh));
+ fcoe_ctlr_recv(&fnic->ctlr, skb);
+ return 1; /* let caller know packet was used */
+ }
+ if (eh->h_proto != htons(ETH_P_FCOE))
+ goto drop;
+ skb_set_network_header(skb, sizeof(*eh));
+ skb_pull(skb, sizeof(*eh));
fcoe_hdr = (struct fcoe_hdr *)skb->data;
if (FC_FCOE_DECAPS_VER(fcoe_hdr) != FC_FCOE_VER)
- return -1;
+ goto drop;
fp = (struct fc_frame *)skb;
fc_frame_init(fp);
fr_sof(fp) = fcoe_hdr->fcoe_sof;
skb_pull(skb, sizeof(struct fcoe_hdr));
- transport_len += sizeof(struct fcoe_hdr);
+ skb_reset_transport_header(skb);
- ft = (struct fcoe_crc_eof *)(skb->data + len -
- transport_len - sizeof(*ft));
+ ft = (struct fcoe_crc_eof *)(skb->data + skb->len - sizeof(*ft));
fr_eof(fp) = ft->fcoe_eof;
- skb_trim(skb, len - transport_len - sizeof(*ft));
+ skb_trim(skb, skb->len - sizeof(*ft));
return 0;
+drop:
+ dev_kfree_skb_irq(skb);
+ return -1;
}
-static inline int fnic_handle_flogi_resp(struct fnic *fnic,
- struct fc_frame *fp)
+/**
+ * fnic_update_mac_locked() - set data MAC address and filters.
+ * @fnic: fnic instance.
+ * @new: newly-assigned FCoE MAC address.
+ *
+ * Called with the fnic lock held.
+ */
+void fnic_update_mac_locked(struct fnic *fnic, u8 *new)
{
- u8 mac[ETH_ALEN] = FC_FCOE_FLOGI_MAC;
- struct ethhdr *eth_hdr;
- struct fc_frame_header *fh;
- int ret = 0;
- unsigned long flags;
- struct fc_frame *old_flogi_resp = NULL;
+ u8 *ctl = fnic->ctlr.ctl_src_addr;
+ u8 *data = fnic->data_src_addr;
- fh = (struct fc_frame_header *)fr_hdr(fp);
+ if (is_zero_ether_addr(new))
+ new = ctl;
+ if (!compare_ether_addr(data, new))
+ return;
+ FNIC_FCS_DBG(KERN_DEBUG, fnic->lport->host, "update_mac %pM\n", new);
+ if (!is_zero_ether_addr(data) && compare_ether_addr(data, ctl))
+ vnic_dev_del_addr(fnic->vdev, data);
+ memcpy(data, new, ETH_ALEN);
+ if (compare_ether_addr(new, ctl))
+ vnic_dev_add_addr(fnic->vdev, new);
+}
- spin_lock_irqsave(&fnic->fnic_lock, flags);
+/**
+ * fnic_update_mac() - set data MAC address and filters.
+ * @lport: local port.
+ * @new: newly-assigned FCoE MAC address.
+ */
+void fnic_update_mac(struct fc_lport *lport, u8 *new)
+{
+ struct fnic *fnic = lport_priv(lport);
- if (fnic->state == FNIC_IN_ETH_MODE) {
+ spin_lock_irq(&fnic->fnic_lock);
+ fnic_update_mac_locked(fnic, new);
+ spin_unlock_irq(&fnic->fnic_lock);
+}
- /*
- * Check if oxid matches on taking the lock. A new Flogi
- * issued by libFC might have changed the fnic cached oxid
- */
- if (fnic->flogi_oxid != ntohs(fh->fh_ox_id)) {
- FNIC_FCS_DBG(KERN_DEBUG, fnic->lport->host,
- "Flogi response oxid not"
- " matching cached oxid, dropping frame"
- "\n");
- ret = -1;
- spin_unlock_irqrestore(&fnic->fnic_lock, flags);
- dev_kfree_skb_irq(fp_skb(fp));
- goto handle_flogi_resp_end;
- }
+/**
+ * fnic_set_port_id() - set the port_ID after successful FLOGI.
+ * @lport: local port.
+ * @port_id: assigned FC_ID.
+ * @fp: received frame containing the FLOGI accept or NULL.
+ *
+ * This is called from libfc when a new FC_ID has been assigned.
+ * This causes us to reset the firmware to FC_MODE and setup the new MAC
+ * address and FC_ID.
+ *
+ * It is also called with FC_ID 0 when we're logged off.
+ *
+ * If the FC_ID is due to point-to-point, fp may be NULL.
+ */
+void fnic_set_port_id(struct fc_lport *lport, u32 port_id, struct fc_frame *fp)
+{
+ struct fnic *fnic = lport_priv(lport);
+ u8 *mac;
+ int ret;
- /* Drop older cached flogi response frame, cache this frame */
- old_flogi_resp = fnic->flogi_resp;
- fnic->flogi_resp = fp;
- fnic->flogi_oxid = FC_XID_UNKNOWN;
+ FNIC_FCS_DBG(KERN_DEBUG, lport->host, "set port_id %x fp %p\n",
+ port_id, fp);
- /*
- * this frame is part of flogi get the src mac addr from this
- * frame if the src mac is fcoui based then we mark the
- * address mode flag to use fcoui base for dst mac addr
- * otherwise we have to store the fcoe gateway addr
- */
- eth_hdr = (struct ethhdr *)skb_mac_header(fp_skb(fp));
- memcpy(mac, eth_hdr->h_source, ETH_ALEN);
+ /*
+ * If we're clearing the FC_ID, change to use the ctl_src_addr.
+ * Set ethernet mode to send FLOGI.
+ */
+ if (!port_id) {
+ fnic_update_mac(lport, fnic->ctlr.ctl_src_addr);
+ fnic_set_eth_mode(fnic);
+ return;
+ }
- if (ntoh24(mac) == FC_FCOE_OUI)
- fnic->fcoui_mode = 1;
- else {
- fnic->fcoui_mode = 0;
- memcpy(fnic->dest_addr, mac, ETH_ALEN);
+ if (fp) {
+ mac = fr_cb(fp)->granted_mac;
+ if (is_zero_ether_addr(mac)) {
+ /* non-FIP - FLOGI already accepted - ignore return */
+ fcoe_ctlr_recv_flogi(&fnic->ctlr, lport, fp);
}
+ fnic_update_mac(lport, mac);
+ }
- /*
- * Except for Flogi frame, all outbound frames from us have the
- * Eth Src address as FC_FCOE_OUI"our_sid". Flogi frame uses
- * the vnic MAC address as the Eth Src address
- */
- fc_fcoe_set_mac(fnic->data_src_addr, fh->fh_d_id);
-
- /* We get our s_id from the d_id of the flogi resp frame */
- fnic->s_id = ntoh24(fh->fh_d_id);
-
- /* Change state to reflect transition from Eth to FC mode */
+ /* Change state to reflect transition to FC mode */
+ spin_lock_irq(&fnic->fnic_lock);
+ if (fnic->state == FNIC_IN_ETH_MODE || fnic->state == FNIC_IN_FC_MODE)
fnic->state = FNIC_IN_ETH_TRANS_FC_MODE;
-
- } else {
+ else {
FNIC_FCS_DBG(KERN_DEBUG, fnic->lport->host,
"Unexpected fnic state %s while"
" processing flogi resp\n",
fnic_state_to_str(fnic->state));
- ret = -1;
- spin_unlock_irqrestore(&fnic->fnic_lock, flags);
- dev_kfree_skb_irq(fp_skb(fp));
- goto handle_flogi_resp_end;
+ spin_unlock_irq(&fnic->fnic_lock);
+ return;
}
-
- spin_unlock_irqrestore(&fnic->fnic_lock, flags);
-
- /* Drop older cached frame */
- if (old_flogi_resp)
- dev_kfree_skb_irq(fp_skb(old_flogi_resp));
+ spin_unlock_irq(&fnic->fnic_lock);
/*
- * send flogi reg request to firmware, this will put the fnic in
- * in FC mode
+ * Send FLOGI registration to firmware to set up FC mode.
+ * The new address will be set up when registration completes.
*/
- ret = fnic_flogi_reg_handler(fnic);
+ ret = fnic_flogi_reg_handler(fnic, port_id);
if (ret < 0) {
- int free_fp = 1;
- spin_lock_irqsave(&fnic->fnic_lock, flags);
- /*
- * free the frame is some other thread is not
- * pointing to it
- */
- if (fnic->flogi_resp != fp)
- free_fp = 0;
- else
- fnic->flogi_resp = NULL;
-
+ spin_lock_irq(&fnic->fnic_lock);
if (fnic->state == FNIC_IN_ETH_TRANS_FC_MODE)
fnic->state = FNIC_IN_ETH_MODE;
- spin_unlock_irqrestore(&fnic->fnic_lock, flags);
- if (free_fp)
- dev_kfree_skb_irq(fp_skb(fp));
+ spin_unlock_irq(&fnic->fnic_lock);
}
-
- handle_flogi_resp_end:
- return ret;
-}
-
-/* Returns 1 for a response that matches cached flogi oxid */
-static inline int is_matching_flogi_resp_frame(struct fnic *fnic,
- struct fc_frame *fp)
-{
- struct fc_frame_header *fh;
- int ret = 0;
- u32 f_ctl;
-
- fh = fc_frame_header_get(fp);
- f_ctl = ntoh24(fh->fh_f_ctl);
-
- if (fnic->flogi_oxid == ntohs(fh->fh_ox_id) &&
- fh->fh_r_ctl == FC_RCTL_ELS_REP &&
- (f_ctl & (FC_FC_EX_CTX | FC_FC_SEQ_CTX)) == FC_FC_EX_CTX &&
- fh->fh_type == FC_TYPE_ELS)
- ret = 1;
-
- return ret;
}
static void fnic_rq_cmpl_frame_recv(struct vnic_rq *rq, struct cq_desc
@@ -326,6 +311,7 @@ static void fnic_rq_cmpl_frame_recv(struct vnic_rq *rq, struct cq_desc
pci_unmap_single(fnic->pdev, buf->dma_addr, buf->len,
PCI_DMA_FROMDEVICE);
skb = buf->os_buf;
+ fp = (struct fc_frame *)skb;
buf->os_buf = NULL;
cq_desc_dec(cq_desc, &type, &color, &q_number, &completed_index);
@@ -338,6 +324,9 @@ static void fnic_rq_cmpl_frame_recv(struct vnic_rq *rq, struct cq_desc
&fcoe_enc_error, &fcs_ok, &vlan_stripped,
&vlan);
eth_hdrs_stripped = 1;
+ skb_trim(skb, fcp_bytes_written);
+ fr_sof(fp) = sof;
+ fr_eof(fp) = eof;
} else if (type == CQ_DESC_TYPE_RQ_ENET) {
cq_enet_rq_desc_dec((struct cq_enet_rq_desc *)cq_desc,
@@ -352,6 +341,14 @@ static void fnic_rq_cmpl_frame_recv(struct vnic_rq *rq, struct cq_desc
&ipv4_csum_ok, &ipv6, &ipv4,
&ipv4_fragment, &fcs_ok);
eth_hdrs_stripped = 0;
+ skb_trim(skb, bytes_written);
+ if (!fcs_ok) {
+ FNIC_FCS_DBG(KERN_DEBUG, fnic->lport->host,
+ "fcs error. dropping packet.\n");
+ goto drop;
+ }
+ if (fnic_import_rq_eth_pkt(fnic, skb))
+ return;
} else {
/* wrong CQ type*/
@@ -370,43 +367,11 @@ static void fnic_rq_cmpl_frame_recv(struct vnic_rq *rq, struct cq_desc
goto drop;
}
- if (eth_hdrs_stripped)
- fnic_import_rq_fc_frame(skb, fcp_bytes_written, sof, eof);
- else if (fnic_import_rq_eth_pkt(skb, bytes_written))
- goto drop;
-
- fp = (struct fc_frame *)skb;
-
- /*
- * If frame is an ELS response that matches the cached FLOGI OX_ID,
- * and is accept, issue flogi_reg_request copy wq request to firmware
- * to register the S_ID and determine whether FC_OUI mode or GW mode.
- */
- if (is_matching_flogi_resp_frame(fnic, fp)) {
- if (!eth_hdrs_stripped) {
- if (fc_frame_payload_op(fp) == ELS_LS_ACC) {
- fnic_handle_flogi_resp(fnic, fp);
- return;
- }
- /*
- * Recd. Flogi reject. No point registering
- * with fw, but forward to libFC
- */
- goto forward;
- }
- goto drop;
- }
- if (!eth_hdrs_stripped)
- goto drop;
-
-forward:
spin_lock_irqsave(&fnic->fnic_lock, flags);
if (fnic->stop_rx_link_events) {
spin_unlock_irqrestore(&fnic->fnic_lock, flags);
goto drop;
}
- /* Use fr_flags to indicate whether succ. flogi resp or not */
- fr_flags(fp) = 0;
fr_dev(fp) = fnic->lport;
spin_unlock_irqrestore(&fnic->fnic_lock, flags);
@@ -494,12 +459,49 @@ void fnic_free_rq_buf(struct vnic_rq *rq, struct vnic_rq_buf *buf)
buf->os_buf = NULL;
}
-static inline int is_flogi_frame(struct fc_frame_header *fh)
+/**
+ * fnic_eth_send() - Send Ethernet frame.
+ * @fip: fcoe_ctlr instance.
+ * @skb: Ethernet Frame, FIP, without VLAN encapsulation.
+ */
+void fnic_eth_send(struct fcoe_ctlr *fip, struct sk_buff *skb)
{
- return fh->fh_r_ctl == FC_RCTL_ELS_REQ && *(u8 *)(fh + 1) == ELS_FLOGI;
+ struct fnic *fnic = fnic_from_ctlr(fip);
+ struct vnic_wq *wq = &fnic->wq[0];
+ dma_addr_t pa;
+ struct ethhdr *eth_hdr;
+ struct vlan_ethhdr *vlan_hdr;
+ unsigned long flags;
+
+ if (!fnic->vlan_hw_insert) {
+ eth_hdr = (struct ethhdr *)skb_mac_header(skb);
+ vlan_hdr = (struct vlan_ethhdr *)skb_push(skb,
+ sizeof(*vlan_hdr) - sizeof(*eth_hdr));
+ memcpy(vlan_hdr, eth_hdr, 2 * ETH_ALEN);
+ vlan_hdr->h_vlan_proto = htons(ETH_P_8021Q);
+ vlan_hdr->h_vlan_encapsulated_proto = eth_hdr->h_proto;
+ vlan_hdr->h_vlan_TCI = htons(fnic->vlan_id);
+ }
+
+ pa = pci_map_single(fnic->pdev, skb->data, skb->len, PCI_DMA_TODEVICE);
+
+ spin_lock_irqsave(&fnic->wq_lock[0], flags);
+ if (!vnic_wq_desc_avail(wq)) {
+ pci_unmap_single(fnic->pdev, pa, skb->len, PCI_DMA_TODEVICE);
+ spin_unlock_irqrestore(&fnic->wq_lock[0], flags);
+ kfree_skb(skb);
+ return;
+ }
+
+ fnic_queue_wq_eth_desc(wq, skb, pa, skb->len,
+ fnic->vlan_hw_insert, fnic->vlan_id, 1);
+ spin_unlock_irqrestore(&fnic->wq_lock[0], flags);
}
-int fnic_send_frame(struct fnic *fnic, struct fc_frame *fp)
+/*
+ * Send FC frame.
+ */
+static int fnic_send_frame(struct fnic *fnic, struct fc_frame *fp)
{
struct vnic_wq *wq = &fnic->wq[0];
struct sk_buff *skb;
@@ -515,6 +517,10 @@ int fnic_send_frame(struct fnic *fnic, struct fc_frame *fp)
fh = fc_frame_header_get(fp);
skb = fp_skb(fp);
+ if (unlikely(fh->fh_r_ctl == FC_RCTL_ELS_REQ) &&
+ fcoe_ctlr_els_send(&fnic->ctlr, fnic->lport, skb))
+ return 0;
+
if (!fnic->vlan_hw_insert) {
eth_hdr_len = sizeof(*vlan_hdr) + sizeof(*fcoe_hdr);
vlan_hdr = (struct vlan_ethhdr *)skb_push(skb, eth_hdr_len);
@@ -530,16 +536,11 @@ int fnic_send_frame(struct fnic *fnic, struct fc_frame *fp)
fcoe_hdr = (struct fcoe_hdr *)(eth_hdr + 1);
}
- if (is_flogi_frame(fh)) {
+ if (fnic->ctlr.map_dest)
fc_fcoe_set_mac(eth_hdr->h_dest, fh->fh_d_id);
- memcpy(eth_hdr->h_source, fnic->mac_addr, ETH_ALEN);
- } else {
- if (fnic->fcoui_mode)
- fc_fcoe_set_mac(eth_hdr->h_dest, fh->fh_d_id);
- else
- memcpy(eth_hdr->h_dest, fnic->dest_addr, ETH_ALEN);
- memcpy(eth_hdr->h_source, fnic->data_src_addr, ETH_ALEN);
- }
+ else
+ memcpy(eth_hdr->h_dest, fnic->ctlr.dest_addr, ETH_ALEN);
+ memcpy(eth_hdr->h_source, fnic->data_src_addr, ETH_ALEN);
tot_len = skb->len;
BUG_ON(tot_len % 4);
@@ -578,109 +579,85 @@ fnic_send_frame_end:
int fnic_send(struct fc_lport *lp, struct fc_frame *fp)
{
struct fnic *fnic = lport_priv(lp);
- struct fc_frame_header *fh;
- int ret = 0;
- enum fnic_state old_state;
unsigned long flags;
- struct fc_frame *old_flogi = NULL;
- struct fc_frame *old_flogi_resp = NULL;
if (fnic->in_remove) {
dev_kfree_skb(fp_skb(fp));
- ret = -1;
- goto fnic_send_end;
+ return -1;
}
- fh = fc_frame_header_get(fp);
- /* if not an Flogi frame, send it out, this is the common case */
- if (!is_flogi_frame(fh))
- return fnic_send_frame(fnic, fp);
+ /*
+ * Queue frame if in a transitional state.
+ * This occurs while registering the Port_ID / MAC address after FLOGI.
+ */
+ spin_lock_irqsave(&fnic->fnic_lock, flags);
+ if (fnic->state != FNIC_IN_FC_MODE && fnic->state != FNIC_IN_ETH_MODE) {
+ skb_queue_tail(&fnic->tx_queue, fp_skb(fp));
+ spin_unlock_irqrestore(&fnic->fnic_lock, flags);
+ return 0;
+ }
+ spin_unlock_irqrestore(&fnic->fnic_lock, flags);
- /* Flogi frame, now enter the state machine */
+ return fnic_send_frame(fnic, fp);
+}
- spin_lock_irqsave(&fnic->fnic_lock, flags);
-again:
- /* Get any old cached frames, free them after dropping lock */
- old_flogi = fnic->flogi;
- fnic->flogi = NULL;
- old_flogi_resp = fnic->flogi_resp;
- fnic->flogi_resp = NULL;
+/**
+ * fnic_flush_tx() - send queued frames.
+ * @fnic: fnic device
+ *
+ * Send frames that were waiting to go out in FC or Ethernet mode.
+ * Whenever changing modes we purge queued frames, so these frames should
+ * be queued for the stable mode that we're in, either FC or Ethernet.
+ *
+ * Called without fnic_lock held.
+ */
+void fnic_flush_tx(struct fnic *fnic)
+{
+ struct sk_buff *skb;
+ struct fc_frame *fp;
- fnic->flogi_oxid = FC_XID_UNKNOWN;
+ while ((skb = skb_dequeue(&fnic->frame_queue))) {
+ fp = (struct fc_frame *)skb;
+ fnic_send_frame(fnic, fp);
+ }
+}
+/**
+ * fnic_set_eth_mode() - put fnic into ethernet mode.
+ * @fnic: fnic device
+ *
+ * Called without fnic lock held.
+ */
+static void fnic_set_eth_mode(struct fnic *fnic)
+{
+ unsigned long flags;
+ enum fnic_state old_state;
+ int ret;
+
+ spin_lock_irqsave(&fnic->fnic_lock, flags);
+again:
old_state = fnic->state;
switch (old_state) {
case FNIC_IN_FC_MODE:
case FNIC_IN_ETH_TRANS_FC_MODE:
default:
fnic->state = FNIC_IN_FC_TRANS_ETH_MODE;
- vnic_dev_del_addr(fnic->vdev, fnic->data_src_addr);
spin_unlock_irqrestore(&fnic->fnic_lock, flags);
- if (old_flogi) {
- dev_kfree_skb(fp_skb(old_flogi));
- old_flogi = NULL;
- }
- if (old_flogi_resp) {
- dev_kfree_skb(fp_skb(old_flogi_resp));
- old_flogi_resp = NULL;
- }
-
ret = fnic_fw_reset_handler(fnic);
spin_lock_irqsave(&fnic->fnic_lock, flags);
if (fnic->state != FNIC_IN_FC_TRANS_ETH_MODE)
goto again;
- if (ret) {
+ if (ret)
fnic->state = old_state;
- spin_unlock_irqrestore(&fnic->fnic_lock, flags);
- dev_kfree_skb(fp_skb(fp));
- goto fnic_send_end;
- }
- old_flogi = fnic->flogi;
- fnic->flogi = fp;
- fnic->flogi_oxid = ntohs(fh->fh_ox_id);
- old_flogi_resp = fnic->flogi_resp;
- fnic->flogi_resp = NULL;
- spin_unlock_irqrestore(&fnic->fnic_lock, flags);
break;
case FNIC_IN_FC_TRANS_ETH_MODE:
- /*
- * A reset is pending with the firmware. Store the flogi
- * and its oxid. The transition out of this state happens
- * only when Firmware completes the reset, either with
- * success or failed. If success, transition to
- * FNIC_IN_ETH_MODE, if fail, then transition to
- * FNIC_IN_FC_MODE
- */
- fnic->flogi = fp;
- fnic->flogi_oxid = ntohs(fh->fh_ox_id);
- spin_unlock_irqrestore(&fnic->fnic_lock, flags);
- break;
-
case FNIC_IN_ETH_MODE:
- /*
- * The fw/hw is already in eth mode. Store the oxid,
- * and send the flogi frame out. The transition out of this
- * state happens only we receive flogi response from the
- * network, and the oxid matches the cached oxid when the
- * flogi frame was sent out. If they match, then we issue
- * a flogi_reg request and transition to state
- * FNIC_IN_ETH_TRANS_FC_MODE
- */
- fnic->flogi_oxid = ntohs(fh->fh_ox_id);
- spin_unlock_irqrestore(&fnic->fnic_lock, flags);
- ret = fnic_send_frame(fnic, fp);
break;
}
-
-fnic_send_end:
- if (old_flogi)
- dev_kfree_skb(fp_skb(old_flogi));
- if (old_flogi_resp)
- dev_kfree_skb(fp_skb(old_flogi_resp));
- return ret;
+ spin_unlock_irqrestore(&fnic->fnic_lock, flags);
}
static void fnic_wq_complete_frame_send(struct vnic_wq *wq,
diff --git a/drivers/scsi/fnic/fnic_isr.c b/drivers/scsi/fnic/fnic_isr.c
index 2b3064828ae..5c1f223cabc 100644
--- a/drivers/scsi/fnic/fnic_isr.c
+++ b/drivers/scsi/fnic/fnic_isr.c
@@ -48,9 +48,9 @@ static irqreturn_t fnic_isr_legacy(int irq, void *data)
}
if (pba & (1 << FNIC_INTX_WQ_RQ_COPYWQ)) {
- work_done += fnic_wq_copy_cmpl_handler(fnic, 8);
- work_done += fnic_wq_cmpl_handler(fnic, 4);
- work_done += fnic_rq_cmpl_handler(fnic, 4);
+ work_done += fnic_wq_copy_cmpl_handler(fnic, -1);
+ work_done += fnic_wq_cmpl_handler(fnic, -1);
+ work_done += fnic_rq_cmpl_handler(fnic, -1);
vnic_intr_return_credits(&fnic->intr[FNIC_INTX_WQ_RQ_COPYWQ],
work_done,
@@ -66,9 +66,9 @@ static irqreturn_t fnic_isr_msi(int irq, void *data)
struct fnic *fnic = data;
unsigned long work_done = 0;
- work_done += fnic_wq_copy_cmpl_handler(fnic, 8);
- work_done += fnic_wq_cmpl_handler(fnic, 4);
- work_done += fnic_rq_cmpl_handler(fnic, 4);
+ work_done += fnic_wq_copy_cmpl_handler(fnic, -1);
+ work_done += fnic_wq_cmpl_handler(fnic, -1);
+ work_done += fnic_rq_cmpl_handler(fnic, -1);
vnic_intr_return_credits(&fnic->intr[0],
work_done,
@@ -83,7 +83,7 @@ static irqreturn_t fnic_isr_msix_rq(int irq, void *data)
struct fnic *fnic = data;
unsigned long rq_work_done = 0;
- rq_work_done = fnic_rq_cmpl_handler(fnic, 4);
+ rq_work_done = fnic_rq_cmpl_handler(fnic, -1);
vnic_intr_return_credits(&fnic->intr[FNIC_MSIX_RQ],
rq_work_done,
1 /* unmask intr */,
@@ -97,7 +97,7 @@ static irqreturn_t fnic_isr_msix_wq(int irq, void *data)
struct fnic *fnic = data;
unsigned long wq_work_done = 0;
- wq_work_done = fnic_wq_cmpl_handler(fnic, 4);
+ wq_work_done = fnic_wq_cmpl_handler(fnic, -1);
vnic_intr_return_credits(&fnic->intr[FNIC_MSIX_WQ],
wq_work_done,
1 /* unmask intr */,
@@ -110,7 +110,7 @@ static irqreturn_t fnic_isr_msix_wq_copy(int irq, void *data)
struct fnic *fnic = data;
unsigned long wq_copy_work_done = 0;
- wq_copy_work_done = fnic_wq_copy_cmpl_handler(fnic, 8);
+ wq_copy_work_done = fnic_wq_copy_cmpl_handler(fnic, -1);
vnic_intr_return_credits(&fnic->intr[FNIC_MSIX_WQ_COPY],
wq_copy_work_done,
1 /* unmask intr */,
diff --git a/drivers/scsi/fnic/fnic_main.c b/drivers/scsi/fnic/fnic_main.c
index 71c7bbe26d0..fe1b1031f7a 100644
--- a/drivers/scsi/fnic/fnic_main.c
+++ b/drivers/scsi/fnic/fnic_main.c
@@ -25,6 +25,8 @@
#include <linux/interrupt.h>
#include <linux/spinlock.h>
#include <linux/workqueue.h>
+#include <linux/if_ether.h>
+#include <scsi/fc/fc_fip.h>
#include <scsi/scsi_host.h>
#include <scsi/scsi_transport.h>
#include <scsi/scsi_transport_fc.h>
@@ -68,6 +70,7 @@ MODULE_PARM_DESC(fnic_log_level, "bit mask of fnic logging levels");
static struct libfc_function_template fnic_transport_template = {
.frame_send = fnic_send,
+ .lport_set_port_id = fnic_set_port_id,
.fcp_abort_io = fnic_empty_scsi_cleanup,
.fcp_cleanup = fnic_empty_scsi_cleanup,
.exch_mgr_reset = fnic_exch_mgr_reset
@@ -140,6 +143,7 @@ static struct fc_function_template fnic_fc_functions = {
.get_fc_host_stats = fnic_get_stats,
.dd_fcrport_size = sizeof(struct fc_rport_libfc_priv),
.terminate_rport_io = fnic_terminate_rport_io,
+ .bsg_request = fc_lport_bsg_request,
};
static void fnic_get_host_speed(struct Scsi_Host *shost)
@@ -324,9 +328,6 @@ static int fnic_cleanup(struct fnic *fnic)
{
unsigned int i;
int err;
- unsigned long flags;
- struct fc_frame *flogi = NULL;
- struct fc_frame *flogi_resp = NULL;
vnic_dev_disable(fnic->vdev);
for (i = 0; i < fnic->intr_count; i++)
@@ -367,24 +368,6 @@ static int fnic_cleanup(struct fnic *fnic)
for (i = 0; i < fnic->intr_count; i++)
vnic_intr_clean(&fnic->intr[i]);
- /*
- * Remove cached flogi and flogi resp frames if any
- * These frames are not in any queue, and therefore queue
- * cleanup does not clean them. So clean them explicitly
- */
- spin_lock_irqsave(&fnic->fnic_lock, flags);
- flogi = fnic->flogi;
- fnic->flogi = NULL;
- flogi_resp = fnic->flogi_resp;
- fnic->flogi_resp = NULL;
- spin_unlock_irqrestore(&fnic->fnic_lock, flags);
-
- if (flogi)
- dev_kfree_skb(fp_skb(flogi));
-
- if (flogi_resp)
- dev_kfree_skb(fp_skb(flogi_resp));
-
mempool_destroy(fnic->io_req_pool);
for (i = 0; i < FNIC_SGL_NUM_CACHES; i++)
mempool_destroy(fnic->io_sgl_pool[i]);
@@ -409,6 +392,17 @@ static void *fnic_alloc_slab_dma(gfp_t gfp_mask, void *pool_data)
return kmem_cache_alloc(mem, gfp_mask | GFP_ATOMIC | GFP_DMA);
}
+/**
+ * fnic_get_mac() - get assigned data MAC address for FIP code.
+ * @lport: local port.
+ */
+static u8 *fnic_get_mac(struct fc_lport *lport)
+{
+ struct fnic *fnic = lport_priv(lport);
+
+ return fnic->data_src_addr;
+}
+
static int __devinit fnic_probe(struct pci_dev *pdev,
const struct pci_device_id *ent)
{
@@ -424,17 +418,16 @@ static int __devinit fnic_probe(struct pci_dev *pdev,
* Allocate SCSI Host and set up association between host,
* local port, and fnic
*/
- host = scsi_host_alloc(&fnic_host_template,
- sizeof(struct fc_lport) + sizeof(struct fnic));
- if (!host) {
- printk(KERN_ERR PFX "Unable to alloc SCSI host\n");
+ lp = libfc_host_alloc(&fnic_host_template, sizeof(struct fnic));
+ if (!lp) {
+ printk(KERN_ERR PFX "Unable to alloc libfc local port\n");
err = -ENOMEM;
goto err_out;
}
- lp = shost_priv(host);
- lp->host = host;
+ host = lp->host;
fnic = lport_priv(lp);
fnic->lport = lp;
+ fnic->ctlr.lp = lp;
snprintf(fnic->name, sizeof(fnic->name) - 1, "%s%d", DRV_NAME,
host->host_no);
@@ -543,12 +536,14 @@ static int __devinit fnic_probe(struct pci_dev *pdev,
goto err_out_dev_close;
}
- err = vnic_dev_mac_addr(fnic->vdev, fnic->mac_addr);
+ err = vnic_dev_mac_addr(fnic->vdev, fnic->ctlr.ctl_src_addr);
if (err) {
shost_printk(KERN_ERR, fnic->lport->host,
"vNIC get MAC addr failed \n");
goto err_out_dev_close;
}
+ /* set data_src for point-to-point mode and to keep it non-zero */
+ memcpy(fnic->data_src_addr, fnic->ctlr.ctl_src_addr, ETH_ALEN);
/* Get vNIC configuration */
err = fnic_get_vnic_config(fnic);
@@ -560,6 +555,7 @@ static int __devinit fnic_probe(struct pci_dev *pdev,
}
host->max_lun = fnic->config.luns_per_tgt;
host->max_id = FNIC_MAX_FCP_TARGET;
+ host->max_cmd_len = FNIC_MAX_CMD_LEN;
fnic_get_res_counts(fnic);
@@ -571,19 +567,12 @@ static int __devinit fnic_probe(struct pci_dev *pdev,
goto err_out_dev_close;
}
- err = fnic_request_intr(fnic);
- if (err) {
- shost_printk(KERN_ERR, fnic->lport->host,
- "Unable to request irq.\n");
- goto err_out_clear_intr;
- }
-
err = fnic_alloc_vnic_resources(fnic);
if (err) {
shost_printk(KERN_ERR, fnic->lport->host,
"Failed to alloc vNIC resources, "
"aborting.\n");
- goto err_out_free_intr;
+ goto err_out_clear_intr;
}
@@ -623,9 +612,21 @@ static int __devinit fnic_probe(struct pci_dev *pdev,
fnic->vlan_hw_insert = 1;
fnic->vlan_id = 0;
- fnic->flogi_oxid = FC_XID_UNKNOWN;
- fnic->flogi = NULL;
- fnic->flogi_resp = NULL;
+ /* Initialize the FIP fcoe_ctrl struct */
+ fnic->ctlr.send = fnic_eth_send;
+ fnic->ctlr.update_mac = fnic_update_mac;
+ fnic->ctlr.get_src_addr = fnic_get_mac;
+ fcoe_ctlr_init(&fnic->ctlr);
+ if (fnic->config.flags & VFCF_FIP_CAPABLE) {
+ shost_printk(KERN_INFO, fnic->lport->host,
+ "firmware supports FIP\n");
+ vnic_dev_add_addr(fnic->vdev, FIP_ALL_ENODE_MACS);
+ vnic_dev_add_addr(fnic->vdev, fnic->ctlr.ctl_src_addr);
+ } else {
+ shost_printk(KERN_INFO, fnic->lport->host,
+ "firmware uses non-FIP mode\n");
+ fnic->ctlr.mode = FIP_ST_NON_FIP;
+ }
fnic->state = FNIC_IN_FC_MODE;
/* Enable hardware stripping of vlan header on ingress */
@@ -716,6 +717,7 @@ static int __devinit fnic_probe(struct pci_dev *pdev,
INIT_WORK(&fnic->link_work, fnic_handle_link);
INIT_WORK(&fnic->frame_work, fnic_handle_frame);
skb_queue_head_init(&fnic->frame_queue);
+ skb_queue_head_init(&fnic->tx_queue);
/* Enable all queues */
for (i = 0; i < fnic->raw_wq_count; i++)
@@ -728,6 +730,14 @@ static int __devinit fnic_probe(struct pci_dev *pdev,
fc_fabric_login(lp);
vnic_dev_enable(fnic->vdev);
+
+ err = fnic_request_intr(fnic);
+ if (err) {
+ shost_printk(KERN_ERR, fnic->lport->host,
+ "Unable to request irq.\n");
+ goto err_out_free_exch_mgr;
+ }
+
for (i = 0; i < fnic->intr_count; i++)
vnic_intr_unmask(&fnic->intr[i]);
@@ -738,8 +748,8 @@ static int __devinit fnic_probe(struct pci_dev *pdev,
err_out_free_exch_mgr:
fc_exch_mgr_free(lp);
err_out_remove_scsi_host:
- fc_remove_host(fnic->lport->host);
- scsi_remove_host(fnic->lport->host);
+ fc_remove_host(lp->host);
+ scsi_remove_host(lp->host);
err_out_free_rq_buf:
for (i = 0; i < fnic->rq_count; i++)
vnic_rq_clean(&fnic->rq[i], fnic_free_rq_buf);
@@ -752,8 +762,6 @@ err_out_free_ioreq_pool:
mempool_destroy(fnic->io_req_pool);
err_out_free_resources:
fnic_free_vnic_resources(fnic);
-err_out_free_intr:
- fnic_free_intr(fnic);
err_out_clear_intr:
fnic_clear_intr_mode(fnic);
err_out_dev_close:
@@ -775,6 +783,7 @@ err_out:
static void __devexit fnic_remove(struct pci_dev *pdev)
{
struct fnic *fnic = pci_get_drvdata(pdev);
+ struct fc_lport *lp = fnic->lport;
unsigned long flags;
/*
@@ -796,6 +805,7 @@ static void __devexit fnic_remove(struct pci_dev *pdev)
*/
flush_workqueue(fnic_event_queue);
skb_queue_purge(&fnic->frame_queue);
+ skb_queue_purge(&fnic->tx_queue);
/*
* Log off the fabric. This stops all remote ports, dns port,
@@ -808,7 +818,8 @@ static void __devexit fnic_remove(struct pci_dev *pdev)
fnic->in_remove = 1;
spin_unlock_irqrestore(&fnic->fnic_lock, flags);
- fc_lport_destroy(fnic->lport);
+ fcoe_ctlr_destroy(&fnic->ctlr);
+ fc_lport_destroy(lp);
/*
* This stops the fnic device, masks all interrupts. Completed
@@ -818,6 +829,7 @@ static void __devexit fnic_remove(struct pci_dev *pdev)
fnic_cleanup(fnic);
BUG_ON(!skb_queue_empty(&fnic->frame_queue));
+ BUG_ON(!skb_queue_empty(&fnic->tx_queue));
spin_lock_irqsave(&fnic_list_lock, flags);
list_del(&fnic->list);
@@ -827,8 +839,8 @@ static void __devexit fnic_remove(struct pci_dev *pdev)
scsi_remove_host(fnic->lport->host);
fc_exch_mgr_free(fnic->lport);
vnic_dev_notify_unset(fnic->vdev);
- fnic_free_vnic_resources(fnic);
fnic_free_intr(fnic);
+ fnic_free_vnic_resources(fnic);
fnic_clear_intr_mode(fnic);
vnic_dev_close(fnic->vdev);
vnic_dev_unregister(fnic->vdev);
@@ -836,7 +848,7 @@ static void __devexit fnic_remove(struct pci_dev *pdev)
pci_release_regions(pdev);
pci_disable_device(pdev);
pci_set_drvdata(pdev, NULL);
- scsi_host_put(fnic->lport->host);
+ scsi_host_put(lp->host);
}
static struct pci_driver fnic_driver = {
diff --git a/drivers/scsi/fnic/fnic_res.c b/drivers/scsi/fnic/fnic_res.c
index 7ba61ec715d..50488f8e169 100644
--- a/drivers/scsi/fnic/fnic_res.c
+++ b/drivers/scsi/fnic/fnic_res.c
@@ -144,10 +144,9 @@ int fnic_get_vnic_config(struct fnic *fnic)
c->intr_timer_type = c->intr_timer_type;
shost_printk(KERN_INFO, fnic->lport->host,
- "vNIC MAC addr %02x:%02x:%02x:%02x:%02x:%02x "
+ "vNIC MAC addr %pM "
"wq/wq_copy/rq %d/%d/%d\n",
- fnic->mac_addr[0], fnic->mac_addr[1], fnic->mac_addr[2],
- fnic->mac_addr[3], fnic->mac_addr[4], fnic->mac_addr[5],
+ fnic->ctlr.ctl_src_addr,
c->wq_enet_desc_count, c->wq_copy_desc_count,
c->rq_desc_count);
shost_printk(KERN_INFO, fnic->lport->host,
diff --git a/drivers/scsi/fnic/fnic_res.h b/drivers/scsi/fnic/fnic_res.h
index b6f31026253..ef8aaf2156d 100644
--- a/drivers/scsi/fnic/fnic_res.h
+++ b/drivers/scsi/fnic/fnic_res.h
@@ -51,6 +51,31 @@ static inline void fnic_queue_wq_desc(struct vnic_wq *wq,
vnic_wq_post(wq, os_buf, dma_addr, len, sop, eop);
}
+static inline void fnic_queue_wq_eth_desc(struct vnic_wq *wq,
+ void *os_buf, dma_addr_t dma_addr,
+ unsigned int len,
+ int vlan_tag_insert,
+ unsigned int vlan_tag,
+ int cq_entry)
+{
+ struct wq_enet_desc *desc = vnic_wq_next_desc(wq);
+
+ wq_enet_desc_enc(desc,
+ (u64)dma_addr | VNIC_PADDR_TARGET,
+ (u16)len,
+ 0, /* mss_or_csum_offset */
+ 0, /* fc_eof */
+ 0, /* offload_mode */
+ 1, /* eop */
+ (u8)cq_entry,
+ 0, /* fcoe_encap */
+ (u8)vlan_tag_insert,
+ (u16)vlan_tag,
+ 0 /* loopback */);
+
+ vnic_wq_post(wq, os_buf, dma_addr, len, 1, 1);
+}
+
static inline void fnic_queue_wq_copy_desc_icmnd_16(struct vnic_wq_copy *wq,
u32 req_id,
u32 lunmap_id, u8 spl_flags,
@@ -58,6 +83,7 @@ static inline void fnic_queue_wq_copy_desc_icmnd_16(struct vnic_wq_copy *wq,
u64 sgl_addr, u64 sns_addr,
u8 crn, u8 pri_ta,
u8 flags, u8 *scsi_cdb,
+ u8 cdb_len,
u32 data_len, u8 *lun,
u32 d_id, u16 mss,
u32 ratov, u32 edtov)
@@ -82,7 +108,8 @@ static inline void fnic_queue_wq_copy_desc_icmnd_16(struct vnic_wq_copy *wq,
desc->u.icmnd_16.pri_ta = pri_ta; /* SCSI Pri & Task attribute */
desc->u.icmnd_16._resvd1 = 0; /* reserved: should be 0 */
desc->u.icmnd_16.flags = flags; /* command flags */
- memcpy(desc->u.icmnd_16.scsi_cdb, scsi_cdb, CDB_16); /* SCSI CDB */
+ memset(desc->u.icmnd_16.scsi_cdb, 0, CDB_16);
+ memcpy(desc->u.icmnd_16.scsi_cdb, scsi_cdb, cdb_len); /* SCSI CDB */
desc->u.icmnd_16.data_len = data_len; /* length of data expected */
memcpy(desc->u.icmnd_16.lun, lun, LUN_ADDRESS); /* LUN address */
desc->u.icmnd_16._resvd2 = 0; /* reserved */
@@ -132,12 +159,37 @@ static inline void fnic_queue_wq_copy_desc_flogi_reg(struct vnic_wq_copy *wq,
desc->hdr.tag.u.req_id = req_id; /* id for this request */
desc->u.flogi_reg.format = format;
+ desc->u.flogi_reg._resvd = 0;
hton24(desc->u.flogi_reg.s_id, s_id);
memcpy(desc->u.flogi_reg.gateway_mac, gw_mac, ETH_ALEN);
vnic_wq_copy_post(wq);
}
+static inline void fnic_queue_wq_copy_desc_fip_reg(struct vnic_wq_copy *wq,
+ u32 req_id, u32 s_id,
+ u8 *fcf_mac, u8 *ha_mac,
+ u32 r_a_tov, u32 e_d_tov)
+{
+ struct fcpio_host_req *desc = vnic_wq_copy_next_desc(wq);
+
+ desc->hdr.type = FCPIO_FLOGI_FIP_REG; /* enum fcpio_type */
+ desc->hdr.status = 0; /* header status entry */
+ desc->hdr._resvd = 0; /* reserved */
+ desc->hdr.tag.u.req_id = req_id; /* id for this request */
+
+ desc->u.flogi_fip_reg._resvd0 = 0;
+ hton24(desc->u.flogi_fip_reg.s_id, s_id);
+ memcpy(desc->u.flogi_fip_reg.fcf_mac, fcf_mac, ETH_ALEN);
+ desc->u.flogi_fip_reg._resvd1 = 0;
+ desc->u.flogi_fip_reg.r_a_tov = r_a_tov;
+ desc->u.flogi_fip_reg.e_d_tov = e_d_tov;
+ memcpy(desc->u.flogi_fip_reg.ha_mac, ha_mac, ETH_ALEN);
+ desc->u.flogi_fip_reg._resvd2 = 0;
+
+ vnic_wq_copy_post(wq);
+}
+
static inline void fnic_queue_wq_copy_desc_fw_reset(struct vnic_wq_copy *wq,
u32 req_id)
{
diff --git a/drivers/scsi/fnic/fnic_scsi.c b/drivers/scsi/fnic/fnic_scsi.c
index bfc996971b8..65a39b0f6dc 100644
--- a/drivers/scsi/fnic/fnic_scsi.c
+++ b/drivers/scsi/fnic/fnic_scsi.c
@@ -174,6 +174,9 @@ int fnic_fw_reset_handler(struct fnic *fnic)
int ret = 0;
unsigned long flags;
+ skb_queue_purge(&fnic->frame_queue);
+ skb_queue_purge(&fnic->tx_queue);
+
spin_lock_irqsave(&fnic->wq_copy_lock[0], flags);
if (vnic_wq_copy_desc_avail(wq) <= fnic->wq_copy_desc_low[0])
@@ -200,9 +203,11 @@ int fnic_fw_reset_handler(struct fnic *fnic)
* fnic_flogi_reg_handler
* Routine to send flogi register msg to fw
*/
-int fnic_flogi_reg_handler(struct fnic *fnic)
+int fnic_flogi_reg_handler(struct fnic *fnic, u32 fc_id)
{
struct vnic_wq_copy *wq = &fnic->wq_copy[0];
+ enum fcpio_flogi_reg_format_type format;
+ struct fc_lport *lp = fnic->lport;
u8 gw_mac[ETH_ALEN];
int ret = 0;
unsigned long flags;
@@ -217,23 +222,32 @@ int fnic_flogi_reg_handler(struct fnic *fnic)
goto flogi_reg_ioreq_end;
}
- if (fnic->fcoui_mode)
+ if (fnic->ctlr.map_dest) {
memset(gw_mac, 0xff, ETH_ALEN);
- else
- memcpy(gw_mac, fnic->dest_addr, ETH_ALEN);
+ format = FCPIO_FLOGI_REG_DEF_DEST;
+ } else {
+ memcpy(gw_mac, fnic->ctlr.dest_addr, ETH_ALEN);
+ format = FCPIO_FLOGI_REG_GW_DEST;
+ }
- fnic_queue_wq_copy_desc_flogi_reg(wq, SCSI_NO_TAG,
- FCPIO_FLOGI_REG_GW_DEST,
- fnic->s_id,
- gw_mac);
+ if ((fnic->config.flags & VFCF_FIP_CAPABLE) && !fnic->ctlr.map_dest) {
+ fnic_queue_wq_copy_desc_fip_reg(wq, SCSI_NO_TAG,
+ fc_id, gw_mac,
+ fnic->data_src_addr,
+ lp->r_a_tov, lp->e_d_tov);
+ FNIC_SCSI_DBG(KERN_DEBUG, fnic->lport->host,
+ "FLOGI FIP reg issued fcid %x src %pM dest %pM\n",
+ fc_id, fnic->data_src_addr, gw_mac);
+ } else {
+ fnic_queue_wq_copy_desc_flogi_reg(wq, SCSI_NO_TAG,
+ format, fc_id, gw_mac);
+ FNIC_SCSI_DBG(KERN_DEBUG, fnic->lport->host,
+ "FLOGI reg issued fcid %x map %d dest %pM\n",
+ fc_id, fnic->ctlr.map_dest, gw_mac);
+ }
flogi_reg_ioreq_end:
spin_unlock_irqrestore(&fnic->wq_copy_lock[0], flags);
-
- if (!ret)
- FNIC_SCSI_DBG(KERN_DEBUG, fnic->lport->host,
- "flog reg issued\n");
-
return ret;
}
@@ -319,7 +333,8 @@ static inline int fnic_queue_wq_copy_desc(struct fnic *fnic,
0, /* scsi cmd ref, always 0 */
pri_tag, /* scsi pri and tag */
flags, /* command flags */
- sc->cmnd, scsi_bufflen(sc),
+ sc->cmnd, sc->cmd_len,
+ scsi_bufflen(sc),
fc_lun.scsi_lun, io_req->port_id,
rport->maxframe_size, rp->r_a_tov,
rp->e_d_tov);
@@ -452,7 +467,6 @@ static int fnic_fcpio_fw_reset_cmpl_handler(struct fnic *fnic,
u8 hdr_status;
struct fcpio_tag tag;
int ret = 0;
- struct fc_frame *flogi;
unsigned long flags;
fcpio_header_dec(&desc->hdr, &type, &hdr_status, &tag);
@@ -462,9 +476,6 @@ static int fnic_fcpio_fw_reset_cmpl_handler(struct fnic *fnic,
spin_lock_irqsave(&fnic->fnic_lock, flags);
- flogi = fnic->flogi;
- fnic->flogi = NULL;
-
/* fnic should be in FC_TRANS_ETH_MODE */
if (fnic->state == FNIC_IN_FC_TRANS_ETH_MODE) {
/* Check status of reset completion */
@@ -505,17 +516,14 @@ static int fnic_fcpio_fw_reset_cmpl_handler(struct fnic *fnic,
* free the flogi frame. Else, send it out
*/
if (fnic->remove_wait || ret) {
- fnic->flogi_oxid = FC_XID_UNKNOWN;
spin_unlock_irqrestore(&fnic->fnic_lock, flags);
- if (flogi)
- dev_kfree_skb_irq(fp_skb(flogi));
+ skb_queue_purge(&fnic->tx_queue);
goto reset_cmpl_handler_end;
}
spin_unlock_irqrestore(&fnic->fnic_lock, flags);
- if (flogi)
- ret = fnic_send_frame(fnic, flogi);
+ fnic_flush_tx(fnic);
reset_cmpl_handler_end:
return ret;
@@ -532,18 +540,13 @@ static int fnic_fcpio_flogi_reg_cmpl_handler(struct fnic *fnic,
u8 hdr_status;
struct fcpio_tag tag;
int ret = 0;
- struct fc_frame *flogi_resp = NULL;
unsigned long flags;
- struct sk_buff *skb;
fcpio_header_dec(&desc->hdr, &type, &hdr_status, &tag);
/* Update fnic state based on status of flogi reg completion */
spin_lock_irqsave(&fnic->fnic_lock, flags);
- flogi_resp = fnic->flogi_resp;
- fnic->flogi_resp = NULL;
-
if (fnic->state == FNIC_IN_ETH_TRANS_FC_MODE) {
/* Check flogi registration completion status */
@@ -567,25 +570,17 @@ static int fnic_fcpio_flogi_reg_cmpl_handler(struct fnic *fnic,
ret = -1;
}
- /* Successful flogi reg cmpl, pass frame to LibFC */
- if (!ret && flogi_resp) {
+ if (!ret) {
if (fnic->stop_rx_link_events) {
spin_unlock_irqrestore(&fnic->fnic_lock, flags);
goto reg_cmpl_handler_end;
}
- skb = (struct sk_buff *)flogi_resp;
- /* Use fr_flags to indicate whether flogi resp or not */
- fr_flags(flogi_resp) = 1;
- fr_dev(flogi_resp) = fnic->lport;
spin_unlock_irqrestore(&fnic->fnic_lock, flags);
- skb_queue_tail(&fnic->frame_queue, skb);
+ fnic_flush_tx(fnic);
queue_work(fnic_event_queue, &fnic->frame_work);
-
} else {
spin_unlock_irqrestore(&fnic->fnic_lock, flags);
- if (flogi_resp)
- dev_kfree_skb_irq(fp_skb(flogi_resp));
}
reg_cmpl_handler_end:
@@ -907,6 +902,7 @@ static int fnic_fcpio_cmpl_handler(struct vnic_dev *vdev,
break;
case FCPIO_FLOGI_REG_CMPL: /* fw completed flogi_reg */
+ case FCPIO_FLOGI_FIP_REG_CMPL: /* fw completed flogi_fip_reg */
ret = fnic_fcpio_flogi_reg_cmpl_handler(fnic, desc);
break;
@@ -1224,22 +1220,6 @@ void fnic_terminate_rport_io(struct fc_rport *rport)
}
-static void fnic_block_error_handler(struct scsi_cmnd *sc)
-{
- struct Scsi_Host *shost = sc->device->host;
- struct fc_rport *rport = starget_to_rport(scsi_target(sc->device));
- unsigned long flags;
-
- spin_lock_irqsave(shost->host_lock, flags);
- while (rport->port_state == FC_PORTSTATE_BLOCKED) {
- spin_unlock_irqrestore(shost->host_lock, flags);
- msleep(1000);
- spin_lock_irqsave(shost->host_lock, flags);
- }
- spin_unlock_irqrestore(shost->host_lock, flags);
-
-}
-
/*
* This function is exported to SCSI for sending abort cmnds.
* A SCSI IO is represented by a io_req in the driver.
@@ -1259,7 +1239,7 @@ int fnic_abort_cmd(struct scsi_cmnd *sc)
DECLARE_COMPLETION_ONSTACK(tm_done);
/* Wait for rport to unblock */
- fnic_block_error_handler(sc);
+ fc_block_scsi_eh(sc);
/* Get local-port, check ready and link up */
lp = shost_priv(sc->device->host);
@@ -1541,7 +1521,7 @@ int fnic_device_reset(struct scsi_cmnd *sc)
DECLARE_COMPLETION_ONSTACK(tm_done);
/* Wait for rport to unblock */
- fnic_block_error_handler(sc);
+ fc_block_scsi_eh(sc);
/* Get local-port, check ready and link up */
lp = shost_priv(sc->device->host);
@@ -1762,7 +1742,7 @@ void fnic_scsi_abort_io(struct fc_lport *lp)
fnic->remove_wait = &remove_wait;
old_state = fnic->state;
fnic->state = FNIC_IN_FC_TRANS_ETH_MODE;
- vnic_dev_del_addr(fnic->vdev, fnic->data_src_addr);
+ fnic_update_mac_locked(fnic, fnic->ctlr.ctl_src_addr);
spin_unlock_irqrestore(&fnic->fnic_lock, flags);
err = fnic_fw_reset_handler(fnic);
@@ -1802,7 +1782,7 @@ void fnic_scsi_cleanup(struct fc_lport *lp)
spin_lock_irqsave(&fnic->fnic_lock, flags);
old_state = fnic->state;
fnic->state = FNIC_IN_FC_TRANS_ETH_MODE;
- vnic_dev_del_addr(fnic->vdev, fnic->data_src_addr);
+ fnic_update_mac_locked(fnic, fnic->ctlr.ctl_src_addr);
spin_unlock_irqrestore(&fnic->fnic_lock, flags);
if (fnic_fw_reset_handler(fnic)) {
diff --git a/drivers/scsi/fnic/vnic_scsi.h b/drivers/scsi/fnic/vnic_scsi.h
index 46baa525400..fbb55364e27 100644
--- a/drivers/scsi/fnic/vnic_scsi.h
+++ b/drivers/scsi/fnic/vnic_scsi.h
@@ -95,5 +95,6 @@ struct vnic_fc_config {
#define VFCF_FCP_SEQ_LVL_ERR 0x1 /* Enable FCP-2 Error Recovery */
#define VFCF_PERBI 0x2 /* persistent binding info available */
+#define VFCF_FIP_CAPABLE 0x4 /* firmware can handle FIP */
#endif /* _VNIC_SCSI_H_ */
diff --git a/drivers/scsi/hosts.c b/drivers/scsi/hosts.c
index c968cc31cd8..554626e1806 100644
--- a/drivers/scsi/hosts.c
+++ b/drivers/scsi/hosts.c
@@ -180,14 +180,20 @@ void scsi_remove_host(struct Scsi_Host *shost)
EXPORT_SYMBOL(scsi_remove_host);
/**
- * scsi_add_host - add a scsi host
+ * scsi_add_host_with_dma - add a scsi host with dma device
* @shost: scsi host pointer to add
* @dev: a struct device of type scsi class
+ * @dma_dev: dma device for the host
+ *
+ * Note: You rarely need to worry about this unless you're in a
+ * virtualised host environments, so use the simpler scsi_add_host()
+ * function instead.
*
* Return value:
* 0 on success / != 0 for error
**/
-int scsi_add_host(struct Scsi_Host *shost, struct device *dev)
+int scsi_add_host_with_dma(struct Scsi_Host *shost, struct device *dev,
+ struct device *dma_dev)
{
struct scsi_host_template *sht = shost->hostt;
int error = -EINVAL;
@@ -207,6 +213,7 @@ int scsi_add_host(struct Scsi_Host *shost, struct device *dev)
if (!shost->shost_gendev.parent)
shost->shost_gendev.parent = dev ? dev : &platform_bus;
+ shost->dma_dev = dma_dev;
error = device_add(&shost->shost_gendev);
if (error)
@@ -262,7 +269,7 @@ int scsi_add_host(struct Scsi_Host *shost, struct device *dev)
fail:
return error;
}
-EXPORT_SYMBOL(scsi_add_host);
+EXPORT_SYMBOL(scsi_add_host_with_dma);
static void scsi_host_dev_release(struct device *dev)
{
diff --git a/drivers/scsi/hptiop.c b/drivers/scsi/hptiop.c
index 5be67a6fca9..4f0556571f8 100644
--- a/drivers/scsi/hptiop.c
+++ b/drivers/scsi/hptiop.c
@@ -861,10 +861,13 @@ static int hptiop_reset(struct scsi_cmnd *scp)
}
static int hptiop_adjust_disk_queue_depth(struct scsi_device *sdev,
- int queue_depth)
+ int queue_depth, int reason)
{
struct hptiop_hba *hba = (struct hptiop_hba *)sdev->host->hostdata;
+ if (reason != SCSI_QDEPTH_DEFAULT)
+ return -EOPNOTSUPP;
+
if (queue_depth > hba->max_requests)
queue_depth = hba->max_requests;
scsi_adjust_queue_depth(sdev, MSG_ORDERED_TAG, queue_depth);
diff --git a/drivers/scsi/ibmvscsi/ibmvfc.c b/drivers/scsi/ibmvscsi/ibmvfc.c
index bb2c696c006..87b536a97cb 100644
--- a/drivers/scsi/ibmvscsi/ibmvfc.c
+++ b/drivers/scsi/ibmvscsi/ibmvfc.c
@@ -39,6 +39,7 @@
#include <scsi/scsi_device.h>
#include <scsi/scsi_tcq.h>
#include <scsi/scsi_transport_fc.h>
+#include <scsi/scsi_bsg_fc.h>
#include "ibmvfc.h"
static unsigned int init_timeout = IBMVFC_INIT_TIMEOUT;
@@ -558,12 +559,11 @@ static void ibmvfc_link_down(struct ibmvfc_host *vhost,
/**
* ibmvfc_init_host - Start host initialization
* @vhost: ibmvfc host struct
- * @relogin: is this a re-login?
*
* Return value:
* nothing
**/
-static void ibmvfc_init_host(struct ibmvfc_host *vhost, int relogin)
+static void ibmvfc_init_host(struct ibmvfc_host *vhost)
{
struct ibmvfc_target *tgt;
@@ -577,10 +577,8 @@ static void ibmvfc_init_host(struct ibmvfc_host *vhost, int relogin)
}
if (!ibmvfc_set_host_state(vhost, IBMVFC_INITIALIZING)) {
- if (!relogin) {
- memset(vhost->async_crq.msgs, 0, PAGE_SIZE);
- vhost->async_crq.cur = 0;
- }
+ memset(vhost->async_crq.msgs, 0, PAGE_SIZE);
+ vhost->async_crq.cur = 0;
list_for_each_entry(tgt, &vhost->targets, queue)
ibmvfc_set_tgt_action(tgt, IBMVFC_TGT_ACTION_DEL_RPORT);
@@ -1678,6 +1676,276 @@ static void ibmvfc_sync_completion(struct ibmvfc_event *evt)
}
/**
+ * ibmvfc_bsg_timeout_done - Completion handler for cancelling BSG commands
+ * @evt: struct ibmvfc_event
+ *
+ **/
+static void ibmvfc_bsg_timeout_done(struct ibmvfc_event *evt)
+{
+ struct ibmvfc_host *vhost = evt->vhost;
+
+ ibmvfc_free_event(evt);
+ vhost->aborting_passthru = 0;
+ dev_info(vhost->dev, "Passthru command cancelled\n");
+}
+
+/**
+ * ibmvfc_bsg_timeout - Handle a BSG timeout
+ * @job: struct fc_bsg_job that timed out
+ *
+ * Returns:
+ * 0 on success / other on failure
+ **/
+static int ibmvfc_bsg_timeout(struct fc_bsg_job *job)
+{
+ struct ibmvfc_host *vhost = shost_priv(job->shost);
+ unsigned long port_id = (unsigned long)job->dd_data;
+ struct ibmvfc_event *evt;
+ struct ibmvfc_tmf *tmf;
+ unsigned long flags;
+ int rc;
+
+ ENTER;
+ spin_lock_irqsave(vhost->host->host_lock, flags);
+ if (vhost->aborting_passthru || vhost->state != IBMVFC_ACTIVE) {
+ __ibmvfc_reset_host(vhost);
+ spin_unlock_irqrestore(vhost->host->host_lock, flags);
+ return 0;
+ }
+
+ vhost->aborting_passthru = 1;
+ evt = ibmvfc_get_event(vhost);
+ ibmvfc_init_event(evt, ibmvfc_bsg_timeout_done, IBMVFC_MAD_FORMAT);
+
+ tmf = &evt->iu.tmf;
+ memset(tmf, 0, sizeof(*tmf));
+ tmf->common.version = 1;
+ tmf->common.opcode = IBMVFC_TMF_MAD;
+ tmf->common.length = sizeof(*tmf);
+ tmf->scsi_id = port_id;
+ tmf->cancel_key = IBMVFC_PASSTHRU_CANCEL_KEY;
+ tmf->my_cancel_key = IBMVFC_INTERNAL_CANCEL_KEY;
+ rc = ibmvfc_send_event(evt, vhost, default_timeout);
+
+ if (rc != 0) {
+ vhost->aborting_passthru = 0;
+ dev_err(vhost->dev, "Failed to send cancel event. rc=%d\n", rc);
+ rc = -EIO;
+ } else
+ dev_info(vhost->dev, "Cancelling passthru command to port id 0x%lx\n",
+ port_id);
+
+ spin_unlock_irqrestore(vhost->host->host_lock, flags);
+
+ LEAVE;
+ return rc;
+}
+
+/**
+ * ibmvfc_bsg_plogi - PLOGI into a target to handle a BSG command
+ * @vhost: struct ibmvfc_host to send command
+ * @port_id: port ID to send command
+ *
+ * Returns:
+ * 0 on success / other on failure
+ **/
+static int ibmvfc_bsg_plogi(struct ibmvfc_host *vhost, unsigned int port_id)
+{
+ struct ibmvfc_port_login *plogi;
+ struct ibmvfc_target *tgt;
+ struct ibmvfc_event *evt;
+ union ibmvfc_iu rsp_iu;
+ unsigned long flags;
+ int rc = 0, issue_login = 1;
+
+ ENTER;
+ spin_lock_irqsave(vhost->host->host_lock, flags);
+ list_for_each_entry(tgt, &vhost->targets, queue) {
+ if (tgt->scsi_id == port_id) {
+ issue_login = 0;
+ break;
+ }
+ }
+
+ if (!issue_login)
+ goto unlock_out;
+ if (unlikely((rc = ibmvfc_host_chkready(vhost))))
+ goto unlock_out;
+
+ evt = ibmvfc_get_event(vhost);
+ ibmvfc_init_event(evt, ibmvfc_sync_completion, IBMVFC_MAD_FORMAT);
+ plogi = &evt->iu.plogi;
+ memset(plogi, 0, sizeof(*plogi));
+ plogi->common.version = 1;
+ plogi->common.opcode = IBMVFC_PORT_LOGIN;
+ plogi->common.length = sizeof(*plogi);
+ plogi->scsi_id = port_id;
+ evt->sync_iu = &rsp_iu;
+ init_completion(&evt->comp);
+
+ rc = ibmvfc_send_event(evt, vhost, default_timeout);
+ spin_unlock_irqrestore(vhost->host->host_lock, flags);
+
+ if (rc)
+ return -EIO;
+
+ wait_for_completion(&evt->comp);
+
+ if (rsp_iu.plogi.common.status)
+ rc = -EIO;
+
+ spin_lock_irqsave(vhost->host->host_lock, flags);
+ ibmvfc_free_event(evt);
+unlock_out:
+ spin_unlock_irqrestore(vhost->host->host_lock, flags);
+ LEAVE;
+ return rc;
+}
+
+/**
+ * ibmvfc_bsg_request - Handle a BSG request
+ * @job: struct fc_bsg_job to be executed
+ *
+ * Returns:
+ * 0 on success / other on failure
+ **/
+static int ibmvfc_bsg_request(struct fc_bsg_job *job)
+{
+ struct ibmvfc_host *vhost = shost_priv(job->shost);
+ struct fc_rport *rport = job->rport;
+ struct ibmvfc_passthru_mad *mad;
+ struct ibmvfc_event *evt;
+ union ibmvfc_iu rsp_iu;
+ unsigned long flags, port_id = -1;
+ unsigned int code = job->request->msgcode;
+ int rc = 0, req_seg, rsp_seg, issue_login = 0;
+ u32 fc_flags, rsp_len;
+
+ ENTER;
+ job->reply->reply_payload_rcv_len = 0;
+ if (rport)
+ port_id = rport->port_id;
+
+ switch (code) {
+ case FC_BSG_HST_ELS_NOLOGIN:
+ port_id = (job->request->rqst_data.h_els.port_id[0] << 16) |
+ (job->request->rqst_data.h_els.port_id[1] << 8) |
+ job->request->rqst_data.h_els.port_id[2];
+ case FC_BSG_RPT_ELS:
+ fc_flags = IBMVFC_FC_ELS;
+ break;
+ case FC_BSG_HST_CT:
+ issue_login = 1;
+ port_id = (job->request->rqst_data.h_ct.port_id[0] << 16) |
+ (job->request->rqst_data.h_ct.port_id[1] << 8) |
+ job->request->rqst_data.h_ct.port_id[2];
+ case FC_BSG_RPT_CT:
+ fc_flags = IBMVFC_FC_CT_IU;
+ break;
+ default:
+ return -ENOTSUPP;
+ };
+
+ if (port_id == -1)
+ return -EINVAL;
+ if (!mutex_trylock(&vhost->passthru_mutex))
+ return -EBUSY;
+
+ job->dd_data = (void *)port_id;
+ req_seg = dma_map_sg(vhost->dev, job->request_payload.sg_list,
+ job->request_payload.sg_cnt, DMA_TO_DEVICE);
+
+ if (!req_seg) {
+ mutex_unlock(&vhost->passthru_mutex);
+ return -ENOMEM;
+ }
+
+ rsp_seg = dma_map_sg(vhost->dev, job->reply_payload.sg_list,
+ job->reply_payload.sg_cnt, DMA_FROM_DEVICE);
+
+ if (!rsp_seg) {
+ dma_unmap_sg(vhost->dev, job->request_payload.sg_list,
+ job->request_payload.sg_cnt, DMA_TO_DEVICE);
+ mutex_unlock(&vhost->passthru_mutex);
+ return -ENOMEM;
+ }
+
+ if (req_seg > 1 || rsp_seg > 1) {
+ rc = -EINVAL;
+ goto out;
+ }
+
+ if (issue_login)
+ rc = ibmvfc_bsg_plogi(vhost, port_id);
+
+ spin_lock_irqsave(vhost->host->host_lock, flags);
+
+ if (unlikely(rc || (rport && (rc = fc_remote_port_chkready(rport)))) ||
+ unlikely((rc = ibmvfc_host_chkready(vhost)))) {
+ spin_unlock_irqrestore(vhost->host->host_lock, flags);
+ goto out;
+ }
+
+ evt = ibmvfc_get_event(vhost);
+ ibmvfc_init_event(evt, ibmvfc_sync_completion, IBMVFC_MAD_FORMAT);
+ mad = &evt->iu.passthru;
+
+ memset(mad, 0, sizeof(*mad));
+ mad->common.version = 1;
+ mad->common.opcode = IBMVFC_PASSTHRU;
+ mad->common.length = sizeof(*mad) - sizeof(mad->fc_iu) - sizeof(mad->iu);
+
+ mad->cmd_ioba.va = (u64)evt->crq.ioba +
+ offsetof(struct ibmvfc_passthru_mad, iu);
+ mad->cmd_ioba.len = sizeof(mad->iu);
+
+ mad->iu.cmd_len = job->request_payload.payload_len;
+ mad->iu.rsp_len = job->reply_payload.payload_len;
+ mad->iu.flags = fc_flags;
+ mad->iu.cancel_key = IBMVFC_PASSTHRU_CANCEL_KEY;
+
+ mad->iu.cmd.va = sg_dma_address(job->request_payload.sg_list);
+ mad->iu.cmd.len = sg_dma_len(job->request_payload.sg_list);
+ mad->iu.rsp.va = sg_dma_address(job->reply_payload.sg_list);
+ mad->iu.rsp.len = sg_dma_len(job->reply_payload.sg_list);
+ mad->iu.scsi_id = port_id;
+ mad->iu.tag = (u64)evt;
+ rsp_len = mad->iu.rsp.len;
+
+ evt->sync_iu = &rsp_iu;
+ init_completion(&evt->comp);
+ rc = ibmvfc_send_event(evt, vhost, 0);
+ spin_unlock_irqrestore(vhost->host->host_lock, flags);
+
+ if (rc) {
+ rc = -EIO;
+ goto out;
+ }
+
+ wait_for_completion(&evt->comp);
+
+ if (rsp_iu.passthru.common.status)
+ rc = -EIO;
+ else
+ job->reply->reply_payload_rcv_len = rsp_len;
+
+ spin_lock_irqsave(vhost->host->host_lock, flags);
+ ibmvfc_free_event(evt);
+ spin_unlock_irqrestore(vhost->host->host_lock, flags);
+ job->reply->result = rc;
+ job->job_done(job);
+ rc = 0;
+out:
+ dma_unmap_sg(vhost->dev, job->request_payload.sg_list,
+ job->request_payload.sg_cnt, DMA_TO_DEVICE);
+ dma_unmap_sg(vhost->dev, job->reply_payload.sg_list,
+ job->reply_payload.sg_cnt, DMA_FROM_DEVICE);
+ mutex_unlock(&vhost->passthru_mutex);
+ LEAVE;
+ return rc;
+}
+
+/**
* ibmvfc_reset_device - Reset the device with the specified reset type
* @sdev: scsi device to reset
* @type: reset type
@@ -1731,7 +1999,10 @@ static int ibmvfc_reset_device(struct scsi_device *sdev, int type, char *desc)
sdev_printk(KERN_INFO, sdev, "Resetting %s\n", desc);
wait_for_completion(&evt->comp);
- if (rsp_iu.cmd.status) {
+ if (rsp_iu.cmd.status)
+ rsp_code = ibmvfc_get_err_result(&rsp_iu.cmd);
+
+ if (rsp_code) {
if (fc_rsp->flags & FCP_RSP_LEN_VALID)
rsp_code = fc_rsp->data.info.rsp_code;
@@ -1820,7 +2091,10 @@ static int ibmvfc_abort_task_set(struct scsi_device *sdev)
sdev_printk(KERN_INFO, sdev, "Aborting outstanding commands\n");
wait_for_completion(&evt->comp);
- if (rsp_iu.cmd.status) {
+ if (rsp_iu.cmd.status)
+ rsp_code = ibmvfc_get_err_result(&rsp_iu.cmd);
+
+ if (rsp_code) {
if (fc_rsp->flags & FCP_RSP_LEN_VALID)
rsp_code = fc_rsp->data.info.rsp_code;
@@ -2061,12 +2335,24 @@ static int ibmvfc_eh_device_reset_handler(struct scsi_cmnd *cmd)
}
/**
- * ibmvfc_dev_cancel_all - Device iterated cancel all function
+ * ibmvfc_dev_cancel_all_abts - Device iterated cancel all function
* @sdev: scsi device struct
* @data: return code
*
**/
-static void ibmvfc_dev_cancel_all(struct scsi_device *sdev, void *data)
+static void ibmvfc_dev_cancel_all_abts(struct scsi_device *sdev, void *data)
+{
+ unsigned long *rc = data;
+ *rc |= ibmvfc_cancel_all(sdev, IBMVFC_TMF_ABORT_TASK_SET);
+}
+
+/**
+ * ibmvfc_dev_cancel_all_reset - Device iterated cancel all function
+ * @sdev: scsi device struct
+ * @data: return code
+ *
+ **/
+static void ibmvfc_dev_cancel_all_reset(struct scsi_device *sdev, void *data)
{
unsigned long *rc = data;
*rc |= ibmvfc_cancel_all(sdev, IBMVFC_TMF_TGT_RESET);
@@ -2102,7 +2388,7 @@ static int ibmvfc_eh_target_reset_handler(struct scsi_cmnd *cmd)
ENTER;
ibmvfc_wait_while_resetting(vhost);
- starget_for_each_device(starget, &cancel_rc, ibmvfc_dev_cancel_all);
+ starget_for_each_device(starget, &cancel_rc, ibmvfc_dev_cancel_all_reset);
reset_rc = ibmvfc_reset_device(sdev, IBMVFC_TARGET_RESET, "target");
if (!cancel_rc && !reset_rc)
@@ -2144,7 +2430,7 @@ static void ibmvfc_terminate_rport_io(struct fc_rport *rport)
int rc = FAILED;
ENTER;
- starget_for_each_device(starget, &cancel_rc, ibmvfc_dev_cancel_all);
+ starget_for_each_device(starget, &cancel_rc, ibmvfc_dev_cancel_all_abts);
starget_for_each_device(starget, &abort_rc, ibmvfc_dev_abort_all);
if (!cancel_rc && !abort_rc)
@@ -2297,13 +2583,13 @@ static void ibmvfc_handle_crq(struct ibmvfc_crq *crq, struct ibmvfc_host *vhost)
/* Send back a response */
rc = ibmvfc_send_crq_init_complete(vhost);
if (rc == 0)
- ibmvfc_init_host(vhost, 0);
+ ibmvfc_init_host(vhost);
else
dev_err(vhost->dev, "Unable to send init rsp. rc=%ld\n", rc);
break;
case IBMVFC_CRQ_INIT_COMPLETE:
dev_info(vhost->dev, "Partner initialization complete\n");
- ibmvfc_init_host(vhost, 0);
+ ibmvfc_init_host(vhost);
break;
default:
dev_err(vhost->dev, "Unknown crq message type: %d\n", crq->format);
@@ -2478,12 +2764,17 @@ static int ibmvfc_slave_configure(struct scsi_device *sdev)
* ibmvfc_change_queue_depth - Change the device's queue depth
* @sdev: scsi device struct
* @qdepth: depth to set
+ * @reason: calling context
*
* Return value:
* actual depth set
**/
-static int ibmvfc_change_queue_depth(struct scsi_device *sdev, int qdepth)
+static int ibmvfc_change_queue_depth(struct scsi_device *sdev, int qdepth,
+ int reason)
{
+ if (reason != SCSI_QDEPTH_DEFAULT)
+ return -EOPNOTSUPP;
+
if (qdepth > IBMVFC_MAX_CMDS_PER_LUN)
qdepth = IBMVFC_MAX_CMDS_PER_LUN;
@@ -3725,7 +4016,7 @@ static void ibmvfc_npiv_logout_done(struct ibmvfc_event *evt)
case IBMVFC_MAD_SUCCESS:
if (list_empty(&vhost->sent) &&
vhost->action == IBMVFC_HOST_ACTION_LOGO_WAIT) {
- ibmvfc_init_host(vhost, 0);
+ ibmvfc_init_host(vhost);
return;
}
break;
@@ -3903,6 +4194,8 @@ static void ibmvfc_tgt_add_rport(struct ibmvfc_target *tgt)
rport->supported_classes |= FC_COS_CLASS2;
if (tgt->service_parms.class3_parms[0] & 0x80000000)
rport->supported_classes |= FC_COS_CLASS3;
+ if (rport->rqst_q)
+ blk_queue_max_hw_segments(rport->rqst_q, 1);
} else
tgt_dbg(tgt, "rport add failed\n");
spin_unlock_irqrestore(vhost->host->host_lock, flags);
@@ -4342,6 +4635,7 @@ static int ibmvfc_probe(struct vio_dev *vdev, const struct vio_device_id *id)
init_waitqueue_head(&vhost->work_wait_q);
init_waitqueue_head(&vhost->init_wait_q);
INIT_WORK(&vhost->rport_add_work_q, ibmvfc_rport_add_thread);
+ mutex_init(&vhost->passthru_mutex);
if ((rc = ibmvfc_alloc_mem(vhost)))
goto free_scsi_host;
@@ -4374,6 +4668,8 @@ static int ibmvfc_probe(struct vio_dev *vdev, const struct vio_device_id *id)
goto remove_shost;
}
+ if (shost_to_fc_host(shost)->rqst_q)
+ blk_queue_max_hw_segments(shost_to_fc_host(shost)->rqst_q, 1);
dev_set_drvdata(dev, vhost);
spin_lock(&ibmvfc_driver_lock);
list_add_tail(&vhost->queue, &ibmvfc_head);
@@ -4414,7 +4710,11 @@ static int ibmvfc_remove(struct vio_dev *vdev)
ENTER;
ibmvfc_remove_trace_file(&vhost->host->shost_dev.kobj, &ibmvfc_trace_attr);
+
+ spin_lock_irqsave(vhost->host->host_lock, flags);
ibmvfc_link_down(vhost, IBMVFC_HOST_OFFLINE);
+ spin_unlock_irqrestore(vhost->host->host_lock, flags);
+
ibmvfc_wait_while_resetting(vhost);
ibmvfc_release_crq_queue(vhost);
kthread_stop(vhost->work_thread);
@@ -4498,6 +4798,9 @@ static struct fc_function_template ibmvfc_transport_functions = {
.get_starget_port_id = ibmvfc_get_starget_port_id,
.show_starget_port_id = 1,
+
+ .bsg_request = ibmvfc_bsg_request,
+ .bsg_timeout = ibmvfc_bsg_timeout,
};
/**
diff --git a/drivers/scsi/ibmvscsi/ibmvfc.h b/drivers/scsi/ibmvscsi/ibmvfc.h
index 007fa1c9ef1..d25106a958d 100644
--- a/drivers/scsi/ibmvscsi/ibmvfc.h
+++ b/drivers/scsi/ibmvscsi/ibmvfc.h
@@ -29,8 +29,8 @@
#include "viosrp.h"
#define IBMVFC_NAME "ibmvfc"
-#define IBMVFC_DRIVER_VERSION "1.0.6"
-#define IBMVFC_DRIVER_DATE "(May 28, 2009)"
+#define IBMVFC_DRIVER_VERSION "1.0.7"
+#define IBMVFC_DRIVER_DATE "(October 16, 2009)"
#define IBMVFC_DEFAULT_TIMEOUT 60
#define IBMVFC_ADISC_CANCEL_TIMEOUT 45
@@ -58,9 +58,10 @@
* 1 for ERP
* 1 for initialization
* 1 for NPIV Logout
+ * 2 for BSG passthru
* 2 for each discovery thread
*/
-#define IBMVFC_NUM_INTERNAL_REQ (1 + 1 + 1 + (disc_threads * 2))
+#define IBMVFC_NUM_INTERNAL_REQ (1 + 1 + 1 + 2 + (disc_threads * 2))
#define IBMVFC_MAD_SUCCESS 0x00
#define IBMVFC_MAD_NOT_SUPPORTED 0xF1
@@ -466,7 +467,10 @@ struct ibmvfc_passthru_iu {
u16 error;
u32 flags;
#define IBMVFC_FC_ELS 0x01
+#define IBMVFC_FC_CT_IU 0x02
u32 cancel_key;
+#define IBMVFC_PASSTHRU_CANCEL_KEY 0x80000000
+#define IBMVFC_INTERNAL_CANCEL_KEY 0x80000001
u32 reserved;
struct srp_direct_buf cmd;
struct srp_direct_buf rsp;
@@ -693,6 +697,7 @@ struct ibmvfc_host {
int disc_buf_sz;
int log_level;
struct ibmvfc_discover_targets_buf *disc_buf;
+ struct mutex passthru_mutex;
int task_set;
int init_retries;
int discovery_threads;
@@ -702,6 +707,7 @@ struct ibmvfc_host {
int delay_init;
int scan_complete;
int logged_in;
+ int aborting_passthru;
int events_to_log;
#define IBMVFC_AE_LINKUP 0x0001
#define IBMVFC_AE_LINKDOWN 0x0002
diff --git a/drivers/scsi/ibmvscsi/ibmvscsi.c b/drivers/scsi/ibmvscsi/ibmvscsi.c
index d9b0e9d3198..e475b7957c2 100644
--- a/drivers/scsi/ibmvscsi/ibmvscsi.c
+++ b/drivers/scsi/ibmvscsi/ibmvscsi.c
@@ -1637,12 +1637,17 @@ static int ibmvscsi_slave_configure(struct scsi_device *sdev)
* ibmvscsi_change_queue_depth - Change the device's queue depth
* @sdev: scsi device struct
* @qdepth: depth to set
+ * @reason: calling context
*
* Return value:
* actual depth set
**/
-static int ibmvscsi_change_queue_depth(struct scsi_device *sdev, int qdepth)
+static int ibmvscsi_change_queue_depth(struct scsi_device *sdev, int qdepth,
+ int reason)
{
+ if (reason != SCSI_QDEPTH_DEFAULT)
+ return -EOPNOTSUPP;
+
if (qdepth > IBMVSCSI_MAX_CMDS_PER_LUN)
qdepth = IBMVSCSI_MAX_CMDS_PER_LUN;
diff --git a/drivers/scsi/ipr.c b/drivers/scsi/ipr.c
index 76d294fc784..206c2fa8c1b 100644
--- a/drivers/scsi/ipr.c
+++ b/drivers/scsi/ipr.c
@@ -3367,16 +3367,21 @@ static int ipr_free_dump(struct ipr_ioa_cfg *ioa_cfg) { return 0; };
* ipr_change_queue_depth - Change the device's queue depth
* @sdev: scsi device struct
* @qdepth: depth to set
+ * @reason: calling context
*
* Return value:
* actual depth set
**/
-static int ipr_change_queue_depth(struct scsi_device *sdev, int qdepth)
+static int ipr_change_queue_depth(struct scsi_device *sdev, int qdepth,
+ int reason)
{
struct ipr_ioa_cfg *ioa_cfg = (struct ipr_ioa_cfg *)sdev->host->hostdata;
struct ipr_resource_entry *res;
unsigned long lock_flags = 0;
+ if (reason != SCSI_QDEPTH_DEFAULT)
+ return -EOPNOTSUPP;
+
spin_lock_irqsave(ioa_cfg->host->host_lock, lock_flags);
res = (struct ipr_resource_entry *)sdev->hostdata;
diff --git a/drivers/scsi/iscsi_tcp.c b/drivers/scsi/iscsi_tcp.c
index edc49ca49ce..517da3fd89d 100644
--- a/drivers/scsi/iscsi_tcp.c
+++ b/drivers/scsi/iscsi_tcp.c
@@ -903,7 +903,7 @@ static struct iscsi_transport iscsi_sw_tcp_transport = {
ISCSI_USERNAME | ISCSI_PASSWORD |
ISCSI_USERNAME_IN | ISCSI_PASSWORD_IN |
ISCSI_FAST_ABORT | ISCSI_ABORT_TMO |
- ISCSI_LU_RESET_TMO |
+ ISCSI_LU_RESET_TMO | ISCSI_TGT_RESET_TMO |
ISCSI_PING_TMO | ISCSI_RECV_TMO |
ISCSI_IFACE_NAME | ISCSI_INITIATOR_NAME,
.host_param_mask = ISCSI_HOST_HWADDRESS | ISCSI_HOST_IPADDRESS |
diff --git a/drivers/scsi/libfc/Makefile b/drivers/scsi/libfc/Makefile
index 55f982de3a9..4bb23ac86a5 100644
--- a/drivers/scsi/libfc/Makefile
+++ b/drivers/scsi/libfc/Makefile
@@ -3,10 +3,12 @@
obj-$(CONFIG_LIBFC) += libfc.o
libfc-objs := \
+ fc_libfc.o \
fc_disc.o \
fc_exch.o \
fc_elsct.o \
fc_frame.o \
fc_lport.o \
fc_rport.o \
- fc_fcp.o
+ fc_fcp.o \
+ fc_npiv.o
diff --git a/drivers/scsi/libfc/fc_disc.c b/drivers/scsi/libfc/fc_disc.c
index c48799e9dd8..9b0a5192a96 100644
--- a/drivers/scsi/libfc/fc_disc.c
+++ b/drivers/scsi/libfc/fc_disc.c
@@ -40,6 +40,8 @@
#include <scsi/libfc.h>
+#include "fc_libfc.h"
+
#define FC_DISC_RETRY_LIMIT 3 /* max retries */
#define FC_DISC_RETRY_DELAY 500UL /* (msecs) delay */
@@ -51,8 +53,8 @@ static int fc_disc_single(struct fc_lport *, struct fc_disc_port *);
static void fc_disc_restart(struct fc_disc *);
/**
- * fc_disc_stop_rports() - delete all the remote ports associated with the lport
- * @disc: The discovery job to stop rports on
+ * fc_disc_stop_rports() - Delete all the remote ports associated with the lport
+ * @disc: The discovery job to stop remote ports on
*
* Locking Note: This function expects that the lport mutex is locked before
* calling it.
@@ -72,9 +74,9 @@ void fc_disc_stop_rports(struct fc_disc *disc)
/**
* 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
+ * @sp: The sequence of the RSCN exchange
+ * @fp: The RSCN frame
+ * @lport: The local port that the request will be sent on
*
* Locking Note: This function expects that the disc_mutex is locked
* before it is called.
@@ -183,9 +185,9 @@ reject:
/**
* fc_disc_recv_req() - Handle incoming requests
- * @sp: Current sequence of the request exchange
- * @fp: The frame
- * @lport: The FC local port
+ * @sp: The sequence of the request exchange
+ * @fp: The request frame
+ * @lport: The local port receiving the request
*
* Locking Note: This function is called from the EM and will lock
* the disc_mutex before calling the handler for the
@@ -213,7 +215,7 @@ static void fc_disc_recv_req(struct fc_seq *sp, struct fc_frame *fp,
/**
* fc_disc_restart() - Restart discovery
- * @lport: FC discovery context
+ * @disc: The discovery object to be restarted
*
* Locking Note: This function expects that the disc mutex
* is already locked.
@@ -240,9 +242,9 @@ static void fc_disc_restart(struct fc_disc *disc)
}
/**
- * fc_disc_start() - Fibre Channel Target discovery
- * @lport: FC local port
- * @disc_callback: function to be called when discovery is complete
+ * fc_disc_start() - Start discovery on a local port
+ * @lport: The local port to have discovery started on
+ * @disc_callback: Callback function to be called when discovery is complete
*/
static void fc_disc_start(void (*disc_callback)(struct fc_lport *,
enum fc_disc_event),
@@ -263,8 +265,8 @@ static void fc_disc_start(void (*disc_callback)(struct fc_lport *,
/**
* fc_disc_done() - Discovery has been completed
- * @disc: FC discovery context
- * @event: discovery completion status
+ * @disc: The discovery context
+ * @event: The discovery completion status
*
* Locking Note: This function expects that the disc mutex is locked before
* it is called. The discovery callback is then made with the lock released,
@@ -284,8 +286,8 @@ static void fc_disc_done(struct fc_disc *disc, enum fc_disc_event event)
}
/*
- * Go through all remote ports. If they were found in the latest
- * discovery, reverify or log them in. Otherwise, log them out.
+ * Go through all remote ports. If they were found in the latest
+ * discovery, reverify or log them in. Otherwise, log them out.
* Skip ports which were never discovered. These are the dNS port
* and ports which were created by PLOGI.
*/
@@ -305,8 +307,8 @@ static void fc_disc_done(struct fc_disc *disc, enum fc_disc_event event)
/**
* fc_disc_error() - Handle error on dNS request
- * @disc: FC discovery context
- * @fp: The frame pointer
+ * @disc: The discovery context
+ * @fp: The error code encoded as a frame pointer
*/
static void fc_disc_error(struct fc_disc *disc, struct fc_frame *fp)
{
@@ -342,7 +344,7 @@ static void fc_disc_error(struct fc_disc *disc, struct fc_frame *fp)
/**
* fc_disc_gpn_ft_req() - Send Get Port Names by FC-4 type (GPN_FT) request
- * @lport: FC discovery context
+ * @lport: The discovery context
*
* Locking Note: This function expects that the disc_mutex is locked
* before it is called.
@@ -368,17 +370,17 @@ static void fc_disc_gpn_ft_req(struct fc_disc *disc)
if (lport->tt.elsct_send(lport, 0, fp,
FC_NS_GPN_FT,
fc_disc_gpn_ft_resp,
- disc, lport->e_d_tov))
+ disc, 3 * lport->r_a_tov))
return;
err:
- fc_disc_error(disc, fp);
+ fc_disc_error(disc, NULL);
}
/**
* fc_disc_gpn_ft_parse() - Parse the body of the dNS GPN_FT response.
- * @lport: Fibre Channel host port instance
- * @buf: GPN_FT response buffer
- * @len: size of response buffer
+ * @lport: The local port the GPN_FT was received on
+ * @buf: The GPN_FT response buffer
+ * @len: The size of response buffer
*
* Goes through the list of IDs and names resulting from a request.
*/
@@ -477,10 +479,8 @@ static int fc_disc_gpn_ft_parse(struct fc_disc *disc, void *buf, size_t len)
}
/**
- * fc_disc_timeout() - Retry handler for the disc component
- * @work: Structure holding disc obj that needs retry discovery
- *
- * Handle retry of memory allocation for remote ports.
+ * fc_disc_timeout() - Handler for discovery timeouts
+ * @work: Structure holding discovery context that needs to retry discovery
*/
static void fc_disc_timeout(struct work_struct *work)
{
@@ -494,9 +494,9 @@ static void fc_disc_timeout(struct work_struct *work)
/**
* 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
+ * @sp: The sequence that the GPN_FT response was received on
+ * @fp: The GPN_FT response frame
+ * @lp_arg: The discovery context
*
* Locking Note: This function is called without disc mutex held, and
* should do all its processing with the mutex held
@@ -567,9 +567,9 @@ static void fc_disc_gpn_ft_resp(struct fc_seq *sp, struct fc_frame *fp,
/**
* fc_disc_gpn_id_resp() - Handle a response frame from Get Port Names (GPN_ID)
- * @sp: exchange sequence
- * @fp: response frame
- * @rdata_arg: remote port private data
+ * @sp: The sequence the GPN_ID is on
+ * @fp: The response frame
+ * @rdata_arg: The remote port that sent the GPN_ID response
*
* Locking Note: This function is called without disc mutex held.
*/
@@ -637,7 +637,7 @@ out:
/**
* fc_disc_gpn_id_req() - Send Get Port Names by ID (GPN_ID) request
- * @lport: local port
+ * @lport: The local port to initiate discovery on
* @rdata: remote port private data
*
* Locking Note: This function expects that the disc_mutex is locked
@@ -654,7 +654,8 @@ static int fc_disc_gpn_id_req(struct fc_lport *lport,
if (!fp)
return -ENOMEM;
if (!lport->tt.elsct_send(lport, rdata->ids.port_id, fp, FC_NS_GPN_ID,
- fc_disc_gpn_id_resp, rdata, lport->e_d_tov))
+ fc_disc_gpn_id_resp, rdata,
+ 3 * lport->r_a_tov))
return -ENOMEM;
kref_get(&rdata->kref);
return 0;
@@ -662,8 +663,8 @@ static int fc_disc_gpn_id_req(struct fc_lport *lport,
/**
* fc_disc_single() - Discover the directory information for a single target
- * @lport: local port
- * @dp: The port to rediscover
+ * @lport: The local port the remote port is associated with
+ * @dp: The port to rediscover
*
* Locking Note: This function expects that the disc_mutex is locked
* before it is called.
@@ -681,7 +682,7 @@ static int fc_disc_single(struct fc_lport *lport, struct fc_disc_port *dp)
/**
* fc_disc_stop() - Stop discovery for a given lport
- * @lport: The lport that discovery should stop for
+ * @lport: The local port that discovery should stop on
*/
void fc_disc_stop(struct fc_lport *lport)
{
@@ -695,7 +696,7 @@ void fc_disc_stop(struct fc_lport *lport)
/**
* fc_disc_stop_final() - Stop discovery for a given lport
- * @lport: The lport that discovery should stop for
+ * @lport: The lport that discovery should stop on
*
* This function will block until discovery has been
* completely stopped and all rports have been deleted.
@@ -707,8 +708,8 @@ void fc_disc_stop_final(struct fc_lport *lport)
}
/**
- * fc_disc_init() - Initialize the discovery block
- * @lport: FC local port
+ * fc_disc_init() - Initialize the discovery layer for a local port
+ * @lport: The local port that needs the discovery layer to be initialized
*/
int fc_disc_init(struct fc_lport *lport)
{
diff --git a/drivers/scsi/libfc/fc_elsct.c b/drivers/scsi/libfc/fc_elsct.c
index 5cfa68732e9..53748724f2c 100644
--- a/drivers/scsi/libfc/fc_elsct.c
+++ b/drivers/scsi/libfc/fc_elsct.c
@@ -28,17 +28,22 @@
#include <scsi/libfc.h>
#include <scsi/fc_encode.h>
-/*
- * fc_elsct_send - sends ELS/CT frame
+/**
+ * fc_elsct_send() - Send an ELS or CT frame
+ * @lport: The local port to send the frame on
+ * @did: The destination ID for the frame
+ * @fp: The frame to be sent
+ * @op: The operational code
+ * @resp: The callback routine when the response is received
+ * @arg: The argument to pass to the response callback routine
+ * @timer_msec: The timeout period for the frame (in msecs)
*/
-static struct fc_seq *fc_elsct_send(struct fc_lport *lport,
- u32 did,
- struct fc_frame *fp,
- unsigned int op,
- void (*resp)(struct fc_seq *,
- struct fc_frame *fp,
- void *arg),
- void *arg, u32 timer_msec)
+struct fc_seq *fc_elsct_send(struct fc_lport *lport, u32 did,
+ struct fc_frame *fp, unsigned int op,
+ void (*resp)(struct fc_seq *,
+ struct fc_frame *,
+ void *),
+ void *arg, u32 timer_msec)
{
enum fc_rctl r_ctl;
enum fc_fh_type fh_type;
@@ -53,15 +58,22 @@ static struct fc_seq *fc_elsct_send(struct fc_lport *lport,
did = FC_FID_DIR_SERV;
}
- if (rc)
+ if (rc) {
+ fc_frame_free(fp);
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);
}
+EXPORT_SYMBOL(fc_elsct_send);
+/**
+ * fc_elsct_init() - Initialize the ELS/CT layer
+ * @lport: The local port to initialize the ELS/CT layer for
+ */
int fc_elsct_init(struct fc_lport *lport)
{
if (!lport->tt.elsct_send)
@@ -72,12 +84,15 @@ int fc_elsct_init(struct fc_lport *lport)
EXPORT_SYMBOL(fc_elsct_init);
/**
- * fc_els_resp_type() - return string describing ELS response for debug.
- * @fp: frame pointer with possible error code.
+ * fc_els_resp_type() - Return a string describing the ELS response
+ * @fp: The frame pointer or possible error code
*/
const char *fc_els_resp_type(struct fc_frame *fp)
{
const char *msg;
+ struct fc_frame_header *fh;
+ struct fc_ct_hdr *ct;
+
if (IS_ERR(fp)) {
switch (-PTR_ERR(fp)) {
case FC_NO_ERR:
@@ -94,15 +109,41 @@ const char *fc_els_resp_type(struct fc_frame *fp)
break;
}
} else {
- switch (fc_frame_payload_op(fp)) {
- case ELS_LS_ACC:
- msg = "accept";
+ fh = fc_frame_header_get(fp);
+ switch (fh->fh_type) {
+ case FC_TYPE_ELS:
+ switch (fc_frame_payload_op(fp)) {
+ case ELS_LS_ACC:
+ msg = "accept";
+ break;
+ case ELS_LS_RJT:
+ msg = "reject";
+ break;
+ default:
+ msg = "response unknown ELS";
+ break;
+ }
break;
- case ELS_LS_RJT:
- msg = "reject";
+ case FC_TYPE_CT:
+ ct = fc_frame_payload_get(fp, sizeof(*ct));
+ if (ct) {
+ switch (ntohs(ct->ct_cmd)) {
+ case FC_FS_ACC:
+ msg = "CT accept";
+ break;
+ case FC_FS_RJT:
+ msg = "CT reject";
+ break;
+ default:
+ msg = "response unknown CT";
+ break;
+ }
+ } else {
+ msg = "short CT response";
+ }
break;
default:
- msg = "response unknown ELS";
+ msg = "response not ELS or CT";
break;
}
}
diff --git a/drivers/scsi/libfc/fc_exch.c b/drivers/scsi/libfc/fc_exch.c
index c1c15748220..19d711cb938 100644
--- a/drivers/scsi/libfc/fc_exch.c
+++ b/drivers/scsi/libfc/fc_exch.c
@@ -32,10 +32,13 @@
#include <scsi/libfc.h>
#include <scsi/fc_encode.h>
+#include "fc_libfc.h"
+
u16 fc_cpu_mask; /* cpu mask for possible cpus */
EXPORT_SYMBOL(fc_cpu_mask);
static u16 fc_cpu_order; /* 2's power to represent total possible cpus */
-static struct kmem_cache *fc_em_cachep; /* cache for exchanges */
+static struct kmem_cache *fc_em_cachep; /* cache for exchanges */
+struct workqueue_struct *fc_exch_workqueue;
/*
* Structure and function definitions for managing Fibre Channel Exchanges
@@ -50,35 +53,46 @@ static struct kmem_cache *fc_em_cachep; /* cache for exchanges */
* fc_seq holds the state for an individual sequence.
*/
-/*
- * Per cpu exchange pool
+/**
+ * struct fc_exch_pool - Per cpu exchange pool
+ * @next_index: Next possible free exchange index
+ * @total_exches: Total allocated exchanges
+ * @lock: Exch pool lock
+ * @ex_list: List of exchanges
*
* This structure manages per cpu exchanges in array of exchange pointers.
* This array is allocated followed by struct fc_exch_pool memory for
* assigned range of exchanges to per cpu pool.
*/
struct fc_exch_pool {
- u16 next_index; /* next possible free exchange index */
- u16 total_exches; /* total allocated exchanges */
- spinlock_t lock; /* exch pool lock */
- struct list_head ex_list; /* allocated exchanges list */
+ u16 next_index;
+ u16 total_exches;
+ spinlock_t lock;
+ struct list_head ex_list;
};
-/*
- * Exchange manager.
+/**
+ * struct fc_exch_mgr - The Exchange Manager (EM).
+ * @class: Default class for new sequences
+ * @kref: Reference counter
+ * @min_xid: Minimum exchange ID
+ * @max_xid: Maximum exchange ID
+ * @ep_pool: Reserved exchange pointers
+ * @pool_max_index: Max exch array index in exch pool
+ * @pool: Per cpu exch pool
+ * @stats: Statistics structure
*
* 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 */
- struct kref kref; /* exchange mgr reference count */
- u16 min_xid; /* min exchange ID */
- u16 max_xid; /* max exchange ID */
- struct list_head ex_list; /* allocated exchanges list */
- mempool_t *ep_pool; /* reserve ep's */
- u16 pool_max_index; /* max exch array index in exch pool */
- struct fc_exch_pool *pool; /* per cpu exch pool */
+ enum fc_class class;
+ struct kref kref;
+ u16 min_xid;
+ u16 max_xid;
+ mempool_t *ep_pool;
+ u16 pool_max_index;
+ struct fc_exch_pool *pool;
/*
* currently exchange mgr stats are updated but not used.
@@ -96,6 +110,18 @@ struct fc_exch_mgr {
};
#define fc_seq_exch(sp) container_of(sp, struct fc_exch, seq)
+/**
+ * struct fc_exch_mgr_anchor - primary structure for list of EMs
+ * @ema_list: Exchange Manager Anchor list
+ * @mp: Exchange Manager associated with this anchor
+ * @match: Routine to determine if this anchor's EM should be used
+ *
+ * When walking the list of anchors the match routine will be called
+ * for each anchor to determine if that EM should be used. The last
+ * anchor in the list will always match to handle any exchanges not
+ * handled by other EMs. The non-default EMs would be added to the
+ * anchor list by HW that provides FCoE offloads.
+ */
struct fc_exch_mgr_anchor {
struct list_head ema_list;
struct fc_exch_mgr *mp;
@@ -108,7 +134,6 @@ 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.
@@ -196,6 +221,15 @@ static char *fc_exch_rctl_names[] = FC_RCTL_NAMES_INIT;
#define FC_TABLE_SIZE(x) (sizeof(x) / sizeof(x[0]))
+/**
+ * fc_exch_name_lookup() - Lookup name by opcode
+ * @op: Opcode to be looked up
+ * @table: Opcode/name table
+ * @max_index: Index not to be exceeded
+ *
+ * This routine is used to determine a human-readable string identifying
+ * a R_CTL opcode.
+ */
static inline const char *fc_exch_name_lookup(unsigned int op, char **table,
unsigned int max_index)
{
@@ -208,25 +242,34 @@ static inline const char *fc_exch_name_lookup(unsigned int op, char **table,
return name;
}
+/**
+ * fc_exch_rctl_name() - Wrapper routine for fc_exch_name_lookup()
+ * @op: The opcode to be looked up
+ */
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.
+/**
+ * fc_exch_hold() - Increment an exchange's reference count
+ * @ep: Echange to be held
*/
-static void fc_exch_hold(struct fc_exch *ep)
+static inline 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
+/**
+ * fc_exch_setup_hdr() - Initialize a FC header by initializing some fields
+ * and determine SOF and EOF.
+ * @ep: The exchange to that will use the header
+ * @fp: The frame whose header is to be modified
+ * @f_ctl: F_CTL bits that will be used for the frame header
+ *
+ * The fields initialized by this routine are: fh_ox_id, fh_rx_id,
+ * fh_seq_id, fh_seq_cnt and the SOF and EOF.
*/
static void fc_exch_setup_hdr(struct fc_exch *ep, struct fc_frame *fp,
u32 f_ctl)
@@ -243,7 +286,7 @@ static void fc_exch_setup_hdr(struct fc_exch *ep, struct fc_frame *fp,
if (fc_sof_needs_ack(ep->class))
fr_eof(fp) = FC_EOF_N;
/*
- * Form f_ctl.
+ * From 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
@@ -273,10 +316,12 @@ static void fc_exch_setup_hdr(struct fc_exch *ep, struct fc_frame *fp,
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.
+/**
+ * fc_exch_release() - Decrement an exchange's reference count
+ * @ep: Exchange to be released
+ *
+ * If the reference count reaches zero and the exchange is complete,
+ * it is freed.
*/
static void fc_exch_release(struct fc_exch *ep)
{
@@ -291,6 +336,10 @@ static void fc_exch_release(struct fc_exch *ep)
}
}
+/**
+ * fc_exch_done_locked() - Complete an exchange with the exchange lock held
+ * @ep: The exchange that is complete
+ */
static int fc_exch_done_locked(struct fc_exch *ep)
{
int rc = 1;
@@ -315,6 +364,15 @@ static int fc_exch_done_locked(struct fc_exch *ep)
return rc;
}
+/**
+ * fc_exch_ptr_get() - Return an exchange from an exchange pool
+ * @pool: Exchange Pool to get an exchange from
+ * @index: Index of the exchange within the pool
+ *
+ * Use the index to get an exchange from within an exchange pool. exches
+ * will point to an array of exchange pointers. The index will select
+ * the exchange within the array.
+ */
static inline struct fc_exch *fc_exch_ptr_get(struct fc_exch_pool *pool,
u16 index)
{
@@ -322,12 +380,22 @@ static inline struct fc_exch *fc_exch_ptr_get(struct fc_exch_pool *pool,
return exches[index];
}
+/**
+ * fc_exch_ptr_set() - Assign an exchange to a slot in an exchange pool
+ * @pool: The pool to assign the exchange to
+ * @index: The index in the pool where the exchange will be assigned
+ * @ep: The exchange to assign to the pool
+ */
static inline void fc_exch_ptr_set(struct fc_exch_pool *pool, u16 index,
struct fc_exch *ep)
{
((struct fc_exch **)(pool + 1))[index] = ep;
}
+/**
+ * fc_exch_delete() - Delete an exchange
+ * @ep: The exchange to be deleted
+ */
static void fc_exch_delete(struct fc_exch *ep)
{
struct fc_exch_pool *pool;
@@ -343,8 +411,14 @@ static void fc_exch_delete(struct fc_exch *ep)
fc_exch_release(ep); /* drop hold for exch in mp */
}
-/*
- * Internal version of fc_exch_timer_set - used with lock held.
+/**
+ * fc_exch_timer_set_locked() - Start a timer for an exchange w/ the
+ * the exchange lock held
+ * @ep: The exchange whose timer will start
+ * @timer_msec: The timeout period
+ *
+ * Used for upper level protocols to time out the exchange.
+ * The timer is cancelled when it fires or when the exchange completes.
*/
static inline void fc_exch_timer_set_locked(struct fc_exch *ep,
unsigned int timer_msec)
@@ -354,17 +428,15 @@ static inline void fc_exch_timer_set_locked(struct fc_exch *ep,
FC_EXCH_DBG(ep, "Exchange timer armed\n");
- if (schedule_delayed_work(&ep->timeout_work,
- msecs_to_jiffies(timer_msec)))
+ if (queue_delayed_work(fc_exch_workqueue, &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.
+/**
+ * fc_exch_timer_set() - Lock the exchange and set the timer
+ * @ep: The exchange whose timer will start
+ * @timer_msec: The timeout period
*/
static void fc_exch_timer_set(struct fc_exch *ep, unsigned int timer_msec)
{
@@ -373,7 +445,115 @@ static void fc_exch_timer_set(struct fc_exch *ep, unsigned int timer_msec)
spin_unlock_bh(&ep->ex_lock);
}
-int fc_seq_exch_abort(const struct fc_seq *req_sp, unsigned int timer_msec)
+/**
+ * fc_seq_send() - Send a frame using existing sequence/exchange pair
+ * @lport: The local port that the exchange will be sent on
+ * @sp: The sequence to be sent
+ * @fp: The frame to be sent on the exchange
+ */
+static int fc_seq_send(struct fc_lport *lport, 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 = lport->tt.frame_send(lport, 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;
+}
+
+/**
+ * fc_seq_alloc() - Allocate a sequence for a given exchange
+ * @ep: The exchange to allocate a new sequence for
+ * @seq_id: The sequence ID to be used
+ *
+ * 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_seq_start_next_locked() - Allocate a new sequence on the same
+ * exchange as the supplied sequence
+ * @sp: The sequence/exchange to get a new sequence for
+ */
+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_EXCH_DBG(ep, "f_ctl %6x seq %2x\n",
+ ep->f_ctl, sp->id);
+ return sp;
+}
+
+/**
+ * fc_seq_start_next() - Lock the exchange and get a new sequence
+ * for a given sequence/exchange pair
+ * @sp: The sequence/exchange to get a new exchange for
+ */
+static 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);
+ sp = fc_seq_start_next_locked(sp);
+ spin_unlock_bh(&ep->ex_lock);
+
+ return sp;
+}
+
+/**
+ * fc_seq_exch_abort() - Abort an exchange and sequence
+ * @req_sp: The sequence to be aborted
+ * @timer_msec: The period of time to wait before aborting
+ *
+ * Generally called because of a timeout or an abort from the upper layer.
+ */
+static int fc_seq_exch_abort(const struct fc_seq *req_sp,
+ unsigned int timer_msec)
{
struct fc_seq *sp;
struct fc_exch *ep;
@@ -422,11 +602,10 @@ int fc_seq_exch_abort(const struct fc_seq *req_sp, unsigned int timer_msec)
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.
+/**
+ * fc_exch_timeout() - Handle exchange timer expiration
+ * @work: The work_struct identifying the exchange that timed out
*/
static void fc_exch_timeout(struct work_struct *work)
{
@@ -474,28 +653,10 @@ done:
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_exch_em_alloc() - allocate an exchange from a specified EM.
- * @lport: ptr to the local port
- * @mp: ptr to the exchange manager
+ * fc_exch_em_alloc() - Allocate an exchange from a specified EM.
+ * @lport: The local port that the exchange is for
+ * @mp: The exchange manager that will allocate the exchange
*
* Returns pointer to allocated fc_exch with exch lock held.
*/
@@ -563,16 +724,18 @@ err:
}
/**
- * fc_exch_alloc() - allocate an exchange.
- * @lport: ptr to the local port
- * @fp: ptr to the FC frame
+ * fc_exch_alloc() - Allocate an exchange from an EM on a
+ * local port's list of EMs.
+ * @lport: The local port that will own the exchange
+ * @fp: The FC frame that the exchange will be for
*
- * This function walks the list of the exchange manager(EM)
- * anchors to select a EM for new exchange allocation. The
- * EM is selected having either a NULL match function pointer
- * or call to match function returning true.
+ * This function walks the list of exchange manager(EM)
+ * anchors to select an EM for a new exchange allocation. The
+ * EM is selected when a NULL match function pointer is encountered
+ * or when a call to a match function returns true.
*/
-struct fc_exch *fc_exch_alloc(struct fc_lport *lport, struct fc_frame *fp)
+static struct fc_exch *fc_exch_alloc(struct fc_lport *lport,
+ struct fc_frame *fp)
{
struct fc_exch_mgr_anchor *ema;
struct fc_exch *ep;
@@ -586,10 +749,11 @@ struct fc_exch *fc_exch_alloc(struct fc_lport *lport, struct fc_frame *fp)
}
return NULL;
}
-EXPORT_SYMBOL(fc_exch_alloc);
-/*
- * Lookup and hold an exchange.
+/**
+ * fc_exch_find() - Lookup and hold an exchange
+ * @mp: The exchange manager to lookup the exchange from
+ * @xid: The XID of the exchange to look up
*/
static struct fc_exch *fc_exch_find(struct fc_exch_mgr *mp, u16 xid)
{
@@ -609,7 +773,13 @@ static struct fc_exch *fc_exch_find(struct fc_exch_mgr *mp, u16 xid)
return ep;
}
-void fc_exch_done(struct fc_seq *sp)
+
+/**
+ * fc_exch_done() - Indicate that an exchange/sequence tuple is complete and
+ * the memory allocated for the related objects may be freed.
+ * @sp: The sequence that has completed
+ */
+static void fc_exch_done(struct fc_seq *sp)
{
struct fc_exch *ep = fc_seq_exch(sp);
int rc;
@@ -620,10 +790,13 @@ void fc_exch_done(struct fc_seq *sp)
if (!rc)
fc_exch_delete(ep);
}
-EXPORT_SYMBOL(fc_exch_done);
-/*
- * Allocate a new exchange as responder.
+/**
+ * fc_exch_resp() - Allocate a new exchange for a response frame
+ * @lport: The local port that the exchange was for
+ * @mp: The exchange manager to allocate the exchange from
+ * @fp: The response frame
+ *
* Sets the responder ID in the frame header.
*/
static struct fc_exch *fc_exch_resp(struct fc_lport *lport,
@@ -664,8 +837,13 @@ static struct fc_exch *fc_exch_resp(struct fc_lport *lport,
return ep;
}
-/*
- * Find a sequence for receive where the other end is originating the sequence.
+/**
+ * fc_seq_lookup_recip() - Find a sequence where the other end
+ * originated the sequence
+ * @lport: The local port that the frame was sent to
+ * @mp: The Exchange Manager to lookup the exchange from
+ * @fp: The frame associated with the sequence we're looking for
+ *
* 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.
*/
@@ -771,10 +949,12 @@ rel:
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.
+/**
+ * fc_seq_lookup_orig() - Find a sequence where this end
+ * originated the sequence
+ * @mp: The Exchange Manager to lookup the exchange from
+ * @fp: The frame associated with the sequence we're looking for
+ *
* Does not hold the sequence for the caller.
*/
static struct fc_seq *fc_seq_lookup_orig(struct fc_exch_mgr *mp,
@@ -806,8 +986,12 @@ static struct fc_seq *fc_seq_lookup_orig(struct fc_exch_mgr *mp,
return sp;
}
-/*
- * Set addresses for an exchange.
+/**
+ * fc_exch_set_addr() - Set the source and destination IDs for an exchange
+ * @ep: The exchange to set the addresses for
+ * @orig_id: The originator's ID
+ * @resp_id: The responder's ID
+ *
* Note this must be done before the first sequence of the exchange is sent.
*/
static void fc_exch_set_addr(struct fc_exch *ep,
@@ -823,76 +1007,15 @@ static void fc_exch_set_addr(struct fc_exch *ep,
}
}
-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_EXCH_DBG(ep, "f_ctl %6x seq %2x\n",
- ep->f_ctl, sp->id);
- return sp;
-}
-/*
- * Allocate a new sequence on the same exchange as the supplied sequence.
- * This will never return NULL.
+/**
+ * fc_seq_els_rsp_send() - Send an ELS response using infomation from
+ * the existing sequence/exchange.
+ * @sp: The sequence/exchange to get information from
+ * @els_cmd: The ELS command to be sent
+ * @els_data: The ELS data to be sent
*/
-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);
- 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)
+static 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:
@@ -911,10 +1034,13 @@ void fc_seq_els_rsp_send(struct fc_seq *sp, enum fc_els_cmd els_cmd,
FC_EXCH_DBG(fc_seq_exch(sp), "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.
+/**
+ * fc_seq_send_last() - Send a sequence that is the last in the exchange
+ * @sp: The sequence that is to be sent
+ * @fp: The frame that will be sent on the sequence
+ * @rctl: The R_CTL information to be sent
+ * @fh_type: The frame header type
*/
static void fc_seq_send_last(struct fc_seq *sp, struct fc_frame *fp,
enum fc_rctl rctl, enum fc_fh_type fh_type)
@@ -928,9 +1054,12 @@ static void fc_seq_send_last(struct fc_seq *sp, struct fc_frame *fp,
fc_seq_send(ep->lp, sp, fp);
}
-/*
+/**
+ * fc_seq_send_ack() - Send an acknowledgement that we've received a frame
+ * @sp: The sequence to send the ACK on
+ * @rx_fp: The received frame that is being acknoledged
+ *
* 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)
{
@@ -938,14 +1067,14 @@ static void fc_seq_send_ack(struct fc_seq *sp, const struct fc_frame *rx_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;
+ struct fc_lport *lport = 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);
+ fp = fc_frame_alloc(lport, 0);
if (!fp)
return;
@@ -980,12 +1109,16 @@ static void fc_seq_send_ack(struct fc_seq *sp, const struct fc_frame *rx_fp)
else
fr_eof(fp) = FC_EOF_N;
- (void) lp->tt.frame_send(lp, fp);
+ lport->tt.frame_send(lport, fp);
}
}
-/*
- * Send BLS Reject.
+/**
+ * fc_exch_send_ba_rjt() - Send BLS Reject
+ * @rx_fp: The frame being rejected
+ * @reason: The reason the frame is being rejected
+ * @explan: The explaination for the rejection
+ *
* This is for rejecting BA_ABTS only.
*/
static void fc_exch_send_ba_rjt(struct fc_frame *rx_fp,
@@ -996,11 +1129,11 @@ static void fc_exch_send_ba_rjt(struct fc_frame *rx_fp,
struct fc_frame_header *rx_fh;
struct fc_frame_header *fh;
struct fc_ba_rjt *rp;
- struct fc_lport *lp;
+ struct fc_lport *lport;
unsigned int f_ctl;
- lp = fr_dev(rx_fp);
- fp = fc_frame_alloc(lp, sizeof(*rp));
+ lport = fr_dev(rx_fp);
+ fp = fc_frame_alloc(lport, sizeof(*rp));
if (!fp)
return;
fh = fc_frame_header_get(fp);
@@ -1045,13 +1178,17 @@ static void fc_exch_send_ba_rjt(struct fc_frame *rx_fp,
if (fc_sof_needs_ack(fr_sof(fp)))
fr_eof(fp) = FC_EOF_N;
- (void) lp->tt.frame_send(lp, fp);
+ lport->tt.frame_send(lport, 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.
+/**
+ * fc_exch_recv_abts() - Handle an incoming ABTS
+ * @ep: The exchange the abort was on
+ * @rx_fp: The ABTS frame
+ *
+ * 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)
{
@@ -1100,10 +1237,14 @@ free:
fc_frame_free(rx_fp);
}
-/*
- * Handle receive where the other end is originating the sequence.
+/**
+ * fc_exch_recv_req() - Handler for an incoming request where is other
+ * end is originating the sequence
+ * @lport: The local port that received the request
+ * @mp: The EM that the exchange is on
+ * @fp: The request frame
*/
-static void fc_exch_recv_req(struct fc_lport *lp, struct fc_exch_mgr *mp,
+static void fc_exch_recv_req(struct fc_lport *lport, struct fc_exch_mgr *mp,
struct fc_frame *fp)
{
struct fc_frame_header *fh = fc_frame_header_get(fp);
@@ -1114,8 +1255,17 @@ static void fc_exch_recv_req(struct fc_lport *lp, struct fc_exch_mgr *mp,
u32 f_ctl;
enum fc_pf_rjt_reason reject;
+ /* We can have the wrong fc_lport at this point with NPIV, which is a
+ * problem now that we know a new exchange needs to be allocated
+ */
+ lport = fc_vport_id_lookup(lport, ntoh24(fh->fh_d_id));
+ if (!lport) {
+ fc_frame_free(fp);
+ return;
+ }
+
fr_seq(fp) = NULL;
- reject = fc_seq_lookup_recip(lp, mp, fp);
+ reject = fc_seq_lookup_recip(lport, mp, fp);
if (reject == FC_RJT_NONE) {
sp = fr_seq(fp); /* sequence will be held */
ep = fc_seq_exch(sp);
@@ -1138,17 +1288,21 @@ static void fc_exch_recv_req(struct fc_lport *lp, struct fc_exch_mgr *mp,
if (ep->resp)
ep->resp(sp, fp, ep->arg);
else
- lp->tt.lport_recv(lp, sp, fp);
+ lport->tt.lport_recv(lport, sp, fp);
fc_exch_release(ep); /* release from lookup */
} else {
- FC_LPORT_DBG(lp, "exch/seq lookup failed: reject %x\n", reject);
+ FC_LPORT_DBG(lport, "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.
+/**
+ * fc_exch_recv_seq_resp() - Handler for an incoming response where the other
+ * end is the originator of the sequence that is a
+ * response to our initial exchange
+ * @mp: The EM that the exchange is on
+ * @fp: The response frame
*/
static void fc_exch_recv_seq_resp(struct fc_exch_mgr *mp, struct fc_frame *fp)
{
@@ -1239,8 +1393,11 @@ out:
fc_frame_free(fp);
}
-/*
- * Handle receive for a sequence where other end is responding to our sequence.
+/**
+ * fc_exch_recv_resp() - Handler for a sequence where other end is
+ * responding to our sequence
+ * @mp: The EM that the exchange is on
+ * @fp: The response frame
*/
static void fc_exch_recv_resp(struct fc_exch_mgr *mp, struct fc_frame *fp)
{
@@ -1256,9 +1413,13 @@ static void fc_exch_recv_resp(struct fc_exch_mgr *mp, struct fc_frame *fp)
fc_frame_free(fp);
}
-/*
- * Handle the response to an ABTS for exchange or sequence.
- * This can be BA_ACC or BA_RJT.
+/**
+ * fc_exch_abts_resp() - Handler for a response to an ABT
+ * @ep: The exchange that the frame is on
+ * @fp: The response frame
+ *
+ * This response would be to an ABTS cancelling an exchange or sequence.
+ * The response can be either BA_ACC or BA_RJT
*/
static void fc_exch_abts_resp(struct fc_exch *ep, struct fc_frame *fp)
{
@@ -1333,9 +1494,12 @@ static void fc_exch_abts_resp(struct fc_exch *ep, struct fc_frame *fp)
}
-/*
- * Receive BLS sequence.
- * This is always a sequence initiated by the remote side.
+/**
+ * fc_exch_recv_bls() - Handler for a BLS sequence
+ * @mp: The EM that the exchange is on
+ * @fp: The request frame
+ *
+ * The BLS frame 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)
@@ -1392,8 +1556,10 @@ static void fc_exch_recv_bls(struct fc_exch_mgr *mp, struct fc_frame *fp)
fc_exch_release(ep); /* release hold taken by fc_exch_find */
}
-/*
- * Accept sequence with LS_ACC.
+/**
+ * fc_seq_ls_acc() - Accept sequence with LS_ACC
+ * @req_sp: The request sequence
+ *
* If this fails due to allocation or transmit congestion, assume the
* originator will repeat the sequence.
*/
@@ -1413,8 +1579,12 @@ static void fc_seq_ls_acc(struct fc_seq *req_sp)
}
}
-/*
- * Reject sequence with ELS LS_RJT.
+/**
+ * fc_seq_ls_rjt() - Reject a sequence with ELS LS_RJT
+ * @req_sp: The request sequence
+ * @reason: The reason the sequence is being rejected
+ * @explan: The explaination for the rejection
+ *
* If this fails due to allocation or transmit congestion, assume the
* originator will repeat the sequence.
*/
@@ -1437,6 +1607,10 @@ static void fc_seq_ls_rjt(struct fc_seq *req_sp, enum fc_els_rjt_reason reason,
}
}
+/**
+ * fc_exch_reset() - Reset an exchange
+ * @ep: The exchange to be reset
+ */
static void fc_exch_reset(struct fc_exch *ep)
{
struct fc_seq *sp;
@@ -1446,12 +1620,6 @@ static void fc_exch_reset(struct fc_exch *ep)
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;
@@ -1471,16 +1639,16 @@ static void fc_exch_reset(struct fc_exch *ep)
}
/**
- * fc_exch_pool_reset() - Resets an per cpu exches pool.
- * @lport: ptr to the local port
- * @pool: ptr to the per cpu exches pool
- * @sid: source FC ID
- * @did: destination FC ID
+ * fc_exch_pool_reset() - Reset a per cpu exchange pool
+ * @lport: The local port that the exchange pool is on
+ * @pool: The exchange pool to be reset
+ * @sid: The source ID
+ * @did: The destination ID
*
- * Resets an per cpu exches pool, releasing its all sequences
- * and exchanges. If sid is non-zero, then reset only exchanges
- * we sourced from that FID. If did is non-zero, reset only
- * exchanges destined to that FID.
+ * Resets a per cpu exches pool, releasing all of its sequences
+ * and exchanges. If sid is non-zero then reset only exchanges
+ * we sourced from the local port's FID. If did is non-zero then
+ * only reset exchanges destined for the local port's FID.
*/
static void fc_exch_pool_reset(struct fc_lport *lport,
struct fc_exch_pool *pool,
@@ -1514,15 +1682,15 @@ restart:
}
/**
- * fc_exch_mgr_reset() - Resets all EMs of a lport
- * @lport: ptr to the local port
- * @sid: source FC ID
- * @did: destination FC ID
+ * fc_exch_mgr_reset() - Reset all EMs of a local port
+ * @lport: The local port whose EMs are to be reset
+ * @sid: The source ID
+ * @did: The destination ID
*
- * Reset all EMs of a lport, releasing its all sequences and
- * exchanges. If sid is non-zero, then reset only exchanges
- * we sourced from that FID. If did is non-zero, reset only
- * exchanges destined to that FID.
+ * Reset all EMs associated with a given local port. Release all
+ * sequences and exchanges. If sid is non-zero then reset only the
+ * exchanges sent from the local port's FID. If did is non-zero then
+ * reset only exchanges destined for the local port's FID.
*/
void fc_exch_mgr_reset(struct fc_lport *lport, u32 sid, u32 did)
{
@@ -1538,8 +1706,11 @@ void fc_exch_mgr_reset(struct fc_lport *lport, u32 sid, u32 did)
}
EXPORT_SYMBOL(fc_exch_mgr_reset);
-/*
- * Handle incoming ELS REC - Read Exchange Concise.
+/**
+ * fc_exch_els_rec() - Handler for ELS REC (Read Exchange Concise) requests
+ * @sp: The sequence the REC is on
+ * @rfp: The REC frame
+ *
* 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)
@@ -1621,10 +1792,11 @@ reject:
fc_frame_free(rfp);
}
-/*
- * Handle response from RRQ.
- * Not much to do here, really.
- * Should report errors.
+/**
+ * fc_exch_rrq_resp() - Handler for RRQ responses
+ * @sp: The sequence that the RRQ is on
+ * @fp: The RRQ frame
+ * @arg: The exchange that the RRQ is on
*
* TODO: fix error handler.
*/
@@ -1664,21 +1836,99 @@ cleanup:
fc_exch_release(aborted_ep);
}
-/*
- * Send ELS RRQ - Reinstate Recovery Qualifier.
+
+/**
+ * fc_exch_seq_send() - Send a frame using a new exchange and sequence
+ * @lport: The local port to send the frame on
+ * @fp: The frame to be sent
+ * @resp: The response handler for this request
+ * @destructor: The destructor for the exchange
+ * @arg: The argument to be passed to the response handler
+ * @timer_msec: The timeout period for the exchange
+ *
+ * The frame pointer with some of the header's fields must be
+ * filled before calling this routine, those fields are:
+ *
+ * - routing control
+ * - FC port did
+ * - FC port sid
+ * - FC header type
+ * - frame control
+ * - parameter or relative offset
+ */
+static struct fc_seq *fc_exch_seq_send(struct fc_lport *lport,
+ 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 = fc_exch_alloc(lport, 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 = lport;
+ 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 (ep->xid <= lport->lro_xid)
+ fc_fcp_ddp_setup(fr_fsp(fp), ep->xid);
+
+ if (unlikely(lport->tt.frame_send(lport, 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_delete(ep);
+ return NULL;
+}
+
+/**
+ * fc_exch_rrq() - Send an ELS RRQ (Reinstate Recovery Qualifier) command
+ * @ep: The exchange to send the RRQ on
+ *
* 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_lport *lport;
struct fc_els_rrq *rrq;
struct fc_frame *fp;
u32 did;
- lp = ep->lp;
+ lport = ep->lp;
- fp = fc_frame_alloc(lp, sizeof(*rrq));
+ fp = fc_frame_alloc(lport, sizeof(*rrq));
if (!fp)
goto retry;
@@ -1694,10 +1944,11 @@ static void fc_exch_rrq(struct fc_exch *ep)
did = ep->sid;
fc_fill_fc_hdr(fp, FC_RCTL_ELS_REQ, did,
- fc_host_port_id(lp->host), FC_TYPE_ELS,
+ fc_host_port_id(lport->host), FC_TYPE_ELS,
FC_FC_FIRST_SEQ | FC_FC_END_SEQ | FC_FC_SEQ_INIT, 0);
- if (fc_exch_seq_send(lp, fp, fc_exch_rrq_resp, NULL, ep, lp->e_d_tov))
+ if (fc_exch_seq_send(lport, fp, fc_exch_rrq_resp, NULL, ep,
+ lport->e_d_tov))
return;
retry:
@@ -1714,12 +1965,14 @@ retry:
}
-/*
- * Handle incoming ELS RRQ - Reset Recovery Qualifier.
+/**
+ * fc_exch_els_rrq() - Handler for ELS RRQ (Reset Recovery Qualifier) requests
+ * @sp: The sequence that the RRQ is on
+ * @fp: The RRQ frame
*/
static void fc_exch_els_rrq(struct fc_seq *sp, struct fc_frame *fp)
{
- struct fc_exch *ep; /* request or subject exchange */
+ struct fc_exch *ep = NULL; /* request or subject exchange */
struct fc_els_rrq *rp;
u32 sid;
u16 xid;
@@ -1769,17 +2022,24 @@ static void fc_exch_els_rrq(struct fc_seq *sp, struct fc_frame *fp)
* Send LS_ACC.
*/
fc_seq_ls_acc(sp);
- fc_frame_free(fp);
- return;
+ goto out;
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);
+out:
fc_frame_free(fp);
+ if (ep)
+ fc_exch_release(ep); /* drop hold from fc_exch_find */
}
+/**
+ * fc_exch_mgr_add() - Add an exchange manager to a local port's list of EMs
+ * @lport: The local port to add the exchange manager to
+ * @mp: The exchange manager to be added to the local port
+ * @match: The match routine that indicates when this EM should be used
+ */
struct fc_exch_mgr_anchor *fc_exch_mgr_add(struct fc_lport *lport,
struct fc_exch_mgr *mp,
bool (*match)(struct fc_frame *))
@@ -1799,6 +2059,10 @@ struct fc_exch_mgr_anchor *fc_exch_mgr_add(struct fc_lport *lport,
}
EXPORT_SYMBOL(fc_exch_mgr_add);
+/**
+ * fc_exch_mgr_destroy() - Destroy an exchange manager
+ * @kref: The reference to the EM to be destroyed
+ */
static void fc_exch_mgr_destroy(struct kref *kref)
{
struct fc_exch_mgr *mp = container_of(kref, struct fc_exch_mgr, kref);
@@ -1808,6 +2072,10 @@ static void fc_exch_mgr_destroy(struct kref *kref)
kfree(mp);
}
+/**
+ * fc_exch_mgr_del() - Delete an EM from a local port's list
+ * @ema: The exchange manager anchor identifying the EM to be deleted
+ */
void fc_exch_mgr_del(struct fc_exch_mgr_anchor *ema)
{
/* remove EM anchor from EM anchors list */
@@ -1817,7 +2085,35 @@ void fc_exch_mgr_del(struct fc_exch_mgr_anchor *ema)
}
EXPORT_SYMBOL(fc_exch_mgr_del);
-struct fc_exch_mgr *fc_exch_mgr_alloc(struct fc_lport *lp,
+/**
+ * fc_exch_mgr_list_clone() - Share all exchange manager objects
+ * @src: Source lport to clone exchange managers from
+ * @dst: New lport that takes references to all the exchange managers
+ */
+int fc_exch_mgr_list_clone(struct fc_lport *src, struct fc_lport *dst)
+{
+ struct fc_exch_mgr_anchor *ema, *tmp;
+
+ list_for_each_entry(ema, &src->ema_list, ema_list) {
+ if (!fc_exch_mgr_add(dst, ema->mp, ema->match))
+ goto err;
+ }
+ return 0;
+err:
+ list_for_each_entry_safe(ema, tmp, &dst->ema_list, ema_list)
+ fc_exch_mgr_del(ema);
+ return -ENOMEM;
+}
+
+/**
+ * fc_exch_mgr_alloc() - Allocate an exchange manager
+ * @lport: The local port that the new EM will be associated with
+ * @class: The default FC class for new exchanges
+ * @min_xid: The minimum XID for exchanges from the new EM
+ * @max_xid: The maximum XID for exchanges from the new EM
+ * @match: The match routine for the new EM
+ */
+struct fc_exch_mgr *fc_exch_mgr_alloc(struct fc_lport *lport,
enum fc_class class,
u16 min_xid, u16 max_xid,
bool (*match)(struct fc_frame *))
@@ -1830,7 +2126,7 @@ struct fc_exch_mgr *fc_exch_mgr_alloc(struct fc_lport *lp,
if (max_xid <= min_xid || max_xid == FC_XID_UNKNOWN ||
(min_xid & fc_cpu_mask) != 0) {
- FC_LPORT_DBG(lp, "Invalid min_xid 0x:%x and max_xid 0x:%x\n",
+ FC_LPORT_DBG(lport, "Invalid min_xid 0x:%x and max_xid 0x:%x\n",
min_xid, max_xid);
return NULL;
}
@@ -1873,7 +2169,7 @@ struct fc_exch_mgr *fc_exch_mgr_alloc(struct fc_lport *lp,
}
kref_init(&mp->kref);
- if (!fc_exch_mgr_add(lp, mp, match)) {
+ if (!fc_exch_mgr_add(lport, mp, match)) {
free_percpu(mp->pool);
goto free_mempool;
}
@@ -1894,76 +2190,26 @@ free_mp:
}
EXPORT_SYMBOL(fc_exch_mgr_alloc);
+/**
+ * fc_exch_mgr_free() - Free all exchange managers on a local port
+ * @lport: The local port whose EMs are to be freed
+ */
void fc_exch_mgr_free(struct fc_lport *lport)
{
struct fc_exch_mgr_anchor *ema, *next;
+ flush_workqueue(fc_exch_workqueue);
list_for_each_entry_safe(ema, next, &lport->ema_list, ema_list)
fc_exch_mgr_del(ema);
}
EXPORT_SYMBOL(fc_exch_mgr_free);
-
-struct fc_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 = fc_exch_alloc(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 (ep->xid <= lp->lro_xid)
- fc_fcp_ddp_setup(fr_fsp(fp), ep->xid);
-
- 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_delete(ep);
- return NULL;
-}
-EXPORT_SYMBOL(fc_exch_seq_send);
-
-/*
- * Receive a frame
+/**
+ * fc_exch_recv() - Handler for received frames
+ * @lport: The local port the frame was received on
+ * @fp: The received frame
*/
-void fc_exch_recv(struct fc_lport *lp, struct fc_frame *fp)
+void fc_exch_recv(struct fc_lport *lport, struct fc_frame *fp)
{
struct fc_frame_header *fh = fc_frame_header_get(fp);
struct fc_exch_mgr_anchor *ema;
@@ -1971,8 +2217,8 @@ void fc_exch_recv(struct fc_lport *lp, struct fc_frame *fp)
u16 oxid;
/* lport lock ? */
- if (!lp || lp->state == LPORT_ST_DISABLED) {
- FC_LPORT_DBG(lp, "Receiving frames for an lport that "
+ if (!lport || lport->state == LPORT_ST_DISABLED) {
+ FC_LPORT_DBG(lport, "Receiving frames for an lport that "
"has not been initialized correctly\n");
fc_frame_free(fp);
return;
@@ -1981,7 +2227,7 @@ void fc_exch_recv(struct fc_lport *lp, struct fc_frame *fp)
f_ctl = ntoh24(fh->fh_f_ctl);
oxid = ntohs(fh->fh_ox_id);
if (f_ctl & FC_FC_EX_CTX) {
- list_for_each_entry(ema, &lp->ema_list, ema_list) {
+ list_for_each_entry(ema, &lport->ema_list, ema_list) {
if ((oxid >= ema->mp->min_xid) &&
(oxid <= ema->mp->max_xid)) {
found = 1;
@@ -1990,13 +2236,13 @@ void fc_exch_recv(struct fc_lport *lp, struct fc_frame *fp)
}
if (!found) {
- FC_LPORT_DBG(lp, "Received response for out "
+ FC_LPORT_DBG(lport, "Received response for out "
"of range oxid:%hx\n", oxid);
fc_frame_free(fp);
return;
}
} else
- ema = list_entry(lp->ema_list.prev, typeof(*ema), ema_list);
+ ema = list_entry(lport->ema_list.prev, typeof(*ema), ema_list);
/*
* If frame is marked invalid, just drop it.
@@ -2015,37 +2261,56 @@ void fc_exch_recv(struct fc_lport *lp, struct fc_frame *fp)
else if (f_ctl & FC_FC_SEQ_CTX)
fc_exch_recv_resp(ema->mp, fp);
else
- fc_exch_recv_req(lp, ema->mp, fp);
+ fc_exch_recv_req(lport, ema->mp, fp);
break;
default:
- FC_LPORT_DBG(lp, "dropping invalid frame (eof %x)", fr_eof(fp));
+ FC_LPORT_DBG(lport, "dropping invalid frame (eof %x)",
+ fr_eof(fp));
fc_frame_free(fp);
}
}
EXPORT_SYMBOL(fc_exch_recv);
-int fc_exch_init(struct fc_lport *lp)
+/**
+ * fc_exch_init() - Initialize the exchange layer for a local port
+ * @lport: The local port to initialize the exchange layer for
+ */
+int fc_exch_init(struct fc_lport *lport)
{
- if (!lp->tt.seq_start_next)
- lp->tt.seq_start_next = fc_seq_start_next;
+ if (!lport->tt.seq_start_next)
+ lport->tt.seq_start_next = fc_seq_start_next;
- if (!lp->tt.exch_seq_send)
- lp->tt.exch_seq_send = fc_exch_seq_send;
+ if (!lport->tt.exch_seq_send)
+ lport->tt.exch_seq_send = fc_exch_seq_send;
- if (!lp->tt.seq_send)
- lp->tt.seq_send = fc_seq_send;
+ if (!lport->tt.seq_send)
+ lport->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 (!lport->tt.seq_els_rsp_send)
+ lport->tt.seq_els_rsp_send = fc_seq_els_rsp_send;
- if (!lp->tt.exch_done)
- lp->tt.exch_done = fc_exch_done;
+ if (!lport->tt.exch_done)
+ lport->tt.exch_done = fc_exch_done;
- if (!lp->tt.exch_mgr_reset)
- lp->tt.exch_mgr_reset = fc_exch_mgr_reset;
+ if (!lport->tt.exch_mgr_reset)
+ lport->tt.exch_mgr_reset = fc_exch_mgr_reset;
- if (!lp->tt.seq_exch_abort)
- lp->tt.seq_exch_abort = fc_seq_exch_abort;
+ if (!lport->tt.seq_exch_abort)
+ lport->tt.seq_exch_abort = fc_seq_exch_abort;
+
+ return 0;
+}
+EXPORT_SYMBOL(fc_exch_init);
+
+/**
+ * fc_setup_exch_mgr() - Setup an exchange manager
+ */
+int fc_setup_exch_mgr()
+{
+ fc_em_cachep = kmem_cache_create("libfc_em", sizeof(struct fc_exch),
+ 0, SLAB_HWCACHE_ALIGN, NULL);
+ if (!fc_em_cachep)
+ return -ENOMEM;
/*
* Initialize fc_cpu_mask and fc_cpu_order. The
@@ -2069,20 +2334,17 @@ int fc_exch_init(struct fc_lport *lp)
}
fc_cpu_mask--;
- 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)
+ fc_exch_workqueue = create_singlethread_workqueue("fc_exch_workqueue");
+ if (!fc_exch_workqueue)
return -ENOMEM;
return 0;
}
-void fc_destroy_exch_mgr(void)
+/**
+ * fc_destroy_exch_mgr() - Destroy an exchange manager
+ */
+void fc_destroy_exch_mgr()
{
+ destroy_workqueue(fc_exch_workqueue);
kmem_cache_destroy(fc_em_cachep);
}
diff --git a/drivers/scsi/libfc/fc_fcp.c b/drivers/scsi/libfc/fc_fcp.c
index 59a4408b27b..c4b58d042f6 100644
--- a/drivers/scsi/libfc/fc_fcp.c
+++ b/drivers/scsi/libfc/fc_fcp.c
@@ -39,15 +39,9 @@
#include <scsi/libfc.h>
#include <scsi/fc_encode.h>
-MODULE_AUTHOR("Open-FCoE.org");
-MODULE_DESCRIPTION("libfc");
-MODULE_LICENSE("GPL v2");
+#include "fc_libfc.h"
-unsigned int fc_debug_logging;
-module_param_named(debug_logging, fc_debug_logging, int, S_IRUGO|S_IWUSR);
-MODULE_PARM_DESC(debug_logging, "a bit mask of logging levels");
-
-static struct kmem_cache *scsi_pkt_cachep;
+struct kmem_cache *scsi_pkt_cachep;
/* SRB state definitions */
#define FC_SRB_FREE 0 /* cmd is free */
@@ -58,7 +52,6 @@ static struct kmem_cache *scsi_pkt_cachep;
#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)
@@ -73,10 +66,20 @@ static struct kmem_cache *scsi_pkt_cachep;
#define CMD_SCSI_STATUS(Cmnd) ((Cmnd)->SCp.Status)
#define CMD_RESID_LEN(Cmnd) ((Cmnd)->SCp.buffers_residual)
+/**
+ * struct fc_fcp_internal - FCP layer internal data
+ * @scsi_pkt_pool: Memory pool to draw FCP packets from
+ * @scsi_pkt_queue: Current FCP packets
+ * @last_can_queue_ramp_down_time: ramp down time
+ * @last_can_queue_ramp_up_time: ramp up time
+ * @max_can_queue: max can_queue size
+ */
struct fc_fcp_internal {
- mempool_t *scsi_pkt_pool;
+ mempool_t *scsi_pkt_pool;
struct list_head scsi_pkt_queue;
- u8 throttled;
+ unsigned long last_can_queue_ramp_down_time;
+ unsigned long last_can_queue_ramp_up_time;
+ int max_can_queue;
};
#define fc_get_scsi_internal(x) ((struct fc_fcp_internal *)(x)->scsi_priv)
@@ -90,9 +93,9 @@ 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_fcp_error(struct fc_fcp_pkt *, struct fc_frame *);
static void fc_timeout_error(struct fc_fcp_pkt *);
-static void fc_fcp_timeout(unsigned long data);
+static void fc_fcp_timeout(unsigned long);
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 *);
@@ -124,6 +127,7 @@ static void fc_fcp_srr_error(struct fc_fcp_pkt *, struct fc_frame *);
#define FC_SCSI_TM_TOV (10 * HZ)
#define FC_SCSI_REC_TOV (2 * HZ)
#define FC_HOST_RESET_TIMEOUT (30 * HZ)
+#define FC_CAN_QUEUE_PERIOD (60 * HZ)
#define FC_MAX_ERROR_CNT 5
#define FC_MAX_RECOV_RETRY 3
@@ -131,23 +135,22 @@ static void fc_fcp_srr_error(struct fc_fcp_pkt *, struct fc_frame *);
#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
+ * fc_fcp_pkt_alloc() - Allocate a fcp_pkt
+ * @lport: The local port that the FCP packet is for
+ * @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.
+ * Return value: fcp_pkt structure or null on allocation failure.
+ * Context: Can be called from process context, no lock is required.
*/
-static struct fc_fcp_pkt *fc_fcp_pkt_alloc(struct fc_lport *lp, gfp_t gfp)
+static struct fc_fcp_pkt *fc_fcp_pkt_alloc(struct fc_lport *lport, gfp_t gfp)
{
- struct fc_fcp_internal *si = fc_get_scsi_internal(lp);
+ struct fc_fcp_internal *si = fc_get_scsi_internal(lport);
struct fc_fcp_pkt *fsp;
fsp = mempool_alloc(si->scsi_pkt_pool, gfp);
if (fsp) {
memset(fsp, 0, sizeof(*fsp));
- fsp->lp = lp;
+ fsp->lp = lport;
atomic_set(&fsp->ref_cnt, 1);
init_timer(&fsp->timer);
INIT_LIST_HEAD(&fsp->list);
@@ -157,12 +160,11 @@ static struct fc_fcp_pkt *fc_fcp_pkt_alloc(struct fc_lport *lp, gfp_t gfp)
}
/**
- * fc_fcp_pkt_release() - release hold on scsi_pkt packet
- * @fsp: fcp packet struct
+ * fc_fcp_pkt_release() - Release hold on a fcp_pkt
+ * @fsp: The FCP packet to be released
*
- * This is used by upper layer scsi driver.
- * Context : call from process and interrupt context.
- * no locking required
+ * Context: Can be called from process or interrupt context,
+ * no lock is required.
*/
static void fc_fcp_pkt_release(struct fc_fcp_pkt *fsp)
{
@@ -173,20 +175,25 @@ static void fc_fcp_pkt_release(struct fc_fcp_pkt *fsp)
}
}
+/**
+ * fc_fcp_pkt_hold() - Hold a fcp_pkt
+ * @fsp: The FCP packet to be held
+ */
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
+ * fc_fcp_pkt_destory() - Release hold on a fcp_pkt
+ * @seq: The sequence that the FCP packet is on (required by destructor API)
+ * @fsp: The FCP packet to be released
+ *
+ * This routine is called by a destructor callback in the exch_seq_send()
+ * routine of the libfc Transport Template. The 'struct fc_seq' is a required
+ * argument even though it is not used by this routine.
*
- * 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
+ * Context: No locking required.
*/
static void fc_fcp_pkt_destroy(struct fc_seq *seq, void *fsp)
{
@@ -194,10 +201,10 @@ static void fc_fcp_pkt_destroy(struct fc_seq *seq, void *fsp)
}
/**
- * fc_fcp_lock_pkt() - lock a packet and get a ref to it.
- * @fsp: fcp packet
+ * fc_fcp_lock_pkt() - Lock a fcp_pkt and increase its reference count
+ * @fsp: The FCP packet to be locked and incremented
*
- * We should only return error if we return a command to scsi-ml before
+ * 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.
@@ -222,18 +229,33 @@ static inline int fc_fcp_lock_pkt(struct fc_fcp_pkt *fsp)
return 0;
}
+/**
+ * fc_fcp_unlock_pkt() - Release a fcp_pkt's lock and decrement its
+ * reference count
+ * @fsp: The FCP packet to be unlocked and decremented
+ */
static inline void fc_fcp_unlock_pkt(struct fc_fcp_pkt *fsp)
{
spin_unlock_bh(&fsp->scsi_pkt_lock);
fc_fcp_pkt_release(fsp);
}
+/**
+ * fc_fcp_timer_set() - Start a timer for a fcp_pkt
+ * @fsp: The FCP packet to start a timer for
+ * @delay: The timeout period for the timer
+ */
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);
}
+/**
+ * fc_fcp_send_abort() - Send an abort for exchanges associated with a
+ * fcp_pkt
+ * @fsp: The FCP packet to abort exchanges on
+ */
static int fc_fcp_send_abort(struct fc_fcp_pkt *fsp)
{
if (!fsp->seq_ptr)
@@ -243,9 +265,14 @@ static int fc_fcp_send_abort(struct fc_fcp_pkt *fsp)
return fsp->lp->tt.seq_exch_abort(fsp->seq_ptr, 0);
}
-/*
- * Retry command.
- * An abort isn't needed.
+/**
+ * fc_fcp_retry_cmd() - Retry a fcp_pkt
+ * @fsp: The FCP packet to be retried
+ *
+ * Sets the status code to be FC_ERROR and then calls
+ * fc_fcp_complete_locked() which in turn calls fc_io_compl().
+ * fc_io_compl() will notify the SCSI-ml that the I/O is done.
+ * The SCSI-ml will retry the command.
*/
static void fc_fcp_retry_cmd(struct fc_fcp_pkt *fsp)
{
@@ -260,64 +287,146 @@ static void fc_fcp_retry_cmd(struct fc_fcp_pkt *fsp)
fc_fcp_complete_locked(fsp);
}
-/*
- * fc_fcp_ddp_setup - calls to LLD's ddp_setup to set up DDP
- * transfer for a read I/O indicated by the fc_fcp_pkt.
- * @fsp: ptr to the fc_fcp_pkt
- *
- * This is called in exch_seq_send() when we have a newly allocated
- * exchange with a valid exchange id to setup ddp.
- *
- * returns: none
+/**
+ * fc_fcp_ddp_setup() - Calls a LLD's ddp_setup routine to set up DDP context
+ * @fsp: The FCP packet that will manage the DDP frames
+ * @xid: The XID that will be used for the DDP exchange
*/
void fc_fcp_ddp_setup(struct fc_fcp_pkt *fsp, u16 xid)
{
- struct fc_lport *lp;
+ struct fc_lport *lport;
if (!fsp)
return;
- lp = fsp->lp;
+ lport = fsp->lp;
if ((fsp->req_flags & FC_SRB_READ) &&
- (lp->lro_enabled) && (lp->tt.ddp_setup)) {
- if (lp->tt.ddp_setup(lp, xid, scsi_sglist(fsp->cmd),
- scsi_sg_count(fsp->cmd)))
+ (lport->lro_enabled) && (lport->tt.ddp_setup)) {
+ if (lport->tt.ddp_setup(lport, xid, scsi_sglist(fsp->cmd),
+ scsi_sg_count(fsp->cmd)))
fsp->xfer_ddp = xid;
}
}
-EXPORT_SYMBOL(fc_fcp_ddp_setup);
-/*
- * fc_fcp_ddp_done - calls to LLD's ddp_done to release any
- * DDP related resources for this I/O if it is initialized
- * as a ddp transfer
- * @fsp: ptr to the fc_fcp_pkt
- *
- * returns: none
+/**
+ * fc_fcp_ddp_done() - Calls a LLD's ddp_done routine to release any
+ * DDP related resources for a fcp_pkt
+ * @fsp: The FCP packet that DDP had been used on
*/
static void fc_fcp_ddp_done(struct fc_fcp_pkt *fsp)
{
- struct fc_lport *lp;
+ struct fc_lport *lport;
if (!fsp)
return;
- lp = fsp->lp;
- if (fsp->xfer_ddp && lp->tt.ddp_done) {
- fsp->xfer_len = lp->tt.ddp_done(lp, fsp->xfer_ddp);
- fsp->xfer_ddp = 0;
+ if (fsp->xfer_ddp == FC_XID_UNKNOWN)
+ return;
+
+ lport = fsp->lp;
+ if (lport->tt.ddp_done) {
+ fsp->xfer_len = lport->tt.ddp_done(lport, fsp->xfer_ddp);
+ fsp->xfer_ddp = FC_XID_UNKNOWN;
}
}
+/**
+ * fc_fcp_can_queue_ramp_up() - increases can_queue
+ * @lport: lport to ramp up can_queue
+ *
+ * Locking notes: Called with Scsi_Host lock held
+ */
+static void fc_fcp_can_queue_ramp_up(struct fc_lport *lport)
+{
+ struct fc_fcp_internal *si = fc_get_scsi_internal(lport);
+ int can_queue;
+
+ if (si->last_can_queue_ramp_up_time &&
+ (time_before(jiffies, si->last_can_queue_ramp_up_time +
+ FC_CAN_QUEUE_PERIOD)))
+ return;
+
+ if (time_before(jiffies, si->last_can_queue_ramp_down_time +
+ FC_CAN_QUEUE_PERIOD))
+ return;
+
+ si->last_can_queue_ramp_up_time = jiffies;
+
+ can_queue = lport->host->can_queue << 1;
+ if (can_queue >= si->max_can_queue) {
+ can_queue = si->max_can_queue;
+ si->last_can_queue_ramp_down_time = 0;
+ }
+ lport->host->can_queue = can_queue;
+ shost_printk(KERN_ERR, lport->host, "libfc: increased "
+ "can_queue to %d.\n", can_queue);
+}
+
+/**
+ * fc_fcp_can_queue_ramp_down() - reduces can_queue
+ * @lport: lport to reduce can_queue
+ *
+ * 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.
+ *
+ * Locking notes: Called with Scsi_Host lock held
+ */
+static void fc_fcp_can_queue_ramp_down(struct fc_lport *lport)
+{
+ struct fc_fcp_internal *si = fc_get_scsi_internal(lport);
+ int can_queue;
+
+ if (si->last_can_queue_ramp_down_time &&
+ (time_before(jiffies, si->last_can_queue_ramp_down_time +
+ FC_CAN_QUEUE_PERIOD)))
+ return;
+
+ si->last_can_queue_ramp_down_time = jiffies;
+
+ can_queue = lport->host->can_queue;
+ can_queue >>= 1;
+ if (!can_queue)
+ can_queue = 1;
+ lport->host->can_queue = can_queue;
+ shost_printk(KERN_ERR, lport->host, "libfc: Could not allocate frame.\n"
+ "Reducing can_queue to %d.\n", can_queue);
+}
/*
- * Receive SCSI data from target.
- * Called after receiving solicited data.
+ * fc_fcp_frame_alloc() - Allocates fc_frame structure and buffer.
+ * @lport: fc lport struct
+ * @len: payload length
+ *
+ * Allocates fc_frame structure and buffer but if fails to allocate
+ * then reduce can_queue.
+ */
+static inline struct fc_frame *fc_fcp_frame_alloc(struct fc_lport *lport,
+ size_t len)
+{
+ struct fc_frame *fp;
+ unsigned long flags;
+
+ fp = fc_frame_alloc(lport, len);
+ if (!fp) {
+ spin_lock_irqsave(lport->host->host_lock, flags);
+ fc_fcp_can_queue_ramp_down(lport);
+ spin_unlock_irqrestore(lport->host->host_lock, flags);
+ }
+ return fp;
+}
+
+/**
+ * fc_fcp_recv_data() - Handler for receiving SCSI-FCP data from a target
+ * @fsp: The FCP packet the data is on
+ * @fp: The data frame
*/
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 fc_lport *lport = fsp->lp;
struct fcoe_dev_stats *stats;
struct fc_frame_header *fh;
size_t start_offset;
@@ -327,7 +436,7 @@ static void fc_fcp_recv_data(struct fc_fcp_pkt *fsp, struct fc_frame *fp)
size_t len;
void *buf;
struct scatterlist *sg;
- size_t remaining;
+ u32 nents;
fh = fc_frame_header_get(fp);
offset = ntohl(fh->fh_parm_offset);
@@ -351,65 +460,29 @@ static void fc_fcp_recv_data(struct fc_fcp_pkt *fsp, struct fc_frame *fp)
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;
+ nents = scsi_sg_count(sc);
- 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) {
+ if (!(fr_flags(fp) & FCPHF_CRC_UNCHECKED)) {
+ copy_len = fc_copy_buffer_to_sglist(buf, len, sg, &nents,
+ &offset, KM_SOFTIRQ0, NULL);
+ } else {
+ crc = crc32(~0, (u8 *) fh, sizeof(*fh));
+ copy_len = fc_copy_buffer_to_sglist(buf, len, sg, &nents,
+ &offset, KM_SOFTIRQ0, &crc);
buf = fc_frame_payload_get(fp, 0);
- if (len % 4) {
+ 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 = fc_lport_get_stats(lp);
+ stats = fc_lport_get_stats(lport);
stats->ErrorFrames++;
/* FIXME - per cpu count, not total count! */
if (stats->InvalidCRCCount++ < 5)
printk(KERN_WARNING "libfc: CRC error on data "
"frame for port (%6x)\n",
- fc_host_port_id(lp->host));
+ fc_host_port_id(lport->host));
/*
* Assume the frame is total garbage.
* We may have copied it over the good part
@@ -437,18 +510,17 @@ crc_err:
}
/**
- * 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
+ * fc_fcp_send_data() - Send SCSI data to a target
+ * @fsp: The FCP packet the data is on
+ * @sp: The sequence the data is to be sent on
+ * @offset: The 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.
+ * If the LLD is capable of sequence offload then send down the
+ * seq_blen ammount of data in single frame, otherwise send
+ * multiple frames of the maximum frame payload supported by
+ * the target port.
*/
static int fc_fcp_send_data(struct fc_fcp_pkt *fsp, struct fc_seq *seq,
size_t offset, size_t seq_blen)
@@ -457,16 +529,18 @@ static int fc_fcp_send_data(struct fc_fcp_pkt *fsp, struct fc_seq *seq,
struct scsi_cmnd *sc;
struct scatterlist *sg;
struct fc_frame *fp = NULL;
- struct fc_lport *lp = fsp->lp;
+ struct fc_lport *lport = fsp->lp;
+ struct page *page;
size_t remaining;
size_t t_blen;
size_t tlen;
size_t sg_bytes;
size_t frame_offset, fh_parm_offset;
+ size_t off;
int error;
void *data = NULL;
void *page_addr;
- int using_sg = lp->sg_supp;
+ int using_sg = lport->sg_supp;
u32 f_ctl;
WARN_ON(seq_blen <= 0);
@@ -488,10 +562,10 @@ static int fc_fcp_send_data(struct fc_fcp_pkt *fsp, struct fc_seq *seq,
* to max FC frame payload previously set in fsp->max_payload.
*/
t_blen = fsp->max_payload;
- if (lp->seq_offload) {
- t_blen = min(seq_blen, (size_t)lp->lso_max);
+ if (lport->seq_offload) {
+ t_blen = min(seq_blen, (size_t)lport->lso_max);
FC_FCP_DBG(fsp, "fsp=%p:lso:blen=%zx lso_max=0x%x t_blen=%zx\n",
- fsp, seq_blen, lp->lso_max, t_blen);
+ fsp, seq_blen, lport->lso_max, t_blen);
}
WARN_ON(t_blen < FC_MIN_MAX_PAYLOAD);
@@ -503,7 +577,7 @@ static int fc_fcp_send_data(struct fc_fcp_pkt *fsp, struct fc_seq *seq,
remaining = seq_blen;
fh_parm_offset = frame_offset = offset;
tlen = 0;
- seq = lp->tt.seq_start_next(seq);
+ seq = lport->tt.seq_start_next(seq);
f_ctl = FC_FC_REL_OFF;
WARN_ON(!seq);
@@ -525,43 +599,34 @@ static int fc_fcp_send_data(struct fc_fcp_pkt *fsp, struct fc_seq *seq,
*/
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;
+ fp = fc_frame_alloc(lport, using_sg ? 0 : tlen);
+ if (!fp)
+ return -ENOMEM;
- data = (void *)(fr_hdr(fp)) +
- sizeof(struct fc_frame_header);
- }
+ data = fc_frame_header_get(fp) + 1;
fh_parm_offset = frame_offset;
fr_max_payload(fp) = fsp->max_payload;
}
+
+ off = offset + sg->offset;
sg_bytes = min(tlen, sg->length - offset);
+ sg_bytes = min(sg_bytes,
+ (size_t) (PAGE_SIZE - (off & ~PAGE_MASK)));
+ page = sg_page(sg) + (off >> PAGE_SHIFT);
if (using_sg) {
- get_page(sg_page(sg));
+ get_page(page);
skb_fill_page_desc(fp_skb(fp),
skb_shinfo(fp_skb(fp))->nr_frags,
- sg_page(sg), sg->offset + offset,
- sg_bytes);
+ page, off & ~PAGE_MASK, 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);
+ page_addr = kmap_atomic(page, KM_SOFTIRQ0);
memcpy(data, (char *)page_addr + (off & ~PAGE_MASK),
sg_bytes);
kunmap_atomic(page_addr, KM_SOFTIRQ0);
@@ -572,7 +637,8 @@ static int fc_fcp_send_data(struct fc_fcp_pkt *fsp, struct fc_seq *seq,
tlen -= sg_bytes;
remaining -= sg_bytes;
- if (tlen)
+ if ((skb_shinfo(fp_skb(fp))->nr_frags < FC_FRAME_SG_LEN) &&
+ (tlen))
continue;
/*
@@ -589,7 +655,7 @@ static int fc_fcp_send_data(struct fc_fcp_pkt *fsp, struct fc_seq *seq,
/*
* send fragment using for a sequence.
*/
- error = lp->tt.seq_send(lp, seq, fp);
+ error = lport->tt.seq_send(lport, seq, fp);
if (error) {
WARN_ON(1); /* send error should be rare */
fc_fcp_retry_cmd(fsp);
@@ -601,6 +667,11 @@ static int fc_fcp_send_data(struct fc_fcp_pkt *fsp, struct fc_seq *seq,
return 0;
}
+/**
+ * fc_fcp_abts_resp() - Send an ABTS response
+ * @fsp: The FCP packet that is being aborted
+ * @fp: The response frame
+ */
static void fc_fcp_abts_resp(struct fc_fcp_pkt *fsp, struct fc_frame *fp)
{
int ba_done = 1;
@@ -637,46 +708,13 @@ static void fc_fcp_abts_resp(struct fc_fcp_pkt *fsp, struct fc_frame *fp)
}
/**
- * 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, "libfc: Could not allocate frame.\n"
- "Reducing can_queue to %d.\n", can_queue);
-done:
- spin_unlock_irqrestore(lp->host->host_lock, flags);
-}
-
-/**
- * fc_fcp_recv() - Reveive FCP frames
+ * fc_fcp_recv() - Reveive an FCP frame
* @seq: The sequence the frame is on
- * @fp: The FC frame
+ * @fp: The received frame
* @arg: The related FCP packet
*
- * Return : None
- * Context : called from Soft IRQ context
- * can not called holding list lock
+ * Context: Called from Soft IRQ context. Can not be called
+ * holding the FCP packet list lock.
*/
static void fc_fcp_recv(struct fc_seq *seq, struct fc_frame *fp, void *arg)
{
@@ -687,8 +725,10 @@ static void fc_fcp_recv(struct fc_seq *seq, struct fc_frame *fp, void *arg)
u8 r_ctl;
int rc = 0;
- if (IS_ERR(fp))
- goto errout;
+ if (IS_ERR(fp)) {
+ fc_fcp_error(fsp, fp);
+ return;
+ }
fh = fc_frame_header_get(fp);
r_ctl = fh->fh_r_ctl;
@@ -721,8 +761,6 @@ static void fc_fcp_recv(struct fc_seq *seq, struct fc_frame *fp, void *arg)
(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
@@ -742,13 +780,13 @@ 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(lport);
}
+/**
+ * fc_fcp_resp() - Handler for FCP responses
+ * @fsp: The FCP packet the response is for
+ * @fp: The response frame
+ */
static void fc_fcp_resp(struct fc_fcp_pkt *fsp, struct fc_frame *fp)
{
struct fc_frame_header *fh;
@@ -862,15 +900,16 @@ err:
}
/**
- * fc_fcp_complete_locked() - complete processing of a fcp packet
- * @fsp: fcp packet
+ * fc_fcp_complete_locked() - Complete processing of a fcp_pkt with the
+ * fcp_pkt lock held
+ * @fsp: The FCP packet to be completed
*
* 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_lport *lport = fsp->lp;
struct fc_seq *seq;
struct fc_exch *ep;
u32 f_ctl;
@@ -901,8 +940,8 @@ static void fc_fcp_complete_locked(struct fc_fcp_pkt *fsp)
struct fc_frame *conf_frame;
struct fc_seq *csp;
- csp = lp->tt.seq_start_next(seq);
- conf_frame = fc_frame_alloc(fsp->lp, 0);
+ csp = lport->tt.seq_start_next(seq);
+ conf_frame = fc_fcp_frame_alloc(fsp->lp, 0);
if (conf_frame) {
f_ctl = FC_FC_SEQ_INIT;
f_ctl |= FC_FC_LAST_SEQ | FC_FC_END_SEQ;
@@ -910,43 +949,48 @@ static void fc_fcp_complete_locked(struct fc_fcp_pkt *fsp)
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);
+ lport->tt.seq_send(lport, csp, conf_frame);
}
}
- lp->tt.exch_done(seq);
+ lport->tt.exch_done(seq);
}
fc_io_compl(fsp);
}
+/**
+ * fc_fcp_cleanup_cmd() - Cancel the active exchange on a fcp_pkt
+ * @fsp: The FCP packet whose exchanges should be canceled
+ * @error: The reason for the cancellation
+ */
static void fc_fcp_cleanup_cmd(struct fc_fcp_pkt *fsp, int error)
{
- struct fc_lport *lp = fsp->lp;
+ struct fc_lport *lport = fsp->lp;
if (fsp->seq_ptr) {
- lp->tt.exch_done(fsp->seq_ptr);
+ lport->tt.exch_done(fsp->seq_ptr);
fsp->seq_ptr = NULL;
}
fsp->status_code = error;
}
/**
- * fc_fcp_cleanup_each_cmd() - Cleanup active commads
- * @lp: logical port
- * @id: target id
- * @lun: lun
- * @error: fsp status code
+ * fc_fcp_cleanup_each_cmd() - Cancel all exchanges on a local port
+ * @lport: The local port whose exchanges should be canceled
+ * @id: The target's ID
+ * @lun: The LUN
+ * @error: The reason for cancellation
*
* If lun or id is -1, they are ignored.
*/
-static void fc_fcp_cleanup_each_cmd(struct fc_lport *lp, unsigned int id,
+static void fc_fcp_cleanup_each_cmd(struct fc_lport *lport, unsigned int id,
unsigned int lun, int error)
{
- struct fc_fcp_internal *si = fc_get_scsi_internal(lp);
+ struct fc_fcp_internal *si = fc_get_scsi_internal(lport);
struct fc_fcp_pkt *fsp;
struct scsi_cmnd *sc_cmd;
unsigned long flags;
- spin_lock_irqsave(lp->host->host_lock, flags);
+ spin_lock_irqsave(lport->host->host_lock, flags);
restart:
list_for_each_entry(fsp, &si->scsi_pkt_queue, list) {
sc_cmd = fsp->cmd;
@@ -957,7 +1001,7 @@ restart:
continue;
fc_fcp_pkt_hold(fsp);
- spin_unlock_irqrestore(lp->host->host_lock, flags);
+ spin_unlock_irqrestore(lport->host->host_lock, flags);
if (!fc_fcp_lock_pkt(fsp)) {
fc_fcp_cleanup_cmd(fsp, error);
@@ -966,35 +1010,36 @@ restart:
}
fc_fcp_pkt_release(fsp);
- spin_lock_irqsave(lp->host->host_lock, flags);
+ spin_lock_irqsave(lport->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);
+ spin_unlock_irqrestore(lport->host->host_lock, flags);
}
-static void fc_fcp_abort_io(struct fc_lport *lp)
+/**
+ * fc_fcp_abort_io() - Abort all FCP-SCSI exchanges on a local port
+ * @lport: The local port whose exchanges are to be aborted
+ */
+static void fc_fcp_abort_io(struct fc_lport *lport)
{
- fc_fcp_cleanup_each_cmd(lp, -1, -1, FC_HRD_ERROR);
+ fc_fcp_cleanup_each_cmd(lport, -1, -1, FC_HRD_ERROR);
}
/**
- * fc_fcp_pkt_send() - send a fcp packet to the lower level.
- * @lp: fc lport
- * @fsp: fc packet.
+ * fc_fcp_pkt_send() - Send a fcp_pkt
+ * @lport: The local port to send the FCP packet on
+ * @fsp: The FCP packet to send
*
- * 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.
+ * Return: Zero for success and -1 for failure
+ * Locks: Called with the host lock and irqs disabled.
*/
-static int fc_fcp_pkt_send(struct fc_lport *lp, struct fc_fcp_pkt *fsp)
+static int fc_fcp_pkt_send(struct fc_lport *lport, struct fc_fcp_pkt *fsp)
{
- struct fc_fcp_internal *si = fc_get_scsi_internal(lp);
+ struct fc_fcp_internal *si = fc_get_scsi_internal(lport);
int rc;
fsp->cmd->SCp.ptr = (char *)fsp;
@@ -1006,16 +1051,22 @@ static int fc_fcp_pkt_send(struct fc_lport *lp, struct fc_fcp_pkt *fsp)
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);
+ spin_unlock_irq(lport->host->host_lock);
+ rc = lport->tt.fcp_cmd_send(lport, fsp, fc_fcp_recv);
+ spin_lock_irq(lport->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,
+/**
+ * fc_fcp_cmd_send() - Send a FCP command
+ * @lport: The local port to send the command on
+ * @fsp: The FCP packet the command is on
+ * @resp: The handler for the response
+ */
+static int fc_fcp_cmd_send(struct fc_lport *lport, struct fc_fcp_pkt *fsp,
void (*resp)(struct fc_seq *,
struct fc_frame *fp,
void *arg))
@@ -1023,14 +1074,14 @@ static int fc_fcp_cmd_send(struct fc_lport *lp, struct fc_fcp_pkt *fsp,
struct fc_frame *fp;
struct fc_seq *seq;
struct fc_rport *rport;
- struct fc_rport_libfc_priv *rp;
+ struct fc_rport_libfc_priv *rpriv;
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));
+ fp = fc_fcp_frame_alloc(lport, sizeof(fsp->cdb_cmd));
if (!fp) {
rc = -1;
goto unlock;
@@ -1040,15 +1091,15 @@ static int fc_fcp_cmd_send(struct fc_lport *lp, struct fc_fcp_pkt *fsp,
fr_fsp(fp) = fsp;
rport = fsp->rport;
fsp->max_payload = rport->maxframe_size;
- rp = rport->dd_data;
+ rpriv = 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_host_port_id(rpriv->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);
+ seq = lport->tt.exch_seq_send(lport, fp, resp, fc_fcp_pkt_destroy,
+ fsp, 0);
if (!seq) {
- fc_frame_free(fp);
rc = -1;
goto unlock;
}
@@ -1065,8 +1116,10 @@ unlock:
return rc;
}
-/*
- * transport error handler
+/**
+ * fc_fcp_error() - Handler for FCP layer errors
+ * @fsp: The FCP packet the error is on
+ * @fp: The frame that has errored
*/
static void fc_fcp_error(struct fc_fcp_pkt *fsp, struct fc_frame *fp)
{
@@ -1091,11 +1144,13 @@ unlock:
fc_fcp_unlock_pkt(fsp);
}
-/*
- * Scsi abort handler- calls to send an abort
- * and then wait for abort completion
+/**
+ * fc_fcp_pkt_abort() - Abort a fcp_pkt
+ * @fsp: The FCP packet to abort on
+ *
+ * Called 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)
+static int fc_fcp_pkt_abort(struct fc_fcp_pkt *fsp)
{
int rc = FAILED;
@@ -1122,14 +1177,15 @@ static int fc_fcp_pkt_abort(struct fc_lport *lp, struct fc_fcp_pkt *fsp)
return rc;
}
-/*
- * Retry LUN reset after resource allocation failed.
+/**
+ * fc_lun_reset_send() - Send LUN reset command
+ * @data: The FCP packet that identifies the LUN to be reset
*/
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)) {
+ struct fc_lport *lport = fsp->lp;
+ if (lport->tt.fcp_cmd_send(lport, fsp, fc_tm_done)) {
if (fsp->recov_retry++ >= FC_MAX_RECOV_RETRY)
return;
if (fc_fcp_lock_pkt(fsp))
@@ -1140,11 +1196,15 @@ static void fc_lun_reset_send(unsigned long data)
}
}
-/*
- * Scsi device reset handler- send a LUN RESET to the device
- * and wait for reset reply
+/**
+ * fc_lun_reset() - Send a LUN RESET command to a device
+ * and wait for the reply
+ * @lport: The local port to sent the comand on
+ * @fsp: The FCP packet that identifies the LUN to be reset
+ * @id: The SCSI command ID
+ * @lun: The LUN ID to be reset
*/
-static int fc_lun_reset(struct fc_lport *lp, struct fc_fcp_pkt *fsp,
+static int fc_lun_reset(struct fc_lport *lport, struct fc_fcp_pkt *fsp,
unsigned int id, unsigned int lun)
{
int rc;
@@ -1172,14 +1232,14 @@ static int fc_lun_reset(struct fc_lport *lp, struct fc_fcp_pkt *fsp,
spin_lock_bh(&fsp->scsi_pkt_lock);
if (fsp->seq_ptr) {
- lp->tt.exch_done(fsp->seq_ptr);
+ lport->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_SCSI_DBG(lp, "lun reset failed\n");
+ FC_SCSI_DBG(lport, "lun reset failed\n");
return FAILED;
}
@@ -1187,13 +1247,16 @@ static int fc_lun_reset(struct fc_lport *lp, struct fc_fcp_pkt *fsp,
if (fsp->cdb_status != FCP_TMF_CMPL)
return FAILED;
- FC_SCSI_DBG(lp, "lun reset to lun %u completed\n", lun);
- fc_fcp_cleanup_each_cmd(lp, id, lun, FC_CMD_ABORTED);
+ FC_SCSI_DBG(lport, "lun reset to lun %u completed\n", lun);
+ fc_fcp_cleanup_each_cmd(lport, id, lun, FC_CMD_ABORTED);
return SUCCESS;
}
-/*
- * Task Managment response handler
+/**
+ * fc_tm_done() - Task Managment response handler
+ * @seq: The sequence that the response is on
+ * @fp: The response frame
+ * @arg: The FCP packet the response is for
*/
static void fc_tm_done(struct fc_seq *seq, struct fc_frame *fp, void *arg)
{
@@ -1230,34 +1293,31 @@ static void fc_tm_done(struct fc_seq *seq, struct fc_frame *fp, void *arg)
fc_fcp_unlock_pkt(fsp);
}
-static void fc_fcp_cleanup(struct fc_lport *lp)
+/**
+ * fc_fcp_cleanup() - Cleanup all FCP exchanges on a local port
+ * @lport: The local port to be cleaned up
+ */
+static void fc_fcp_cleanup(struct fc_lport *lport)
{
- fc_fcp_cleanup_each_cmd(lp, -1, -1, FC_ERROR);
+ fc_fcp_cleanup_each_cmd(lport, -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.
+/**
+ * fc_fcp_timeout() - Handler for fcp_pkt timeouts
+ * @data: The FCP packet that has timed out
*
- * 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.
+ * If REC is supported then 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 then 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;
+ struct fc_rport_libfc_priv *rpriv = rport->dd_data;
if (fc_fcp_lock_pkt(fsp))
return;
@@ -1267,7 +1327,7 @@ static void fc_fcp_timeout(unsigned long data)
fsp->state |= FC_SRB_FCP_PROCESSING_TMO;
- if (rp->flags & FC_RP_FLAGS_REC_SUPPORTED)
+ if (rpriv->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))
@@ -1281,39 +1341,40 @@ unlock:
fc_fcp_unlock_pkt(fsp);
}
-/*
- * Send a REC ELS request
+/**
+ * fc_fcp_rec() - Send a REC ELS request
+ * @fsp: The FCP packet to send the REC request on
*/
static void fc_fcp_rec(struct fc_fcp_pkt *fsp)
{
- struct fc_lport *lp;
+ struct fc_lport *lport;
struct fc_frame *fp;
struct fc_rport *rport;
- struct fc_rport_libfc_priv *rp;
+ struct fc_rport_libfc_priv *rpriv;
- lp = fsp->lp;
+ lport = fsp->lp;
rport = fsp->rport;
- rp = rport->dd_data;
- if (!fsp->seq_ptr || rp->rp_state != RPORT_ST_READY) {
+ rpriv = rport->dd_data;
+ if (!fsp->seq_ptr || rpriv->rp_state != RPORT_ST_READY) {
fsp->status_code = FC_HRD_ERROR;
fsp->io_status = 0;
fc_fcp_complete_locked(fsp);
return;
}
- fp = fc_frame_alloc(lp, sizeof(struct fc_els_rec));
+ fp = fc_fcp_frame_alloc(lport, 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_host_port_id(rpriv->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->port_id, fp, ELS_REC, fc_fcp_rec_resp,
- fsp, jiffies_to_msecs(FC_SCSI_REC_TOV))) {
+ if (lport->tt.elsct_send(lport, rport->port_id, fp, ELS_REC,
+ fc_fcp_rec_resp, fsp,
+ jiffies_to_msecs(FC_SCSI_REC_TOV))) {
fc_fcp_pkt_hold(fsp); /* hold while REC outstanding */
return;
}
- fc_frame_free(fp);
retry:
if (fsp->recov_retry++ < FC_MAX_RECOV_RETRY)
fc_fcp_timer_set(fsp, FC_SCSI_REC_TOV);
@@ -1321,12 +1382,16 @@ retry:
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.
+/**
+ * fc_fcp_rec_resp() - Handler for REC ELS responses
+ * @seq: The sequence the response is on
+ * @fp: The response frame
+ * @arg: The FCP packet the response is on
+ *
+ * If the response is a reject then the scsi layer will handle
+ * the timeout. If the response is a LS_ACC then if the I/O was not completed
+ * set the timeout and return. If the I/O was completed then complete the
+ * exchange and tell the SCSI layer.
*/
static void fc_fcp_rec_resp(struct fc_seq *seq, struct fc_frame *fp, void *arg)
{
@@ -1338,7 +1403,7 @@ static void fc_fcp_rec_resp(struct fc_seq *seq, struct fc_frame *fp, void *arg)
u32 offset;
enum dma_data_direction data_dir;
enum fc_rctl r_ctl;
- struct fc_rport_libfc_priv *rp;
+ struct fc_rport_libfc_priv *rpriv;
if (IS_ERR(fp)) {
fc_fcp_rec_error(fsp, fp);
@@ -1361,13 +1426,13 @@ static void fc_fcp_rec_resp(struct fc_seq *seq, struct fc_frame *fp, void *arg)
/* fall through */
case ELS_RJT_UNSUP:
FC_FCP_DBG(fsp, "device does not support REC\n");
- rp = fsp->rport->dd_data;
+ rpriv = 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;
+ rpriv->flags &= ~FC_RP_FLAGS_REC_SUPPORTED;
fc_fcp_timer_set(fsp, FC_SCSI_ER_TIMEOUT);
break;
case ELS_RJT_LOGIC:
@@ -1464,8 +1529,10 @@ out:
fc_frame_free(fp);
}
-/*
- * Handle error response or timeout for REC exchange.
+/**
+ * fc_fcp_rec_error() - Handler for REC errors
+ * @fsp: The FCP packet the error is on
+ * @fp: The REC frame
*/
static void fc_fcp_rec_error(struct fc_fcp_pkt *fsp, struct fc_frame *fp)
{
@@ -1504,10 +1571,9 @@ 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
+/**
+ * fc_timeout_error() - Handler for fcp_pkt timeouts
+ * @fsp: The FCP packt that has timed out
*/
static void fc_timeout_error(struct fc_fcp_pkt *fsp)
{
@@ -1521,16 +1587,18 @@ static void fc_timeout_error(struct fc_fcp_pkt *fsp)
fc_fcp_send_abort(fsp);
}
-/*
- * Sequence retransmission request.
+/**
+ * fc_fcp_srr() - Send a SRR request (Sequence Retransmission Request)
+ * @fsp: The FCP packet the SRR is to be sent on
+ * @r_ctl: The R_CTL field for the SRR 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_lport *lport = fsp->lp;
struct fc_rport *rport;
- struct fc_rport_libfc_priv *rp;
+ struct fc_rport_libfc_priv *rpriv;
struct fc_exch *ep = fc_seq_exch(fsp->seq_ptr);
struct fc_seq *seq;
struct fcp_srr *srr;
@@ -1538,12 +1606,13 @@ static void fc_fcp_srr(struct fc_fcp_pkt *fsp, enum fc_rctl r_ctl, u32 offset)
u8 cdb_op;
rport = fsp->rport;
- rp = rport->dd_data;
+ rpriv = rport->dd_data;
cdb_op = fsp->cdb_cmd.fc_cdb[0];
- if (!(rp->flags & FC_RP_FLAGS_RETRY) || rp->rp_state != RPORT_ST_READY)
+ if (!(rpriv->flags & FC_RP_FLAGS_RETRY) ||
+ rpriv->rp_state != RPORT_ST_READY)
goto retry; /* shouldn't happen */
- fp = fc_frame_alloc(lp, sizeof(*srr));
+ fp = fc_fcp_frame_alloc(lport, sizeof(*srr));
if (!fp)
goto retry;
@@ -1556,15 +1625,14 @@ static void fc_fcp_srr(struct fc_fcp_pkt *fsp, enum fc_rctl r_ctl, u32 offset)
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_host_port_id(rpriv->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);
+ seq = lport->tt.exch_seq_send(lport, fp, fc_fcp_srr_resp, NULL,
+ fsp, jiffies_to_msecs(FC_SCSI_REC_TOV));
+ if (!seq)
goto retry;
- }
+
fsp->recov_seq = seq;
fsp->xfer_len = offset;
fsp->xfer_contig_end = offset;
@@ -1575,8 +1643,11 @@ retry:
fc_fcp_retry_cmd(fsp);
}
-/*
- * Handle response from SRR.
+/**
+ * fc_fcp_srr_resp() - Handler for SRR response
+ * @seq: The sequence the SRR is on
+ * @fp: The SRR frame
+ * @arg: The FCP packet the SRR is on
*/
static void fc_fcp_srr_resp(struct fc_seq *seq, struct fc_frame *fp, void *arg)
{
@@ -1622,6 +1693,11 @@ out:
fc_fcp_pkt_release(fsp); /* drop hold for outstanding SRR */
}
+/**
+ * fc_fcp_srr_error() - Handler for SRR errors
+ * @fsp: The FCP packet that the SRR error is on
+ * @fp: The SRR frame
+ */
static void fc_fcp_srr_error(struct fc_fcp_pkt *fsp, struct fc_frame *fp)
{
if (fc_fcp_lock_pkt(fsp))
@@ -1646,31 +1722,36 @@ out:
fc_fcp_pkt_release(fsp); /* drop hold for outstanding SRR */
}
-static inline int fc_fcp_lport_queue_ready(struct fc_lport *lp)
+/**
+ * fc_fcp_lport_queue_ready() - Determine if the lport and it's queue is ready
+ * @lport: The local port to be checked
+ */
+static inline int fc_fcp_lport_queue_ready(struct fc_lport *lport)
{
/* lock ? */
- return (lp->state == LPORT_ST_READY) && lp->link_up && !lp->qfull;
+ return (lport->state == LPORT_ST_READY) &&
+ lport->link_up && !lport->qfull;
}
/**
- * 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
+ * fc_queuecommand() - The queuecommand function of the SCSI template
+ * @cmd: The scsi_cmnd to be executed
+ * @done: The callback function to be called when the scsi_cmnd is complete
*
- * this is the i/o strategy routine, called by the scsi layer
- * this routine is called with holding the host_lock.
+ * This is the i/o strategy routine, called by the SCSI layer. This routine
+ * is called with the host_lock held.
*/
int fc_queuecommand(struct scsi_cmnd *sc_cmd, void (*done)(struct scsi_cmnd *))
{
- struct fc_lport *lp;
+ struct fc_lport *lport;
struct fc_rport *rport = starget_to_rport(scsi_target(sc_cmd->device));
struct fc_fcp_pkt *fsp;
- struct fc_rport_libfc_priv *rp;
+ struct fc_rport_libfc_priv *rpriv;
int rval;
int rc = 0;
struct fcoe_dev_stats *stats;
- lp = shost_priv(sc_cmd->device->host);
+ lport = shost_priv(sc_cmd->device->host);
rval = fc_remote_port_chkready(rport);
if (rval) {
@@ -1689,14 +1770,16 @@ int fc_queuecommand(struct scsi_cmnd *sc_cmd, void (*done)(struct scsi_cmnd *))
goto out;
}
- rp = rport->dd_data;
+ rpriv = rport->dd_data;
- if (!fc_fcp_lport_queue_ready(lp)) {
+ if (!fc_fcp_lport_queue_ready(lport)) {
+ if (lport->qfull)
+ fc_fcp_can_queue_ramp_down(lport);
rc = SCSI_MLQUEUE_HOST_BUSY;
goto out;
}
- fsp = fc_fcp_pkt_alloc(lp, GFP_ATOMIC);
+ fsp = fc_fcp_pkt_alloc(lport, GFP_ATOMIC);
if (fsp == NULL) {
rc = SCSI_MLQUEUE_HOST_BUSY;
goto out;
@@ -1706,8 +1789,9 @@ int fc_queuecommand(struct scsi_cmnd *sc_cmd, void (*done)(struct scsi_cmnd *))
* build the libfc request pkt
*/
fsp->cmd = sc_cmd; /* save the cmd */
- fsp->lp = lp; /* save the softc ptr */
+ fsp->lp = lport; /* save the softc ptr */
fsp->rport = rport; /* set the remote port ptr */
+ fsp->xfer_ddp = FC_XID_UNKNOWN;
sc_cmd->scsi_done = done;
/*
@@ -1719,7 +1803,7 @@ int fc_queuecommand(struct scsi_cmnd *sc_cmd, void (*done)(struct scsi_cmnd *))
/*
* setup the data direction
*/
- stats = fc_lport_get_stats(lp);
+ stats = fc_lport_get_stats(lport);
if (sc_cmd->sc_data_direction == DMA_FROM_DEVICE) {
fsp->req_flags = FC_SRB_READ;
stats->InputRequests++;
@@ -1733,7 +1817,7 @@ int fc_queuecommand(struct scsi_cmnd *sc_cmd, void (*done)(struct scsi_cmnd *))
stats->ControlRequests++;
}
- fsp->tgt_flags = rp->flags;
+ fsp->tgt_flags = rpriv->flags;
init_timer(&fsp->timer);
fsp->timer.data = (unsigned long)fsp;
@@ -1743,7 +1827,7 @@ int fc_queuecommand(struct scsi_cmnd *sc_cmd, void (*done)(struct scsi_cmnd *))
* if we get -1 return then put the request in the pending
* queue.
*/
- rval = fc_fcp_pkt_send(lp, fsp);
+ rval = fc_fcp_pkt_send(lport, fsp);
if (rval != 0) {
fsp->state = FC_SRB_FREE;
fc_fcp_pkt_release(fsp);
@@ -1755,18 +1839,17 @@ out:
EXPORT_SYMBOL(fc_queuecommand);
/**
- * fc_io_compl() - Handle responses for completed commands
- * @fsp: scsi packet
- *
- * Translates a error to a Linux SCSI error.
+ * fc_io_compl() - Handle responses for completed commands
+ * @fsp: The FCP packet that is complete
*
+ * Translates fcp_pkt errors to a Linux SCSI errors.
* 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;
+ struct fc_lport *lport;
unsigned long flags;
/* release outstanding ddp context */
@@ -1779,28 +1862,26 @@ static void fc_io_compl(struct fc_fcp_pkt *fsp)
spin_lock_bh(&fsp->scsi_pkt_lock);
}
- lp = fsp->lp;
- si = fc_get_scsi_internal(lp);
- spin_lock_irqsave(lp->host->host_lock, flags);
+ lport = fsp->lp;
+ si = fc_get_scsi_internal(lport);
+ spin_lock_irqsave(lport->host->host_lock, flags);
if (!fsp->cmd) {
- spin_unlock_irqrestore(lp->host->host_lock, flags);
+ spin_unlock_irqrestore(lport->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 can_queue ramp down is done then try can_queue ramp up
+ * since commands are completing now.
*/
- if (si->throttled && fsp->state & FC_SRB_NOMEM)
- si->throttled = 0;
+ if (si->last_can_queue_ramp_down_time)
+ fc_fcp_can_queue_ramp_up(lport);
sc_cmd = fsp->cmd;
fsp->cmd = NULL;
if (!sc_cmd->SCp.ptr) {
- spin_unlock_irqrestore(lp->host->host_lock, flags);
+ spin_unlock_irqrestore(lport->host->host_lock, flags);
return;
}
@@ -1814,21 +1895,6 @@ static void fc_io_compl(struct fc_fcp_pkt *fsp)
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
@@ -1846,7 +1912,8 @@ static void fc_io_compl(struct fc_fcp_pkt *fsp)
* scsi status is good but transport level
* underrun.
*/
- sc_cmd->result = DID_OK << 16;
+ sc_cmd->result = (fsp->state & FC_SRB_RCV_STATUS ?
+ DID_OK : DID_ERROR) << 16;
} else {
/*
* scsi got underrun, this is an error
@@ -1881,60 +1948,42 @@ static void fc_io_compl(struct fc_fcp_pkt *fsp)
list_del(&fsp->list);
sc_cmd->SCp.ptr = NULL;
sc_cmd->scsi_done(sc_cmd);
- spin_unlock_irqrestore(lp->host->host_lock, flags);
+ spin_unlock_irqrestore(lport->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
- * @sc_cmd: scsi command to abort
+ * @sc_cmd: The SCSI command to abort
*
- * From scsi host template.
- * send ABTS to the target device and wait for the response
- * sc_cmd is the pointer to the command to be aborted.
+ * From SCSI host template.
+ * Send an ABTS to the target device and wait for the response.
*/
int fc_eh_abort(struct scsi_cmnd *sc_cmd)
{
struct fc_fcp_pkt *fsp;
- struct fc_lport *lp;
+ struct fc_lport *lport;
int rc = FAILED;
unsigned long flags;
- lp = shost_priv(sc_cmd->device->host);
- if (lp->state != LPORT_ST_READY)
+ lport = shost_priv(sc_cmd->device->host);
+ if (lport->state != LPORT_ST_READY)
return rc;
- else if (!lp->link_up)
+ else if (!lport->link_up)
return rc;
- spin_lock_irqsave(lp->host->host_lock, flags);
+ spin_lock_irqsave(lport->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);
+ spin_unlock_irqrestore(lport->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);
+ spin_unlock_irqrestore(lport->host->host_lock, flags);
if (fc_fcp_lock_pkt(fsp)) {
/* completed while we were waiting for timer to be deleted */
@@ -1942,7 +1991,7 @@ int fc_eh_abort(struct scsi_cmnd *sc_cmd)
goto release_pkt;
}
- rc = fc_fcp_pkt_abort(lp, fsp);
+ rc = fc_fcp_pkt_abort(fsp);
fc_fcp_unlock_pkt(fsp);
release_pkt:
@@ -1952,37 +2001,34 @@ release_pkt:
EXPORT_SYMBOL(fc_eh_abort);
/**
- * fc_eh_device_reset() Reset a single LUN
- * @sc_cmd: scsi command
+ * fc_eh_device_reset() - Reset a single LUN
+ * @sc_cmd: The SCSI command which identifies the device whose
+ * LUN is to be reset
*
- * Set from scsi host template to send tm cmd to the target and wait for the
- * response.
+ * Set from SCSI host template.
*/
int fc_eh_device_reset(struct scsi_cmnd *sc_cmd)
{
- struct fc_lport *lp;
+ struct fc_lport *lport;
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);
+ lport = shost_priv(sc_cmd->device->host);
- if (lp->state != LPORT_ST_READY)
+ if (lport->state != LPORT_ST_READY)
return rc;
- FC_SCSI_DBG(lp, "Resetting rport (%6x)\n", rport->port_id);
+ FC_SCSI_DBG(lport, "Resetting rport (%6x)\n", rport->port_id);
- fsp = fc_fcp_pkt_alloc(lp, GFP_NOIO);
+ fsp = fc_fcp_pkt_alloc(lport, GFP_NOIO);
if (fsp == NULL) {
printk(KERN_WARNING "libfc: could not allocate scsi_pkt\n");
- sc_cmd->result = DID_NO_CONNECT << 16;
goto out;
}
@@ -1991,13 +2037,13 @@ int fc_eh_device_reset(struct scsi_cmnd *sc_cmd)
* the sc passed in is not setup for execution like when sent
* through the queuecommand callout.
*/
- fsp->lp = lp; /* save the softc ptr */
+ fsp->lp = lport; /* 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);
+ rc = fc_lun_reset(lport, fsp, scmd_id(sc_cmd), sc_cmd->device->lun);
fsp->state = FC_SRB_FREE;
fc_fcp_pkt_release(fsp);
@@ -2007,38 +2053,39 @@ out:
EXPORT_SYMBOL(fc_eh_device_reset);
/**
- * fc_eh_host_reset() - The reset function will reset the ports on the host.
- * @sc_cmd: scsi command
+ * fc_eh_host_reset() - Reset a Scsi_Host.
+ * @sc_cmd: The SCSI command that identifies the SCSI host to be reset
*/
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);
+ struct fc_lport *lport = shost_priv(shost);
unsigned long wait_tmo;
- FC_SCSI_DBG(lp, "Resetting host\n");
+ FC_SCSI_DBG(lport, "Resetting host\n");
- lp->tt.lport_reset(lp);
+ lport->tt.lport_reset(lport);
wait_tmo = jiffies + FC_HOST_RESET_TIMEOUT;
- while (!fc_fcp_lport_queue_ready(lp) && time_before(jiffies, wait_tmo))
+ while (!fc_fcp_lport_queue_ready(lport) && time_before(jiffies,
+ wait_tmo))
msleep(1000);
- if (fc_fcp_lport_queue_ready(lp)) {
+ if (fc_fcp_lport_queue_ready(lport)) {
shost_printk(KERN_INFO, shost, "libfc: Host reset succeeded "
- "on port (%6x)\n", fc_host_port_id(lp->host));
+ "on port (%6x)\n", fc_host_port_id(lport->host));
return SUCCESS;
} else {
shost_printk(KERN_INFO, shost, "libfc: Host reset failed, "
"port (%6x) is not ready.\n",
- fc_host_port_id(lp->host));
+ fc_host_port_id(lport->host));
return FAILED;
}
}
EXPORT_SYMBOL(fc_eh_host_reset);
/**
- * fc_slave_alloc() - configure queue depth
- * @sdev: scsi device
+ * fc_slave_alloc() - Configure the queue depth of a Scsi_Host
+ * @sdev: The SCSI device that identifies the SCSI host
*
* Configures queue depth based on host's cmd_per_len. If not set
* then we use the libfc default.
@@ -2046,29 +2093,50 @@ EXPORT_SYMBOL(fc_eh_host_reset);
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);
- }
+ if (sdev->tagged_supported)
+ scsi_activate_tcq(sdev, FC_FCP_DFLT_QUEUE_DEPTH);
+ else
+ scsi_adjust_queue_depth(sdev, scsi_get_tag_type(sdev),
+ FC_FCP_DFLT_QUEUE_DEPTH);
+
return 0;
}
EXPORT_SYMBOL(fc_slave_alloc);
-int fc_change_queue_depth(struct scsi_device *sdev, int qdepth)
+/**
+ * fc_change_queue_depth() - Change a device's queue depth
+ * @sdev: The SCSI device whose queue depth is to change
+ * @qdepth: The new queue depth
+ * @reason: The resason for the change
+ */
+int fc_change_queue_depth(struct scsi_device *sdev, int qdepth, int reason)
{
- scsi_adjust_queue_depth(sdev, scsi_get_tag_type(sdev), qdepth);
+ switch (reason) {
+ case SCSI_QDEPTH_DEFAULT:
+ scsi_adjust_queue_depth(sdev, scsi_get_tag_type(sdev), qdepth);
+ break;
+ case SCSI_QDEPTH_QFULL:
+ scsi_track_queue_full(sdev, qdepth);
+ break;
+ case SCSI_QDEPTH_RAMP_UP:
+ scsi_adjust_queue_depth(sdev, scsi_get_tag_type(sdev), qdepth);
+ break;
+ default:
+ return -EOPNOTSUPP;
+ }
return sdev->queue_depth;
}
EXPORT_SYMBOL(fc_change_queue_depth);
+/**
+ * fc_change_queue_type() - Change a device's queue type
+ * @sdev: The SCSI device whose queue depth is to change
+ * @tag_type: Identifier for queue type
+ */
int fc_change_queue_type(struct scsi_device *sdev, int tag_type)
{
if (sdev->tagged_supported) {
@@ -2084,38 +2152,69 @@ int fc_change_queue_type(struct scsi_device *sdev, int tag_type)
}
EXPORT_SYMBOL(fc_change_queue_type);
-void fc_fcp_destroy(struct fc_lport *lp)
+/**
+ * fc_fcp_destory() - Tear down the FCP layer for a given local port
+ * @lport: The local port that no longer needs the FCP layer
+ */
+void fc_fcp_destroy(struct fc_lport *lport)
{
- struct fc_fcp_internal *si = fc_get_scsi_internal(lp);
+ struct fc_fcp_internal *si = fc_get_scsi_internal(lport);
if (!list_empty(&si->scsi_pkt_queue))
printk(KERN_ERR "libfc: Leaked SCSI packets when destroying "
- "port (%6x)\n", fc_host_port_id(lp->host));
+ "port (%6x)\n", fc_host_port_id(lport->host));
mempool_destroy(si->scsi_pkt_pool);
kfree(si);
- lp->scsi_priv = NULL;
+ lport->scsi_priv = NULL;
}
EXPORT_SYMBOL(fc_fcp_destroy);
-int fc_fcp_init(struct fc_lport *lp)
+int fc_setup_fcp()
+{
+ int rc = 0;
+
+ scsi_pkt_cachep = kmem_cache_create("libfc_fcp_pkt",
+ sizeof(struct fc_fcp_pkt),
+ 0, SLAB_HWCACHE_ALIGN, NULL);
+ if (!scsi_pkt_cachep) {
+ printk(KERN_ERR "libfc: Unable to allocate SRB cache, "
+ "module load failed!");
+ rc = -ENOMEM;
+ }
+
+ return rc;
+}
+
+void fc_destroy_fcp()
+{
+ if (scsi_pkt_cachep)
+ kmem_cache_destroy(scsi_pkt_cachep);
+}
+
+/**
+ * fc_fcp_init() - Initialize the FCP layer for a local port
+ * @lport: The local port to initialize the exchange layer for
+ */
+int fc_fcp_init(struct fc_lport *lport)
{
int rc;
struct fc_fcp_internal *si;
- if (!lp->tt.fcp_cmd_send)
- lp->tt.fcp_cmd_send = fc_fcp_cmd_send;
+ if (!lport->tt.fcp_cmd_send)
+ lport->tt.fcp_cmd_send = fc_fcp_cmd_send;
- if (!lp->tt.fcp_cleanup)
- lp->tt.fcp_cleanup = fc_fcp_cleanup;
+ if (!lport->tt.fcp_cleanup)
+ lport->tt.fcp_cleanup = fc_fcp_cleanup;
- if (!lp->tt.fcp_abort_io)
- lp->tt.fcp_abort_io = fc_fcp_abort_io;
+ if (!lport->tt.fcp_abort_io)
+ lport->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;
+ lport->scsi_priv = si;
+ si->max_can_queue = lport->host->can_queue;
INIT_LIST_HEAD(&si->scsi_pkt_queue);
si->scsi_pkt_pool = mempool_create_slab_pool(2, scsi_pkt_cachep);
@@ -2130,42 +2229,3 @@ free_internal:
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) {
- printk(KERN_ERR "libfc: 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
index 63fe00cfe66..6da01c61696 100644
--- a/drivers/scsi/libfc/fc_frame.c
+++ b/drivers/scsi/libfc/fc_frame.c
@@ -51,24 +51,24 @@ 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 *_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);
+ skb = alloc_skb_fclone(len + FC_FRAME_HEADROOM + FC_FRAME_TAILROOM +
+ NET_SKB_PAD, GFP_ATOMIC);
if (!skb)
return NULL;
+ skb_reserve(skb, NET_SKB_PAD + FC_FRAME_HEADROOM);
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);
-
+EXPORT_SYMBOL(_fc_frame_alloc);
struct fc_frame *fc_frame_alloc_fill(struct fc_lport *lp, size_t payload_len)
{
@@ -78,7 +78,7 @@ struct fc_frame *fc_frame_alloc_fill(struct fc_lport *lp, size_t payload_len)
fill = payload_len % 4;
if (fill != 0)
fill = 4 - fill;
- fp = __fc_frame_alloc(payload_len + 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 */
@@ -87,3 +87,4 @@ struct fc_frame *fc_frame_alloc_fill(struct fc_lport *lp, size_t payload_len)
}
return fp;
}
+EXPORT_SYMBOL(fc_frame_alloc_fill);
diff --git a/drivers/scsi/libfc/fc_libfc.c b/drivers/scsi/libfc/fc_libfc.c
new file mode 100644
index 00000000000..39f4b6ab04b
--- /dev/null
+++ b/drivers/scsi/libfc/fc_libfc.c
@@ -0,0 +1,134 @@
+/*
+ * Copyright(c) 2009 Intel Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Maintained at www.Open-FCoE.org
+ */
+
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/scatterlist.h>
+#include <linux/crc32.h>
+
+#include <scsi/libfc.h>
+
+#include "fc_libfc.h"
+
+MODULE_AUTHOR("Open-FCoE.org");
+MODULE_DESCRIPTION("libfc");
+MODULE_LICENSE("GPL v2");
+
+unsigned int fc_debug_logging;
+module_param_named(debug_logging, fc_debug_logging, int, S_IRUGO|S_IWUSR);
+MODULE_PARM_DESC(debug_logging, "a bit mask of logging levels");
+
+/**
+ * libfc_init() - Initialize libfc.ko
+ */
+static int __init libfc_init(void)
+{
+ int rc = 0;
+
+ rc = fc_setup_fcp();
+ if (rc)
+ return rc;
+
+ 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:
+ fc_destroy_fcp();
+ return rc;
+}
+module_init(libfc_init);
+
+/**
+ * libfc_exit() - Tear down libfc.ko
+ */
+static void __exit libfc_exit(void)
+{
+ fc_destroy_fcp();
+ fc_destroy_exch_mgr();
+ fc_destroy_rport();
+}
+module_exit(libfc_exit);
+
+/**
+ * fc_copy_buffer_to_sglist() - This routine copies the data of a buffer
+ * into a scatter-gather list (SG list).
+ *
+ * @buf: pointer to the data buffer.
+ * @len: the byte-length of the data buffer.
+ * @sg: pointer to the pointer of the SG list.
+ * @nents: pointer to the remaining number of entries in the SG list.
+ * @offset: pointer to the current offset in the SG list.
+ * @km_type: dedicated page table slot type for kmap_atomic.
+ * @crc: pointer to the 32-bit crc value.
+ * If crc is NULL, CRC is not calculated.
+ */
+u32 fc_copy_buffer_to_sglist(void *buf, size_t len,
+ struct scatterlist *sg,
+ u32 *nents, size_t *offset,
+ enum km_type km_type, u32 *crc)
+{
+ size_t remaining = len;
+ u32 copy_len = 0;
+
+ while (remaining > 0 && sg) {
+ size_t off, sg_bytes;
+ void *page_addr;
+
+ if (*offset >= sg->length) {
+ /*
+ * Check for end and drop resources
+ * from the last iteration.
+ */
+ if (!(*nents))
+ break;
+ --(*nents);
+ *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_type);
+ if (crc)
+ *crc = crc32(*crc, buf, sg_bytes);
+ memcpy((char *)page_addr + (off & ~PAGE_MASK), buf, sg_bytes);
+ kunmap_atomic(page_addr, km_type);
+ buf += sg_bytes;
+ *offset += sg_bytes;
+ remaining -= sg_bytes;
+ copy_len += sg_bytes;
+ }
+ return copy_len;
+}
diff --git a/drivers/scsi/libfc/fc_libfc.h b/drivers/scsi/libfc/fc_libfc.h
new file mode 100644
index 00000000000..741fd5c72e1
--- /dev/null
+++ b/drivers/scsi/libfc/fc_libfc.h
@@ -0,0 +1,112 @@
+/*
+ * Copyright(c) 2009 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_LIBFC_H_
+#define _FC_LIBFC_H_
+
+#define FC_LIBFC_LOGGING 0x01 /* General logging, not categorized */
+#define FC_LPORT_LOGGING 0x02 /* lport layer logging */
+#define FC_DISC_LOGGING 0x04 /* discovery layer logging */
+#define FC_RPORT_LOGGING 0x08 /* rport layer logging */
+#define FC_FCP_LOGGING 0x10 /* I/O path logging */
+#define FC_EM_LOGGING 0x20 /* Exchange Manager logging */
+#define FC_EXCH_LOGGING 0x40 /* Exchange/Sequence logging */
+#define FC_SCSI_LOGGING 0x80 /* SCSI logging (mostly error handling) */
+
+extern unsigned int fc_debug_logging;
+
+#define FC_CHECK_LOGGING(LEVEL, CMD) \
+ do { \
+ if (unlikely(fc_debug_logging & LEVEL)) \
+ do { \
+ CMD; \
+ } while (0); \
+ } while (0)
+
+#define FC_LIBFC_DBG(fmt, args...) \
+ FC_CHECK_LOGGING(FC_LIBFC_LOGGING, \
+ printk(KERN_INFO "libfc: " fmt, ##args))
+
+#define FC_LPORT_DBG(lport, fmt, args...) \
+ FC_CHECK_LOGGING(FC_LPORT_LOGGING, \
+ printk(KERN_INFO "host%u: lport %6x: " fmt, \
+ (lport)->host->host_no, \
+ fc_host_port_id((lport)->host), ##args))
+
+#define FC_DISC_DBG(disc, fmt, args...) \
+ FC_CHECK_LOGGING(FC_DISC_LOGGING, \
+ printk(KERN_INFO "host%u: disc: " fmt, \
+ (disc)->lport->host->host_no, \
+ ##args))
+
+#define FC_RPORT_ID_DBG(lport, port_id, fmt, args...) \
+ FC_CHECK_LOGGING(FC_RPORT_LOGGING, \
+ printk(KERN_INFO "host%u: rport %6x: " fmt, \
+ (lport)->host->host_no, \
+ (port_id), ##args))
+
+#define FC_RPORT_DBG(rdata, fmt, args...) \
+ FC_RPORT_ID_DBG((rdata)->local_port, (rdata)->ids.port_id, fmt, ##args)
+
+#define FC_FCP_DBG(pkt, fmt, args...) \
+ FC_CHECK_LOGGING(FC_FCP_LOGGING, \
+ printk(KERN_INFO "host%u: fcp: %6x: " fmt, \
+ (pkt)->lp->host->host_no, \
+ pkt->rport->port_id, ##args))
+
+#define FC_EXCH_DBG(exch, fmt, args...) \
+ FC_CHECK_LOGGING(FC_EXCH_LOGGING, \
+ printk(KERN_INFO "host%u: xid %4x: " fmt, \
+ (exch)->lp->host->host_no, \
+ exch->xid, ##args))
+
+#define FC_SCSI_DBG(lport, fmt, args...) \
+ FC_CHECK_LOGGING(FC_SCSI_LOGGING, \
+ printk(KERN_INFO "host%u: scsi: " fmt, \
+ (lport)->host->host_no, ##args))
+
+/*
+ * Set up direct-data placement for this I/O request
+ */
+void fc_fcp_ddp_setup(struct fc_fcp_pkt *fsp, u16 xid);
+
+/*
+ * 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);
+int fc_setup_fcp(void);
+void fc_destroy_fcp(void);
+
+/*
+ * Internal libfc functions
+ */
+const char *fc_els_resp_type(struct fc_frame *);
+
+/*
+ * Copies a buffer into an sg list
+ */
+u32 fc_copy_buffer_to_sglist(void *buf, size_t len,
+ struct scatterlist *sg,
+ u32 *nents, size_t *offset,
+ enum km_type km_type, u32 *crc);
+
+#endif /* _FC_LIBFC_H_ */
diff --git a/drivers/scsi/libfc/fc_lport.c b/drivers/scsi/libfc/fc_lport.c
index 6486ae4591b..74338c83ad0 100644
--- a/drivers/scsi/libfc/fc_lport.c
+++ b/drivers/scsi/libfc/fc_lport.c
@@ -94,6 +94,9 @@
#include <scsi/libfc.h>
#include <scsi/fc_encode.h>
+#include <linux/scatterlist.h>
+
+#include "fc_libfc.h"
/* Fabric IDs to use for point-to-point mode, chosen on whims. */
#define FC_LOCAL_PTP_FID_LO 0x010101
@@ -106,8 +109,7 @@ 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_ns(struct fc_lport *, enum fc_lport_state);
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 *);
@@ -116,14 +118,40 @@ static const char *fc_lport_state_names[] = {
[LPORT_ST_DISABLED] = "disabled",
[LPORT_ST_FLOGI] = "FLOGI",
[LPORT_ST_DNS] = "dNS",
- [LPORT_ST_RPN_ID] = "RPN_ID",
+ [LPORT_ST_RNN_ID] = "RNN_ID",
+ [LPORT_ST_RSNN_NN] = "RSNN_NN",
+ [LPORT_ST_RSPN_ID] = "RSPN_ID",
[LPORT_ST_RFT_ID] = "RFT_ID",
+ [LPORT_ST_RFF_ID] = "RFF_ID",
[LPORT_ST_SCR] = "SCR",
[LPORT_ST_READY] = "Ready",
[LPORT_ST_LOGO] = "LOGO",
[LPORT_ST_RESET] = "reset",
};
+/**
+ * struct fc_bsg_info - FC Passthrough managemet structure
+ * @job: The passthrough job
+ * @lport: The local port to pass through a command
+ * @rsp_code: The expected response code
+ * @sg: job->reply_payload.sg_list
+ * @nents: job->reply_payload.sg_cnt
+ * @offset: The offset into the response data
+ */
+struct fc_bsg_info {
+ struct fc_bsg_job *job;
+ struct fc_lport *lport;
+ u16 rsp_code;
+ struct scatterlist *sg;
+ u32 nents;
+ size_t offset;
+};
+
+/**
+ * fc_frame_drop() - Dummy frame handler
+ * @lport: The local port the frame was received on
+ * @fp: The received frame
+ */
static int fc_frame_drop(struct fc_lport *lport, struct fc_frame *fp)
{
fc_frame_free(fp);
@@ -150,8 +178,8 @@ static void fc_lport_rport_callback(struct fc_lport *lport,
switch (event) {
case RPORT_EV_READY:
if (lport->state == LPORT_ST_DNS) {
- lport->dns_rp = rdata;
- fc_lport_enter_rpn_id(lport);
+ lport->dns_rdata = rdata;
+ fc_lport_enter_ns(lport, LPORT_ST_RNN_ID);
} else {
FC_LPORT_DBG(lport, "Received an READY event "
"on port (%6x) for the directory "
@@ -165,7 +193,7 @@ static void fc_lport_rport_callback(struct fc_lport *lport,
case RPORT_EV_LOGO:
case RPORT_EV_FAILED:
case RPORT_EV_STOP:
- lport->dns_rp = NULL;
+ lport->dns_rdata = NULL;
break;
case RPORT_EV_NONE:
break;
@@ -189,8 +217,8 @@ static const char *fc_lport_state(struct fc_lport *lport)
/**
* 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
+ * @lport: The lport to attach the ptp rport to
+ * @remote_fid: The FID of the ptp rport
* @remote_wwpn: The WWPN of the ptp rport
* @remote_wwnn: The WWNN of the ptp rport
*/
@@ -199,18 +227,22 @@ static void fc_lport_ptp_setup(struct fc_lport *lport,
u64 remote_wwnn)
{
mutex_lock(&lport->disc.disc_mutex);
- if (lport->ptp_rp)
- lport->tt.rport_logoff(lport->ptp_rp);
- lport->ptp_rp = lport->tt.rport_create(lport, remote_fid);
- lport->ptp_rp->ids.port_name = remote_wwpn;
- lport->ptp_rp->ids.node_name = remote_wwnn;
+ if (lport->ptp_rdata)
+ lport->tt.rport_logoff(lport->ptp_rdata);
+ lport->ptp_rdata = lport->tt.rport_create(lport, remote_fid);
+ lport->ptp_rdata->ids.port_name = remote_wwpn;
+ lport->ptp_rdata->ids.node_name = remote_wwnn;
mutex_unlock(&lport->disc.disc_mutex);
- lport->tt.rport_login(lport->ptp_rp);
+ lport->tt.rport_login(lport->ptp_rdata);
fc_lport_enter_ready(lport);
}
+/**
+ * fc_get_host_port_type() - Return the port type of the given Scsi_Host
+ * @shost: The SCSI host whose port type is to be determined
+ */
void fc_get_host_port_type(struct Scsi_Host *shost)
{
/* TODO - currently just NPORT */
@@ -218,17 +250,33 @@ void fc_get_host_port_type(struct Scsi_Host *shost)
}
EXPORT_SYMBOL(fc_get_host_port_type);
+/**
+ * fc_get_host_port_state() - Return the port state of the given Scsi_Host
+ * @shost: The SCSI host whose port state is to be determined
+ */
void fc_get_host_port_state(struct Scsi_Host *shost)
{
- struct fc_lport *lp = shost_priv(shost);
+ struct fc_lport *lport = shost_priv(shost);
- if (lp->link_up)
- fc_host_port_state(shost) = FC_PORTSTATE_ONLINE;
+ mutex_lock(&lport->lp_mutex);
+ if (!lport->link_up)
+ fc_host_port_state(shost) = FC_PORTSTATE_LINKDOWN;
else
- fc_host_port_state(shost) = FC_PORTSTATE_OFFLINE;
+ switch (lport->state) {
+ case LPORT_ST_READY:
+ fc_host_port_state(shost) = FC_PORTSTATE_ONLINE;
+ break;
+ default:
+ fc_host_port_state(shost) = FC_PORTSTATE_OFFLINE;
+ }
+ mutex_unlock(&lport->lp_mutex);
}
EXPORT_SYMBOL(fc_get_host_port_state);
+/**
+ * fc_get_host_speed() - Return the speed of the given Scsi_Host
+ * @shost: The SCSI host whose port speed is to be determined
+ */
void fc_get_host_speed(struct Scsi_Host *shost)
{
struct fc_lport *lport = shost_priv(shost);
@@ -237,24 +285,28 @@ void fc_get_host_speed(struct Scsi_Host *shost)
}
EXPORT_SYMBOL(fc_get_host_speed);
+/**
+ * fc_get_host_stats() - Return the Scsi_Host's statistics
+ * @shost: The SCSI host whose statistics are to be returned
+ */
struct fc_host_statistics *fc_get_host_stats(struct Scsi_Host *shost)
{
struct fc_host_statistics *fcoe_stats;
- struct fc_lport *lp = shost_priv(shost);
+ struct fc_lport *lport = shost_priv(shost);
struct timespec v0, v1;
unsigned int cpu;
- fcoe_stats = &lp->host_stats;
+ fcoe_stats = &lport->host_stats;
memset(fcoe_stats, 0, sizeof(struct fc_host_statistics));
jiffies_to_timespec(jiffies, &v0);
- jiffies_to_timespec(lp->boot_time, &v1);
+ jiffies_to_timespec(lport->boot_time, &v1);
fcoe_stats->seconds_since_last_reset = (v0.tv_sec - v1.tv_sec);
for_each_possible_cpu(cpu) {
struct fcoe_dev_stats *stats;
- stats = per_cpu_ptr(lp->dev_stats, cpu);
+ stats = per_cpu_ptr(lport->dev_stats, cpu);
fcoe_stats->tx_frames += stats->TxFrames;
fcoe_stats->tx_words += stats->TxWords;
@@ -279,12 +331,15 @@ struct fc_host_statistics *fc_get_host_stats(struct Scsi_Host *shost)
}
EXPORT_SYMBOL(fc_get_host_stats);
-/*
- * Fill in FLOGI command for request.
+/**
+ * fc_lport_flogi_fill() - Fill in FLOGI command for request
+ * @lport: The local port the FLOGI is for
+ * @flogi: The FLOGI command
+ * @op: The opcode
*/
-static void
-fc_lport_flogi_fill(struct fc_lport *lport, struct fc_els_flogi *flogi,
- unsigned int op)
+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;
@@ -312,8 +367,10 @@ fc_lport_flogi_fill(struct fc_lport *lport, struct fc_els_flogi *flogi,
}
}
-/*
- * Add a supported FC-4 type.
+/**
+ * fc_lport_add_fc4_type() - Add a supported FC-4 type to a local port
+ * @lport: The local port to add a new FC-4 type to
+ * @type: The new FC-4 type
*/
static void fc_lport_add_fc4_type(struct fc_lport *lport, enum fc_fh_type type)
{
@@ -325,11 +382,11 @@ static void fc_lport_add_fc4_type(struct fc_lport *lport, enum fc_fh_type type)
/**
* fc_lport_recv_rlir_req() - Handle received Registered Link Incident Report.
+ * @sp: The sequence in the RLIR exchange
+ * @fp: The RLIR request frame
* @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
+ * Locking Note: The lport lock is expected to be held before calling
* this function.
*/
static void fc_lport_recv_rlir_req(struct fc_seq *sp, struct fc_frame *fp,
@@ -344,11 +401,11 @@ static void fc_lport_recv_rlir_req(struct fc_seq *sp, struct fc_frame *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
+ * @sp: The sequence in the ECHO exchange
+ * @fp: ECHO request frame
+ * @lport: The local port recieving the ECHO
*
- * Locking Note: The lport lock is exected to be held before calling
+ * Locking Note: The lport lock is expected to be held before calling
* this function.
*/
static void fc_lport_recv_echo_req(struct fc_seq *sp, struct fc_frame *in_fp,
@@ -361,7 +418,7 @@ static void fc_lport_recv_echo_req(struct fc_seq *sp, struct fc_frame *in_fp,
void *dp;
u32 f_ctl;
- FC_LPORT_DBG(lport, "Received RLIR request while in state %s\n",
+ FC_LPORT_DBG(lport, "Received ECHO request while in state %s\n",
fc_lport_state(lport));
len = fr_len(in_fp) - sizeof(struct fc_frame_header);
@@ -374,7 +431,7 @@ static void fc_lport_recv_echo_req(struct fc_seq *sp, struct fc_frame *in_fp,
if (fp) {
dp = fc_frame_payload_get(fp, len);
memcpy(dp, pp, len);
- *((u32 *)dp) = htonl(ELS_LS_ACC << 24);
+ *((__be32 *)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,
@@ -385,12 +442,12 @@ static void fc_lport_recv_echo_req(struct fc_seq *sp, struct fc_frame *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
+ * fc_lport_recv_rnid_req() - Handle received Request Node ID data request
+ * @sp: The sequence in the RNID exchange
+ * @fp: The RNID request frame
+ * @lport: The local port recieving the RNID
*
- * Locking Note: The lport lock is exected to be held before calling
+ * Locking Note: The lport lock is expected to be held before calling
* this function.
*/
static void fc_lport_recv_rnid_req(struct fc_seq *sp, struct fc_frame *in_fp,
@@ -453,9 +510,9 @@ static void fc_lport_recv_rnid_req(struct fc_seq *sp, struct fc_frame *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
+ * @sp: The sequence in the LOGO exchange
+ * @fp: The LOGO request frame
+ * @lport: The local port recieving the LOGO
*
* Locking Note: The lport lock is exected to be held before calling
* this function.
@@ -470,7 +527,7 @@ static void fc_lport_recv_logo_req(struct fc_seq *sp, struct fc_frame *fp,
/**
* fc_fabric_login() - Start the lport state machine
- * @lport: The lport that should log into the fabric
+ * @lport: The local port that should log into the fabric
*
* Locking Note: This function should not be called
* with the lport lock held.
@@ -491,47 +548,69 @@ int fc_fabric_login(struct fc_lport *lport)
EXPORT_SYMBOL(fc_fabric_login);
/**
- * fc_linkup() - Handler for transport linkup events
+ * __fc_linkup() - Handler for transport linkup events
* @lport: The lport whose link is up
+ *
+ * Locking: must be called with the lp_mutex held
*/
-void fc_linkup(struct fc_lport *lport)
+void __fc_linkup(struct fc_lport *lport)
{
- printk(KERN_INFO "libfc: Link up on port (%6x)\n",
- fc_host_port_id(lport->host));
-
- mutex_lock(&lport->lp_mutex);
if (!lport->link_up) {
lport->link_up = 1;
if (lport->state == LPORT_ST_RESET)
fc_lport_enter_flogi(lport);
}
+}
+
+/**
+ * fc_linkup() - Handler for transport linkup events
+ * @lport: The local port whose link is up
+ */
+void fc_linkup(struct fc_lport *lport)
+{
+ printk(KERN_INFO "host%d: libfc: Link up on port (%6x)\n",
+ lport->host->host_no, fc_host_port_id(lport->host));
+
+ mutex_lock(&lport->lp_mutex);
+ __fc_linkup(lport);
mutex_unlock(&lport->lp_mutex);
}
EXPORT_SYMBOL(fc_linkup);
/**
- * fc_linkdown() - Handler for transport linkdown events
+ * __fc_linkdown() - Handler for transport linkdown events
* @lport: The lport whose link is down
+ *
+ * Locking: must be called with the lp_mutex held
*/
-void fc_linkdown(struct fc_lport *lport)
+void __fc_linkdown(struct fc_lport *lport)
{
- mutex_lock(&lport->lp_mutex);
- printk(KERN_INFO "libfc: Link down on port (%6x)\n",
- fc_host_port_id(lport->host));
-
if (lport->link_up) {
lport->link_up = 0;
fc_lport_enter_reset(lport);
lport->tt.fcp_cleanup(lport);
}
+}
+
+/**
+ * fc_linkdown() - Handler for transport linkdown events
+ * @lport: The local port whose link is down
+ */
+void fc_linkdown(struct fc_lport *lport)
+{
+ printk(KERN_INFO "host%d: libfc: Link down on port (%6x)\n",
+ lport->host->host_no, fc_host_port_id(lport->host));
+
+ mutex_lock(&lport->lp_mutex);
+ __fc_linkdown(lport);
mutex_unlock(&lport->lp_mutex);
}
EXPORT_SYMBOL(fc_linkdown);
/**
* fc_fabric_logoff() - Logout of the fabric
- * @lport: fc_lport pointer to logoff the fabric
+ * @lport: The local port to logoff the fabric
*
* Return value:
* 0 for success, -1 for failure
@@ -540,8 +619,8 @@ int fc_fabric_logoff(struct fc_lport *lport)
{
lport->tt.disc_stop_final(lport);
mutex_lock(&lport->lp_mutex);
- if (lport->dns_rp)
- lport->tt.rport_logoff(lport->dns_rp);
+ if (lport->dns_rdata)
+ lport->tt.rport_logoff(lport->dns_rdata);
mutex_unlock(&lport->lp_mutex);
lport->tt.rport_flush_queue();
mutex_lock(&lport->lp_mutex);
@@ -553,11 +632,9 @@ int fc_fabric_logoff(struct fc_lport *lport)
EXPORT_SYMBOL(fc_fabric_logoff);
/**
- * fc_lport_destroy() - unregister a fc_lport
- * @lport: fc_lport pointer to unregister
+ * fc_lport_destroy() - Unregister a fc_lport
+ * @lport: The local port to unregister
*
- * Return value:
- * None
* Note:
* exit routine for fc_lport instance
* clean-up all the allocated memory
@@ -580,13 +657,9 @@ int fc_lport_destroy(struct fc_lport *lport)
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
+ * fc_set_mfs() - Set the maximum frame size for a local port
+ * @lport: The local port to set the MFS for
+ * @mfs: The new MFS
*/
int fc_set_mfs(struct fc_lport *lport, u32 mfs)
{
@@ -617,7 +690,7 @@ EXPORT_SYMBOL(fc_set_mfs);
/**
* fc_lport_disc_callback() - Callback for discovery events
- * @lport: FC local port
+ * @lport: The local port receiving the event
* @event: The discovery event
*/
void fc_lport_disc_callback(struct fc_lport *lport, enum fc_disc_event event)
@@ -627,8 +700,9 @@ void fc_lport_disc_callback(struct fc_lport *lport, enum fc_disc_event event)
FC_LPORT_DBG(lport, "Discovery succeeded\n");
break;
case DISC_EV_FAILED:
- printk(KERN_ERR "libfc: Discovery failed for port (%6x)\n",
- fc_host_port_id(lport->host));
+ printk(KERN_ERR "host%d: libfc: "
+ "Discovery failed for port (%6x)\n",
+ lport->host->host_no, fc_host_port_id(lport->host));
mutex_lock(&lport->lp_mutex);
fc_lport_enter_reset(lport);
mutex_unlock(&lport->lp_mutex);
@@ -641,7 +715,7 @@ void fc_lport_disc_callback(struct fc_lport *lport, enum fc_disc_event event)
/**
* fc_rport_enter_ready() - Enter the ready state and start discovery
- * @lport: Fibre Channel local port that is ready
+ * @lport: The local port that is ready
*
* Locking Note: The lport lock is expected to be held before calling
* this routine.
@@ -652,22 +726,46 @@ static void fc_lport_enter_ready(struct fc_lport *lport)
fc_lport_state(lport));
fc_lport_state_enter(lport, LPORT_ST_READY);
+ if (lport->vport)
+ fc_vport_set_state(lport->vport, FC_VPORT_ACTIVE);
+ fc_vports_linkchange(lport);
- if (!lport->ptp_rp)
+ if (!lport->ptp_rdata)
lport->tt.disc_start(fc_lport_disc_callback, lport);
}
/**
+ * fc_lport_set_port_id() - set the local port Port ID
+ * @lport: The local port which will have its Port ID set.
+ * @port_id: The new port ID.
+ * @fp: The frame containing the incoming request, or NULL.
+ *
+ * Locking Note: The lport lock is expected to be held before calling
+ * this function.
+ */
+static void fc_lport_set_port_id(struct fc_lport *lport, u32 port_id,
+ struct fc_frame *fp)
+{
+ if (port_id)
+ printk(KERN_INFO "host%d: Assigned Port ID %6x\n",
+ lport->host->host_no, port_id);
+
+ fc_host_port_id(lport->host) = port_id;
+ if (lport->tt.lport_set_port_id)
+ lport->tt.lport_set_port_id(lport, port_id, fp);
+}
+
+/**
* 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
+ * @rx_fp: The FLOGI frame
+ * @lport: The local port 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
+ * Locking Note: The lport lock is expected to be held before calling
* this function.
*/
static void fc_lport_recv_flogi_req(struct fc_seq *sp_in,
@@ -695,8 +793,9 @@ static void fc_lport_recv_flogi_req(struct fc_seq *sp_in,
goto out;
remote_wwpn = get_unaligned_be64(&flp->fl_wwpn);
if (remote_wwpn == lport->wwpn) {
- printk(KERN_WARNING "libfc: Received FLOGI from port "
- "with same WWPN %llx\n", remote_wwpn);
+ printk(KERN_WARNING "host%d: libfc: Received FLOGI from port "
+ "with same WWPN %llx\n",
+ lport->host->host_no, remote_wwpn);
goto out;
}
FC_LPORT_DBG(lport, "FLOGI from port WWPN %llx\n", remote_wwpn);
@@ -715,7 +814,7 @@ static void fc_lport_recv_flogi_req(struct fc_seq *sp_in,
remote_fid = FC_LOCAL_PTP_FID_HI;
}
- fc_host_port_id(lport->host) = local_fid;
+ fc_lport_set_port_id(lport, local_fid, rx_fp);
fp = fc_frame_alloc(lport, sizeof(*flp));
if (fp) {
@@ -747,9 +846,9 @@ out:
/**
* 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
+ * @lport: The local port that received the request
+ * @sp: The sequence the request is on
+ * @fp: The request frame
*
* This function will see if the lport handles the request or
* if an rport should handle the request.
@@ -817,8 +916,8 @@ static void fc_lport_recv_req(struct fc_lport *lport, struct fc_seq *sp,
}
/**
- * fc_lport_reset() - Reset an lport
- * @lport: The lport which should be reset
+ * fc_lport_reset() - Reset a local port
+ * @lport: The local port which should be reset
*
* Locking Note: This functions should not be called with the
* lport lock held.
@@ -834,29 +933,31 @@ int fc_lport_reset(struct fc_lport *lport)
EXPORT_SYMBOL(fc_lport_reset);
/**
- * fc_lport_reset_locked() - Reset the local port
- * @lport: Fibre Channel local port to be reset
+ * fc_lport_reset_locked() - Reset the local port w/ the lport lock held
+ * @lport: The local port to be reset
*
* Locking Note: The lport lock is expected to be held before calling
* this routine.
*/
static void fc_lport_reset_locked(struct fc_lport *lport)
{
- if (lport->dns_rp)
- lport->tt.rport_logoff(lport->dns_rp);
+ if (lport->dns_rdata)
+ lport->tt.rport_logoff(lport->dns_rdata);
- lport->ptp_rp = NULL;
+ lport->ptp_rdata = NULL;
lport->tt.disc_stop(lport);
lport->tt.exch_mgr_reset(lport, 0, 0);
fc_host_fabric_name(lport->host) = 0;
- fc_host_port_id(lport->host) = 0;
+
+ if (fc_host_port_id(lport->host))
+ fc_lport_set_port_id(lport, 0, NULL);
}
/**
* fc_lport_enter_reset() - Reset the local port
- * @lport: Fibre Channel local port to be reset
+ * @lport: The local port to be reset
*
* Locking Note: The lport lock is expected to be held before calling
* this routine.
@@ -866,15 +967,22 @@ static void fc_lport_enter_reset(struct fc_lport *lport)
FC_LPORT_DBG(lport, "Entered RESET state from %s state\n",
fc_lport_state(lport));
+ if (lport->vport) {
+ if (lport->link_up)
+ fc_vport_set_state(lport->vport, FC_VPORT_INITIALIZING);
+ else
+ fc_vport_set_state(lport->vport, FC_VPORT_LINKDOWN);
+ }
fc_lport_state_enter(lport, LPORT_ST_RESET);
+ fc_vports_linkchange(lport);
fc_lport_reset_locked(lport);
if (lport->link_up)
fc_lport_enter_flogi(lport);
}
/**
- * fc_lport_enter_disabled() - disable the local port
- * @lport: Fibre Channel local port to be reset
+ * fc_lport_enter_disabled() - Disable the local port
+ * @lport: The local port to be reset
*
* Locking Note: The lport lock is expected to be held before calling
* this routine.
@@ -885,13 +993,14 @@ static void fc_lport_enter_disabled(struct fc_lport *lport)
fc_lport_state(lport));
fc_lport_state_enter(lport, LPORT_ST_DISABLED);
+ fc_vports_linkchange(lport);
fc_lport_reset_locked(lport);
}
/**
* fc_lport_error() - Handler for any errors
- * @lport: The fc_lport object
- * @fp: The frame pointer
+ * @lport: The local port that the error was on
+ * @fp: The error code encoded in a frame pointer
*
* If the error was caused by a resource allocation failure
* then wait for half a second and retry, otherwise retry
@@ -922,8 +1031,11 @@ static void fc_lport_error(struct fc_lport *lport, struct fc_frame *fp)
case LPORT_ST_DISABLED:
case LPORT_ST_READY:
case LPORT_ST_RESET:
- case LPORT_ST_RPN_ID:
+ case LPORT_ST_RNN_ID:
+ case LPORT_ST_RSNN_NN:
+ case LPORT_ST_RSPN_ID:
case LPORT_ST_RFT_ID:
+ case LPORT_ST_RFF_ID:
case LPORT_ST_SCR:
case LPORT_ST_DNS:
case LPORT_ST_FLOGI:
@@ -936,33 +1048,33 @@ static void fc_lport_error(struct fc_lport *lport, struct fc_frame *fp)
}
/**
- * 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
+ * fc_lport_ns_resp() - Handle response to a name server
+ * registration exchange
+ * @sp: current sequence in 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
+ * 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)
+static void fc_lport_ns_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;
- FC_LPORT_DBG(lport, "Received a RFT_ID %s\n", fc_els_resp_type(fp));
+ FC_LPORT_DBG(lport, "Received a ns %s\n", fc_els_resp_type(fp));
if (fp == ERR_PTR(-FC_EX_CLOSED))
return;
mutex_lock(&lport->lp_mutex);
- if (lport->state != LPORT_ST_RFT_ID) {
- FC_LPORT_DBG(lport, "Received a RFT_ID response, but in state "
- "%s\n", fc_lport_state(lport));
+ if (lport->state < LPORT_ST_RNN_ID || lport->state > LPORT_ST_RFF_ID) {
+ FC_LPORT_DBG(lport, "Received a name server response, "
+ "but in state %s\n", fc_lport_state(lport));
if (IS_ERR(fp))
goto err;
goto out;
@@ -980,63 +1092,28 @@ static void fc_lport_rft_id_resp(struct fc_seq *sp, struct fc_frame *fp,
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;
-
- FC_LPORT_DBG(lport, "Received a RPN_ID %s\n", fc_els_resp_type(fp));
-
- if (fp == ERR_PTR(-FC_EX_CLOSED))
- return;
-
- mutex_lock(&lport->lp_mutex);
-
- if (lport->state != LPORT_ST_RPN_ID) {
- FC_LPORT_DBG(lport, "Received a RPN_ID response, but in state "
- "%s\n", fc_lport_state(lport));
- if (IS_ERR(fp))
- goto err;
- 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);
+ switch (lport->state) {
+ case LPORT_ST_RNN_ID:
+ fc_lport_enter_ns(lport, LPORT_ST_RSNN_NN);
+ break;
+ case LPORT_ST_RSNN_NN:
+ fc_lport_enter_ns(lport, LPORT_ST_RSPN_ID);
+ break;
+ case LPORT_ST_RSPN_ID:
+ fc_lport_enter_ns(lport, LPORT_ST_RFT_ID);
+ break;
+ case LPORT_ST_RFT_ID:
+ fc_lport_enter_ns(lport, LPORT_ST_RFF_ID);
+ break;
+ case LPORT_ST_RFF_ID:
+ fc_lport_enter_scr(lport);
+ break;
+ default:
+ /* should have already been caught by state checks */
+ break;
+ }
else
fc_lport_error(lport, fp);
-
out:
fc_frame_free(fp);
err:
@@ -1045,8 +1122,8 @@ err:
/**
* fc_lport_scr_resp() - Handle response to State Change Register (SCR) request
- * @sp: current sequence in SCR exchange
- * @fp: response frame
+ * @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
@@ -1092,8 +1169,8 @@ err:
}
/**
- * fc_lport_enter_scr() - Send a State Change Register (SCR) request
- * @lport: Fibre Channel local port to register for state changes
+ * fc_lport_enter_scr() - Send a SCR (State Change Register) request
+ * @lport: The local port to register for state changes
*
* Locking Note: The lport lock is expected to be held before calling
* this routine.
@@ -1114,78 +1191,74 @@ static void fc_lport_enter_scr(struct fc_lport *lport)
}
if (!lport->tt.elsct_send(lport, FC_FID_FCTRL, fp, ELS_SCR,
- fc_lport_scr_resp, lport, lport->e_d_tov))
- fc_lport_error(lport, fp);
+ fc_lport_scr_resp, lport,
+ 2 * lport->r_a_tov))
+ fc_lport_error(lport, NULL);
}
/**
- * fc_lport_enter_rft_id() - Register FC4-types with the name server
+ * fc_lport_enter_ns() - register some object 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)
+static void fc_lport_enter_ns(struct fc_lport *lport, enum fc_lport_state state)
{
struct fc_frame *fp;
- struct fc_ns_fts *lps;
- int i;
+ enum fc_ns_req cmd;
+ int size = sizeof(struct fc_ct_hdr);
+ size_t len;
- FC_LPORT_DBG(lport, "Entered RFT_ID state from %s state\n",
+ FC_LPORT_DBG(lport, "Entered %s state from %s state\n",
+ fc_lport_state_names[state],
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;
- }
+ fc_lport_state_enter(lport, state);
- fp = fc_frame_alloc(lport, sizeof(struct fc_ct_hdr) +
- sizeof(struct fc_ns_rft));
- if (!fp) {
- fc_lport_error(lport, fp);
+ switch (state) {
+ case LPORT_ST_RNN_ID:
+ cmd = FC_NS_RNN_ID;
+ size += sizeof(struct fc_ns_rn_id);
+ break;
+ case LPORT_ST_RSNN_NN:
+ len = strnlen(fc_host_symbolic_name(lport->host), 255);
+ /* if there is no symbolic name, skip to RFT_ID */
+ if (!len)
+ return fc_lport_enter_ns(lport, LPORT_ST_RFT_ID);
+ cmd = FC_NS_RSNN_NN;
+ size += sizeof(struct fc_ns_rsnn) + len;
+ break;
+ case LPORT_ST_RSPN_ID:
+ len = strnlen(fc_host_symbolic_name(lport->host), 255);
+ /* if there is no symbolic name, skip to RFT_ID */
+ if (!len)
+ return fc_lport_enter_ns(lport, LPORT_ST_RFT_ID);
+ cmd = FC_NS_RSPN_ID;
+ size += sizeof(struct fc_ns_rspn) + len;
+ break;
+ case LPORT_ST_RFT_ID:
+ cmd = FC_NS_RFT_ID;
+ size += sizeof(struct fc_ns_rft);
+ break;
+ case LPORT_ST_RFF_ID:
+ cmd = FC_NS_RFF_ID;
+ size += sizeof(struct fc_ns_rff_id);
+ break;
+ default:
+ fc_lport_error(lport, NULL);
return;
}
- if (!lport->tt.elsct_send(lport, FC_FID_DIR_SERV, fp, FC_NS_RFT_ID,
- fc_lport_rft_id_resp,
- lport, lport->e_d_tov))
- fc_lport_error(lport, fp);
-}
-
-/**
- * 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_LPORT_DBG(lport, "Entered RPN_ID state from %s state\n",
- 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));
+ fp = fc_frame_alloc(lport, size);
if (!fp) {
fc_lport_error(lport, fp);
return;
}
- if (!lport->tt.elsct_send(lport, FC_FID_DIR_SERV, fp, FC_NS_RPN_ID,
- fc_lport_rpn_id_resp,
- lport, lport->e_d_tov))
+ if (!lport->tt.elsct_send(lport, FC_FID_DIR_SERV, fp, cmd,
+ fc_lport_ns_resp,
+ lport, 3 * lport->r_a_tov))
fc_lport_error(lport, fp);
}
@@ -1194,8 +1267,8 @@ static struct fc_rport_operations fc_lport_rport_ops = {
};
/**
- * fc_rport_enter_dns() - Create a rport to the name server
- * @lport: Fibre Channel local port requesting a rport for the name server
+ * fc_rport_enter_dns() - Create a fc_rport for the name server
+ * @lport: The local port requesting a remote port for the name server
*
* Locking Note: The lport lock is expected to be held before calling
* this routine.
@@ -1224,8 +1297,8 @@ err:
}
/**
- * fc_lport_timeout() - Handler for the retry_work timer.
- * @work: The work struct of the fc_lport
+ * fc_lport_timeout() - Handler for the retry_work timer
+ * @work: The work struct of the local port
*/
static void fc_lport_timeout(struct work_struct *work)
{
@@ -1237,21 +1310,25 @@ static void fc_lport_timeout(struct work_struct *work)
switch (lport->state) {
case LPORT_ST_DISABLED:
+ WARN_ON(1);
+ break;
case LPORT_ST_READY:
- case LPORT_ST_RESET:
WARN_ON(1);
break;
+ case LPORT_ST_RESET:
+ 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_RNN_ID:
+ case LPORT_ST_RSNN_NN:
+ case LPORT_ST_RSPN_ID:
case LPORT_ST_RFT_ID:
- fc_lport_enter_rft_id(lport);
+ case LPORT_ST_RFF_ID:
+ fc_lport_enter_ns(lport, lport->state);
break;
case LPORT_ST_SCR:
fc_lport_enter_scr(lport);
@@ -1266,16 +1343,16 @@ static void fc_lport_timeout(struct work_struct *work)
/**
* 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
+ * @sp: The sequence that the LOGO was on
+ * @fp: The LOGO frame
+ * @lp_arg: The lport port that received 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
+ * 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)
+void fc_lport_logo_resp(struct fc_seq *sp, struct fc_frame *fp,
+ void *lp_arg)
{
struct fc_lport *lport = lp_arg;
u8 op;
@@ -1311,10 +1388,11 @@ out:
err:
mutex_unlock(&lport->lp_mutex);
}
+EXPORT_SYMBOL(fc_lport_logo_resp);
/**
* fc_rport_enter_logo() - Logout of the fabric
- * @lport: Fibre Channel local port to be logged out
+ * @lport: The local port to be logged out
*
* Locking Note: The lport lock is expected to be held before calling
* this routine.
@@ -1328,6 +1406,7 @@ static void fc_lport_enter_logo(struct fc_lport *lport)
fc_lport_state(lport));
fc_lport_state_enter(lport, LPORT_ST_LOGO);
+ fc_vports_linkchange(lport);
fp = fc_frame_alloc(lport, sizeof(*logo));
if (!fp) {
@@ -1336,22 +1415,23 @@ static void fc_lport_enter_logo(struct fc_lport *lport)
}
if (!lport->tt.elsct_send(lport, FC_FID_FLOGI, fp, ELS_LOGO,
- fc_lport_logo_resp, lport, lport->e_d_tov))
- fc_lport_error(lport, fp);
+ fc_lport_logo_resp, lport,
+ 2 * lport->r_a_tov))
+ fc_lport_error(lport, NULL);
}
/**
* 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
+ * @sp: The sequence that the FLOGI was on
+ * @fp: The FLOGI response frame
+ * @lp_arg: The lport port that received the FLOGI response
*
* Locking Note: This function will be called without the lport lock
- * held, but it will lock, call an _enter_* function or fc_lport_error
+ * 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)
+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;
@@ -1385,11 +1465,6 @@ static void fc_lport_flogi_resp(struct fc_seq *sp, struct fc_frame *fp,
fh = fc_frame_header_get(fp);
did = ntoh24(fh->fh_d_id);
if (fc_frame_payload_op(fp) == ELS_LS_ACC && did != 0) {
-
- printk(KERN_INFO "libfc: Assigned FID (%6x) in FLOGI response\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) &
@@ -1402,12 +1477,18 @@ static void fc_lport_flogi_resp(struct fc_seq *sp, struct fc_frame *fp,
e_d_tov = ntohl(flp->fl_csp.sp_e_d_tov);
if (csp_flags & FC_SP_FT_EDTR)
e_d_tov /= 1000000;
+
+ lport->npiv_enabled = !!(csp_flags & FC_SP_FT_NPIV_ACC);
+
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;
- printk(KERN_INFO "libfc: Port (%6x) entered "
- "point to point mode\n", did);
+ fc_lport_set_port_id(lport, did, fp);
+ printk(KERN_INFO "host%d: libfc: "
+ "Port (%6x) entered "
+ "point-to-point mode\n",
+ lport->host->host_no, did);
fc_lport_ptp_setup(lport, ntoh24(fh->fh_s_id),
get_unaligned_be64(
&flp->fl_wwpn),
@@ -1418,6 +1499,7 @@ static void fc_lport_flogi_resp(struct fc_seq *sp, struct fc_frame *fp,
lport->r_a_tov = r_a_tov;
fc_host_fabric_name(lport->host) =
get_unaligned_be64(&flp->fl_wwnn);
+ fc_lport_set_port_id(lport, did, fp);
fc_lport_enter_dns(lport);
}
}
@@ -1430,6 +1512,7 @@ out:
err:
mutex_unlock(&lport->lp_mutex);
}
+EXPORT_SYMBOL(fc_lport_flogi_resp);
/**
* fc_rport_enter_flogi() - Send a FLOGI request to the fabric manager
@@ -1451,12 +1534,18 @@ void fc_lport_enter_flogi(struct fc_lport *lport)
if (!fp)
return fc_lport_error(lport, fp);
- if (!lport->tt.elsct_send(lport, FC_FID_FLOGI, fp, ELS_FLOGI,
- fc_lport_flogi_resp, lport, lport->e_d_tov))
- fc_lport_error(lport, fp);
+ if (!lport->tt.elsct_send(lport, FC_FID_FLOGI, fp,
+ lport->vport ? ELS_FDISC : ELS_FLOGI,
+ fc_lport_flogi_resp, lport,
+ lport->vport ? 2 * lport->r_a_tov :
+ lport->e_d_tov))
+ fc_lport_error(lport, NULL);
}
-/* Configure a fc_lport */
+/**
+ * fc_lport_config() - Configure a fc_lport
+ * @lport: The local port to be configured
+ */
int fc_lport_config(struct fc_lport *lport)
{
INIT_DELAYED_WORK(&lport->retry_work, fc_lport_timeout);
@@ -1471,6 +1560,10 @@ int fc_lport_config(struct fc_lport *lport)
}
EXPORT_SYMBOL(fc_lport_config);
+/**
+ * fc_lport_init() - Initialize the lport layer for a local port
+ * @lport: The local port to initialize the exchange layer for
+ */
int fc_lport_init(struct fc_lport *lport)
{
if (!lport->tt.lport_recv)
@@ -1500,7 +1593,253 @@ int fc_lport_init(struct fc_lport *lport)
if (lport->link_supported_speeds & FC_PORTSPEED_10GBIT)
fc_host_supported_speeds(lport->host) |= FC_PORTSPEED_10GBIT;
- INIT_LIST_HEAD(&lport->ema_list);
return 0;
}
EXPORT_SYMBOL(fc_lport_init);
+
+/**
+ * fc_lport_bsg_resp() - The common response handler for FC Passthrough requests
+ * @sp: The sequence for the FC Passthrough response
+ * @fp: The response frame
+ * @info_arg: The BSG info that the response is for
+ */
+static void fc_lport_bsg_resp(struct fc_seq *sp, struct fc_frame *fp,
+ void *info_arg)
+{
+ struct fc_bsg_info *info = info_arg;
+ struct fc_bsg_job *job = info->job;
+ struct fc_lport *lport = info->lport;
+ struct fc_frame_header *fh;
+ size_t len;
+ void *buf;
+
+ if (IS_ERR(fp)) {
+ job->reply->result = (PTR_ERR(fp) == -FC_EX_CLOSED) ?
+ -ECONNABORTED : -ETIMEDOUT;
+ job->reply_len = sizeof(uint32_t);
+ job->state_flags |= FC_RQST_STATE_DONE;
+ job->job_done(job);
+ kfree(info);
+ return;
+ }
+
+ mutex_lock(&lport->lp_mutex);
+ fh = fc_frame_header_get(fp);
+ len = fr_len(fp) - sizeof(*fh);
+ buf = fc_frame_payload_get(fp, 0);
+
+ if (fr_sof(fp) == FC_SOF_I3 && !ntohs(fh->fh_seq_cnt)) {
+ /* Get the response code from the first frame payload */
+ unsigned short cmd = (info->rsp_code == FC_FS_ACC) ?
+ ntohs(((struct fc_ct_hdr *)buf)->ct_cmd) :
+ (unsigned short)fc_frame_payload_op(fp);
+
+ /* Save the reply status of the job */
+ job->reply->reply_data.ctels_reply.status =
+ (cmd == info->rsp_code) ?
+ FC_CTELS_STATUS_OK : FC_CTELS_STATUS_REJECT;
+ }
+
+ job->reply->reply_payload_rcv_len +=
+ fc_copy_buffer_to_sglist(buf, len, info->sg, &info->nents,
+ &info->offset, KM_BIO_SRC_IRQ, NULL);
+
+ if (fr_eof(fp) == FC_EOF_T &&
+ (ntoh24(fh->fh_f_ctl) & (FC_FC_LAST_SEQ | FC_FC_END_SEQ)) ==
+ (FC_FC_LAST_SEQ | FC_FC_END_SEQ)) {
+ if (job->reply->reply_payload_rcv_len >
+ job->reply_payload.payload_len)
+ job->reply->reply_payload_rcv_len =
+ job->reply_payload.payload_len;
+ job->reply->result = 0;
+ job->state_flags |= FC_RQST_STATE_DONE;
+ job->job_done(job);
+ kfree(info);
+ }
+ fc_frame_free(fp);
+ mutex_unlock(&lport->lp_mutex);
+}
+
+/**
+ * fc_lport_els_request() - Send ELS passthrough request
+ * @job: The BSG Passthrough job
+ * @lport: The local port sending the request
+ * @did: The destination port id
+ *
+ * Locking Note: The lport lock is expected to be held before calling
+ * this routine.
+ */
+static int fc_lport_els_request(struct fc_bsg_job *job,
+ struct fc_lport *lport,
+ u32 did, u32 tov)
+{
+ struct fc_bsg_info *info;
+ struct fc_frame *fp;
+ struct fc_frame_header *fh;
+ char *pp;
+ int len;
+
+ fp = fc_frame_alloc(lport, job->request_payload.payload_len);
+ if (!fp)
+ return -ENOMEM;
+
+ len = job->request_payload.payload_len;
+ pp = fc_frame_payload_get(fp, len);
+
+ sg_copy_to_buffer(job->request_payload.sg_list,
+ job->request_payload.sg_cnt,
+ pp, len);
+
+ fh = fc_frame_header_get(fp);
+ fh->fh_r_ctl = FC_RCTL_ELS_REQ;
+ hton24(fh->fh_d_id, did);
+ hton24(fh->fh_s_id, fc_host_port_id(lport->host));
+ fh->fh_type = FC_TYPE_ELS;
+ hton24(fh->fh_f_ctl, FC_FC_FIRST_SEQ |
+ FC_FC_END_SEQ | FC_FC_SEQ_INIT);
+ fh->fh_cs_ctl = 0;
+ fh->fh_df_ctl = 0;
+ fh->fh_parm_offset = 0;
+
+ info = kzalloc(sizeof(struct fc_bsg_info), GFP_KERNEL);
+ if (!info) {
+ fc_frame_free(fp);
+ return -ENOMEM;
+ }
+
+ info->job = job;
+ info->lport = lport;
+ info->rsp_code = ELS_LS_ACC;
+ info->nents = job->reply_payload.sg_cnt;
+ info->sg = job->reply_payload.sg_list;
+
+ if (!lport->tt.exch_seq_send(lport, fp, fc_lport_bsg_resp,
+ NULL, info, tov))
+ return -ECOMM;
+ return 0;
+}
+
+/**
+ * fc_lport_ct_request() - Send CT Passthrough request
+ * @job: The BSG Passthrough job
+ * @lport: The local port sending the request
+ * @did: The destination FC-ID
+ * @tov: The timeout period to wait for the response
+ *
+ * Locking Note: The lport lock is expected to be held before calling
+ * this routine.
+ */
+static int fc_lport_ct_request(struct fc_bsg_job *job,
+ struct fc_lport *lport, u32 did, u32 tov)
+{
+ struct fc_bsg_info *info;
+ struct fc_frame *fp;
+ struct fc_frame_header *fh;
+ struct fc_ct_req *ct;
+ size_t len;
+
+ fp = fc_frame_alloc(lport, sizeof(struct fc_ct_hdr) +
+ job->request_payload.payload_len);
+ if (!fp)
+ return -ENOMEM;
+
+ len = job->request_payload.payload_len;
+ ct = fc_frame_payload_get(fp, len);
+
+ sg_copy_to_buffer(job->request_payload.sg_list,
+ job->request_payload.sg_cnt,
+ ct, len);
+
+ fh = fc_frame_header_get(fp);
+ fh->fh_r_ctl = FC_RCTL_DD_UNSOL_CTL;
+ hton24(fh->fh_d_id, did);
+ hton24(fh->fh_s_id, fc_host_port_id(lport->host));
+ fh->fh_type = FC_TYPE_CT;
+ hton24(fh->fh_f_ctl, FC_FC_FIRST_SEQ |
+ FC_FC_END_SEQ | FC_FC_SEQ_INIT);
+ fh->fh_cs_ctl = 0;
+ fh->fh_df_ctl = 0;
+ fh->fh_parm_offset = 0;
+
+ info = kzalloc(sizeof(struct fc_bsg_info), GFP_KERNEL);
+ if (!info) {
+ fc_frame_free(fp);
+ return -ENOMEM;
+ }
+
+ info->job = job;
+ info->lport = lport;
+ info->rsp_code = FC_FS_ACC;
+ info->nents = job->reply_payload.sg_cnt;
+ info->sg = job->reply_payload.sg_list;
+
+ if (!lport->tt.exch_seq_send(lport, fp, fc_lport_bsg_resp,
+ NULL, info, tov))
+ return -ECOMM;
+ return 0;
+}
+
+/**
+ * fc_lport_bsg_request() - The common entry point for sending
+ * FC Passthrough requests
+ * @job: The BSG passthrough job
+ */
+int fc_lport_bsg_request(struct fc_bsg_job *job)
+{
+ struct request *rsp = job->req->next_rq;
+ struct Scsi_Host *shost = job->shost;
+ struct fc_lport *lport = shost_priv(shost);
+ struct fc_rport *rport;
+ struct fc_rport_priv *rdata;
+ int rc = -EINVAL;
+ u32 did;
+
+ job->reply->reply_payload_rcv_len = 0;
+ rsp->resid_len = job->reply_payload.payload_len;
+
+ mutex_lock(&lport->lp_mutex);
+
+ switch (job->request->msgcode) {
+ case FC_BSG_RPT_ELS:
+ rport = job->rport;
+ if (!rport)
+ break;
+
+ rdata = rport->dd_data;
+ rc = fc_lport_els_request(job, lport, rport->port_id,
+ rdata->e_d_tov);
+ break;
+
+ case FC_BSG_RPT_CT:
+ rport = job->rport;
+ if (!rport)
+ break;
+
+ rdata = rport->dd_data;
+ rc = fc_lport_ct_request(job, lport, rport->port_id,
+ rdata->e_d_tov);
+ break;
+
+ case FC_BSG_HST_CT:
+ did = ntoh24(job->request->rqst_data.h_ct.port_id);
+ if (did == FC_FID_DIR_SERV)
+ rdata = lport->dns_rdata;
+ else
+ rdata = lport->tt.rport_lookup(lport, did);
+
+ if (!rdata)
+ break;
+
+ rc = fc_lport_ct_request(job, lport, did, rdata->e_d_tov);
+ break;
+
+ case FC_BSG_HST_ELS_NOLOGIN:
+ did = ntoh24(job->request->rqst_data.h_els.port_id);
+ rc = fc_lport_els_request(job, lport, did, lport->e_d_tov);
+ break;
+ }
+
+ mutex_unlock(&lport->lp_mutex);
+ return rc;
+}
+EXPORT_SYMBOL(fc_lport_bsg_request);
diff --git a/drivers/scsi/libfc/fc_npiv.c b/drivers/scsi/libfc/fc_npiv.c
new file mode 100644
index 00000000000..c68f6c7341c
--- /dev/null
+++ b/drivers/scsi/libfc/fc_npiv.c
@@ -0,0 +1,161 @@
+/*
+ * Copyright(c) 2009 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
+ */
+
+/*
+ * NPIV VN_Port helper functions for libfc
+ */
+
+#include <scsi/libfc.h>
+
+/**
+ * fc_vport_create() - Create a new NPIV vport instance
+ * @vport: fc_vport structure from scsi_transport_fc
+ * @privsize: driver private data size to allocate along with the Scsi_Host
+ */
+
+struct fc_lport *libfc_vport_create(struct fc_vport *vport, int privsize)
+{
+ struct Scsi_Host *shost = vport_to_shost(vport);
+ struct fc_lport *n_port = shost_priv(shost);
+ struct fc_lport *vn_port;
+
+ vn_port = libfc_host_alloc(shost->hostt, privsize);
+ if (!vn_port)
+ goto err_out;
+ if (fc_exch_mgr_list_clone(n_port, vn_port))
+ goto err_put;
+
+ vn_port->vport = vport;
+ vport->dd_data = vn_port;
+
+ mutex_lock(&n_port->lp_mutex);
+ list_add_tail(&vn_port->list, &n_port->vports);
+ mutex_unlock(&n_port->lp_mutex);
+
+ return vn_port;
+
+err_put:
+ scsi_host_put(vn_port->host);
+err_out:
+ return NULL;
+}
+EXPORT_SYMBOL(libfc_vport_create);
+
+/**
+ * fc_vport_id_lookup() - find NPIV lport that matches a given fabric ID
+ * @n_port: Top level N_Port which may have multiple NPIV VN_Ports
+ * @port_id: Fabric ID to find a match for
+ *
+ * Returns: matching lport pointer or NULL if there is no match
+ */
+struct fc_lport *fc_vport_id_lookup(struct fc_lport *n_port, u32 port_id)
+{
+ struct fc_lport *lport = NULL;
+ struct fc_lport *vn_port;
+
+ if (fc_host_port_id(n_port->host) == port_id)
+ return n_port;
+
+ mutex_lock(&n_port->lp_mutex);
+ list_for_each_entry(vn_port, &n_port->vports, list) {
+ if (fc_host_port_id(vn_port->host) == port_id) {
+ lport = vn_port;
+ break;
+ }
+ }
+ mutex_unlock(&n_port->lp_mutex);
+
+ return lport;
+}
+
+/*
+ * When setting the link state of vports during an lport state change, it's
+ * necessary to hold the lp_mutex of both the N_Port and the VN_Port.
+ * This tells the lockdep engine to treat the nested locking of the VN_Port
+ * as a different lock class.
+ */
+enum libfc_lport_mutex_class {
+ LPORT_MUTEX_NORMAL = 0,
+ LPORT_MUTEX_VN_PORT = 1,
+};
+
+/**
+ * __fc_vport_setlink() - update link and status on a VN_Port
+ * @n_port: parent N_Port
+ * @vn_port: VN_Port to update
+ *
+ * Locking: must be called with both the N_Port and VN_Port lp_mutex held
+ */
+static void __fc_vport_setlink(struct fc_lport *n_port,
+ struct fc_lport *vn_port)
+{
+ struct fc_vport *vport = vn_port->vport;
+
+ if (vn_port->state == LPORT_ST_DISABLED)
+ return;
+
+ if (n_port->state == LPORT_ST_READY) {
+ if (n_port->npiv_enabled) {
+ fc_vport_set_state(vport, FC_VPORT_INITIALIZING);
+ __fc_linkup(vn_port);
+ } else {
+ fc_vport_set_state(vport, FC_VPORT_NO_FABRIC_SUPP);
+ __fc_linkdown(vn_port);
+ }
+ } else {
+ fc_vport_set_state(vport, FC_VPORT_LINKDOWN);
+ __fc_linkdown(vn_port);
+ }
+}
+
+/**
+ * fc_vport_setlink() - update link and status on a VN_Port
+ * @vn_port: virtual port to update
+ */
+void fc_vport_setlink(struct fc_lport *vn_port)
+{
+ struct fc_vport *vport = vn_port->vport;
+ struct Scsi_Host *shost = vport_to_shost(vport);
+ struct fc_lport *n_port = shost_priv(shost);
+
+ mutex_lock(&n_port->lp_mutex);
+ mutex_lock_nested(&vn_port->lp_mutex, LPORT_MUTEX_VN_PORT);
+ __fc_vport_setlink(n_port, vn_port);
+ mutex_unlock(&vn_port->lp_mutex);
+ mutex_unlock(&n_port->lp_mutex);
+}
+EXPORT_SYMBOL(fc_vport_setlink);
+
+/**
+ * fc_vports_linkchange() - change the link state of all vports
+ * @n_port: Parent N_Port that has changed state
+ *
+ * Locking: called with the n_port lp_mutex held
+ */
+void fc_vports_linkchange(struct fc_lport *n_port)
+{
+ struct fc_lport *vn_port;
+
+ list_for_each_entry(vn_port, &n_port->vports, list) {
+ mutex_lock_nested(&vn_port->lp_mutex, LPORT_MUTEX_VN_PORT);
+ __fc_vport_setlink(n_port, vn_port);
+ mutex_unlock(&vn_port->lp_mutex);
+ }
+}
+
diff --git a/drivers/scsi/libfc/fc_rport.c b/drivers/scsi/libfc/fc_rport.c
index 03ea6748e7e..35ca0e72df4 100644
--- a/drivers/scsi/libfc/fc_rport.c
+++ b/drivers/scsi/libfc/fc_rport.c
@@ -55,6 +55,8 @@
#include <scsi/libfc.h>
#include <scsi/fc_encode.h>
+#include "fc_libfc.h"
+
struct workqueue_struct *rport_event_queue;
static void fc_rport_enter_plogi(struct fc_rport_priv *);
@@ -86,12 +88,13 @@ static const char *fc_rport_state_names[] = {
[RPORT_ST_LOGO] = "LOGO",
[RPORT_ST_ADISC] = "ADISC",
[RPORT_ST_DELETE] = "Delete",
+ [RPORT_ST_RESTART] = "Restart",
};
/**
- * fc_rport_lookup() - lookup a remote port by port_id
- * @lport: Fibre Channel host port instance
- * @port_id: remote port port_id to match
+ * fc_rport_lookup() - Lookup a remote port by port_id
+ * @lport: The local port to lookup the remote port on
+ * @port_id: The remote port ID to look up
*/
static struct fc_rport_priv *fc_rport_lookup(const struct fc_lport *lport,
u32 port_id)
@@ -99,16 +102,17 @@ static struct fc_rport_priv *fc_rport_lookup(const struct fc_lport *lport,
struct fc_rport_priv *rdata;
list_for_each_entry(rdata, &lport->disc.rports, peers)
- if (rdata->ids.port_id == port_id &&
- rdata->rp_state != RPORT_ST_DELETE)
+ if (rdata->ids.port_id == port_id)
return rdata;
return NULL;
}
/**
* fc_rport_create() - Create a new remote port
- * @lport: The local port that the new remote port is for
- * @port_id: The port ID for the new remote port
+ * @lport: The local port this remote port will be associated with
+ * @ids: The identifiers for the new remote port
+ *
+ * The remote port will start in the INIT state.
*
* Locking note: must be called with the disc_mutex held.
*/
@@ -147,8 +151,8 @@ static struct fc_rport_priv *fc_rport_create(struct fc_lport *lport,
}
/**
- * fc_rport_destroy() - free a remote port after last reference is released.
- * @kref: pointer to kref inside struct fc_rport_priv
+ * fc_rport_destroy() - Free a remote port after last reference is released
+ * @kref: The remote port's kref
*/
static void fc_rport_destroy(struct kref *kref)
{
@@ -159,8 +163,8 @@ static void fc_rport_destroy(struct kref *kref)
}
/**
- * fc_rport_state() - return a string for the state the rport is in
- * @rdata: remote port private data
+ * fc_rport_state() - Return a string identifying the remote port's state
+ * @rdata: The remote port
*/
static const char *fc_rport_state(struct fc_rport_priv *rdata)
{
@@ -173,9 +177,9 @@ static const char *fc_rport_state(struct fc_rport_priv *rdata)
}
/**
- * 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
+ * fc_set_rport_loss_tmo() - Set the remote port loss timeout
+ * @rport: The remote port that gets a new timeout value
+ * @timeout: The new timeout value (in seconds)
*/
void fc_set_rport_loss_tmo(struct fc_rport *rport, u32 timeout)
{
@@ -187,9 +191,11 @@ void fc_set_rport_loss_tmo(struct fc_rport *rport, u32 timeout)
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
+ * fc_plogi_get_maxframe() - Get the maximum payload from the common service
+ * parameters in a FLOGI frame
+ * @flp: The FLOGI payload
+ * @maxval: The maximum frame size upper limit; this 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)
@@ -210,9 +216,9 @@ static unsigned int fc_plogi_get_maxframe(struct fc_els_flogi *flp,
}
/**
- * fc_rport_state_enter() - Change the rport's state
- * @rdata: The rport whose state should change
- * @new: The new state of the rport
+ * fc_rport_state_enter() - Change the state of a remote port
+ * @rdata: The remote port whose state should change
+ * @new: The new state
*
* Locking Note: Called with the rport lock held
*/
@@ -224,17 +230,22 @@ static void fc_rport_state_enter(struct fc_rport_priv *rdata,
rdata->rp_state = new;
}
+/**
+ * fc_rport_work() - Handler for remote port events in the rport_event_queue
+ * @work: Handle to the remote port being dequeued
+ */
static void fc_rport_work(struct work_struct *work)
{
u32 port_id;
struct fc_rport_priv *rdata =
container_of(work, struct fc_rport_priv, event_work);
- struct fc_rport_libfc_priv *rp;
+ struct fc_rport_libfc_priv *rpriv;
enum fc_rport_event event;
struct fc_lport *lport = rdata->local_port;
struct fc_rport_operations *rport_ops;
struct fc_rport_identifiers ids;
struct fc_rport *rport;
+ int restart = 0;
mutex_lock(&rdata->rp_mutex);
event = rdata->event;
@@ -265,12 +276,12 @@ static void fc_rport_work(struct work_struct *work)
rport->maxframe_size = rdata->maxframe_size;
rport->supported_classes = rdata->supported_classes;
- rp = rport->dd_data;
- rp->local_port = lport;
- rp->rp_state = rdata->rp_state;
- rp->flags = rdata->flags;
- rp->e_d_tov = rdata->e_d_tov;
- rp->r_a_tov = rdata->r_a_tov;
+ rpriv = rport->dd_data;
+ rpriv->local_port = lport;
+ rpriv->rp_state = rdata->rp_state;
+ rpriv->flags = rdata->flags;
+ rpriv->e_d_tov = rdata->e_d_tov;
+ rpriv->r_a_tov = rdata->r_a_tov;
mutex_unlock(&rdata->rp_mutex);
if (rport_ops && rport_ops->event_callback) {
@@ -287,8 +298,19 @@ static void fc_rport_work(struct work_struct *work)
mutex_unlock(&rdata->rp_mutex);
if (port_id != FC_FID_DIR_SERV) {
+ /*
+ * We must drop rp_mutex before taking disc_mutex.
+ * Re-evaluate state to allow for restart.
+ * A transition to RESTART state must only happen
+ * while disc_mutex is held and rdata is on the list.
+ */
mutex_lock(&lport->disc.disc_mutex);
- list_del(&rdata->peers);
+ mutex_lock(&rdata->rp_mutex);
+ if (rdata->rp_state == RPORT_ST_RESTART)
+ restart = 1;
+ else
+ list_del(&rdata->peers);
+ mutex_unlock(&rdata->rp_mutex);
mutex_unlock(&lport->disc.disc_mutex);
}
@@ -305,14 +327,20 @@ static void fc_rport_work(struct work_struct *work)
lport->tt.exch_mgr_reset(lport, port_id, 0);
if (rport) {
- rp = rport->dd_data;
- rp->rp_state = RPORT_ST_DELETE;
+ rpriv = rport->dd_data;
+ rpriv->rp_state = RPORT_ST_DELETE;
mutex_lock(&rdata->rp_mutex);
rdata->rport = NULL;
mutex_unlock(&rdata->rp_mutex);
fc_remote_port_delete(rport);
}
- kref_put(&rdata->kref, lport->tt.rport_destroy);
+ if (restart) {
+ mutex_lock(&rdata->rp_mutex);
+ FC_RPORT_DBG(rdata, "work restart\n");
+ fc_rport_enter_plogi(rdata);
+ mutex_unlock(&rdata->rp_mutex);
+ } else
+ kref_put(&rdata->kref, lport->tt.rport_destroy);
break;
default:
@@ -323,7 +351,7 @@ static void fc_rport_work(struct work_struct *work)
/**
* fc_rport_login() - Start the remote port login state machine
- * @rdata: private remote port
+ * @rdata: The remote port to be logged in to
*
* Locking Note: Called without the rport lock held. This
* function will hold the rport lock, call an _enter_*
@@ -342,6 +370,12 @@ int fc_rport_login(struct fc_rport_priv *rdata)
FC_RPORT_DBG(rdata, "ADISC port\n");
fc_rport_enter_adisc(rdata);
break;
+ case RPORT_ST_RESTART:
+ break;
+ case RPORT_ST_DELETE:
+ FC_RPORT_DBG(rdata, "Restart deleted port\n");
+ fc_rport_state_enter(rdata, RPORT_ST_RESTART);
+ break;
default:
FC_RPORT_DBG(rdata, "Login to port\n");
fc_rport_enter_plogi(rdata);
@@ -353,9 +387,9 @@ int fc_rport_login(struct fc_rport_priv *rdata)
}
/**
- * fc_rport_enter_delete() - schedule a remote port to be deleted.
- * @rdata: private remote port
- * @event: event to report as the reason for deletion
+ * fc_rport_enter_delete() - Schedule a remote port to be deleted
+ * @rdata: The remote port to be deleted
+ * @event: The event to report as the reason for deletion
*
* Locking Note: Called with the rport lock held.
*
@@ -382,8 +416,8 @@ static void fc_rport_enter_delete(struct fc_rport_priv *rdata,
}
/**
- * fc_rport_logoff() - Logoff and remove an rport
- * @rdata: private remote port
+ * fc_rport_logoff() - Logoff and remove a remote port
+ * @rdata: The remote port to be logged off of
*
* Locking Note: Called without the rport lock held. This
* function will hold the rport lock, call an _enter_*
@@ -397,26 +431,27 @@ int fc_rport_logoff(struct fc_rport_priv *rdata)
if (rdata->rp_state == RPORT_ST_DELETE) {
FC_RPORT_DBG(rdata, "Port in Delete state, not removing\n");
- mutex_unlock(&rdata->rp_mutex);
goto out;
}
- fc_rport_enter_logo(rdata);
+ if (rdata->rp_state == RPORT_ST_RESTART)
+ FC_RPORT_DBG(rdata, "Port in Restart state, deleting\n");
+ else
+ fc_rport_enter_logo(rdata);
/*
* Change the state to Delete so that we discard
* the response.
*/
fc_rport_enter_delete(rdata, RPORT_EV_STOP);
- mutex_unlock(&rdata->rp_mutex);
-
out:
+ mutex_unlock(&rdata->rp_mutex);
return 0;
}
/**
- * fc_rport_enter_ready() - The rport is ready
- * @rdata: private remote port
+ * fc_rport_enter_ready() - Transition to the RPORT_ST_READY state
+ * @rdata: The remote port that is ready
*
* Locking Note: The rport lock is expected to be held before calling
* this routine.
@@ -433,8 +468,8 @@ static void fc_rport_enter_ready(struct fc_rport_priv *rdata)
}
/**
- * fc_rport_timeout() - Handler for the retry_work timer.
- * @work: The work struct of the fc_rport_priv
+ * fc_rport_timeout() - Handler for the retry_work timer
+ * @work: Handle to the remote port that has timed out
*
* Locking Note: Called without the rport lock held. This
* function will hold the rport lock, call an _enter_*
@@ -466,6 +501,7 @@ static void fc_rport_timeout(struct work_struct *work)
case RPORT_ST_READY:
case RPORT_ST_INIT:
case RPORT_ST_DELETE:
+ case RPORT_ST_RESTART:
break;
}
@@ -474,8 +510,8 @@ static void fc_rport_timeout(struct work_struct *work)
/**
* fc_rport_error() - Error handler, called once retries have been exhausted
- * @rdata: private remote port
- * @fp: The frame pointer
+ * @rdata: The remote port the error is happened on
+ * @fp: The error code encapsulated in a frame pointer
*
* Locking Note: The rport lock is expected to be held before
* calling this routine
@@ -499,6 +535,7 @@ static void fc_rport_error(struct fc_rport_priv *rdata, struct fc_frame *fp)
fc_rport_enter_logo(rdata);
break;
case RPORT_ST_DELETE:
+ case RPORT_ST_RESTART:
case RPORT_ST_READY:
case RPORT_ST_INIT:
break;
@@ -506,9 +543,9 @@ static void fc_rport_error(struct fc_rport_priv *rdata, struct fc_frame *fp)
}
/**
- * fc_rport_error_retry() - Error handler when retries are desired
- * @rdata: private remote port data
- * @fp: The frame pointer
+ * fc_rport_error_retry() - Handler for remote port state retries
+ * @rdata: The remote port whose state is to be retried
+ * @fp: The error code encapsulated in a frame pointer
*
* If the error was an exchange timeout retry immediately,
* otherwise wait for E_D_TOV.
@@ -540,10 +577,10 @@ static void fc_rport_error_retry(struct fc_rport_priv *rdata,
}
/**
- * fc_rport_plogi_recv_resp() - Handle incoming ELS PLOGI response
- * @sp: current sequence in the PLOGI exchange
- * @fp: response frame
- * @rdata_arg: private remote port data
+ * fc_rport_plogi_recv_resp() - Handler for ELS PLOGI responses
+ * @sp: The sequence the PLOGI is on
+ * @fp: The PLOGI response frame
+ * @rdata_arg: The remote port that sent the PLOGI response
*
* Locking Note: This function will be called without the rport lock
* held, but it will lock, call an _enter_* function or fc_rport_error
@@ -606,8 +643,8 @@ err:
}
/**
- * fc_rport_enter_plogi() - Send Port Login (PLOGI) request to peer
- * @rdata: private remote port data
+ * fc_rport_enter_plogi() - Send Port Login (PLOGI) request
+ * @rdata: The remote port to send a PLOGI to
*
* Locking Note: The rport lock is expected to be held before calling
* this routine.
@@ -631,17 +668,18 @@ static void fc_rport_enter_plogi(struct fc_rport_priv *rdata)
rdata->e_d_tov = lport->e_d_tov;
if (!lport->tt.elsct_send(lport, rdata->ids.port_id, fp, ELS_PLOGI,
- fc_rport_plogi_resp, rdata, lport->e_d_tov))
- fc_rport_error_retry(rdata, fp);
+ fc_rport_plogi_resp, rdata,
+ 2 * lport->r_a_tov))
+ fc_rport_error_retry(rdata, NULL);
else
kref_get(&rdata->kref);
}
/**
* fc_rport_prli_resp() - Process Login (PRLI) response handler
- * @sp: current sequence in the PRLI exchange
- * @fp: response frame
- * @rdata_arg: private remote port data
+ * @sp: The sequence the PRLI response was on
+ * @fp: The PRLI response frame
+ * @rdata_arg: The remote port that sent the PRLI response
*
* Locking Note: This function will be called without the rport lock
* held, but it will lock, call an _enter_* function or fc_rport_error
@@ -710,10 +748,10 @@ err:
}
/**
- * fc_rport_logo_resp() - Logout (LOGO) response handler
- * @sp: current sequence in the LOGO exchange
- * @fp: response frame
- * @rdata_arg: private remote port data
+ * fc_rport_logo_resp() - Handler for logout (LOGO) responses
+ * @sp: The sequence the LOGO was on
+ * @fp: The LOGO response frame
+ * @rdata_arg: The remote port that sent the LOGO response
*
* Locking Note: This function will be called without the rport lock
* held, but it will lock, call an _enter_* function or fc_rport_error
@@ -756,8 +794,8 @@ err:
}
/**
- * fc_rport_enter_prli() - Send Process Login (PRLI) request to peer
- * @rdata: private remote port data
+ * fc_rport_enter_prli() - Send Process Login (PRLI) request
+ * @rdata: The remote port to send the PRLI request to
*
* Locking Note: The rport lock is expected to be held before calling
* this routine.
@@ -792,17 +830,18 @@ static void fc_rport_enter_prli(struct fc_rport_priv *rdata)
}
if (!lport->tt.elsct_send(lport, rdata->ids.port_id, fp, ELS_PRLI,
- fc_rport_prli_resp, rdata, lport->e_d_tov))
- fc_rport_error_retry(rdata, fp);
+ fc_rport_prli_resp, rdata,
+ 2 * lport->r_a_tov))
+ fc_rport_error_retry(rdata, NULL);
else
kref_get(&rdata->kref);
}
/**
- * fc_rport_els_rtv_resp() - Request Timeout Value response handler
- * @sp: current sequence in the RTV exchange
- * @fp: response frame
- * @rdata_arg: private remote port data
+ * fc_rport_els_rtv_resp() - Handler for Request Timeout Value (RTV) responses
+ * @sp: The sequence the RTV was on
+ * @fp: The RTV response frame
+ * @rdata_arg: The remote port that sent the RTV response
*
* Many targets don't seem to support this.
*
@@ -865,8 +904,8 @@ err:
}
/**
- * fc_rport_enter_rtv() - Send Request Timeout Value (RTV) request to peer
- * @rdata: private remote port data
+ * fc_rport_enter_rtv() - Send Request Timeout Value (RTV) request
+ * @rdata: The remote port to send the RTV request to
*
* Locking Note: The rport lock is expected to be held before calling
* this routine.
@@ -888,15 +927,16 @@ static void fc_rport_enter_rtv(struct fc_rport_priv *rdata)
}
if (!lport->tt.elsct_send(lport, rdata->ids.port_id, fp, ELS_RTV,
- fc_rport_rtv_resp, rdata, lport->e_d_tov))
- fc_rport_error_retry(rdata, fp);
+ fc_rport_rtv_resp, rdata,
+ 2 * lport->r_a_tov))
+ fc_rport_error_retry(rdata, NULL);
else
kref_get(&rdata->kref);
}
/**
- * fc_rport_enter_logo() - Send Logout (LOGO) request to peer
- * @rdata: private remote port data
+ * fc_rport_enter_logo() - Send a logout (LOGO) request
+ * @rdata: The remote port to send the LOGO request to
*
* Locking Note: The rport lock is expected to be held before calling
* this routine.
@@ -918,24 +958,25 @@ static void fc_rport_enter_logo(struct fc_rport_priv *rdata)
}
if (!lport->tt.elsct_send(lport, rdata->ids.port_id, fp, ELS_LOGO,
- fc_rport_logo_resp, rdata, lport->e_d_tov))
- fc_rport_error_retry(rdata, fp);
+ fc_rport_logo_resp, rdata,
+ 2 * lport->r_a_tov))
+ fc_rport_error_retry(rdata, NULL);
else
kref_get(&rdata->kref);
}
/**
- * fc_rport_els_adisc_resp() - Address Discovery response handler
- * @sp: current sequence in the ADISC exchange
- * @fp: response frame
- * @rdata_arg: remote port private.
+ * fc_rport_els_adisc_resp() - Handler for Address Discovery (ADISC) responses
+ * @sp: The sequence the ADISC response was on
+ * @fp: The ADISC response frame
+ * @rdata_arg: The remote port that sent the ADISC response
*
* 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_adisc_resp(struct fc_seq *sp, struct fc_frame *fp,
- void *rdata_arg)
+ void *rdata_arg)
{
struct fc_rport_priv *rdata = rdata_arg;
struct fc_els_adisc *adisc;
@@ -983,8 +1024,8 @@ err:
}
/**
- * fc_rport_enter_adisc() - Send Address Discover (ADISC) request to peer
- * @rdata: remote port private data
+ * fc_rport_enter_adisc() - Send Address Discover (ADISC) request
+ * @rdata: The remote port to send the ADISC request to
*
* Locking Note: The rport lock is expected to be held before calling
* this routine.
@@ -1005,17 +1046,18 @@ static void fc_rport_enter_adisc(struct fc_rport_priv *rdata)
return;
}
if (!lport->tt.elsct_send(lport, rdata->ids.port_id, fp, ELS_ADISC,
- fc_rport_adisc_resp, rdata, lport->e_d_tov))
- fc_rport_error_retry(rdata, fp);
+ fc_rport_adisc_resp, rdata,
+ 2 * lport->r_a_tov))
+ fc_rport_error_retry(rdata, NULL);
else
kref_get(&rdata->kref);
}
/**
- * fc_rport_recv_adisc_req() - Handle incoming Address Discovery (ADISC) Request
- * @rdata: remote port private
- * @sp: current sequence in the ADISC exchange
- * @in_fp: ADISC request frame
+ * fc_rport_recv_adisc_req() - Handler for Address Discovery (ADISC) requests
+ * @rdata: The remote port that sent the ADISC request
+ * @sp: The sequence the ADISC request was on
+ * @in_fp: The ADISC request frame
*
* Locking Note: Called with the lport and rport locks held.
*/
@@ -1056,10 +1098,82 @@ drop:
}
/**
- * fc_rport_recv_els_req() - handle a validated ELS request.
- * @lport: Fibre Channel local port
- * @sp: current sequence in the PLOGI exchange
- * @fp: response frame
+ * fc_rport_recv_rls_req() - Handle received Read Link Status request
+ * @rdata: The remote port that sent the RLS request
+ * @sp: The sequence that the RLS was on
+ * @rx_fp: The PRLI request frame
+ *
+ * Locking Note: The rport lock is expected to be held before calling
+ * this function.
+ */
+static void fc_rport_recv_rls_req(struct fc_rport_priv *rdata,
+ struct fc_seq *sp, struct fc_frame *rx_fp)
+
+{
+ struct fc_lport *lport = rdata->local_port;
+ struct fc_frame *fp;
+ struct fc_exch *ep = fc_seq_exch(sp);
+ struct fc_els_rls *rls;
+ struct fc_els_rls_resp *rsp;
+ struct fc_els_lesb *lesb;
+ struct fc_seq_els_data rjt_data;
+ struct fc_host_statistics *hst;
+ u32 f_ctl;
+
+ FC_RPORT_DBG(rdata, "Received RLS request while in state %s\n",
+ fc_rport_state(rdata));
+
+ rls = fc_frame_payload_get(rx_fp, sizeof(*rls));
+ if (!rls) {
+ rjt_data.reason = ELS_RJT_PROT;
+ rjt_data.explan = ELS_EXPL_INV_LEN;
+ goto out_rjt;
+ }
+
+ fp = fc_frame_alloc(lport, sizeof(*rsp));
+ if (!fp) {
+ rjt_data.reason = ELS_RJT_UNAB;
+ rjt_data.explan = ELS_EXPL_INSUF_RES;
+ goto out_rjt;
+ }
+
+ rsp = fc_frame_payload_get(fp, sizeof(*rsp));
+ memset(rsp, 0, sizeof(*rsp));
+ rsp->rls_cmd = ELS_LS_ACC;
+ lesb = &rsp->rls_lesb;
+ if (lport->tt.get_lesb) {
+ /* get LESB from LLD if it supports it */
+ lport->tt.get_lesb(lport, lesb);
+ } else {
+ fc_get_host_stats(lport->host);
+ hst = &lport->host_stats;
+ lesb->lesb_link_fail = htonl(hst->link_failure_count);
+ lesb->lesb_sync_loss = htonl(hst->loss_of_sync_count);
+ lesb->lesb_sig_loss = htonl(hst->loss_of_signal_count);
+ lesb->lesb_prim_err = htonl(hst->prim_seq_protocol_err_count);
+ lesb->lesb_inv_word = htonl(hst->invalid_tx_word_count);
+ lesb->lesb_inv_crc = htonl(hst->invalid_crc_count);
+ }
+
+ 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);
+ goto out;
+
+out_rjt:
+ rjt_data.fp = NULL;
+ lport->tt.seq_els_rsp_send(sp, ELS_LS_RJT, &rjt_data);
+out:
+ fc_frame_free(rx_fp);
+}
+
+/**
+ * fc_rport_recv_els_req() - Handler for validated ELS requests
+ * @lport: The local port that received the ELS request
+ * @sp: The sequence that the ELS request was on
+ * @fp: The ELS request frame
*
* Handle incoming ELS requests that require port login.
* The ELS opcode has already been validated by the caller.
@@ -1117,6 +1231,9 @@ static void fc_rport_recv_els_req(struct fc_lport *lport,
els_data.fp = fp;
lport->tt.seq_els_rsp_send(sp, ELS_REC, &els_data);
break;
+ case ELS_RLS:
+ fc_rport_recv_rls_req(rdata, sp, fp);
+ break;
default:
fc_frame_free(fp); /* can't happen */
break;
@@ -1131,10 +1248,10 @@ reject:
}
/**
- * fc_rport_recv_req() - Handle a received ELS request from a rport
- * @sp: current sequence in the PLOGI exchange
- * @fp: response frame
- * @lport: Fibre Channel local port
+ * fc_rport_recv_req() - Handler for requests
+ * @sp: The sequence the request was on
+ * @fp: The request frame
+ * @lport: The local port that received the request
*
* Locking Note: Called with the lport lock held.
*/
@@ -1161,6 +1278,7 @@ void fc_rport_recv_req(struct fc_seq *sp, struct fc_frame *fp,
case ELS_ADISC:
case ELS_RRQ:
case ELS_REC:
+ case ELS_RLS:
fc_rport_recv_els_req(lport, sp, fp);
break;
default:
@@ -1174,10 +1292,10 @@ void fc_rport_recv_req(struct fc_seq *sp, struct fc_frame *fp,
}
/**
- * fc_rport_recv_plogi_req() - Handle incoming Port Login (PLOGI) request
- * @lport: local port
- * @sp: current sequence in the PLOGI exchange
- * @fp: PLOGI request frame
+ * fc_rport_recv_plogi_req() - Handler for Port Login (PLOGI) requests
+ * @lport: The local port that received the PLOGI request
+ * @sp: The sequence that the PLOGI request was on
+ * @rx_fp: The PLOGI request frame
*
* Locking Note: The rport lock is held before calling this function.
*/
@@ -1248,6 +1366,7 @@ static void fc_rport_recv_plogi_req(struct fc_lport *lport,
}
break;
case RPORT_ST_PRLI:
+ case RPORT_ST_RTV:
case RPORT_ST_READY:
case RPORT_ST_ADISC:
FC_RPORT_DBG(rdata, "Received PLOGI in logged-in state %d "
@@ -1255,11 +1374,14 @@ static void fc_rport_recv_plogi_req(struct fc_lport *lport,
/* XXX TBD - should reset */
break;
case RPORT_ST_DELETE:
- default:
- FC_RPORT_DBG(rdata, "Received PLOGI in unexpected state %d\n",
- rdata->rp_state);
- fc_frame_free(rx_fp);
- goto out;
+ case RPORT_ST_LOGO:
+ case RPORT_ST_RESTART:
+ FC_RPORT_DBG(rdata, "Received PLOGI in state %s - send busy\n",
+ fc_rport_state(rdata));
+ mutex_unlock(&rdata->rp_mutex);
+ rjt_data.reason = ELS_RJT_BUSY;
+ rjt_data.explan = ELS_EXPL_NONE;
+ goto reject;
}
/*
@@ -1295,10 +1417,10 @@ reject:
}
/**
- * fc_rport_recv_prli_req() - Handle incoming Process Login (PRLI) request
- * @rdata: private remote port data
- * @sp: current sequence in the PRLI exchange
- * @fp: PRLI request frame
+ * fc_rport_recv_prli_req() - Handler for process login (PRLI) requests
+ * @rdata: The remote port that sent the PRLI request
+ * @sp: The sequence that the PRLI was on
+ * @rx_fp: The PRLI request frame
*
* Locking Note: The rport lock is exected to be held before calling
* this function.
@@ -1402,7 +1524,7 @@ static void fc_rport_recv_prli_req(struct fc_rport_priv *rdata,
break;
case FC_TYPE_FCP:
fcp_parm = ntohl(rspp->spp_params);
- if (fcp_parm * FCP_SPPF_RETRY)
+ if (fcp_parm & FCP_SPPF_RETRY)
rdata->flags |= FC_RP_FLAGS_RETRY;
rdata->supported_classes = FC_COS_CLASS3;
if (fcp_parm & FCP_SPPF_INIT_FCN)
@@ -1452,10 +1574,10 @@ static void fc_rport_recv_prli_req(struct fc_rport_priv *rdata,
}
/**
- * fc_rport_recv_prlo_req() - Handle incoming Process Logout (PRLO) request
- * @rdata: private remote port data
- * @sp: current sequence in the PRLO exchange
- * @fp: PRLO request frame
+ * fc_rport_recv_prlo_req() - Handler for process logout (PRLO) requests
+ * @rdata: The remote port that sent the PRLO request
+ * @sp: The sequence that the PRLO was on
+ * @fp: The PRLO request frame
*
* Locking Note: The rport lock is exected to be held before calling
* this function.
@@ -1482,10 +1604,10 @@ static void fc_rport_recv_prlo_req(struct fc_rport_priv *rdata,
}
/**
- * fc_rport_recv_logo_req() - Handle incoming Logout (LOGO) request
- * @lport: local port.
- * @sp: current sequence in the LOGO exchange
- * @fp: LOGO request frame
+ * fc_rport_recv_logo_req() - Handler for logout (LOGO) requests
+ * @lport: The local port that received the LOGO request
+ * @sp: The sequence that the LOGO request was on
+ * @fp: The LOGO request frame
*
* Locking Note: The rport lock is exected to be held before calling
* this function.
@@ -1510,14 +1632,14 @@ static void fc_rport_recv_logo_req(struct fc_lport *lport,
FC_RPORT_DBG(rdata, "Received LOGO request while in state %s\n",
fc_rport_state(rdata));
+ fc_rport_enter_delete(rdata, RPORT_EV_LOGO);
+
/*
- * If the remote port was created due to discovery,
- * log back in. It may have seen a stale RSCN about us.
+ * If the remote port was created due to discovery, set state
+ * to log back in. It may have seen a stale RSCN about us.
*/
- if (rdata->rp_state != RPORT_ST_DELETE && rdata->disc_id)
- fc_rport_enter_plogi(rdata);
- else
- fc_rport_enter_delete(rdata, RPORT_EV_LOGO);
+ if (rdata->disc_id)
+ fc_rport_state_enter(rdata, RPORT_ST_RESTART);
mutex_unlock(&rdata->rp_mutex);
} else
FC_RPORT_ID_DBG(lport, sid,
@@ -1526,11 +1648,18 @@ static void fc_rport_recv_logo_req(struct fc_lport *lport,
fc_frame_free(fp);
}
+/**
+ * fc_rport_flush_queue() - Flush the rport_event_queue
+ */
static void fc_rport_flush_queue(void)
{
flush_workqueue(rport_event_queue);
}
+/**
+ * fc_rport_init() - Initialize the remote port layer for a local port
+ * @lport: The local port to initialize the remote port layer for
+ */
int fc_rport_init(struct fc_lport *lport)
{
if (!lport->tt.rport_lookup)
@@ -1558,25 +1687,33 @@ int fc_rport_init(struct fc_lport *lport)
}
EXPORT_SYMBOL(fc_rport_init);
-int fc_setup_rport(void)
+/**
+ * fc_setup_rport() - Initialize the rport_event_queue
+ */
+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(void)
+/**
+ * fc_destroy_rport() - Destroy the rport_event_queue
+ */
+void fc_destroy_rport()
{
destroy_workqueue(rport_event_queue);
}
-EXPORT_SYMBOL(fc_destroy_rport);
+/**
+ * fc_rport_terminate_io() - Stop all outstanding I/O on a remote port
+ * @rport: The remote port whose I/O should be terminated
+ */
void fc_rport_terminate_io(struct fc_rport *rport)
{
- struct fc_rport_libfc_priv *rp = rport->dd_data;
- struct fc_lport *lport = rp->local_port;
+ struct fc_rport_libfc_priv *rpriv = rport->dd_data;
+ struct fc_lport *lport = rpriv->local_port;
lport->tt.exch_mgr_reset(lport, 0, rport->port_id);
lport->tt.exch_mgr_reset(lport, rport->port_id, 0);
diff --git a/drivers/scsi/libiscsi.c b/drivers/scsi/libiscsi.c
index f1a4246f890..b7689f3d05f 100644
--- a/drivers/scsi/libiscsi.c
+++ b/drivers/scsi/libiscsi.c
@@ -266,6 +266,88 @@ static int iscsi_prep_bidi_ahs(struct iscsi_task *task)
}
/**
+ * iscsi_check_tmf_restrictions - check if a task is affected by TMF
+ * @task: iscsi task
+ * @opcode: opcode to check for
+ *
+ * During TMF a task has to be checked if it's affected.
+ * All unrelated I/O can be passed through, but I/O to the
+ * affected LUN should be restricted.
+ * If 'fast_abort' is set we won't be sending any I/O to the
+ * affected LUN.
+ * Otherwise the target is waiting for all TTTs to be completed,
+ * so we have to send all outstanding Data-Out PDUs to the target.
+ */
+static int iscsi_check_tmf_restrictions(struct iscsi_task *task, int opcode)
+{
+ struct iscsi_conn *conn = task->conn;
+ struct iscsi_tm *tmf = &conn->tmhdr;
+ unsigned int hdr_lun;
+
+ if (conn->tmf_state == TMF_INITIAL)
+ return 0;
+
+ if ((tmf->opcode & ISCSI_OPCODE_MASK) != ISCSI_OP_SCSI_TMFUNC)
+ return 0;
+
+ switch (ISCSI_TM_FUNC_VALUE(tmf)) {
+ case ISCSI_TM_FUNC_LOGICAL_UNIT_RESET:
+ /*
+ * Allow PDUs for unrelated LUNs
+ */
+ hdr_lun = scsilun_to_int((struct scsi_lun *)tmf->lun);
+ if (hdr_lun != task->sc->device->lun)
+ return 0;
+ /* fall through */
+ case ISCSI_TM_FUNC_TARGET_WARM_RESET:
+ /*
+ * Fail all SCSI cmd PDUs
+ */
+ if (opcode != ISCSI_OP_SCSI_DATA_OUT) {
+ iscsi_conn_printk(KERN_INFO, conn,
+ "task [op %x/%x itt "
+ "0x%x/0x%x] "
+ "rejected.\n",
+ task->hdr->opcode, opcode,
+ task->itt, task->hdr_itt);
+ return -EACCES;
+ }
+ /*
+ * And also all data-out PDUs in response to R2T
+ * if fast_abort is set.
+ */
+ if (conn->session->fast_abort) {
+ iscsi_conn_printk(KERN_INFO, conn,
+ "task [op %x/%x itt "
+ "0x%x/0x%x] fast abort.\n",
+ task->hdr->opcode, opcode,
+ task->itt, task->hdr_itt);
+ return -EACCES;
+ }
+ break;
+ case ISCSI_TM_FUNC_ABORT_TASK:
+ /*
+ * the caller has already checked if the task
+ * they want to abort was in the pending queue so if
+ * we are here the cmd pdu has gone out already, and
+ * we will only hit this for data-outs
+ */
+ if (opcode == ISCSI_OP_SCSI_DATA_OUT &&
+ task->hdr_itt == tmf->rtt) {
+ ISCSI_DBG_SESSION(conn->session,
+ "Preventing task %x/%x from sending "
+ "data-out due to abort task in "
+ "progress\n", task->itt,
+ task->hdr_itt);
+ return -EACCES;
+ }
+ break;
+ }
+
+ return 0;
+}
+
+/**
* iscsi_prep_scsi_cmd_pdu - prep iscsi scsi cmd pdu
* @task: iscsi task
*
@@ -282,6 +364,10 @@ static int iscsi_prep_scsi_cmd_pdu(struct iscsi_task *task)
itt_t itt;
int rc;
+ rc = iscsi_check_tmf_restrictions(task, ISCSI_OP_SCSI_CMD);
+ if (rc)
+ return rc;
+
if (conn->session->tt->alloc_pdu) {
rc = conn->session->tt->alloc_pdu(task, ISCSI_OP_SCSI_CMD);
if (rc)
@@ -577,12 +663,12 @@ static int iscsi_prep_mgmt_task(struct iscsi_conn *conn,
struct iscsi_session *session = conn->session;
struct iscsi_hdr *hdr = task->hdr;
struct iscsi_nopout *nop = (struct iscsi_nopout *)hdr;
+ uint8_t opcode = hdr->opcode & ISCSI_OPCODE_MASK;
if (conn->session->state == ISCSI_STATE_LOGGING_OUT)
return -ENOTCONN;
- if (hdr->opcode != (ISCSI_OP_LOGIN | ISCSI_OP_IMMEDIATE) &&
- hdr->opcode != (ISCSI_OP_TEXT | ISCSI_OP_IMMEDIATE))
+ if (opcode != ISCSI_OP_LOGIN && opcode != ISCSI_OP_TEXT)
nop->exp_statsn = cpu_to_be32(conn->exp_statsn);
/*
* pre-format CmdSN for outgoing PDU.
@@ -590,9 +676,12 @@ static int iscsi_prep_mgmt_task(struct iscsi_conn *conn,
nop->cmdsn = cpu_to_be32(session->cmdsn);
if (hdr->itt != RESERVED_ITT) {
/*
- * TODO: We always use immediate, so we never hit this.
+ * TODO: We always use immediate for normal session pdus.
* If we start to send tmfs or nops as non-immediate then
* we should start checking the cmdsn numbers for mgmt tasks.
+ *
+ * During discovery sessions iscsid sends TEXT as non immediate,
+ * but we always only send one PDU at a time.
*/
if (conn->c_stage == ISCSI_CONN_STARTED &&
!(hdr->opcode & ISCSI_OP_IMMEDIATE)) {
@@ -620,22 +709,28 @@ __iscsi_conn_send_pdu(struct iscsi_conn *conn, struct iscsi_hdr *hdr,
{
struct iscsi_session *session = conn->session;
struct iscsi_host *ihost = shost_priv(session->host);
+ uint8_t opcode = hdr->opcode & ISCSI_OPCODE_MASK;
struct iscsi_task *task;
itt_t itt;
if (session->state == ISCSI_STATE_TERMINATE)
return NULL;
- if (hdr->opcode == (ISCSI_OP_LOGIN | ISCSI_OP_IMMEDIATE) ||
- hdr->opcode == (ISCSI_OP_TEXT | ISCSI_OP_IMMEDIATE))
+ if (opcode == ISCSI_OP_LOGIN || opcode == ISCSI_OP_TEXT) {
/*
* Login and Text are sent serially, in
* request-followed-by-response sequence.
* Same task can be used. Same ITT must be used.
* Note that login_task is preallocated at conn_create().
*/
+ if (conn->login_task->state != ISCSI_TASK_FREE) {
+ iscsi_conn_printk(KERN_ERR, conn, "Login/Text in "
+ "progress. Cannot start new task.\n");
+ return NULL;
+ }
+
task = conn->login_task;
- else {
+ } else {
if (session->state != ISCSI_STATE_LOGGED_IN)
return NULL;
@@ -1357,6 +1452,7 @@ EXPORT_SYMBOL_GPL(iscsi_requeue_task);
**/
static int iscsi_data_xmit(struct iscsi_conn *conn)
{
+ struct iscsi_task *task;
int rc = 0;
spin_lock_bh(&conn->session->lock);
@@ -1394,11 +1490,8 @@ check_mgmt:
/* process pending command queue */
while (!list_empty(&conn->cmdqueue)) {
- if (conn->tmf_state == TMF_QUEUED)
- break;
-
- conn->task = list_entry(conn->cmdqueue.next,
- struct iscsi_task, running);
+ conn->task = list_entry(conn->cmdqueue.next, struct iscsi_task,
+ running);
list_del_init(&conn->task->running);
if (conn->session->state == ISCSI_STATE_LOGGING_OUT) {
fail_scsi_task(conn->task, DID_IMM_RETRY);
@@ -1406,7 +1499,7 @@ check_mgmt:
}
rc = iscsi_prep_scsi_cmd_pdu(conn->task);
if (rc) {
- if (rc == -ENOMEM) {
+ if (rc == -ENOMEM || rc == -EACCES) {
list_add_tail(&conn->task->running,
&conn->cmdqueue);
conn->task = NULL;
@@ -1428,17 +1521,18 @@ check_mgmt:
}
while (!list_empty(&conn->requeue)) {
- if (conn->session->fast_abort && conn->tmf_state != TMF_INITIAL)
- break;
-
/*
* we always do fastlogout - conn stop code will clean up.
*/
if (conn->session->state == ISCSI_STATE_LOGGING_OUT)
break;
- conn->task = list_entry(conn->requeue.next,
- struct iscsi_task, running);
+ task = list_entry(conn->requeue.next, struct iscsi_task,
+ running);
+ if (iscsi_check_tmf_restrictions(task, ISCSI_OP_SCSI_DATA_OUT))
+ break;
+
+ conn->task = task;
list_del_init(&conn->task->running);
conn->task->state = ISCSI_TASK_RUNNING;
rc = iscsi_xmit_task(conn);
@@ -1591,7 +1685,7 @@ int iscsi_queuecommand(struct scsi_cmnd *sc, void (*done)(struct scsi_cmnd *))
if (!ihost->workq) {
reason = iscsi_prep_scsi_cmd_pdu(task);
if (reason) {
- if (reason == -ENOMEM) {
+ if (reason == -ENOMEM || reason == -EACCES) {
reason = FAILURE_OOM;
goto prepd_reject;
} else {
@@ -1643,9 +1737,21 @@ fault:
}
EXPORT_SYMBOL_GPL(iscsi_queuecommand);
-int iscsi_change_queue_depth(struct scsi_device *sdev, int depth)
+int iscsi_change_queue_depth(struct scsi_device *sdev, int depth, int reason)
{
- scsi_adjust_queue_depth(sdev, scsi_get_tag_type(sdev), depth);
+ switch (reason) {
+ case SCSI_QDEPTH_DEFAULT:
+ scsi_adjust_queue_depth(sdev, scsi_get_tag_type(sdev), depth);
+ break;
+ case SCSI_QDEPTH_QFULL:
+ scsi_track_queue_full(sdev, depth);
+ break;
+ case SCSI_QDEPTH_RAMP_UP:
+ scsi_adjust_queue_depth(sdev, scsi_get_tag_type(sdev), depth);
+ break;
+ default:
+ return -EOPNOTSUPP;
+ }
return sdev->queue_depth;
}
EXPORT_SYMBOL_GPL(iscsi_change_queue_depth);
@@ -1660,72 +1766,6 @@ int iscsi_target_alloc(struct scsi_target *starget)
}
EXPORT_SYMBOL_GPL(iscsi_target_alloc);
-void iscsi_session_recovery_timedout(struct iscsi_cls_session *cls_session)
-{
- struct iscsi_session *session = cls_session->dd_data;
-
- spin_lock_bh(&session->lock);
- if (session->state != ISCSI_STATE_LOGGED_IN) {
- session->state = ISCSI_STATE_RECOVERY_FAILED;
- if (session->leadconn)
- wake_up(&session->leadconn->ehwait);
- }
- spin_unlock_bh(&session->lock);
-}
-EXPORT_SYMBOL_GPL(iscsi_session_recovery_timedout);
-
-int iscsi_eh_target_reset(struct scsi_cmnd *sc)
-{
- struct iscsi_cls_session *cls_session;
- struct iscsi_session *session;
- struct iscsi_conn *conn;
-
- cls_session = starget_to_session(scsi_target(sc->device));
- session = cls_session->dd_data;
- conn = session->leadconn;
-
- mutex_lock(&session->eh_mutex);
- spin_lock_bh(&session->lock);
- if (session->state == ISCSI_STATE_TERMINATE) {
-failed:
- ISCSI_DBG_EH(session,
- "failing target reset: Could not log back into "
- "target [age %d]\n",
- session->age);
- spin_unlock_bh(&session->lock);
- mutex_unlock(&session->eh_mutex);
- return FAILED;
- }
-
- spin_unlock_bh(&session->lock);
- mutex_unlock(&session->eh_mutex);
- /*
- * we drop the lock here but the leadconn cannot be destoyed while
- * we are in the scsi eh
- */
- iscsi_conn_failure(conn, ISCSI_ERR_CONN_FAILED);
-
- ISCSI_DBG_EH(session, "wait for relogin\n");
- wait_event_interruptible(conn->ehwait,
- session->state == ISCSI_STATE_TERMINATE ||
- session->state == ISCSI_STATE_LOGGED_IN ||
- session->state == ISCSI_STATE_RECOVERY_FAILED);
- if (signal_pending(current))
- flush_signals(current);
-
- mutex_lock(&session->eh_mutex);
- spin_lock_bh(&session->lock);
- if (session->state == ISCSI_STATE_LOGGED_IN) {
- ISCSI_DBG_EH(session,
- "target reset succeeded\n");
- } else
- goto failed;
- spin_unlock_bh(&session->lock);
- mutex_unlock(&session->eh_mutex);
- return SUCCESS;
-}
-EXPORT_SYMBOL_GPL(iscsi_eh_target_reset);
-
static void iscsi_tmf_timedout(unsigned long data)
{
struct iscsi_conn *conn = (struct iscsi_conn *)data;
@@ -2108,6 +2148,7 @@ int iscsi_eh_abort(struct scsi_cmnd *sc)
spin_lock_bh(&session->lock);
fail_scsi_task(task, DID_ABORT);
conn->tmf_state = TMF_INITIAL;
+ memset(hdr, 0, sizeof(*hdr));
spin_unlock_bh(&session->lock);
iscsi_start_tx(conn);
goto success_unlocked;
@@ -2118,6 +2159,7 @@ int iscsi_eh_abort(struct scsi_cmnd *sc)
case TMF_NOT_FOUND:
if (!sc->SCp.ptr) {
conn->tmf_state = TMF_INITIAL;
+ memset(hdr, 0, sizeof(*hdr));
/* task completed before tmf abort response */
ISCSI_DBG_EH(session, "sc completed while abort in "
"progress\n");
@@ -2212,6 +2254,7 @@ int iscsi_eh_device_reset(struct scsi_cmnd *sc)
iscsi_suspend_tx(conn);
spin_lock_bh(&session->lock);
+ memset(hdr, 0, sizeof(*hdr));
fail_scsi_tasks(conn, sc->device->lun, DID_ERROR);
conn->tmf_state = TMF_INITIAL;
spin_unlock_bh(&session->lock);
@@ -2229,6 +2272,172 @@ done:
}
EXPORT_SYMBOL_GPL(iscsi_eh_device_reset);
+void iscsi_session_recovery_timedout(struct iscsi_cls_session *cls_session)
+{
+ struct iscsi_session *session = cls_session->dd_data;
+
+ spin_lock_bh(&session->lock);
+ if (session->state != ISCSI_STATE_LOGGED_IN) {
+ session->state = ISCSI_STATE_RECOVERY_FAILED;
+ if (session->leadconn)
+ wake_up(&session->leadconn->ehwait);
+ }
+ spin_unlock_bh(&session->lock);
+}
+EXPORT_SYMBOL_GPL(iscsi_session_recovery_timedout);
+
+/**
+ * iscsi_eh_session_reset - drop session and attempt relogin
+ * @sc: scsi command
+ *
+ * This function will wait for a relogin, session termination from
+ * userspace, or a recovery/replacement timeout.
+ */
+static int iscsi_eh_session_reset(struct scsi_cmnd *sc)
+{
+ struct iscsi_cls_session *cls_session;
+ struct iscsi_session *session;
+ struct iscsi_conn *conn;
+
+ cls_session = starget_to_session(scsi_target(sc->device));
+ session = cls_session->dd_data;
+ conn = session->leadconn;
+
+ mutex_lock(&session->eh_mutex);
+ spin_lock_bh(&session->lock);
+ if (session->state == ISCSI_STATE_TERMINATE) {
+failed:
+ ISCSI_DBG_EH(session,
+ "failing session reset: Could not log back into "
+ "%s, %s [age %d]\n", session->targetname,
+ conn->persistent_address, session->age);
+ spin_unlock_bh(&session->lock);
+ mutex_unlock(&session->eh_mutex);
+ return FAILED;
+ }
+
+ spin_unlock_bh(&session->lock);
+ mutex_unlock(&session->eh_mutex);
+ /*
+ * we drop the lock here but the leadconn cannot be destoyed while
+ * we are in the scsi eh
+ */
+ iscsi_conn_failure(conn, ISCSI_ERR_CONN_FAILED);
+
+ ISCSI_DBG_EH(session, "wait for relogin\n");
+ wait_event_interruptible(conn->ehwait,
+ session->state == ISCSI_STATE_TERMINATE ||
+ session->state == ISCSI_STATE_LOGGED_IN ||
+ session->state == ISCSI_STATE_RECOVERY_FAILED);
+ if (signal_pending(current))
+ flush_signals(current);
+
+ mutex_lock(&session->eh_mutex);
+ spin_lock_bh(&session->lock);
+ if (session->state == ISCSI_STATE_LOGGED_IN) {
+ ISCSI_DBG_EH(session,
+ "session reset succeeded for %s,%s\n",
+ session->targetname, conn->persistent_address);
+ } else
+ goto failed;
+ spin_unlock_bh(&session->lock);
+ mutex_unlock(&session->eh_mutex);
+ return SUCCESS;
+}
+
+static void iscsi_prep_tgt_reset_pdu(struct scsi_cmnd *sc, struct iscsi_tm *hdr)
+{
+ memset(hdr, 0, sizeof(*hdr));
+ hdr->opcode = ISCSI_OP_SCSI_TMFUNC | ISCSI_OP_IMMEDIATE;
+ hdr->flags = ISCSI_TM_FUNC_TARGET_WARM_RESET & ISCSI_FLAG_TM_FUNC_MASK;
+ hdr->flags |= ISCSI_FLAG_CMD_FINAL;
+ hdr->rtt = RESERVED_ITT;
+}
+
+/**
+ * iscsi_eh_target_reset - reset target
+ * @sc: scsi command
+ *
+ * This will attempt to send a warm target reset. If that fails
+ * then we will drop the session and attempt ERL0 recovery.
+ */
+int iscsi_eh_target_reset(struct scsi_cmnd *sc)
+{
+ struct iscsi_cls_session *cls_session;
+ struct iscsi_session *session;
+ struct iscsi_conn *conn;
+ struct iscsi_tm *hdr;
+ int rc = FAILED;
+
+ cls_session = starget_to_session(scsi_target(sc->device));
+ session = cls_session->dd_data;
+
+ ISCSI_DBG_EH(session, "tgt Reset [sc %p tgt %s]\n", sc,
+ session->targetname);
+
+ mutex_lock(&session->eh_mutex);
+ spin_lock_bh(&session->lock);
+ /*
+ * Just check if we are not logged in. We cannot check for
+ * the phase because the reset could come from a ioctl.
+ */
+ if (!session->leadconn || session->state != ISCSI_STATE_LOGGED_IN)
+ goto unlock;
+ conn = session->leadconn;
+
+ /* only have one tmf outstanding at a time */
+ if (conn->tmf_state != TMF_INITIAL)
+ goto unlock;
+ conn->tmf_state = TMF_QUEUED;
+
+ hdr = &conn->tmhdr;
+ iscsi_prep_tgt_reset_pdu(sc, hdr);
+
+ if (iscsi_exec_task_mgmt_fn(conn, hdr, session->age,
+ session->tgt_reset_timeout)) {
+ rc = FAILED;
+ goto unlock;
+ }
+
+ switch (conn->tmf_state) {
+ case TMF_SUCCESS:
+ break;
+ case TMF_TIMEDOUT:
+ spin_unlock_bh(&session->lock);
+ iscsi_conn_failure(conn, ISCSI_ERR_CONN_FAILED);
+ goto done;
+ default:
+ conn->tmf_state = TMF_INITIAL;
+ goto unlock;
+ }
+
+ rc = SUCCESS;
+ spin_unlock_bh(&session->lock);
+
+ iscsi_suspend_tx(conn);
+
+ spin_lock_bh(&session->lock);
+ memset(hdr, 0, sizeof(*hdr));
+ fail_scsi_tasks(conn, -1, DID_ERROR);
+ conn->tmf_state = TMF_INITIAL;
+ spin_unlock_bh(&session->lock);
+
+ iscsi_start_tx(conn);
+ goto done;
+
+unlock:
+ spin_unlock_bh(&session->lock);
+done:
+ ISCSI_DBG_EH(session, "tgt %s reset result = %s\n", session->targetname,
+ rc == SUCCESS ? "SUCCESS" : "FAILED");
+ mutex_unlock(&session->eh_mutex);
+
+ if (rc == FAILED)
+ rc = iscsi_eh_session_reset(sc);
+ return rc;
+}
+EXPORT_SYMBOL_GPL(iscsi_eh_target_reset);
+
/*
* Pre-allocate a pool of @max items of @item_size. By default, the pool
* should be accessed via kfifo_{get,put} on q->queue.
@@ -2495,6 +2704,7 @@ iscsi_session_setup(struct iscsi_transport *iscsit, struct Scsi_Host *shost,
session->host = shost;
session->state = ISCSI_STATE_FREE;
session->fast_abort = 1;
+ session->tgt_reset_timeout = 30;
session->lu_reset_timeout = 15;
session->abort_timeout = 10;
session->scsi_cmds_max = scsi_cmds;
@@ -2856,6 +3066,7 @@ static void iscsi_start_session_recovery(struct iscsi_session *session,
spin_lock_bh(&session->lock);
fail_scsi_tasks(conn, -1, DID_TRANSPORT_DISRUPTED);
fail_mgmt_tasks(session, conn);
+ memset(&conn->tmhdr, 0, sizeof(conn->tmhdr));
spin_unlock_bh(&session->lock);
mutex_unlock(&session->eh_mutex);
}
@@ -2932,6 +3143,9 @@ int iscsi_set_param(struct iscsi_cls_conn *cls_conn,
case ISCSI_PARAM_LU_RESET_TMO:
sscanf(buf, "%d", &session->lu_reset_timeout);
break;
+ case ISCSI_PARAM_TGT_RESET_TMO:
+ sscanf(buf, "%d", &session->tgt_reset_timeout);
+ break;
case ISCSI_PARAM_PING_TMO:
sscanf(buf, "%d", &conn->ping_timeout);
break;
@@ -3031,6 +3245,9 @@ int iscsi_session_get_param(struct iscsi_cls_session *cls_session,
case ISCSI_PARAM_LU_RESET_TMO:
len = sprintf(buf, "%d\n", session->lu_reset_timeout);
break;
+ case ISCSI_PARAM_TGT_RESET_TMO:
+ len = sprintf(buf, "%d\n", session->tgt_reset_timeout);
+ break;
case ISCSI_PARAM_INITIAL_R2T_EN:
len = sprintf(buf, "%d\n", session->initial_r2t_en);
break;
diff --git a/drivers/scsi/libsas/sas_scsi_host.c b/drivers/scsi/libsas/sas_scsi_host.c
index 1c558d3bce1..14b13196b22 100644
--- a/drivers/scsi/libsas/sas_scsi_host.c
+++ b/drivers/scsi/libsas/sas_scsi_host.c
@@ -820,10 +820,14 @@ void sas_slave_destroy(struct scsi_device *scsi_dev)
ata_port_disable(dev->sata_dev.ap);
}
-int sas_change_queue_depth(struct scsi_device *scsi_dev, int new_depth)
+int sas_change_queue_depth(struct scsi_device *scsi_dev, int new_depth,
+ int reason)
{
int res = min(new_depth, SAS_MAX_QD);
+ if (reason != SCSI_QDEPTH_DEFAULT)
+ return -EOPNOTSUPP;
+
if (scsi_dev->tagged_supported)
scsi_adjust_queue_depth(scsi_dev, scsi_get_tag_type(scsi_dev),
res);
diff --git a/drivers/scsi/lpfc/lpfc.h b/drivers/scsi/lpfc/lpfc.h
index aa10f795163..1cc23a69db5 100644
--- a/drivers/scsi/lpfc/lpfc.h
+++ b/drivers/scsi/lpfc/lpfc.h
@@ -109,7 +109,8 @@ struct hbq_dmabuf {
struct lpfc_dmabuf dbuf;
uint32_t size;
uint32_t tag;
- struct lpfc_rcqe rcqe;
+ struct lpfc_cq_event cq_event;
+ unsigned long time_stamp;
};
/* Priority bit. Set value to exceed low water mark in lpfc_mem. */
@@ -201,6 +202,7 @@ struct lpfc_stats {
uint32_t elsRcvLIRR;
uint32_t elsRcvRPS;
uint32_t elsRcvRPL;
+ uint32_t elsRcvRRQ;
uint32_t elsXmitFLOGI;
uint32_t elsXmitFDISC;
uint32_t elsXmitPLOGI;
@@ -289,8 +291,8 @@ struct lpfc_vport {
uint16_t vpi;
uint16_t vfi;
- uint8_t vfi_state;
-#define LPFC_VFI_REGISTERED 0x1
+ uint8_t vpi_state;
+#define LPFC_VPI_REGISTERED 0x1
uint32_t fc_flag; /* FC flags */
/* Several of these flags are HBA centric and should be moved to
@@ -405,6 +407,7 @@ struct lpfc_vport {
uint8_t stat_data_enabled;
uint8_t stat_data_blocked;
struct list_head rcv_buffer_list;
+ unsigned long rcv_buffer_time_stamp;
uint32_t vport_flag;
#define STATIC_VPORT 1
};
@@ -527,13 +530,16 @@ struct lpfc_hba {
#define HBA_ERATT_HANDLED 0x1 /* This flag is set when eratt handled */
#define DEFER_ERATT 0x2 /* Deferred error attention in progress */
#define HBA_FCOE_SUPPORT 0x4 /* HBA function supports FCOE */
-#define HBA_RECEIVE_BUFFER 0x8 /* Rcv buffer posted to worker thread */
+#define HBA_SP_QUEUE_EVT 0x8 /* Slow-path qevt posted to worker thread*/
#define HBA_POST_RECEIVE_BUFFER 0x10 /* Rcv buffers need to be posted */
#define FCP_XRI_ABORT_EVENT 0x20
#define ELS_XRI_ABORT_EVENT 0x40
#define ASYNC_EVENT 0x80
#define LINK_DISABLED 0x100 /* Link disabled by user */
#define FCF_DISC_INPROGRESS 0x200 /* FCF discovery in progress */
+#define HBA_FIP_SUPPORT 0x400 /* FIP support in HBA */
+#define HBA_AER_ENABLED 0x800 /* AER enabled with HBA */
+ uint32_t fcp_ring_in_use; /* When polling test if intr-hndlr active*/
struct lpfc_dmabuf slim2p;
MAILBOX_t *mbox;
@@ -551,6 +557,7 @@ struct lpfc_hba {
uint8_t fc_linkspeed; /* Link speed after last READ_LA */
uint32_t fc_eventTag; /* event tag for link attention */
+ uint32_t link_events;
/* These fields used to be binfo */
uint32_t fc_pref_DID; /* preferred D_ID */
@@ -604,8 +611,8 @@ struct lpfc_hba {
uint32_t cfg_enable_hba_reset;
uint32_t cfg_enable_hba_heartbeat;
uint32_t cfg_enable_bg;
- uint32_t cfg_enable_fip;
uint32_t cfg_log_verbose;
+ uint32_t cfg_aer_support;
lpfc_vpd_t vpd; /* vital product data */
diff --git a/drivers/scsi/lpfc/lpfc_attr.c b/drivers/scsi/lpfc/lpfc_attr.c
index 9bd19aa1424..91542f786ed 100644
--- a/drivers/scsi/lpfc/lpfc_attr.c
+++ b/drivers/scsi/lpfc/lpfc_attr.c
@@ -23,12 +23,14 @@
#include <linux/delay.h>
#include <linux/pci.h>
#include <linux/interrupt.h>
+#include <linux/aer.h>
#include <scsi/scsi.h>
#include <scsi/scsi_device.h>
#include <scsi/scsi_host.h>
#include <scsi/scsi_tcq.h>
#include <scsi/scsi_transport_fc.h>
+#include <scsi/fc/fc_fs.h>
#include "lpfc_hw4.h"
#include "lpfc_hw.h"
@@ -98,6 +100,28 @@ lpfc_drvr_version_show(struct device *dev, struct device_attribute *attr,
return snprintf(buf, PAGE_SIZE, LPFC_MODULE_DESC "\n");
}
+/**
+ * lpfc_enable_fip_show - Return the fip mode of the HBA
+ * @dev: class unused variable.
+ * @attr: device attribute, not used.
+ * @buf: on return contains the module description text.
+ *
+ * Returns: size of formatted string.
+ **/
+static ssize_t
+lpfc_enable_fip_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct Scsi_Host *shost = class_to_shost(dev);
+ struct lpfc_vport *vport = (struct lpfc_vport *) shost->hostdata;
+ struct lpfc_hba *phba = vport->phba;
+
+ if (phba->hba_flag & HBA_FIP_SUPPORT)
+ return snprintf(buf, PAGE_SIZE, "1\n");
+ else
+ return snprintf(buf, PAGE_SIZE, "0\n");
+}
+
static ssize_t
lpfc_bg_info_show(struct device *dev, struct device_attribute *attr,
char *buf)
@@ -762,9 +786,15 @@ lpfc_board_mode_store(struct device *dev, struct device_attribute *attr,
} else if (strncmp(buf, "offline", sizeof("offline") - 1) == 0)
status = lpfc_do_offline(phba, LPFC_EVT_OFFLINE);
else if (strncmp(buf, "warm", sizeof("warm") - 1) == 0)
- status = lpfc_do_offline(phba, LPFC_EVT_WARM_START);
+ if (phba->sli_rev == LPFC_SLI_REV4)
+ return -EINVAL;
+ else
+ status = lpfc_do_offline(phba, LPFC_EVT_WARM_START);
else if (strncmp(buf, "error", sizeof("error") - 1) == 0)
- status = lpfc_do_offline(phba, LPFC_EVT_KILL);
+ if (phba->sli_rev == LPFC_SLI_REV4)
+ return -EINVAL;
+ else
+ status = lpfc_do_offline(phba, LPFC_EVT_KILL);
else
return -EINVAL;
@@ -1126,6 +1156,9 @@ lpfc_poll_store(struct device *dev, struct device_attribute *attr,
if ((val & 0x3) != val)
return -EINVAL;
+ if (phba->sli_rev == LPFC_SLI_REV4)
+ val = 0;
+
spin_lock_irq(&phba->hbalock);
old_val = phba->cfg_poll;
@@ -1589,6 +1622,7 @@ static DEVICE_ATTR(num_discovered_ports, S_IRUGO,
static DEVICE_ATTR(menlo_mgmt_mode, S_IRUGO, lpfc_mlomgmt_show, NULL);
static DEVICE_ATTR(nport_evt_cnt, S_IRUGO, lpfc_nport_evt_cnt_show, NULL);
static DEVICE_ATTR(lpfc_drvr_version, S_IRUGO, lpfc_drvr_version_show, NULL);
+static DEVICE_ATTR(lpfc_enable_fip, S_IRUGO, lpfc_enable_fip_show, NULL);
static DEVICE_ATTR(board_mode, S_IRUGO | S_IWUSR,
lpfc_board_mode_show, lpfc_board_mode_store);
static DEVICE_ATTR(issue_reset, S_IWUSR, NULL, lpfc_issue_reset);
@@ -2759,6 +2793,196 @@ static DEVICE_ATTR(lpfc_link_speed, S_IRUGO | S_IWUSR,
lpfc_link_speed_show, lpfc_link_speed_store);
/*
+# lpfc_aer_support: Support PCIe device Advanced Error Reporting (AER)
+# 0 = aer disabled or not supported
+# 1 = aer supported and enabled (default)
+# Value range is [0,1]. Default value is 1.
+*/
+
+/**
+ * lpfc_aer_support_store - Set the adapter for aer support
+ *
+ * @dev: class device that is converted into a Scsi_host.
+ * @attr: device attribute, not used.
+ * @buf: containing the string "selective".
+ * @count: unused variable.
+ *
+ * Description:
+ * If the val is 1 and currently the device's AER capability was not
+ * enabled, invoke the kernel's enable AER helper routine, trying to
+ * enable the device's AER capability. If the helper routine enabling
+ * AER returns success, update the device's cfg_aer_support flag to
+ * indicate AER is supported by the device; otherwise, if the device
+ * AER capability is already enabled to support AER, then do nothing.
+ *
+ * If the val is 0 and currently the device's AER support was enabled,
+ * invoke the kernel's disable AER helper routine. After that, update
+ * the device's cfg_aer_support flag to indicate AER is not supported
+ * by the device; otherwise, if the device AER capability is already
+ * disabled from supporting AER, then do nothing.
+ *
+ * Returns:
+ * length of the buf on success if val is in range the intended mode
+ * is supported.
+ * -EINVAL if val out of range or intended mode is not supported.
+ **/
+static ssize_t
+lpfc_aer_support_store(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct Scsi_Host *shost = class_to_shost(dev);
+ struct lpfc_vport *vport = (struct lpfc_vport *)shost->hostdata;
+ struct lpfc_hba *phba = vport->phba;
+ int val = 0, rc = -EINVAL;
+
+ /* AER not supported on OC devices yet */
+ if (phba->pci_dev_grp == LPFC_PCI_DEV_OC)
+ return -EPERM;
+ if (!isdigit(buf[0]))
+ return -EINVAL;
+ if (sscanf(buf, "%i", &val) != 1)
+ return -EINVAL;
+
+ switch (val) {
+ case 0:
+ if (phba->hba_flag & HBA_AER_ENABLED) {
+ rc = pci_disable_pcie_error_reporting(phba->pcidev);
+ if (!rc) {
+ spin_lock_irq(&phba->hbalock);
+ phba->hba_flag &= ~HBA_AER_ENABLED;
+ spin_unlock_irq(&phba->hbalock);
+ phba->cfg_aer_support = 0;
+ rc = strlen(buf);
+ } else
+ rc = -EPERM;
+ } else {
+ phba->cfg_aer_support = 0;
+ rc = strlen(buf);
+ }
+ break;
+ case 1:
+ if (!(phba->hba_flag & HBA_AER_ENABLED)) {
+ rc = pci_enable_pcie_error_reporting(phba->pcidev);
+ if (!rc) {
+ spin_lock_irq(&phba->hbalock);
+ phba->hba_flag |= HBA_AER_ENABLED;
+ spin_unlock_irq(&phba->hbalock);
+ phba->cfg_aer_support = 1;
+ rc = strlen(buf);
+ } else
+ rc = -EPERM;
+ } else {
+ phba->cfg_aer_support = 1;
+ rc = strlen(buf);
+ }
+ break;
+ default:
+ rc = -EINVAL;
+ break;
+ }
+ return rc;
+}
+
+static int lpfc_aer_support = 1;
+module_param(lpfc_aer_support, int, 1);
+MODULE_PARM_DESC(lpfc_aer_support, "Enable PCIe device AER support");
+lpfc_param_show(aer_support)
+
+/**
+ * lpfc_aer_support_init - Set the initial adapters aer support flag
+ * @phba: lpfc_hba pointer.
+ * @val: link speed value.
+ *
+ * Description:
+ * If val is in a valid range [0,1], then set the adapter's initial
+ * cfg_aer_support field. It will be up to the driver's probe_one
+ * routine to determine whether the device's AER support can be set
+ * or not.
+ *
+ * Notes:
+ * If the value is not in range log a kernel error message, and
+ * choose the default value of setting AER support and return.
+ *
+ * Returns:
+ * zero if val saved.
+ * -EINVAL val out of range
+ **/
+static int
+lpfc_aer_support_init(struct lpfc_hba *phba, int val)
+{
+ /* AER not supported on OC devices yet */
+ if (phba->pci_dev_grp == LPFC_PCI_DEV_OC) {
+ phba->cfg_aer_support = 0;
+ return -EPERM;
+ }
+
+ if (val == 0 || val == 1) {
+ phba->cfg_aer_support = val;
+ return 0;
+ }
+ lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+ "2712 lpfc_aer_support attribute value %d out "
+ "of range, allowed values are 0|1, setting it "
+ "to default value of 1\n", val);
+ /* By default, try to enable AER on a device */
+ phba->cfg_aer_support = 1;
+ return -EINVAL;
+}
+
+static DEVICE_ATTR(lpfc_aer_support, S_IRUGO | S_IWUSR,
+ lpfc_aer_support_show, lpfc_aer_support_store);
+
+/**
+ * lpfc_aer_cleanup_state - Clean up aer state to the aer enabled device
+ * @dev: class device that is converted into a Scsi_host.
+ * @attr: device attribute, not used.
+ * @buf: containing the string "selective".
+ * @count: unused variable.
+ *
+ * Description:
+ * If the @buf contains 1 and the device currently has the AER support
+ * enabled, then invokes the kernel AER helper routine
+ * pci_cleanup_aer_uncorrect_error_status to clean up the uncorrectable
+ * error status register.
+ *
+ * Notes:
+ *
+ * Returns:
+ * -EINVAL if the buf does not contain the 1 or the device is not currently
+ * enabled with the AER support.
+ **/
+static ssize_t
+lpfc_aer_cleanup_state(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct Scsi_Host *shost = class_to_shost(dev);
+ struct lpfc_vport *vport = (struct lpfc_vport *) shost->hostdata;
+ struct lpfc_hba *phba = vport->phba;
+ int val, rc = -1;
+
+ /* AER not supported on OC devices yet */
+ if (phba->pci_dev_grp == LPFC_PCI_DEV_OC)
+ return -EPERM;
+ if (!isdigit(buf[0]))
+ return -EINVAL;
+ if (sscanf(buf, "%i", &val) != 1)
+ return -EINVAL;
+ if (val != 1)
+ return -EINVAL;
+
+ if (phba->hba_flag & HBA_AER_ENABLED)
+ rc = pci_cleanup_aer_uncorrect_error_status(phba->pcidev);
+
+ if (rc == 0)
+ return strlen(buf);
+ else
+ return -EPERM;
+}
+
+static DEVICE_ATTR(lpfc_aer_state_cleanup, S_IWUSR, NULL,
+ lpfc_aer_cleanup_state);
+
+/*
# lpfc_fcp_class: Determines FC class to use for the FCP protocol.
# Value range is [2,3]. Default value is 3.
*/
@@ -2846,7 +3070,7 @@ LPFC_ATTR_R(multi_ring_support, 1, 1, 2, "Determines number of primary "
# identifies what rctl value to configure the additional ring for.
# Value range is [1,0xff]. Default value is 4 (Unsolicated Data).
*/
-LPFC_ATTR_R(multi_ring_rctl, FC_UNSOL_DATA, 1,
+LPFC_ATTR_R(multi_ring_rctl, FC_RCTL_DD_UNSOL_DATA, 1,
255, "Identifies RCTL for additional ring configuration");
/*
@@ -2854,7 +3078,7 @@ LPFC_ATTR_R(multi_ring_rctl, FC_UNSOL_DATA, 1,
# identifies what type value to configure the additional ring for.
# Value range is [1,0xff]. Default value is 5 (LLC/SNAP).
*/
-LPFC_ATTR_R(multi_ring_type, FC_LLC_SNAP, 1,
+LPFC_ATTR_R(multi_ring_type, FC_TYPE_IP, 1,
255, "Identifies TYPE for additional ring configuration");
/*
@@ -2947,15 +3171,6 @@ LPFC_ATTR_R(enable_hba_heartbeat, 1, 0, 1, "Enable HBA Heartbeat.");
LPFC_ATTR_R(enable_bg, 0, 0, 1, "Enable BlockGuard Support");
/*
-# lpfc_enable_fip: When set, FIP is required to start discovery. If not
-# set, the driver will add an FCF record manually if the port has no
-# FCF records available and start discovery.
-# Value range is [0,1]. Default value is 1 (enabled)
-*/
-LPFC_ATTR_RW(enable_fip, 0, 0, 1, "Enable FIP Discovery");
-
-
-/*
# lpfc_prot_mask: i
# - Bit mask of host protection capabilities used to register with the
# SCSI mid-layer
@@ -3013,6 +3228,7 @@ struct device_attribute *lpfc_hba_attrs[] = {
&dev_attr_num_discovered_ports,
&dev_attr_menlo_mgmt_mode,
&dev_attr_lpfc_drvr_version,
+ &dev_attr_lpfc_enable_fip,
&dev_attr_lpfc_temp_sensor,
&dev_attr_lpfc_log_verbose,
&dev_attr_lpfc_lun_queue_depth,
@@ -3020,7 +3236,6 @@ struct device_attribute *lpfc_hba_attrs[] = {
&dev_attr_lpfc_peer_port_login,
&dev_attr_lpfc_nodev_tmo,
&dev_attr_lpfc_devloss_tmo,
- &dev_attr_lpfc_enable_fip,
&dev_attr_lpfc_fcp_class,
&dev_attr_lpfc_use_adisc,
&dev_attr_lpfc_ack0,
@@ -3061,6 +3276,8 @@ struct device_attribute *lpfc_hba_attrs[] = {
&dev_attr_lpfc_max_scsicmpl_time,
&dev_attr_lpfc_stat_data_ctrl,
&dev_attr_lpfc_prot_sg_seg_cnt,
+ &dev_attr_lpfc_aer_support,
+ &dev_attr_lpfc_aer_state_cleanup,
NULL,
};
@@ -3073,7 +3290,6 @@ struct device_attribute *lpfc_vport_attrs[] = {
&dev_attr_lpfc_lun_queue_depth,
&dev_attr_lpfc_nodev_tmo,
&dev_attr_lpfc_devloss_tmo,
- &dev_attr_lpfc_enable_fip,
&dev_attr_lpfc_hba_queue_depth,
&dev_attr_lpfc_peer_port_login,
&dev_attr_lpfc_restrict_login,
@@ -3815,7 +4031,11 @@ lpfc_get_stats(struct Scsi_Host *shost)
hs->invalid_crc_count -= lso->invalid_crc_count;
hs->error_frames -= lso->error_frames;
- if (phba->fc_topology == TOPOLOGY_LOOP) {
+ if (phba->hba_flag & HBA_FCOE_SUPPORT) {
+ hs->lip_count = -1;
+ hs->nos_count = (phba->link_events >> 1);
+ hs->nos_count -= lso->link_events;
+ } else if (phba->fc_topology == TOPOLOGY_LOOP) {
hs->lip_count = (phba->fc_eventTag >> 1);
hs->lip_count -= lso->link_events;
hs->nos_count = -1;
@@ -3906,7 +4126,10 @@ lpfc_reset_stats(struct Scsi_Host *shost)
lso->invalid_tx_word_count = pmb->un.varRdLnk.invalidXmitWord;
lso->invalid_crc_count = pmb->un.varRdLnk.crcCnt;
lso->error_frames = pmb->un.varRdLnk.crcCnt;
- lso->link_events = (phba->fc_eventTag >> 1);
+ if (phba->hba_flag & HBA_FCOE_SUPPORT)
+ lso->link_events = (phba->link_events >> 1);
+ else
+ lso->link_events = (phba->fc_eventTag >> 1);
psli->stats_start = get_seconds();
@@ -4222,14 +4445,17 @@ lpfc_get_cfgparam(struct lpfc_hba *phba)
lpfc_enable_hba_reset_init(phba, lpfc_enable_hba_reset);
lpfc_enable_hba_heartbeat_init(phba, lpfc_enable_hba_heartbeat);
lpfc_enable_bg_init(phba, lpfc_enable_bg);
+ if (phba->sli_rev == LPFC_SLI_REV4)
+ phba->cfg_poll = 0;
+ else
phba->cfg_poll = lpfc_poll;
phba->cfg_soft_wwnn = 0L;
phba->cfg_soft_wwpn = 0L;
lpfc_sg_seg_cnt_init(phba, lpfc_sg_seg_cnt);
lpfc_prot_sg_seg_cnt_init(phba, lpfc_prot_sg_seg_cnt);
lpfc_hba_queue_depth_init(phba, lpfc_hba_queue_depth);
- lpfc_enable_fip_init(phba, lpfc_enable_fip);
lpfc_hba_log_verbose_init(phba, lpfc_log_verbose);
+ lpfc_aer_support_init(phba, lpfc_aer_support);
return;
}
diff --git a/drivers/scsi/lpfc/lpfc_bsg.c b/drivers/scsi/lpfc/lpfc_bsg.c
index da6bf5aac9d..a5d9048235d 100644
--- a/drivers/scsi/lpfc/lpfc_bsg.c
+++ b/drivers/scsi/lpfc/lpfc_bsg.c
@@ -26,6 +26,7 @@
#include <scsi/scsi_host.h>
#include <scsi/scsi_transport_fc.h>
#include <scsi/scsi_bsg_fc.h>
+#include <scsi/fc/fc_fs.h>
#include "lpfc_hw4.h"
#include "lpfc_hw.h"
@@ -148,8 +149,8 @@ lpfc_bsg_rport_ct(struct fc_bsg_job *job)
cmd->ulpCommand = CMD_GEN_REQUEST64_CR;
cmd->un.genreq64.w5.hcsw.Fctl = (SI | LA);
cmd->un.genreq64.w5.hcsw.Dfctl = 0;
- cmd->un.genreq64.w5.hcsw.Rctl = FC_UNSOL_CTL;
- cmd->un.genreq64.w5.hcsw.Type = FC_COMMON_TRANSPORT_ULP;
+ cmd->un.genreq64.w5.hcsw.Rctl = FC_RCTL_DD_UNSOL_CTL;
+ cmd->un.genreq64.w5.hcsw.Type = FC_TYPE_CT;
cmd->ulpBdeCount = 1;
cmd->ulpLe = 1;
cmd->ulpClass = CLASS3;
diff --git a/drivers/scsi/lpfc/lpfc_crtn.h b/drivers/scsi/lpfc/lpfc_crtn.h
index 0830f37409a..650494d622c 100644
--- a/drivers/scsi/lpfc/lpfc_crtn.h
+++ b/drivers/scsi/lpfc/lpfc_crtn.h
@@ -49,6 +49,8 @@ void lpfc_init_link(struct lpfc_hba *, LPFC_MBOXQ_t *, uint32_t, uint32_t);
void lpfc_request_features(struct lpfc_hba *, struct lpfcMboxq *);
struct lpfc_vport *lpfc_find_vport_by_did(struct lpfc_hba *, uint32_t);
+void lpfc_cleanup_rcv_buffers(struct lpfc_vport *);
+void lpfc_rcv_seq_check_edtov(struct lpfc_vport *);
void lpfc_cleanup_rpis(struct lpfc_vport *, int);
int lpfc_linkdown(struct lpfc_hba *);
void lpfc_linkdown_port(struct lpfc_vport *);
@@ -144,6 +146,8 @@ void lpfc_hb_timeout_handler(struct lpfc_hba *);
void lpfc_ct_unsol_event(struct lpfc_hba *, struct lpfc_sli_ring *,
struct lpfc_iocbq *);
+void lpfc_sli4_ct_abort_unsol_event(struct lpfc_hba *, struct lpfc_sli_ring *,
+ struct lpfc_iocbq *);
int lpfc_ns_cmd(struct lpfc_vport *, int, uint8_t, uint32_t);
int lpfc_fdmi_cmd(struct lpfc_vport *, struct lpfc_nodelist *, int);
void lpfc_fdmi_tmo(unsigned long);
@@ -188,7 +192,7 @@ int lpfc_mbox_tmo_val(struct lpfc_hba *, int);
void lpfc_init_vfi(struct lpfcMboxq *, struct lpfc_vport *);
void lpfc_reg_vfi(struct lpfcMboxq *, struct lpfc_vport *, dma_addr_t);
void lpfc_init_vpi(struct lpfc_hba *, struct lpfcMboxq *, uint16_t);
-void lpfc_unreg_vfi(struct lpfcMboxq *, uint16_t);
+void lpfc_unreg_vfi(struct lpfcMboxq *, struct lpfc_vport *);
void lpfc_reg_fcfi(struct lpfc_hba *, struct lpfcMboxq *);
void lpfc_unreg_fcfi(struct lpfcMboxq *, uint16_t);
void lpfc_resume_rpi(struct lpfcMboxq *, struct lpfc_nodelist *);
@@ -212,7 +216,10 @@ void lpfc_stop_vport_timers(struct lpfc_vport *);
void lpfc_poll_timeout(unsigned long ptr);
void lpfc_poll_start_timer(struct lpfc_hba *);
void lpfc_poll_eratt(unsigned long);
-void lpfc_sli_poll_fcp_ring(struct lpfc_hba *);
+int
+lpfc_sli_handle_fast_ring_event(struct lpfc_hba *,
+ struct lpfc_sli_ring *, uint32_t);
+
struct lpfc_iocbq * lpfc_sli_get_iocbq(struct lpfc_hba *);
void lpfc_sli_release_iocbq(struct lpfc_hba *, struct lpfc_iocbq *);
uint16_t lpfc_sli_next_iotag(struct lpfc_hba *, struct lpfc_iocbq *);
@@ -235,7 +242,7 @@ void lpfc_sli_mbox_sys_shutdown(struct lpfc_hba *);
int lpfc_sli_check_eratt(struct lpfc_hba *);
void lpfc_sli_handle_slow_ring_event(struct lpfc_hba *,
struct lpfc_sli_ring *, uint32_t);
-int lpfc_sli4_handle_received_buffer(struct lpfc_hba *);
+void lpfc_sli4_handle_received_buffer(struct lpfc_hba *, struct hbq_dmabuf *);
void lpfc_sli_def_mbox_cmpl(struct lpfc_hba *, LPFC_MBOXQ_t *);
int lpfc_sli_issue_iocb(struct lpfc_hba *, uint32_t,
struct lpfc_iocbq *, uint32_t);
@@ -361,6 +368,7 @@ void lpfc_stop_port(struct lpfc_hba *);
void lpfc_parse_fcoe_conf(struct lpfc_hba *, uint8_t *, uint32_t);
int lpfc_parse_vpd(struct lpfc_hba *, uint8_t *, int);
void lpfc_start_fdiscs(struct lpfc_hba *phba);
+struct lpfc_vport *lpfc_find_vport_by_vpid(struct lpfc_hba *, uint16_t);
#define ScsiResult(host_code, scsi_code) (((host_code) << 16) | scsi_code)
#define HBA_EVENT_RSCN 5
diff --git a/drivers/scsi/lpfc/lpfc_ct.c b/drivers/scsi/lpfc/lpfc_ct.c
index 9a1bd9534d7..0ebcd9baca7 100644
--- a/drivers/scsi/lpfc/lpfc_ct.c
+++ b/drivers/scsi/lpfc/lpfc_ct.c
@@ -31,6 +31,7 @@
#include <scsi/scsi_device.h>
#include <scsi/scsi_host.h>
#include <scsi/scsi_transport_fc.h>
+#include <scsi/fc/fc_fs.h>
#include "lpfc_hw4.h"
#include "lpfc_hw.h"
@@ -87,7 +88,6 @@ void
lpfc_ct_unsol_event(struct lpfc_hba *phba, struct lpfc_sli_ring *pring,
struct lpfc_iocbq *piocbq)
{
-
struct lpfc_dmabuf *mp = NULL;
IOCB_t *icmd = &piocbq->iocb;
int i;
@@ -160,6 +160,39 @@ lpfc_ct_unsol_event(struct lpfc_hba *phba, struct lpfc_sli_ring *pring,
}
}
+/**
+ * lpfc_sli4_ct_abort_unsol_event - Default handle for sli4 unsol abort
+ * @phba: Pointer to HBA context object.
+ * @pring: Pointer to the driver internal I/O ring.
+ * @piocbq: Pointer to the IOCBQ.
+ *
+ * This function serves as the default handler for the sli4 unsolicited
+ * abort event. It shall be invoked when there is no application interface
+ * registered unsolicited abort handler. This handler does nothing but
+ * just simply releases the dma buffer used by the unsol abort event.
+ **/
+void
+lpfc_sli4_ct_abort_unsol_event(struct lpfc_hba *phba,
+ struct lpfc_sli_ring *pring,
+ struct lpfc_iocbq *piocbq)
+{
+ IOCB_t *icmd = &piocbq->iocb;
+ struct lpfc_dmabuf *bdeBuf;
+ uint32_t size;
+
+ /* Forward abort event to any process registered to receive ct event */
+ lpfc_bsg_ct_unsol_event(phba, pring, piocbq);
+
+ /* If there is no BDE associated with IOCB, there is nothing to do */
+ if (icmd->ulpBdeCount == 0)
+ return;
+ bdeBuf = piocbq->context2;
+ piocbq->context2 = NULL;
+ size = icmd->un.cont64[0].tus.f.bdeSize;
+ lpfc_ct_unsol_buffer(phba, piocbq, bdeBuf, size);
+ lpfc_in_buf_free(phba, bdeBuf);
+}
+
static void
lpfc_free_ct_rsp(struct lpfc_hba *phba, struct lpfc_dmabuf *mlist)
{
@@ -304,8 +337,8 @@ lpfc_gen_req(struct lpfc_vport *vport, struct lpfc_dmabuf *bmp,
/* Fill in rest of iocb */
icmd->un.genreq64.w5.hcsw.Fctl = (SI | LA);
icmd->un.genreq64.w5.hcsw.Dfctl = 0;
- icmd->un.genreq64.w5.hcsw.Rctl = FC_UNSOL_CTL;
- icmd->un.genreq64.w5.hcsw.Type = FC_COMMON_TRANSPORT_ULP;
+ icmd->un.genreq64.w5.hcsw.Rctl = FC_RCTL_DD_UNSOL_CTL;
+ icmd->un.genreq64.w5.hcsw.Type = FC_TYPE_CT;
if (!tmo) {
/* FC spec states we need 3 * ratov for CT requests */
@@ -363,9 +396,14 @@ lpfc_ct_cmd(struct lpfc_vport *vport, struct lpfc_dmabuf *inmp,
outmp = lpfc_alloc_ct_rsp(phba, cmdcode, bpl, rsp_size, &cnt);
if (!outmp)
return -ENOMEM;
-
+ /*
+ * Form the CT IOCB. The total number of BDEs in this IOCB
+ * is the single command plus response count from
+ * lpfc_alloc_ct_rsp.
+ */
+ cnt += 1;
status = lpfc_gen_req(vport, bmp, inmp, outmp, cmpl, ndlp, 0,
- cnt+1, 0, retry);
+ cnt, 0, retry);
if (status) {
lpfc_free_ct_rsp(phba, outmp);
return -ENOMEM;
@@ -501,6 +539,9 @@ lpfc_ns_rsp(struct lpfc_vport *vport, struct lpfc_dmabuf *mp, uint32_t Size)
SLI_CTNS_GFF_ID,
0, Did) == 0)
vport->num_disc_nodes++;
+ else
+ lpfc_setup_disc_node
+ (vport, Did);
}
else {
lpfc_debugfs_disc_trc(vport,
@@ -1209,7 +1250,7 @@ lpfc_ns_cmd(struct lpfc_vport *vport, int cmdcode,
be16_to_cpu(SLI_CTNS_RFF_ID);
CtReq->un.rff.PortId = cpu_to_be32(vport->fc_myDID);
CtReq->un.rff.fbits = FC4_FEATURE_INIT;
- CtReq->un.rff.type_code = FC_FCP_DATA;
+ CtReq->un.rff.type_code = FC_TYPE_FCP;
cmpl = lpfc_cmpl_ct_cmd_rff_id;
break;
}
diff --git a/drivers/scsi/lpfc/lpfc_debugfs.c b/drivers/scsi/lpfc/lpfc_debugfs.c
index 8d0f0de76b6..391584183d8 100644
--- a/drivers/scsi/lpfc/lpfc_debugfs.c
+++ b/drivers/scsi/lpfc/lpfc_debugfs.c
@@ -926,7 +926,7 @@ lpfc_debugfs_dumpData_open(struct inode *inode, struct file *file)
goto out;
/* Round to page boundry */
- printk(KERN_ERR "BLKGRD %s: _dump_buf_data=0x%p\n",
+ printk(KERN_ERR "9059 BLKGRD: %s: _dump_buf_data=0x%p\n",
__func__, _dump_buf_data);
debug->buffer = _dump_buf_data;
if (!debug->buffer) {
@@ -956,8 +956,8 @@ lpfc_debugfs_dumpDif_open(struct inode *inode, struct file *file)
goto out;
/* Round to page boundry */
- printk(KERN_ERR "BLKGRD %s: _dump_buf_dif=0x%p file=%s\n", __func__,
- _dump_buf_dif, file->f_dentry->d_name.name);
+ printk(KERN_ERR "9060 BLKGRD: %s: _dump_buf_dif=0x%p file=%s\n",
+ __func__, _dump_buf_dif, file->f_dentry->d_name.name);
debug->buffer = _dump_buf_dif;
if (!debug->buffer) {
kfree(debug);
@@ -1377,7 +1377,7 @@ lpfc_debugfs_initialize(struct lpfc_vport *vport)
debugfs_create_dir(name, phba->hba_debugfs_root);
if (!vport->vport_debugfs_root) {
lpfc_printf_vlog(vport, KERN_ERR, LOG_INIT,
- "0417 Cant create debugfs");
+ "0417 Cant create debugfs\n");
goto debug_failed;
}
atomic_inc(&phba->debugfs_vport_count);
@@ -1430,7 +1430,7 @@ lpfc_debugfs_initialize(struct lpfc_vport *vport)
vport, &lpfc_debugfs_op_nodelist);
if (!vport->debug_nodelist) {
lpfc_printf_vlog(vport, KERN_ERR, LOG_INIT,
- "0409 Cant create debugfs nodelist");
+ "0409 Cant create debugfs nodelist\n");
goto debug_failed;
}
debug_failed:
diff --git a/drivers/scsi/lpfc/lpfc_disc.h b/drivers/scsi/lpfc/lpfc_disc.h
index 1142070e948..2851d75ffc6 100644
--- a/drivers/scsi/lpfc/lpfc_disc.h
+++ b/drivers/scsi/lpfc/lpfc_disc.h
@@ -19,7 +19,7 @@
*******************************************************************/
#define FC_MAX_HOLD_RSCN 32 /* max number of deferred RSCNs */
-#define FC_MAX_NS_RSP 65536 /* max size NameServer rsp */
+#define FC_MAX_NS_RSP 64512 /* max size NameServer rsp */
#define FC_MAXLOOP 126 /* max devices supported on a fc loop */
#define LPFC_DISC_FLOGI_TMO 10 /* Discovery FLOGI ratov */
@@ -105,8 +105,6 @@ struct lpfc_nodelist {
struct lpfc_vport *vport;
struct lpfc_work_evt els_retry_evt;
struct lpfc_work_evt dev_loss_evt;
- unsigned long last_ramp_up_time; /* jiffy of last ramp up */
- unsigned long last_q_full_time; /* jiffy of last queue full */
struct kref kref;
atomic_t cmd_pending;
uint32_t cmd_qdepth;
diff --git a/drivers/scsi/lpfc/lpfc_els.c b/drivers/scsi/lpfc/lpfc_els.c
index a14ab4580d4..ce522702a6c 100644
--- a/drivers/scsi/lpfc/lpfc_els.c
+++ b/drivers/scsi/lpfc/lpfc_els.c
@@ -173,13 +173,26 @@ lpfc_prep_els_iocb(struct lpfc_vport *vport, uint8_t expectRsp,
* in FIP mode send FLOGI, FDISC and LOGO as FIP frames.
*/
if ((did == Fabric_DID) &&
- bf_get(lpfc_fip_flag, &phba->sli4_hba.sli4_flags) &&
+ (phba->hba_flag & HBA_FIP_SUPPORT) &&
((elscmd == ELS_CMD_FLOGI) ||
(elscmd == ELS_CMD_FDISC) ||
(elscmd == ELS_CMD_LOGO)))
- elsiocb->iocb_flag |= LPFC_FIP_ELS;
+ switch (elscmd) {
+ case ELS_CMD_FLOGI:
+ elsiocb->iocb_flag |= ((ELS_ID_FLOGI << LPFC_FIP_ELS_ID_SHIFT)
+ & LPFC_FIP_ELS_ID_MASK);
+ break;
+ case ELS_CMD_FDISC:
+ elsiocb->iocb_flag |= ((ELS_ID_FDISC << LPFC_FIP_ELS_ID_SHIFT)
+ & LPFC_FIP_ELS_ID_MASK);
+ break;
+ case ELS_CMD_LOGO:
+ elsiocb->iocb_flag |= ((ELS_ID_LOGO << LPFC_FIP_ELS_ID_SHIFT)
+ & LPFC_FIP_ELS_ID_MASK);
+ break;
+ }
else
- elsiocb->iocb_flag &= ~LPFC_FIP_ELS;
+ elsiocb->iocb_flag &= ~LPFC_FIP_ELS_ID_MASK;
icmd = &elsiocb->iocb;
@@ -591,7 +604,7 @@ lpfc_cmpl_els_flogi_fabric(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp,
} else {
ndlp->nlp_type |= NLP_FABRIC;
lpfc_nlp_set_state(vport, ndlp, NLP_STE_UNMAPPED_NODE);
- if (vport->vfi_state & LPFC_VFI_REGISTERED) {
+ if (vport->vpi_state & LPFC_VPI_REGISTERED) {
lpfc_start_fdiscs(phba);
lpfc_do_scr_ns_plogi(phba, vport);
} else
@@ -2452,6 +2465,7 @@ lpfc_els_retry_delay_handler(struct lpfc_nodelist *ndlp)
*/
del_timer_sync(&ndlp->nlp_delayfunc);
retry = ndlp->nlp_retry;
+ ndlp->nlp_retry = 0;
switch (cmd) {
case ELS_CMD_FLOGI:
@@ -2711,12 +2725,16 @@ lpfc_els_retry(struct lpfc_hba *phba, struct lpfc_iocbq *cmdiocb,
!lpfc_error_lost_link(irsp)) {
/* FLOGI retry policy */
retry = 1;
- maxretry = 48;
- if (cmdiocb->retry >= 32)
+ /* retry forever */
+ maxretry = 0;
+ if (cmdiocb->retry >= 100)
+ delay = 5000;
+ else if (cmdiocb->retry >= 32)
delay = 1000;
}
- if ((++cmdiocb->retry) >= maxretry) {
+ cmdiocb->retry++;
+ if (maxretry && (cmdiocb->retry >= maxretry)) {
phba->fc_stat.elsRetryExceeded++;
retry = 0;
}
@@ -4503,6 +4521,29 @@ lpfc_els_rcv_lirr(struct lpfc_vport *vport, struct lpfc_iocbq *cmdiocb,
}
/**
+ * lpfc_els_rcv_rrq - Process an unsolicited rrq iocb
+ * @vport: pointer to a host virtual N_Port data structure.
+ * @cmdiocb: pointer to lpfc command iocb data structure.
+ * @ndlp: pointer to a node-list data structure.
+ *
+ * This routine processes a Reinstate Recovery Qualifier (RRQ) IOCB
+ * received as an ELS unsolicited event. A request to RRQ shall only
+ * be accepted if the Originator Nx_Port N_Port_ID or the Responder
+ * Nx_Port N_Port_ID of the target Exchange is the same as the
+ * N_Port_ID of the Nx_Port that makes the request. If the RRQ is
+ * not accepted, an LS_RJT with reason code "Unable to perform
+ * command request" and reason code explanation "Invalid Originator
+ * S_ID" shall be returned. For now, we just unconditionally accept
+ * RRQ from the target.
+ **/
+static void
+lpfc_els_rcv_rrq(struct lpfc_vport *vport, struct lpfc_iocbq *cmdiocb,
+ struct lpfc_nodelist *ndlp)
+{
+ lpfc_els_rsp_acc(vport, ELS_CMD_ACC, cmdiocb, ndlp, NULL);
+}
+
+/**
* lpfc_els_rsp_rps_acc - Completion callbk func for MBX_READ_LNK_STAT mbox cmd
* @phba: pointer to lpfc hba data structure.
* @pmb: pointer to the driver internal queue element for mailbox command.
@@ -5396,7 +5437,7 @@ lpfc_els_unsol_buffer(struct lpfc_hba *phba, struct lpfc_sli_ring *pring,
if (lpfc_els_chk_latt(vport))
goto dropit;
- /* Ignore traffic recevied during vport shutdown. */
+ /* Ignore traffic received during vport shutdown. */
if (vport->load_flag & FC_UNLOADING)
goto dropit;
@@ -5618,6 +5659,16 @@ lpfc_els_unsol_buffer(struct lpfc_hba *phba, struct lpfc_sli_ring *pring,
if (newnode)
lpfc_nlp_put(ndlp);
break;
+ case ELS_CMD_RRQ:
+ lpfc_debugfs_disc_trc(vport, LPFC_DISC_TRC_ELS_UNSOL,
+ "RCV RRQ: did:x%x/ste:x%x flg:x%x",
+ did, vport->port_state, ndlp->nlp_flag);
+
+ phba->fc_stat.elsRcvRRQ++;
+ lpfc_els_rcv_rrq(vport, elsiocb, ndlp);
+ if (newnode)
+ lpfc_nlp_put(ndlp);
+ break;
default:
lpfc_debugfs_disc_trc(vport, LPFC_DISC_TRC_ELS_UNSOL,
"RCV ELS cmd: cmd:x%x did:x%x/ste:x%x",
@@ -5670,7 +5721,7 @@ dropit:
* NULL - No vport with the matching @vpi found
* Otherwise - Address to the vport with the matching @vpi.
**/
-static struct lpfc_vport *
+struct lpfc_vport *
lpfc_find_vport_by_vpid(struct lpfc_hba *phba, uint16_t vpi)
{
struct lpfc_vport *vport;
@@ -6024,11 +6075,6 @@ lpfc_cmpl_els_fdisc(struct lpfc_hba *phba, struct lpfc_iocbq *cmdiocb,
irsp->ulpStatus, irsp->un.ulpWord[4]);
goto fdisc_failed;
}
- if (vport->fc_vport->vport_state == FC_VPORT_INITIALIZING)
- lpfc_vport_set_state(vport, FC_VPORT_FAILED);
- lpfc_nlp_put(ndlp);
- /* giving up on FDISC. Cancel discovery timer */
- lpfc_can_disctmo(vport);
spin_lock_irq(shost->host_lock);
vport->fc_flag |= FC_FABRIC;
if (vport->phba->fc_topology == TOPOLOGY_LOOP)
@@ -6107,6 +6153,7 @@ lpfc_issue_els_fdisc(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp,
int did = ndlp->nlp_DID;
int rc;
+ vport->port_state = LPFC_FDISC;
cmdsize = (sizeof(uint32_t) + sizeof(struct serv_parm));
elsiocb = lpfc_prep_els_iocb(vport, 1, cmdsize, retry, ndlp, did,
ELS_CMD_FDISC);
@@ -6172,7 +6219,6 @@ lpfc_issue_els_fdisc(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp,
return 1;
}
lpfc_vport_set_state(vport, FC_VPORT_INITIALIZING);
- vport->port_state = LPFC_FDISC;
return 0;
}
diff --git a/drivers/scsi/lpfc/lpfc_hbadisc.c b/drivers/scsi/lpfc/lpfc_hbadisc.c
index e6a47e25b21..3b942442765 100644..100755
--- a/drivers/scsi/lpfc/lpfc_hbadisc.c
+++ b/drivers/scsi/lpfc/lpfc_hbadisc.c
@@ -525,8 +525,6 @@ lpfc_work_done(struct lpfc_hba *phba)
spin_unlock_irq(&phba->hbalock);
lpfc_sli_hbqbuf_add_hbqs(phba, LPFC_ELS_HBQ);
}
- if (phba->hba_flag & HBA_RECEIVE_BUFFER)
- lpfc_sli4_handle_received_buffer(phba);
}
vports = lpfc_create_vport_work_array(phba);
@@ -568,8 +566,9 @@ lpfc_work_done(struct lpfc_hba *phba)
pring = &phba->sli.ring[LPFC_ELS_RING];
status = (ha_copy & (HA_RXMASK << (4*LPFC_ELS_RING)));
status >>= (4*LPFC_ELS_RING);
- if ((status & HA_RXMASK)
- || (pring->flag & LPFC_DEFERRED_RING_EVENT)) {
+ if ((status & HA_RXMASK) ||
+ (pring->flag & LPFC_DEFERRED_RING_EVENT) ||
+ (phba->hba_flag & HBA_SP_QUEUE_EVT)) {
if (pring->flag & LPFC_STOP_IOCB_EVENT) {
pring->flag |= LPFC_DEFERRED_RING_EVENT;
/* Set the lpfc data pending flag */
@@ -688,7 +687,8 @@ lpfc_cleanup_rpis(struct lpfc_vport *vport, int remove)
lpfc_unreg_rpi(vport, ndlp);
/* Leave Fabric nodes alone on link down */
- if (!remove && ndlp->nlp_type & NLP_FABRIC)
+ if ((phba->sli_rev < LPFC_SLI_REV4) &&
+ (!remove && ndlp->nlp_type & NLP_FABRIC))
continue;
rc = lpfc_disc_state_machine(vport, ndlp, NULL,
remove
@@ -706,6 +706,9 @@ lpfc_cleanup_rpis(struct lpfc_vport *vport, int remove)
void
lpfc_port_link_failure(struct lpfc_vport *vport)
{
+ /* Cleanup any outstanding received buffers */
+ lpfc_cleanup_rcv_buffers(vport);
+
/* Cleanup any outstanding RSCN activity */
lpfc_els_flush_rscn(vport);
@@ -1015,13 +1018,12 @@ lpfc_mbx_cmpl_reg_fcfi(struct lpfc_hba *phba, LPFC_MBOXQ_t *mboxq)
mempool_free(mboxq, phba->mbox_mem_pool);
return;
}
- if (vport->port_state != LPFC_FLOGI) {
- spin_lock_irqsave(&phba->hbalock, flags);
- phba->fcf.fcf_flag |= (FCF_DISCOVERED | FCF_IN_USE);
- phba->hba_flag &= ~FCF_DISC_INPROGRESS;
- spin_unlock_irqrestore(&phba->hbalock, flags);
+ spin_lock_irqsave(&phba->hbalock, flags);
+ phba->fcf.fcf_flag |= (FCF_DISCOVERED | FCF_IN_USE);
+ phba->hba_flag &= ~FCF_DISC_INPROGRESS;
+ spin_unlock_irqrestore(&phba->hbalock, flags);
+ if (vport->port_state != LPFC_FLOGI)
lpfc_initial_flogi(vport);
- }
mempool_free(mboxq, phba->mbox_mem_pool);
return;
@@ -1199,6 +1201,7 @@ lpfc_register_fcf(struct lpfc_hba *phba)
/* If the FCF is not availabe do nothing. */
if (!(phba->fcf.fcf_flag & FCF_AVAILABLE)) {
+ phba->hba_flag &= ~FCF_DISC_INPROGRESS;
spin_unlock_irqrestore(&phba->hbalock, flags);
return;
}
@@ -1216,15 +1219,23 @@ lpfc_register_fcf(struct lpfc_hba *phba)
fcf_mbxq = mempool_alloc(phba->mbox_mem_pool,
GFP_KERNEL);
- if (!fcf_mbxq)
+ if (!fcf_mbxq) {
+ spin_lock_irqsave(&phba->hbalock, flags);
+ phba->hba_flag &= ~FCF_DISC_INPROGRESS;
+ spin_unlock_irqrestore(&phba->hbalock, flags);
return;
+ }
lpfc_reg_fcfi(phba, fcf_mbxq);
fcf_mbxq->vport = phba->pport;
fcf_mbxq->mbox_cmpl = lpfc_mbx_cmpl_reg_fcfi;
rc = lpfc_sli_issue_mbox(phba, fcf_mbxq, MBX_NOWAIT);
- if (rc == MBX_NOT_FINISHED)
+ if (rc == MBX_NOT_FINISHED) {
+ spin_lock_irqsave(&phba->hbalock, flags);
+ phba->hba_flag &= ~FCF_DISC_INPROGRESS;
+ spin_unlock_irqrestore(&phba->hbalock, flags);
mempool_free(fcf_mbxq, phba->mbox_mem_pool);
+ }
return;
}
@@ -1253,13 +1264,27 @@ lpfc_match_fcf_conn_list(struct lpfc_hba *phba,
uint16_t *vlan_id)
{
struct lpfc_fcf_conn_entry *conn_entry;
+ int i, j, fcf_vlan_id = 0;
+
+ /* Find the lowest VLAN id in the FCF record */
+ for (i = 0; i < 512; i++) {
+ if (new_fcf_record->vlan_bitmap[i]) {
+ fcf_vlan_id = i * 8;
+ j = 0;
+ while (!((new_fcf_record->vlan_bitmap[i] >> j) & 1)) {
+ j++;
+ fcf_vlan_id++;
+ }
+ break;
+ }
+ }
/* If FCF not available return 0 */
if (!bf_get(lpfc_fcf_record_fcf_avail, new_fcf_record) ||
!bf_get(lpfc_fcf_record_fcf_valid, new_fcf_record))
return 0;
- if (!phba->cfg_enable_fip) {
+ if (!(phba->hba_flag & HBA_FIP_SUPPORT)) {
*boot_flag = 0;
*addr_mode = bf_get(lpfc_fcf_record_mac_addr_prov,
new_fcf_record);
@@ -1286,7 +1311,11 @@ lpfc_match_fcf_conn_list(struct lpfc_hba *phba,
if (*addr_mode & LPFC_FCF_FPMA)
*addr_mode = LPFC_FCF_FPMA;
- *vlan_id = 0xFFFF;
+ /* If FCF record report a vlan id use that vlan id */
+ if (fcf_vlan_id)
+ *vlan_id = fcf_vlan_id;
+ else
+ *vlan_id = 0xFFFF;
return 1;
}
@@ -1384,8 +1413,15 @@ lpfc_match_fcf_conn_list(struct lpfc_hba *phba,
(*addr_mode & LPFC_FCF_FPMA))
*addr_mode = LPFC_FCF_FPMA;
+ /* If matching connect list has a vlan id, use it */
if (conn_entry->conn_rec.flags & FCFCNCT_VLAN_VALID)
*vlan_id = conn_entry->conn_rec.vlan_tag;
+ /*
+ * If no vlan id is specified in connect list, use the vlan id
+ * in the FCF record
+ */
+ else if (fcf_vlan_id)
+ *vlan_id = fcf_vlan_id;
else
*vlan_id = 0xFFFF;
@@ -1423,6 +1459,15 @@ lpfc_check_pending_fcoe_event(struct lpfc_hba *phba, uint8_t unreg_fcf)
if (phba->link_state >= LPFC_LINK_UP)
lpfc_sli4_read_fcf_record(phba, LPFC_FCOE_FCF_GET_FIRST);
+ else {
+ /*
+ * Do not continue FCF discovery and clear FCF_DISC_INPROGRESS
+ * flag
+ */
+ spin_lock_irq(&phba->hbalock);
+ phba->hba_flag &= ~FCF_DISC_INPROGRESS;
+ spin_unlock_irq(&phba->hbalock);
+ }
if (unreg_fcf) {
spin_lock_irq(&phba->hbalock);
@@ -1659,9 +1704,8 @@ lpfc_init_vpi_cmpl(struct lpfc_hba *phba, LPFC_MBOXQ_t *mboxq)
lpfc_initial_fdisc(vport);
else {
lpfc_vport_set_state(vport, FC_VPORT_NO_FABRIC_SUPP);
- lpfc_printf_vlog(vport, KERN_ERR,
- LOG_ELS,
- "2606 No NPIV Fabric support\n");
+ lpfc_printf_vlog(vport, KERN_ERR, LOG_ELS,
+ "2606 No NPIV Fabric support\n");
}
return;
}
@@ -1756,8 +1800,8 @@ lpfc_mbx_cmpl_reg_vfi(struct lpfc_hba *phba, LPFC_MBOXQ_t *mboxq)
lpfc_vport_set_state(vport, FC_VPORT_FAILED);
goto fail_free_mem;
}
- /* Mark the vport has registered with its VFI */
- vport->vfi_state |= LPFC_VFI_REGISTERED;
+ /* The VPI is implicitly registered when the VFI is registered */
+ vport->vpi_state |= LPFC_VPI_REGISTERED;
if (vport->port_state == LPFC_FABRIC_CFG_LINK) {
lpfc_start_fdiscs(phba);
@@ -1861,7 +1905,10 @@ lpfc_mbx_process_link_up(struct lpfc_hba *phba, READ_LA_VAR *la)
if (phba->fc_topology == TOPOLOGY_LOOP) {
phba->sli3_options &= ~LPFC_SLI3_NPIV_ENABLED;
- if (phba->cfg_enable_npiv)
+ /* if npiv is enabled and this adapter supports npiv log
+ * a message that npiv is not supported in this topology
+ */
+ if (phba->cfg_enable_npiv && phba->max_vpi)
lpfc_printf_log(phba, KERN_ERR, LOG_LINK_EVENT,
"1309 Link Up Event npiv not supported in loop "
"topology\n");
@@ -1955,7 +2002,7 @@ lpfc_mbx_process_link_up(struct lpfc_hba *phba, READ_LA_VAR *la)
* is phase 1 implementation that support FCF index 0 and driver
* defaults.
*/
- if (phba->cfg_enable_fip == 0) {
+ if (!(phba->hba_flag & HBA_FIP_SUPPORT)) {
fcf_record = kzalloc(sizeof(struct fcf_record),
GFP_KERNEL);
if (unlikely(!fcf_record)) {
@@ -2085,6 +2132,7 @@ lpfc_mbx_cmpl_read_la(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmb)
else
phba->sli.sli_flag &= ~LPFC_MENLO_MAINT;
+ phba->link_events++;
if (la->attType == AT_LINK_UP && (!la->mm)) {
phba->fc_stat.LinkUp++;
if (phba->link_flag & LS_LOOPBACK_MODE) {
@@ -2211,13 +2259,14 @@ lpfc_mbx_cmpl_unreg_vpi(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmb)
mb->mbxStatus);
break;
}
+ vport->vpi_state &= ~LPFC_VPI_REGISTERED;
vport->unreg_vpi_cmpl = VPORT_OK;
mempool_free(pmb, phba->mbox_mem_pool);
/*
* This shost reference might have been taken at the beginning of
* lpfc_vport_delete()
*/
- if (vport->load_flag & FC_UNLOADING)
+ if ((vport->load_flag & FC_UNLOADING) && (vport != phba->pport))
scsi_host_put(shost);
}
@@ -2268,6 +2317,7 @@ lpfc_mbx_cmpl_reg_vpi(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmb)
goto out;
}
+ vport->vpi_state |= LPFC_VPI_REGISTERED;
vport->num_disc_nodes = 0;
/* go thru NPR list and issue ELS PLOGIs */
if (vport->fc_npr_cnt)
@@ -3077,7 +3127,7 @@ lpfc_no_rpi(struct lpfc_hba *phba, struct lpfc_nodelist *ndlp)
struct lpfc_sli *psli;
struct lpfc_sli_ring *pring;
struct lpfc_iocbq *iocb, *next_iocb;
- uint32_t rpi, i;
+ uint32_t i;
lpfc_fabric_abort_nport(ndlp);
@@ -3086,7 +3136,6 @@ lpfc_no_rpi(struct lpfc_hba *phba, struct lpfc_nodelist *ndlp)
* by firmware with a no rpi error.
*/
psli = &phba->sli;
- rpi = ndlp->nlp_rpi;
if (ndlp->nlp_flag & NLP_RPI_VALID) {
/* Now process each ring */
for (i = 0; i < psli->num_rings; i++) {
@@ -4322,6 +4371,14 @@ lpfc_fcf_inuse(struct lpfc_hba *phba)
ret = 1;
spin_unlock_irq(shost->host_lock);
goto out;
+ } else {
+ lpfc_printf_log(phba, KERN_INFO, LOG_ELS,
+ "2624 RPI %x DID %x flg %x still "
+ "logged in\n",
+ ndlp->nlp_rpi, ndlp->nlp_DID,
+ ndlp->nlp_flag);
+ if (ndlp->nlp_flag & NLP_RPI_VALID)
+ ret = 1;
}
}
spin_unlock_irq(shost->host_lock);
@@ -4400,7 +4457,7 @@ lpfc_unregister_unused_fcf(struct lpfc_hba *phba)
*/
if (!(phba->hba_flag & HBA_FCOE_SUPPORT) ||
!(phba->fcf.fcf_flag & FCF_REGISTERED) ||
- (phba->cfg_enable_fip == 0)) {
+ (!(phba->hba_flag & HBA_FIP_SUPPORT))) {
spin_unlock_irq(&phba->hbalock);
return;
}
@@ -4409,6 +4466,8 @@ lpfc_unregister_unused_fcf(struct lpfc_hba *phba)
if (lpfc_fcf_inuse(phba))
return;
+ /* At this point, all discovery is aborted */
+ phba->pport->port_state = LPFC_VPORT_UNKNOWN;
/* Unregister VPIs */
vports = lpfc_create_vport_work_array(phba);
@@ -4416,8 +4475,8 @@ lpfc_unregister_unused_fcf(struct lpfc_hba *phba)
(phba->sli3_options & LPFC_SLI3_NPIV_ENABLED))
for (i = 0; i <= phba->max_vports && vports[i] != NULL; i++) {
lpfc_mbx_unreg_vpi(vports[i]);
- vports[i]->fc_flag |= FC_VPORT_NEEDS_REG_VPI;
- vports[i]->vfi_state &= ~LPFC_VFI_REGISTERED;
+ vports[i]->fc_flag |= FC_VPORT_NEEDS_INIT_VPI;
+ vports[i]->vpi_state &= ~LPFC_VPI_REGISTERED;
}
lpfc_destroy_vport_work_array(phba, vports);
@@ -4431,7 +4490,7 @@ lpfc_unregister_unused_fcf(struct lpfc_hba *phba)
return;
}
- lpfc_unreg_vfi(mbox, phba->pport->vfi);
+ lpfc_unreg_vfi(mbox, phba->pport);
mbox->vport = phba->pport;
mbox->mbox_cmpl = lpfc_unregister_vfi_cmpl;
@@ -4512,8 +4571,10 @@ lpfc_read_fcf_conn_tbl(struct lpfc_hba *phba,
/* Free the current connect table */
list_for_each_entry_safe(conn_entry, next_conn_entry,
- &phba->fcf_conn_rec_list, list)
+ &phba->fcf_conn_rec_list, list) {
+ list_del_init(&conn_entry->list);
kfree(conn_entry);
+ }
conn_hdr = (struct lpfc_fcf_conn_hdr *) buff;
record_count = conn_hdr->length * sizeof(uint32_t)/
@@ -4569,14 +4630,6 @@ lpfc_read_fcoe_param(struct lpfc_hba *phba,
(fcoe_param_hdr->length != FCOE_PARAM_LENGTH))
return;
- if (bf_get(lpfc_fip_param_hdr_fipp_mode, fcoe_param_hdr) ==
- FIPP_MODE_ON)
- phba->cfg_enable_fip = 1;
-
- if (bf_get(lpfc_fip_param_hdr_fipp_mode, fcoe_param_hdr) ==
- FIPP_MODE_OFF)
- phba->cfg_enable_fip = 0;
-
if (fcoe_param_hdr->parm_flags & FIPP_VLAN_VALID) {
phba->valid_vlan = 1;
phba->vlan_id = le16_to_cpu(fcoe_param->vlan_tag) &
diff --git a/drivers/scsi/lpfc/lpfc_hw.h b/drivers/scsi/lpfc/lpfc_hw.h
index ccb26724dc5..c9faa1d8c3c 100644
--- a/drivers/scsi/lpfc/lpfc_hw.h
+++ b/drivers/scsi/lpfc/lpfc_hw.h
@@ -1124,21 +1124,6 @@ typedef struct {
/* Number of 4-byte words in an IOCB. */
#define IOCB_WORD_SZ 8
-/* defines for type field in fc header */
-#define FC_ELS_DATA 0x1
-#define FC_LLC_SNAP 0x5
-#define FC_FCP_DATA 0x8
-#define FC_COMMON_TRANSPORT_ULP 0x20
-
-/* defines for rctl field in fc header */
-#define FC_DEV_DATA 0x0
-#define FC_UNSOL_CTL 0x2
-#define FC_SOL_CTL 0x3
-#define FC_UNSOL_DATA 0x4
-#define FC_FCP_CMND 0x6
-#define FC_ELS_REQ 0x22
-#define FC_ELS_RSP 0x23
-
/* network headers for Dfctl field */
#define FC_NET_HDR 0x20
@@ -1183,6 +1168,8 @@ typedef struct {
#define PCI_DEVICE_ID_ZEPHYR_DCSP 0xfe12
#define PCI_VENDOR_ID_SERVERENGINE 0x19a2
#define PCI_DEVICE_ID_TIGERSHARK 0x0704
+#define PCI_DEVICE_ID_TOMCAT 0x0714
+#define PCI_DEVICE_ID_FALCON 0xf180
#define JEDEC_ID_ADDRESS 0x0080001c
#define FIREFLY_JEDEC_ID 0x1ACC
@@ -1444,6 +1431,7 @@ typedef struct { /* FireFly BIU registers */
#define CMD_ABORT_MXRI64_CN 0x8C
#define CMD_RCV_ELS_REQ64_CX 0x8D
#define CMD_XMIT_ELS_RSP64_CX 0x95
+#define CMD_XMIT_BLS_RSP64_CX 0x97
#define CMD_FCP_IWRITE64_CR 0x98
#define CMD_FCP_IWRITE64_CX 0x99
#define CMD_FCP_IREAD64_CR 0x9A
@@ -2306,8 +2294,7 @@ typedef struct {
uint32_t rsvd1;
uint32_t rsvd2:8;
uint32_t sid:24;
- uint32_t rsvd3;
- uint32_t rsvd4;
+ uint32_t wwn[2];
uint32_t rsvd5;
uint16_t vfi;
uint16_t vpi;
@@ -2315,8 +2302,7 @@ typedef struct {
uint32_t rsvd1;
uint32_t sid:24;
uint32_t rsvd2:8;
- uint32_t rsvd3;
- uint32_t rsvd4;
+ uint32_t wwn[2];
uint32_t rsvd5;
uint16_t vpi;
uint16_t vfi;
@@ -2326,7 +2312,13 @@ typedef struct {
/* Structure for MB Command UNREG_VPI (0x97) */
typedef struct {
uint32_t rsvd1;
- uint32_t rsvd2;
+#ifdef __BIG_ENDIAN_BITFIELD
+ uint16_t rsvd2;
+ uint16_t sli4_vpi;
+#else /* __LITTLE_ENDIAN */
+ uint16_t sli4_vpi;
+ uint16_t rsvd2;
+#endif
uint32_t rsvd3;
uint32_t rsvd4;
uint32_t rsvd5;
@@ -3547,7 +3539,7 @@ typedef struct _IOCB { /* IOCB structure */
ASYNCSTAT_FIELDS asyncstat; /* async_status iocb */
QUE_XRI64_CX_FIELDS quexri64cx; /* que_xri64_cx fields */
struct rcv_seq64 rcvseq64; /* RCV_SEQ64 and RCV_CONT64 */
-
+ struct sli4_bls_acc bls_acc; /* UNSOL ABTS BLS_ACC params */
uint32_t ulpWord[IOCB_WORD_SZ - 2]; /* generic 6 'words' */
} un;
union {
diff --git a/drivers/scsi/lpfc/lpfc_hw4.h b/drivers/scsi/lpfc/lpfc_hw4.h
index 3689eee0453..1585148a17e 100644
--- a/drivers/scsi/lpfc/lpfc_hw4.h
+++ b/drivers/scsi/lpfc/lpfc_hw4.h
@@ -194,6 +194,26 @@ struct lpfc_sli4_flags {
#define lpfc_fip_flag_WORD word0
};
+struct sli4_bls_acc {
+ uint32_t word0_rsvd; /* Word0 must be reserved */
+ uint32_t word1;
+#define lpfc_abts_orig_SHIFT 0
+#define lpfc_abts_orig_MASK 0x00000001
+#define lpfc_abts_orig_WORD word1
+#define LPFC_ABTS_UNSOL_RSP 1
+#define LPFC_ABTS_UNSOL_INT 0
+ uint32_t word2;
+#define lpfc_abts_rxid_SHIFT 0
+#define lpfc_abts_rxid_MASK 0x0000FFFF
+#define lpfc_abts_rxid_WORD word2
+#define lpfc_abts_oxid_SHIFT 16
+#define lpfc_abts_oxid_MASK 0x0000FFFF
+#define lpfc_abts_oxid_WORD word2
+ uint32_t word3;
+ uint32_t word4;
+ uint32_t word5_rsvd; /* Word5 must be reserved */
+};
+
/* event queue entry structure */
struct lpfc_eqe {
uint32_t word0;
@@ -425,7 +445,7 @@ struct lpfc_wqe_generic{
#define lpfc_wqe_gen_status_MASK 0x0000000F
#define lpfc_wqe_gen_status_WORD word7
#define lpfc_wqe_gen_ct_SHIFT 2
-#define lpfc_wqe_gen_ct_MASK 0x00000007
+#define lpfc_wqe_gen_ct_MASK 0x00000003
#define lpfc_wqe_gen_ct_WORD word7
uint32_t abort_tag;
uint32_t word9;
@@ -453,6 +473,13 @@ struct lpfc_wqe_generic{
#define lpfc_wqe_gen_wqec_SHIFT 7
#define lpfc_wqe_gen_wqec_MASK 0x00000001
#define lpfc_wqe_gen_wqec_WORD word11
+#define ELS_ID_FLOGI 3
+#define ELS_ID_FDISC 2
+#define ELS_ID_LOGO 1
+#define ELS_ID_DEFAULT 0
+#define lpfc_wqe_gen_els_id_SHIFT 4
+#define lpfc_wqe_gen_els_id_MASK 0x00000003
+#define lpfc_wqe_gen_els_id_WORD word11
#define lpfc_wqe_gen_cmd_type_SHIFT 0
#define lpfc_wqe_gen_cmd_type_MASK 0x0000000F
#define lpfc_wqe_gen_cmd_type_WORD word11
@@ -487,8 +514,8 @@ struct lpfc_register {
#define LPFC_UERR_STATUS_HI 0x00A4
#define LPFC_UERR_STATUS_LO 0x00A0
-#define LPFC_ONLINE0 0x00B0
-#define LPFC_ONLINE1 0x00B4
+#define LPFC_UE_MASK_HI 0x00AC
+#define LPFC_UE_MASK_LO 0x00A8
#define LPFC_SCRATCHPAD 0x0058
/* BAR0 Registers */
@@ -760,6 +787,7 @@ struct mbox_header {
#define LPFC_MBOX_OPCODE_MQ_DESTROY 0x35
#define LPFC_MBOX_OPCODE_CQ_DESTROY 0x36
#define LPFC_MBOX_OPCODE_EQ_DESTROY 0x37
+#define LPFC_MBOX_OPCODE_QUERY_FW_CFG 0x3A
#define LPFC_MBOX_OPCODE_FUNCTION_RESET 0x3D
/* FCoE Opcodes */
@@ -1273,6 +1301,51 @@ struct lpfc_mbx_del_fcf_tbl_entry {
#define lpfc_mbx_del_fcf_tbl_index_WORD word10
};
+struct lpfc_mbx_query_fw_cfg {
+ struct mbox_header header;
+ uint32_t config_number;
+ uint32_t asic_rev;
+ uint32_t phys_port;
+ uint32_t function_mode;
+/* firmware Function Mode */
+#define lpfc_function_mode_toe_SHIFT 0
+#define lpfc_function_mode_toe_MASK 0x00000001
+#define lpfc_function_mode_toe_WORD function_mode
+#define lpfc_function_mode_nic_SHIFT 1
+#define lpfc_function_mode_nic_MASK 0x00000001
+#define lpfc_function_mode_nic_WORD function_mode
+#define lpfc_function_mode_rdma_SHIFT 2
+#define lpfc_function_mode_rdma_MASK 0x00000001
+#define lpfc_function_mode_rdma_WORD function_mode
+#define lpfc_function_mode_vm_SHIFT 3
+#define lpfc_function_mode_vm_MASK 0x00000001
+#define lpfc_function_mode_vm_WORD function_mode
+#define lpfc_function_mode_iscsi_i_SHIFT 4
+#define lpfc_function_mode_iscsi_i_MASK 0x00000001
+#define lpfc_function_mode_iscsi_i_WORD function_mode
+#define lpfc_function_mode_iscsi_t_SHIFT 5
+#define lpfc_function_mode_iscsi_t_MASK 0x00000001
+#define lpfc_function_mode_iscsi_t_WORD function_mode
+#define lpfc_function_mode_fcoe_i_SHIFT 6
+#define lpfc_function_mode_fcoe_i_MASK 0x00000001
+#define lpfc_function_mode_fcoe_i_WORD function_mode
+#define lpfc_function_mode_fcoe_t_SHIFT 7
+#define lpfc_function_mode_fcoe_t_MASK 0x00000001
+#define lpfc_function_mode_fcoe_t_WORD function_mode
+#define lpfc_function_mode_dal_SHIFT 8
+#define lpfc_function_mode_dal_MASK 0x00000001
+#define lpfc_function_mode_dal_WORD function_mode
+#define lpfc_function_mode_lro_SHIFT 9
+#define lpfc_function_mode_lro_MASK 0x00000001
+#define lpfc_function_mode_lro_WORD function_mode9
+#define lpfc_function_mode_flex10_SHIFT 10
+#define lpfc_function_mode_flex10_MASK 0x00000001
+#define lpfc_function_mode_flex10_WORD function_mode
+#define lpfc_function_mode_ncsi_SHIFT 11
+#define lpfc_function_mode_ncsi_MASK 0x00000001
+#define lpfc_function_mode_ncsi_WORD function_mode
+};
+
/* Status field for embedded SLI_CONFIG mailbox command */
#define STATUS_SUCCESS 0x0
#define STATUS_FAILED 0x1
@@ -1349,8 +1422,7 @@ struct lpfc_mbx_reg_vfi {
#define lpfc_reg_vfi_fcfi_SHIFT 0
#define lpfc_reg_vfi_fcfi_MASK 0x0000FFFF
#define lpfc_reg_vfi_fcfi_WORD word2
- uint32_t word3_rsvd;
- uint32_t word4_rsvd;
+ uint32_t wwn[2];
struct ulp_bde64 bde;
uint32_t word8_rsvd;
uint32_t word9_rsvd;
@@ -1555,6 +1627,11 @@ struct lpfc_mbx_read_rev {
#define lpfc_mbx_rd_rev_fcoe_SHIFT 20
#define lpfc_mbx_rd_rev_fcoe_MASK 0x00000001
#define lpfc_mbx_rd_rev_fcoe_WORD word1
+#define lpfc_mbx_rd_rev_cee_ver_SHIFT 21
+#define lpfc_mbx_rd_rev_cee_ver_MASK 0x00000003
+#define lpfc_mbx_rd_rev_cee_ver_WORD word1
+#define LPFC_PREDCBX_CEE_MODE 0
+#define LPFC_DCBX_CEE_MODE 1
#define lpfc_mbx_rd_rev_vpd_SHIFT 29
#define lpfc_mbx_rd_rev_vpd_MASK 0x00000001
#define lpfc_mbx_rd_rev_vpd_WORD word1
@@ -1804,6 +1881,7 @@ struct lpfc_mqe {
struct lpfc_mbx_read_config rd_config;
struct lpfc_mbx_request_features req_ftrs;
struct lpfc_mbx_post_hdr_tmpl hdr_tmpl;
+ struct lpfc_mbx_query_fw_cfg query_fw_cfg;
struct lpfc_mbx_nop nop;
} un;
};
@@ -1885,7 +1963,7 @@ struct lpfc_acqe_link {
};
struct lpfc_acqe_fcoe {
- uint32_t fcf_index;
+ uint32_t index;
uint32_t word1;
#define lpfc_acqe_fcoe_fcf_count_SHIFT 0
#define lpfc_acqe_fcoe_fcf_count_MASK 0x0000FFFF
@@ -1896,6 +1974,7 @@ struct lpfc_acqe_fcoe {
#define LPFC_FCOE_EVENT_TYPE_NEW_FCF 0x1
#define LPFC_FCOE_EVENT_TYPE_FCF_TABLE_FULL 0x2
#define LPFC_FCOE_EVENT_TYPE_FCF_DEAD 0x3
+#define LPFC_FCOE_EVENT_TYPE_CVL 0x4
uint32_t event_tag;
uint32_t trailer;
};
@@ -1921,12 +2000,13 @@ struct lpfc_bmbx_create {
#define SGL_ALIGN_SZ 64
#define SGL_PAGE_SIZE 4096
/* align SGL addr on a size boundary - adjust address up */
-#define NO_XRI ((uint16_t)-1)
+#define NO_XRI ((uint16_t)-1)
+
struct wqe_common {
uint32_t word6;
-#define wqe_xri_SHIFT 0
-#define wqe_xri_MASK 0x0000FFFF
-#define wqe_xri_WORD word6
+#define wqe_xri_tag_SHIFT 0
+#define wqe_xri_tag_MASK 0x0000FFFF
+#define wqe_xri_tag_WORD word6
#define wqe_ctxt_tag_SHIFT 16
#define wqe_ctxt_tag_MASK 0x0000FFFF
#define wqe_ctxt_tag_WORD word6
@@ -1987,7 +2067,7 @@ struct wqe_common {
#define wqe_wqec_MASK 0x00000001
#define wqe_wqec_WORD word11
#define wqe_cqid_SHIFT 16
-#define wqe_cqid_MASK 0x000003ff
+#define wqe_cqid_MASK 0x0000ffff
#define wqe_cqid_WORD word11
};
@@ -1996,6 +2076,9 @@ struct wqe_did {
#define wqe_els_did_SHIFT 0
#define wqe_els_did_MASK 0x00FFFFFF
#define wqe_els_did_WORD word5
+#define wqe_xmit_bls_pt_SHIFT 28
+#define wqe_xmit_bls_pt_MASK 0x00000003
+#define wqe_xmit_bls_pt_WORD word5
#define wqe_xmit_bls_ar_SHIFT 30
#define wqe_xmit_bls_ar_MASK 0x00000001
#define wqe_xmit_bls_ar_WORD word5
@@ -2044,6 +2127,23 @@ struct xmit_els_rsp64_wqe {
struct xmit_bls_rsp64_wqe {
uint32_t payload0;
+/* Payload0 for BA_ACC */
+#define xmit_bls_rsp64_acc_seq_id_SHIFT 16
+#define xmit_bls_rsp64_acc_seq_id_MASK 0x000000ff
+#define xmit_bls_rsp64_acc_seq_id_WORD payload0
+#define xmit_bls_rsp64_acc_seq_id_vald_SHIFT 24
+#define xmit_bls_rsp64_acc_seq_id_vald_MASK 0x000000ff
+#define xmit_bls_rsp64_acc_seq_id_vald_WORD payload0
+/* Payload0 for BA_RJT */
+#define xmit_bls_rsp64_rjt_vspec_SHIFT 0
+#define xmit_bls_rsp64_rjt_vspec_MASK 0x000000ff
+#define xmit_bls_rsp64_rjt_vspec_WORD payload0
+#define xmit_bls_rsp64_rjt_expc_SHIFT 8
+#define xmit_bls_rsp64_rjt_expc_MASK 0x000000ff
+#define xmit_bls_rsp64_rjt_expc_WORD payload0
+#define xmit_bls_rsp64_rjt_rsnc_SHIFT 16
+#define xmit_bls_rsp64_rjt_rsnc_MASK 0x000000ff
+#define xmit_bls_rsp64_rjt_rsnc_WORD payload0
uint32_t word1;
#define xmit_bls_rsp64_rxid_SHIFT 0
#define xmit_bls_rsp64_rxid_MASK 0x0000ffff
@@ -2052,18 +2152,19 @@ struct xmit_bls_rsp64_wqe {
#define xmit_bls_rsp64_oxid_MASK 0x0000ffff
#define xmit_bls_rsp64_oxid_WORD word1
uint32_t word2;
-#define xmit_bls_rsp64_seqcntlo_SHIFT 0
-#define xmit_bls_rsp64_seqcntlo_MASK 0x0000ffff
-#define xmit_bls_rsp64_seqcntlo_WORD word2
-#define xmit_bls_rsp64_seqcnthi_SHIFT 16
+#define xmit_bls_rsp64_seqcnthi_SHIFT 0
#define xmit_bls_rsp64_seqcnthi_MASK 0x0000ffff
#define xmit_bls_rsp64_seqcnthi_WORD word2
+#define xmit_bls_rsp64_seqcntlo_SHIFT 16
+#define xmit_bls_rsp64_seqcntlo_MASK 0x0000ffff
+#define xmit_bls_rsp64_seqcntlo_WORD word2
uint32_t rsrvd3;
uint32_t rsrvd4;
struct wqe_did wqe_dest;
struct wqe_common wqe_com; /* words 6-11 */
uint32_t rsvd_12_15[4];
};
+
struct wqe_rctl_dfctl {
uint32_t word5;
#define wqe_si_SHIFT 2
diff --git a/drivers/scsi/lpfc/lpfc_init.c b/drivers/scsi/lpfc/lpfc_init.c
index 82f8ab5c72c..226920d15ea 100644
--- a/drivers/scsi/lpfc/lpfc_init.c
+++ b/drivers/scsi/lpfc/lpfc_init.c
@@ -28,6 +28,7 @@
#include <linux/pci.h>
#include <linux/spinlock.h>
#include <linux/ctype.h>
+#include <linux/aer.h>
#include <scsi/scsi.h>
#include <scsi/scsi_device.h>
@@ -852,12 +853,19 @@ lpfc_hb_mbox_cmpl(struct lpfc_hba * phba, LPFC_MBOXQ_t * pmboxq)
void
lpfc_hb_timeout_handler(struct lpfc_hba *phba)
{
+ struct lpfc_vport **vports;
LPFC_MBOXQ_t *pmboxq;
struct lpfc_dmabuf *buf_ptr;
- int retval;
+ int retval, i;
struct lpfc_sli *psli = &phba->sli;
LIST_HEAD(completions);
+ vports = lpfc_create_vport_work_array(phba);
+ if (vports != NULL)
+ for (i = 0; i <= phba->max_vports && vports[i] != NULL; i++)
+ lpfc_rcv_seq_check_edtov(vports[i]);
+ lpfc_destroy_vport_work_array(phba, vports);
+
if ((phba->link_state == LPFC_HBA_ERROR) ||
(phba->pport->load_flag & FC_UNLOADING) ||
(phba->pport->fc_flag & FC_OFFLINE_MODE))
@@ -1521,10 +1529,10 @@ lpfc_get_hba_model_desc(struct lpfc_hba *phba, uint8_t *mdp, uint8_t *descp)
int GE = 0;
int oneConnect = 0; /* default is not a oneConnect */
struct {
- char * name;
- int max_speed;
- char * bus;
- } m = {"<Unknown>", 0, ""};
+ char *name;
+ char *bus;
+ char *function;
+ } m = {"<Unknown>", "", ""};
if (mdp && mdp[0] != '\0'
&& descp && descp[0] != '\0')
@@ -1545,132 +1553,155 @@ lpfc_get_hba_model_desc(struct lpfc_hba *phba, uint8_t *mdp, uint8_t *descp)
switch (dev_id) {
case PCI_DEVICE_ID_FIREFLY:
- m = (typeof(m)){"LP6000", max_speed, "PCI"};
+ m = (typeof(m)){"LP6000", "PCI", "Fibre Channel Adapter"};
break;
case PCI_DEVICE_ID_SUPERFLY:
if (vp->rev.biuRev >= 1 && vp->rev.biuRev <= 3)
- m = (typeof(m)){"LP7000", max_speed, "PCI"};
+ m = (typeof(m)){"LP7000", "PCI",
+ "Fibre Channel Adapter"};
else
- m = (typeof(m)){"LP7000E", max_speed, "PCI"};
+ m = (typeof(m)){"LP7000E", "PCI",
+ "Fibre Channel Adapter"};
break;
case PCI_DEVICE_ID_DRAGONFLY:
- m = (typeof(m)){"LP8000", max_speed, "PCI"};
+ m = (typeof(m)){"LP8000", "PCI",
+ "Fibre Channel Adapter"};
break;
case PCI_DEVICE_ID_CENTAUR:
if (FC_JEDEC_ID(vp->rev.biuRev) == CENTAUR_2G_JEDEC_ID)
- m = (typeof(m)){"LP9002", max_speed, "PCI"};
+ m = (typeof(m)){"LP9002", "PCI",
+ "Fibre Channel Adapter"};
else
- m = (typeof(m)){"LP9000", max_speed, "PCI"};
+ m = (typeof(m)){"LP9000", "PCI",
+ "Fibre Channel Adapter"};
break;
case PCI_DEVICE_ID_RFLY:
- m = (typeof(m)){"LP952", max_speed, "PCI"};
+ m = (typeof(m)){"LP952", "PCI",
+ "Fibre Channel Adapter"};
break;
case PCI_DEVICE_ID_PEGASUS:
- m = (typeof(m)){"LP9802", max_speed, "PCI-X"};
+ m = (typeof(m)){"LP9802", "PCI-X",
+ "Fibre Channel Adapter"};
break;
case PCI_DEVICE_ID_THOR:
- m = (typeof(m)){"LP10000", max_speed, "PCI-X"};
+ m = (typeof(m)){"LP10000", "PCI-X",
+ "Fibre Channel Adapter"};
break;
case PCI_DEVICE_ID_VIPER:
- m = (typeof(m)){"LPX1000", max_speed, "PCI-X"};
+ m = (typeof(m)){"LPX1000", "PCI-X",
+ "Fibre Channel Adapter"};
break;
case PCI_DEVICE_ID_PFLY:
- m = (typeof(m)){"LP982", max_speed, "PCI-X"};
+ m = (typeof(m)){"LP982", "PCI-X",
+ "Fibre Channel Adapter"};
break;
case PCI_DEVICE_ID_TFLY:
- m = (typeof(m)){"LP1050", max_speed, "PCI-X"};
+ m = (typeof(m)){"LP1050", "PCI-X",
+ "Fibre Channel Adapter"};
break;
case PCI_DEVICE_ID_HELIOS:
- m = (typeof(m)){"LP11000", max_speed, "PCI-X2"};
+ m = (typeof(m)){"LP11000", "PCI-X2",
+ "Fibre Channel Adapter"};
break;
case PCI_DEVICE_ID_HELIOS_SCSP:
- m = (typeof(m)){"LP11000-SP", max_speed, "PCI-X2"};
+ m = (typeof(m)){"LP11000-SP", "PCI-X2",
+ "Fibre Channel Adapter"};
break;
case PCI_DEVICE_ID_HELIOS_DCSP:
- m = (typeof(m)){"LP11002-SP", max_speed, "PCI-X2"};
+ m = (typeof(m)){"LP11002-SP", "PCI-X2",
+ "Fibre Channel Adapter"};
break;
case PCI_DEVICE_ID_NEPTUNE:
- m = (typeof(m)){"LPe1000", max_speed, "PCIe"};
+ m = (typeof(m)){"LPe1000", "PCIe", "Fibre Channel Adapter"};
break;
case PCI_DEVICE_ID_NEPTUNE_SCSP:
- m = (typeof(m)){"LPe1000-SP", max_speed, "PCIe"};
+ m = (typeof(m)){"LPe1000-SP", "PCIe", "Fibre Channel Adapter"};
break;
case PCI_DEVICE_ID_NEPTUNE_DCSP:
- m = (typeof(m)){"LPe1002-SP", max_speed, "PCIe"};
+ m = (typeof(m)){"LPe1002-SP", "PCIe", "Fibre Channel Adapter"};
break;
case PCI_DEVICE_ID_BMID:
- m = (typeof(m)){"LP1150", max_speed, "PCI-X2"};
+ m = (typeof(m)){"LP1150", "PCI-X2", "Fibre Channel Adapter"};
break;
case PCI_DEVICE_ID_BSMB:
- m = (typeof(m)){"LP111", max_speed, "PCI-X2"};
+ m = (typeof(m)){"LP111", "PCI-X2", "Fibre Channel Adapter"};
break;
case PCI_DEVICE_ID_ZEPHYR:
- m = (typeof(m)){"LPe11000", max_speed, "PCIe"};
+ m = (typeof(m)){"LPe11000", "PCIe", "Fibre Channel Adapter"};
break;
case PCI_DEVICE_ID_ZEPHYR_SCSP:
- m = (typeof(m)){"LPe11000", max_speed, "PCIe"};
+ m = (typeof(m)){"LPe11000", "PCIe", "Fibre Channel Adapter"};
break;
case PCI_DEVICE_ID_ZEPHYR_DCSP:
- m = (typeof(m)){"LP2105", max_speed, "PCIe"};
+ m = (typeof(m)){"LP2105", "PCIe", "FCoE Adapter"};
GE = 1;
break;
case PCI_DEVICE_ID_ZMID:
- m = (typeof(m)){"LPe1150", max_speed, "PCIe"};
+ m = (typeof(m)){"LPe1150", "PCIe", "Fibre Channel Adapter"};
break;
case PCI_DEVICE_ID_ZSMB:
- m = (typeof(m)){"LPe111", max_speed, "PCIe"};
+ m = (typeof(m)){"LPe111", "PCIe", "Fibre Channel Adapter"};
break;
case PCI_DEVICE_ID_LP101:
- m = (typeof(m)){"LP101", max_speed, "PCI-X"};
+ m = (typeof(m)){"LP101", "PCI-X", "Fibre Channel Adapter"};
break;
case PCI_DEVICE_ID_LP10000S:
- m = (typeof(m)){"LP10000-S", max_speed, "PCI"};
+ m = (typeof(m)){"LP10000-S", "PCI", "Fibre Channel Adapter"};
break;
case PCI_DEVICE_ID_LP11000S:
- m = (typeof(m)){"LP11000-S", max_speed,
- "PCI-X2"};
+ m = (typeof(m)){"LP11000-S", "PCI-X2", "Fibre Channel Adapter"};
break;
case PCI_DEVICE_ID_LPE11000S:
- m = (typeof(m)){"LPe11000-S", max_speed,
- "PCIe"};
+ m = (typeof(m)){"LPe11000-S", "PCIe", "Fibre Channel Adapter"};
break;
case PCI_DEVICE_ID_SAT:
- m = (typeof(m)){"LPe12000", max_speed, "PCIe"};
+ m = (typeof(m)){"LPe12000", "PCIe", "Fibre Channel Adapter"};
break;
case PCI_DEVICE_ID_SAT_MID:
- m = (typeof(m)){"LPe1250", max_speed, "PCIe"};
+ m = (typeof(m)){"LPe1250", "PCIe", "Fibre Channel Adapter"};
break;
case PCI_DEVICE_ID_SAT_SMB:
- m = (typeof(m)){"LPe121", max_speed, "PCIe"};
+ m = (typeof(m)){"LPe121", "PCIe", "Fibre Channel Adapter"};
break;
case PCI_DEVICE_ID_SAT_DCSP:
- m = (typeof(m)){"LPe12002-SP", max_speed, "PCIe"};
+ m = (typeof(m)){"LPe12002-SP", "PCIe", "Fibre Channel Adapter"};
break;
case PCI_DEVICE_ID_SAT_SCSP:
- m = (typeof(m)){"LPe12000-SP", max_speed, "PCIe"};
+ m = (typeof(m)){"LPe12000-SP", "PCIe", "Fibre Channel Adapter"};
break;
case PCI_DEVICE_ID_SAT_S:
- m = (typeof(m)){"LPe12000-S", max_speed, "PCIe"};
+ m = (typeof(m)){"LPe12000-S", "PCIe", "Fibre Channel Adapter"};
break;
case PCI_DEVICE_ID_HORNET:
- m = (typeof(m)){"LP21000", max_speed, "PCIe"};
+ m = (typeof(m)){"LP21000", "PCIe", "FCoE Adapter"};
GE = 1;
break;
case PCI_DEVICE_ID_PROTEUS_VF:
- m = (typeof(m)) {"LPev12000", max_speed, "PCIe IOV"};
+ m = (typeof(m)){"LPev12000", "PCIe IOV",
+ "Fibre Channel Adapter"};
break;
case PCI_DEVICE_ID_PROTEUS_PF:
- m = (typeof(m)) {"LPev12000", max_speed, "PCIe IOV"};
+ m = (typeof(m)){"LPev12000", "PCIe IOV",
+ "Fibre Channel Adapter"};
break;
case PCI_DEVICE_ID_PROTEUS_S:
- m = (typeof(m)) {"LPemv12002-S", max_speed, "PCIe IOV"};
+ m = (typeof(m)){"LPemv12002-S", "PCIe IOV",
+ "Fibre Channel Adapter"};
break;
case PCI_DEVICE_ID_TIGERSHARK:
oneConnect = 1;
- m = (typeof(m)) {"OCe10100-F", max_speed, "PCIe"};
+ m = (typeof(m)){"OCe10100", "PCIe", "FCoE"};
+ break;
+ case PCI_DEVICE_ID_TOMCAT:
+ oneConnect = 1;
+ m = (typeof(m)){"OCe11100", "PCIe", "FCoE"};
+ break;
+ case PCI_DEVICE_ID_FALCON:
+ m = (typeof(m)){"LPSe12002-ML1-E", "PCIe",
+ "EmulexSecure Fibre"};
break;
default:
- m = (typeof(m)){ NULL };
+ m = (typeof(m)){"Unknown", "", ""};
break;
}
@@ -1682,17 +1713,14 @@ lpfc_get_hba_model_desc(struct lpfc_hba *phba, uint8_t *mdp, uint8_t *descp)
if (descp && descp[0] == '\0') {
if (oneConnect)
snprintf(descp, 255,
- "Emulex OneConnect %s, FCoE Initiator, Port %s",
- m.name,
+ "Emulex OneConnect %s, %s Initiator, Port %s",
+ m.name, m.function,
phba->Port);
else
snprintf(descp, 255,
"Emulex %s %d%s %s %s",
- m.name, m.max_speed,
- (GE) ? "GE" : "Gb",
- m.bus,
- (GE) ? "FCoE Adapter" :
- "Fibre Channel Adapter");
+ m.name, max_speed, (GE) ? "GE" : "Gb",
+ m.bus, m.function);
}
}
@@ -2217,7 +2245,7 @@ lpfc_offline_prep(struct lpfc_hba * phba)
if (vports[i]->load_flag & FC_UNLOADING)
continue;
- vports[i]->vfi_state &= ~LPFC_VFI_REGISTERED;
+ vports[i]->vpi_state &= ~LPFC_VPI_REGISTERED;
shost = lpfc_shost_from_vport(vports[i]);
list_for_each_entry_safe(ndlp, next_ndlp,
&vports[i]->fc_nodes,
@@ -2308,6 +2336,7 @@ lpfc_scsi_free(struct lpfc_hba *phba)
spin_lock_irq(&phba->hbalock);
/* Release all the lpfc_scsi_bufs maintained by this host. */
+ spin_lock(&phba->scsi_buf_list_lock);
list_for_each_entry_safe(sb, sb_next, &phba->lpfc_scsi_buf_list, list) {
list_del(&sb->list);
pci_pool_free(phba->lpfc_scsi_dma_buf_pool, sb->data,
@@ -2315,6 +2344,7 @@ lpfc_scsi_free(struct lpfc_hba *phba)
kfree(sb);
phba->total_scsi_bufs--;
}
+ spin_unlock(&phba->scsi_buf_list_lock);
/* Release all the lpfc_iocbq entries maintained by this host. */
list_for_each_entry_safe(io, io_next, &phba->lpfc_iocb_list, list) {
@@ -2322,9 +2352,7 @@ lpfc_scsi_free(struct lpfc_hba *phba)
kfree(io);
phba->total_iocbq_bufs--;
}
-
spin_unlock_irq(&phba->hbalock);
-
return 0;
}
@@ -2408,7 +2436,7 @@ lpfc_create_port(struct lpfc_hba *phba, int instance, struct device *dev)
vport->els_tmofunc.function = lpfc_els_timeout;
vport->els_tmofunc.data = (unsigned long)vport;
- error = scsi_add_host(shost, dev);
+ error = scsi_add_host_with_dma(shost, dev, &phba->pcidev->dev);
if (error)
goto out_put_shost;
@@ -2699,6 +2727,63 @@ lpfc_sli_remove_dflt_fcf(struct lpfc_hba *phba)
}
/**
+ * lpfc_sli4_fw_cfg_check - Read the firmware config and verify FCoE support
+ * @phba: pointer to lpfc hba data structure.
+ *
+ * This function uses the QUERY_FW_CFG mailbox command to determine if the
+ * firmware loaded supports FCoE. A return of zero indicates that the mailbox
+ * was successful and the firmware supports FCoE. Any other return indicates
+ * a error. It is assumed that this function will be called before interrupts
+ * are enabled.
+ **/
+static int
+lpfc_sli4_fw_cfg_check(struct lpfc_hba *phba)
+{
+ int rc = 0;
+ LPFC_MBOXQ_t *mboxq;
+ struct lpfc_mbx_query_fw_cfg *query_fw_cfg;
+ uint32_t length;
+ uint32_t shdr_status, shdr_add_status;
+
+ mboxq = mempool_alloc(phba->mbox_mem_pool, GFP_KERNEL);
+ if (!mboxq) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+ "2621 Failed to allocate mbox for "
+ "query firmware config cmd\n");
+ return -ENOMEM;
+ }
+ query_fw_cfg = &mboxq->u.mqe.un.query_fw_cfg;
+ length = (sizeof(struct lpfc_mbx_query_fw_cfg) -
+ sizeof(struct lpfc_sli4_cfg_mhdr));
+ lpfc_sli4_config(phba, mboxq, LPFC_MBOX_SUBSYSTEM_COMMON,
+ LPFC_MBOX_OPCODE_QUERY_FW_CFG,
+ length, LPFC_SLI4_MBX_EMBED);
+ rc = lpfc_sli_issue_mbox(phba, mboxq, MBX_POLL);
+ /* The IOCTL status is embedded in the mailbox subheader. */
+ shdr_status = bf_get(lpfc_mbox_hdr_status,
+ &query_fw_cfg->header.cfg_shdr.response);
+ shdr_add_status = bf_get(lpfc_mbox_hdr_add_status,
+ &query_fw_cfg->header.cfg_shdr.response);
+ if (shdr_status || shdr_add_status || rc != MBX_SUCCESS) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_SLI,
+ "2622 Query Firmware Config failed "
+ "mbx status x%x, status x%x add_status x%x\n",
+ rc, shdr_status, shdr_add_status);
+ return -EINVAL;
+ }
+ if (!bf_get(lpfc_function_mode_fcoe_i, query_fw_cfg)) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_SLI,
+ "2623 FCoE Function not supported by firmware. "
+ "Function mode = %08x\n",
+ query_fw_cfg->function_mode);
+ return -EINVAL;
+ }
+ if (rc != MBX_TIMEOUT)
+ mempool_free(mboxq, phba->mbox_mem_pool);
+ return 0;
+}
+
+/**
* lpfc_sli4_parse_latt_fault - Parse sli4 link-attention link fault code
* @phba: pointer to lpfc hba data structure.
* @acqe_link: pointer to the async link completion queue entry.
@@ -2918,13 +3003,17 @@ lpfc_sli4_async_fcoe_evt(struct lpfc_hba *phba,
{
uint8_t event_type = bf_get(lpfc_acqe_fcoe_event_type, acqe_fcoe);
int rc;
+ struct lpfc_vport *vport;
+ struct lpfc_nodelist *ndlp;
+ struct Scsi_Host *shost;
+ phba->fc_eventTag = acqe_fcoe->event_tag;
phba->fcoe_eventtag = acqe_fcoe->event_tag;
switch (event_type) {
case LPFC_FCOE_EVENT_TYPE_NEW_FCF:
lpfc_printf_log(phba, KERN_ERR, LOG_DISCOVERY,
"2546 New FCF found index 0x%x tag 0x%x\n",
- acqe_fcoe->fcf_index,
+ acqe_fcoe->index,
acqe_fcoe->event_tag);
/*
* If the current FCF is in discovered state, or
@@ -2939,12 +3028,11 @@ lpfc_sli4_async_fcoe_evt(struct lpfc_hba *phba,
spin_unlock_irq(&phba->hbalock);
/* Read the FCF table and re-discover SAN. */
- rc = lpfc_sli4_read_fcf_record(phba,
- LPFC_FCOE_FCF_GET_FIRST);
+ rc = lpfc_sli4_read_fcf_record(phba, LPFC_FCOE_FCF_GET_FIRST);
if (rc)
lpfc_printf_log(phba, KERN_ERR, LOG_DISCOVERY,
- "2547 Read FCF record failed 0x%x\n",
- rc);
+ "2547 Read FCF record failed 0x%x\n",
+ rc);
break;
case LPFC_FCOE_EVENT_TYPE_FCF_TABLE_FULL:
@@ -2956,11 +3044,11 @@ lpfc_sli4_async_fcoe_evt(struct lpfc_hba *phba,
case LPFC_FCOE_EVENT_TYPE_FCF_DEAD:
lpfc_printf_log(phba, KERN_ERR, LOG_DISCOVERY,
- "2549 FCF disconnected fron network index 0x%x"
- " tag 0x%x\n", acqe_fcoe->fcf_index,
+ "2549 FCF disconnected from network index 0x%x"
+ " tag 0x%x\n", acqe_fcoe->index,
acqe_fcoe->event_tag);
/* If the event is not for currently used fcf do nothing */
- if (phba->fcf.fcf_indx != acqe_fcoe->fcf_index)
+ if (phba->fcf.fcf_indx != acqe_fcoe->index)
break;
/*
* Currently, driver support only one FCF - so treat this as
@@ -2970,7 +3058,28 @@ lpfc_sli4_async_fcoe_evt(struct lpfc_hba *phba,
/* Unregister FCF if no devices connected to it */
lpfc_unregister_unused_fcf(phba);
break;
-
+ case LPFC_FCOE_EVENT_TYPE_CVL:
+ lpfc_printf_log(phba, KERN_ERR, LOG_DISCOVERY,
+ "2718 Clear Virtual Link Received for VPI 0x%x"
+ " tag 0x%x\n", acqe_fcoe->index, acqe_fcoe->event_tag);
+ vport = lpfc_find_vport_by_vpid(phba,
+ acqe_fcoe->index - phba->vpi_base);
+ if (!vport)
+ break;
+ ndlp = lpfc_findnode_did(vport, Fabric_DID);
+ if (!ndlp)
+ break;
+ shost = lpfc_shost_from_vport(vport);
+ lpfc_linkdown_port(vport);
+ if (vport->port_type != LPFC_NPIV_PORT) {
+ mod_timer(&ndlp->nlp_delayfunc, jiffies + HZ);
+ spin_lock_irq(shost->host_lock);
+ ndlp->nlp_flag |= NLP_DELAY_TMO;
+ spin_unlock_irq(shost->host_lock);
+ ndlp->nlp_last_elscmd = ELS_CMD_FLOGI;
+ vport->port_state = LPFC_FLOGI;
+ }
+ break;
default:
lpfc_printf_log(phba, KERN_ERR, LOG_SLI,
"0288 Unknown FCoE event type 0x%x event tag "
@@ -2990,6 +3099,7 @@ static void
lpfc_sli4_async_dcbx_evt(struct lpfc_hba *phba,
struct lpfc_acqe_dcbx *acqe_dcbx)
{
+ phba->fc_eventTag = acqe_dcbx->event_tag;
lpfc_printf_log(phba, KERN_ERR, LOG_SLI,
"0290 The SLI4 DCBX asynchronous event is not "
"handled yet\n");
@@ -3432,7 +3542,7 @@ lpfc_sli4_driver_resource_setup(struct lpfc_hba *phba)
/* Driver internel slow-path CQ Event pool */
INIT_LIST_HEAD(&phba->sli4_hba.sp_cqe_event_pool);
/* Response IOCB work queue list */
- INIT_LIST_HEAD(&phba->sli4_hba.sp_rspiocb_work_queue);
+ INIT_LIST_HEAD(&phba->sli4_hba.sp_queue_event);
/* Asynchronous event CQ Event work queue list */
INIT_LIST_HEAD(&phba->sli4_hba.sp_asynce_work_queue);
/* Fast-path XRI aborted CQ Event work queue list */
@@ -3461,6 +3571,10 @@ lpfc_sli4_driver_resource_setup(struct lpfc_hba *phba)
if (unlikely(rc))
goto out_free_bsmbx;
+ rc = lpfc_sli4_fw_cfg_check(phba);
+ if (unlikely(rc))
+ goto out_free_bsmbx;
+
/* Set up the hba's configuration parameters. */
rc = lpfc_sli4_read_config(phba);
if (unlikely(rc))
@@ -3594,8 +3708,10 @@ lpfc_sli4_driver_resource_unset(struct lpfc_hba *phba)
/* Free the current connect table */
list_for_each_entry_safe(conn_entry, next_conn_entry,
- &phba->fcf_conn_rec_list, list)
+ &phba->fcf_conn_rec_list, list) {
+ list_del_init(&conn_entry->list);
kfree(conn_entry);
+ }
return;
}
@@ -3824,7 +3940,7 @@ lpfc_free_sgl_list(struct lpfc_hba *phba)
rc = lpfc_sli4_remove_all_sgl_pages(phba);
if (rc) {
lpfc_printf_log(phba, KERN_ERR, LOG_SLI,
- "2005 Unable to deregister pages from HBA: %x", rc);
+ "2005 Unable to deregister pages from HBA: %x\n", rc);
}
kfree(phba->sli4_hba.lpfc_els_sgl_array);
}
@@ -4273,7 +4389,8 @@ lpfc_setup_bg(struct lpfc_hba *phba, struct Scsi_Host *shost)
_dump_buf_data =
(char *) __get_free_pages(GFP_KERNEL, pagecnt);
if (_dump_buf_data) {
- printk(KERN_ERR "BLKGRD allocated %d pages for "
+ lpfc_printf_log(phba, KERN_ERR, LOG_BG,
+ "9043 BLKGRD: allocated %d pages for "
"_dump_buf_data at 0x%p\n",
(1 << pagecnt), _dump_buf_data);
_dump_buf_data_order = pagecnt;
@@ -4284,17 +4401,20 @@ lpfc_setup_bg(struct lpfc_hba *phba, struct Scsi_Host *shost)
--pagecnt;
}
if (!_dump_buf_data_order)
- printk(KERN_ERR "BLKGRD ERROR unable to allocate "
+ lpfc_printf_log(phba, KERN_ERR, LOG_BG,
+ "9044 BLKGRD: ERROR unable to allocate "
"memory for hexdump\n");
} else
- printk(KERN_ERR "BLKGRD already allocated _dump_buf_data=0x%p"
+ lpfc_printf_log(phba, KERN_ERR, LOG_BG,
+ "9045 BLKGRD: already allocated _dump_buf_data=0x%p"
"\n", _dump_buf_data);
if (!_dump_buf_dif) {
while (pagecnt) {
_dump_buf_dif =
(char *) __get_free_pages(GFP_KERNEL, pagecnt);
if (_dump_buf_dif) {
- printk(KERN_ERR "BLKGRD allocated %d pages for "
+ lpfc_printf_log(phba, KERN_ERR, LOG_BG,
+ "9046 BLKGRD: allocated %d pages for "
"_dump_buf_dif at 0x%p\n",
(1 << pagecnt), _dump_buf_dif);
_dump_buf_dif_order = pagecnt;
@@ -4305,10 +4425,12 @@ lpfc_setup_bg(struct lpfc_hba *phba, struct Scsi_Host *shost)
--pagecnt;
}
if (!_dump_buf_dif_order)
- printk(KERN_ERR "BLKGRD ERROR unable to allocate "
+ lpfc_printf_log(phba, KERN_ERR, LOG_BG,
+ "9047 BLKGRD: ERROR unable to allocate "
"memory for hexdump\n");
} else
- printk(KERN_ERR "BLKGRD already allocated _dump_buf_dif=0x%p\n",
+ lpfc_printf_log(phba, KERN_ERR, LOG_BG,
+ "9048 BLKGRD: already allocated _dump_buf_dif=0x%p\n",
_dump_buf_dif);
}
@@ -4512,7 +4634,6 @@ int
lpfc_sli4_post_status_check(struct lpfc_hba *phba)
{
struct lpfc_register sta_reg, uerrlo_reg, uerrhi_reg, scratchpad;
- uint32_t onlnreg0, onlnreg1;
int i, port_error = -ENODEV;
if (!phba->sli4_hba.STAregaddr)
@@ -4556,21 +4677,20 @@ lpfc_sli4_post_status_check(struct lpfc_hba *phba)
bf_get(lpfc_scratchpad_slirev, &scratchpad),
bf_get(lpfc_scratchpad_featurelevel1, &scratchpad),
bf_get(lpfc_scratchpad_featurelevel2, &scratchpad));
-
+ phba->sli4_hba.ue_mask_lo = readl(phba->sli4_hba.UEMASKLOregaddr);
+ phba->sli4_hba.ue_mask_hi = readl(phba->sli4_hba.UEMASKHIregaddr);
/* With uncoverable error, log the error message and return error */
- onlnreg0 = readl(phba->sli4_hba.ONLINE0regaddr);
- onlnreg1 = readl(phba->sli4_hba.ONLINE1regaddr);
- if ((onlnreg0 != LPFC_ONLINE_NERR) || (onlnreg1 != LPFC_ONLINE_NERR)) {
- uerrlo_reg.word0 = readl(phba->sli4_hba.UERRLOregaddr);
- uerrhi_reg.word0 = readl(phba->sli4_hba.UERRHIregaddr);
- if (uerrlo_reg.word0 || uerrhi_reg.word0) {
- lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
- "1422 HBA Unrecoverable error: "
- "uerr_lo_reg=0x%x, uerr_hi_reg=0x%x, "
- "online0_reg=0x%x, online1_reg=0x%x\n",
- uerrlo_reg.word0, uerrhi_reg.word0,
- onlnreg0, onlnreg1);
- }
+ uerrlo_reg.word0 = readl(phba->sli4_hba.UERRLOregaddr);
+ uerrhi_reg.word0 = readl(phba->sli4_hba.UERRHIregaddr);
+ if ((~phba->sli4_hba.ue_mask_lo & uerrlo_reg.word0) ||
+ (~phba->sli4_hba.ue_mask_hi & uerrhi_reg.word0)) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+ "1422 HBA Unrecoverable error: "
+ "uerr_lo_reg=0x%x, uerr_hi_reg=0x%x, "
+ "ue_mask_lo_reg=0x%x, ue_mask_hi_reg=0x%x\n",
+ uerrlo_reg.word0, uerrhi_reg.word0,
+ phba->sli4_hba.ue_mask_lo,
+ phba->sli4_hba.ue_mask_hi);
return -ENODEV;
}
@@ -4591,10 +4711,10 @@ lpfc_sli4_bar0_register_memmap(struct lpfc_hba *phba)
LPFC_UERR_STATUS_LO;
phba->sli4_hba.UERRHIregaddr = phba->sli4_hba.conf_regs_memmap_p +
LPFC_UERR_STATUS_HI;
- phba->sli4_hba.ONLINE0regaddr = phba->sli4_hba.conf_regs_memmap_p +
- LPFC_ONLINE0;
- phba->sli4_hba.ONLINE1regaddr = phba->sli4_hba.conf_regs_memmap_p +
- LPFC_ONLINE1;
+ phba->sli4_hba.UEMASKLOregaddr = phba->sli4_hba.conf_regs_memmap_p +
+ LPFC_UE_MASK_LO;
+ phba->sli4_hba.UEMASKHIregaddr = phba->sli4_hba.conf_regs_memmap_p +
+ LPFC_UE_MASK_HI;
phba->sli4_hba.SCRATCHPADregaddr = phba->sli4_hba.conf_regs_memmap_p +
LPFC_SCRATCHPAD;
}
@@ -4825,7 +4945,8 @@ lpfc_sli4_read_config(struct lpfc_hba *phba)
phba->vpi_base = phba->sli4_hba.max_cfg_param.vpi_base;
phba->vfi_base = phba->sli4_hba.max_cfg_param.vfi_base;
phba->sli4_hba.next_rpi = phba->sli4_hba.max_cfg_param.rpi_base;
- phba->max_vpi = phba->sli4_hba.max_cfg_param.max_vpi;
+ phba->max_vpi = (phba->sli4_hba.max_cfg_param.max_vpi > 0) ?
+ (phba->sli4_hba.max_cfg_param.max_vpi - 1) : 0;
phba->max_vports = phba->max_vpi;
lpfc_printf_log(phba, KERN_INFO, LOG_SLI,
"2003 cfg params XRI(B:%d M:%d), "
@@ -4979,10 +5100,9 @@ lpfc_sli4_queue_create(struct lpfc_hba *phba)
/* It does not make sense to have more EQs than WQs */
if (cfg_fcp_eq_count > phba->cfg_fcp_wq_count) {
lpfc_printf_log(phba, KERN_WARNING, LOG_INIT,
- "2593 The number of FCP EQs (%d) is more "
- "than the number of FCP WQs (%d), take "
- "the number of FCP EQs same as than of "
- "WQs (%d)\n", cfg_fcp_eq_count,
+ "2593 The FCP EQ count(%d) cannot be greater "
+ "than the FCP WQ count(%d), limiting the "
+ "FCP EQ count to %d\n", cfg_fcp_eq_count,
phba->cfg_fcp_wq_count,
phba->cfg_fcp_wq_count);
cfg_fcp_eq_count = phba->cfg_fcp_wq_count;
@@ -5058,15 +5178,6 @@ lpfc_sli4_queue_create(struct lpfc_hba *phba)
}
phba->sli4_hba.els_cq = qdesc;
- /* Create slow-path Unsolicited Receive Complete Queue */
- qdesc = lpfc_sli4_queue_alloc(phba, phba->sli4_hba.cq_esize,
- phba->sli4_hba.cq_ecount);
- if (!qdesc) {
- lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
- "0502 Failed allocate slow-path USOL RX CQ\n");
- goto out_free_els_cq;
- }
- phba->sli4_hba.rxq_cq = qdesc;
/* Create fast-path FCP Completion Queue(s), one-to-one with EQs */
phba->sli4_hba.fcp_cq = kzalloc((sizeof(struct lpfc_queue *) *
@@ -5075,7 +5186,7 @@ lpfc_sli4_queue_create(struct lpfc_hba *phba)
lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
"2577 Failed allocate memory for fast-path "
"CQ record array\n");
- goto out_free_rxq_cq;
+ goto out_free_els_cq;
}
for (fcp_cqidx = 0; fcp_cqidx < phba->cfg_fcp_eq_count; fcp_cqidx++) {
qdesc = lpfc_sli4_queue_alloc(phba, phba->sli4_hba.cq_esize,
@@ -5188,9 +5299,6 @@ out_free_fcp_cq:
phba->sli4_hba.fcp_cq[fcp_cqidx] = NULL;
}
kfree(phba->sli4_hba.fcp_cq);
-out_free_rxq_cq:
- lpfc_sli4_queue_free(phba->sli4_hba.rxq_cq);
- phba->sli4_hba.rxq_cq = NULL;
out_free_els_cq:
lpfc_sli4_queue_free(phba->sli4_hba.els_cq);
phba->sli4_hba.els_cq = NULL;
@@ -5247,10 +5355,6 @@ lpfc_sli4_queue_destroy(struct lpfc_hba *phba)
lpfc_sli4_queue_free(phba->sli4_hba.dat_rq);
phba->sli4_hba.dat_rq = NULL;
- /* Release unsolicited receive complete queue */
- lpfc_sli4_queue_free(phba->sli4_hba.rxq_cq);
- phba->sli4_hba.rxq_cq = NULL;
-
/* Release ELS complete queue */
lpfc_sli4_queue_free(phba->sli4_hba.els_cq);
phba->sli4_hba.els_cq = NULL;
@@ -5383,25 +5487,6 @@ lpfc_sli4_queue_setup(struct lpfc_hba *phba)
phba->sli4_hba.els_cq->queue_id,
phba->sli4_hba.sp_eq->queue_id);
- /* Set up slow-path Unsolicited Receive Complete Queue */
- if (!phba->sli4_hba.rxq_cq) {
- lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
- "0532 USOL RX CQ not allocated\n");
- goto out_destroy_els_cq;
- }
- rc = lpfc_cq_create(phba, phba->sli4_hba.rxq_cq, phba->sli4_hba.sp_eq,
- LPFC_RCQ, LPFC_USOL);
- if (rc) {
- lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
- "0533 Failed setup of slow-path USOL RX CQ: "
- "rc = 0x%x\n", rc);
- goto out_destroy_els_cq;
- }
- lpfc_printf_log(phba, KERN_INFO, LOG_INIT,
- "2587 USL CQ setup: cq-id=%d, parent eq-id=%d\n",
- phba->sli4_hba.rxq_cq->queue_id,
- phba->sli4_hba.sp_eq->queue_id);
-
/* Set up fast-path FCP Response Complete Queue */
for (fcp_cqidx = 0; fcp_cqidx < phba->cfg_fcp_eq_count; fcp_cqidx++) {
if (!phba->sli4_hba.fcp_cq[fcp_cqidx]) {
@@ -5507,7 +5592,7 @@ lpfc_sli4_queue_setup(struct lpfc_hba *phba)
goto out_destroy_fcp_wq;
}
rc = lpfc_rq_create(phba, phba->sli4_hba.hdr_rq, phba->sli4_hba.dat_rq,
- phba->sli4_hba.rxq_cq, LPFC_USOL);
+ phba->sli4_hba.els_cq, LPFC_USOL);
if (rc) {
lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
"0541 Failed setup of Receive Queue: "
@@ -5519,7 +5604,7 @@ lpfc_sli4_queue_setup(struct lpfc_hba *phba)
"parent cq-id=%d\n",
phba->sli4_hba.hdr_rq->queue_id,
phba->sli4_hba.dat_rq->queue_id,
- phba->sli4_hba.rxq_cq->queue_id);
+ phba->sli4_hba.els_cq->queue_id);
return 0;
out_destroy_fcp_wq:
@@ -5531,8 +5616,6 @@ out_destroy_mbx_wq:
out_destroy_fcp_cq:
for (--fcp_cqidx; fcp_cqidx >= 0; fcp_cqidx--)
lpfc_cq_destroy(phba, phba->sli4_hba.fcp_cq[fcp_cqidx]);
- lpfc_cq_destroy(phba, phba->sli4_hba.rxq_cq);
-out_destroy_els_cq:
lpfc_cq_destroy(phba, phba->sli4_hba.els_cq);
out_destroy_mbx_cq:
lpfc_cq_destroy(phba, phba->sli4_hba.mbx_cq);
@@ -5574,8 +5657,6 @@ lpfc_sli4_queue_unset(struct lpfc_hba *phba)
lpfc_cq_destroy(phba, phba->sli4_hba.mbx_cq);
/* Unset ELS complete queue */
lpfc_cq_destroy(phba, phba->sli4_hba.els_cq);
- /* Unset unsolicited receive complete queue */
- lpfc_cq_destroy(phba, phba->sli4_hba.rxq_cq);
/* Unset FCP response complete queue */
for (fcp_qidx = 0; fcp_qidx < phba->cfg_fcp_eq_count; fcp_qidx++)
lpfc_cq_destroy(phba, phba->sli4_hba.fcp_cq[fcp_qidx]);
@@ -6722,6 +6803,7 @@ lpfc_pci_probe_one_s3(struct pci_dev *pdev, const struct pci_device_id *pid)
{
struct lpfc_hba *phba;
struct lpfc_vport *vport = NULL;
+ struct Scsi_Host *shost = NULL;
int error;
uint32_t cfg_mode, intr_mode;
@@ -6800,6 +6882,7 @@ lpfc_pci_probe_one_s3(struct pci_dev *pdev, const struct pci_device_id *pid)
goto out_destroy_shost;
}
+ shost = lpfc_shost_from_vport(vport); /* save shost for error cleanup */
/* Now, trying to enable interrupt and bring up the device */
cfg_mode = phba->cfg_use_msi;
while (true) {
@@ -6866,6 +6949,8 @@ out_unset_pci_mem_s3:
lpfc_sli_pci_mem_unset(phba);
out_disable_pci_dev:
lpfc_disable_pci_dev(phba);
+ if (shost)
+ scsi_host_put(shost);
out_free_phba:
lpfc_hba_free(phba);
return error;
@@ -7036,6 +7121,7 @@ lpfc_pci_resume_one_s3(struct pci_dev *pdev)
/* Restore device state from PCI config space */
pci_set_power_state(pdev, PCI_D0);
pci_restore_state(pdev);
+
if (pdev->is_busmaster)
pci_set_master(pdev);
@@ -7070,6 +7156,75 @@ lpfc_pci_resume_one_s3(struct pci_dev *pdev)
}
/**
+ * lpfc_sli_prep_dev_for_recover - Prepare SLI3 device for pci slot recover
+ * @phba: pointer to lpfc hba data structure.
+ *
+ * This routine is called to prepare the SLI3 device for PCI slot recover. It
+ * aborts and stops all the on-going I/Os on the pci device.
+ **/
+static void
+lpfc_sli_prep_dev_for_recover(struct lpfc_hba *phba)
+{
+ lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+ "2723 PCI channel I/O abort preparing for recovery\n");
+ /* Prepare for bringing HBA offline */
+ lpfc_offline_prep(phba);
+ /* Clear sli active flag to prevent sysfs access to HBA */
+ spin_lock_irq(&phba->hbalock);
+ phba->sli.sli_flag &= ~LPFC_SLI_ACTIVE;
+ spin_unlock_irq(&phba->hbalock);
+ /* Stop and flush all I/Os and bring HBA offline */
+ lpfc_offline(phba);
+}
+
+/**
+ * lpfc_sli_prep_dev_for_reset - Prepare SLI3 device for pci slot reset
+ * @phba: pointer to lpfc hba data structure.
+ *
+ * This routine is called to prepare the SLI3 device for PCI slot reset. It
+ * disables the device interrupt and pci device, and aborts the internal FCP
+ * pending I/Os.
+ **/
+static void
+lpfc_sli_prep_dev_for_reset(struct lpfc_hba *phba)
+{
+ struct lpfc_sli *psli = &phba->sli;
+ struct lpfc_sli_ring *pring;
+
+ lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+ "2710 PCI channel disable preparing for reset\n");
+ /* Disable interrupt and pci device */
+ lpfc_sli_disable_intr(phba);
+ pci_disable_device(phba->pcidev);
+ /*
+ * There may be I/Os dropped by the firmware.
+ * Error iocb (I/O) on txcmplq and let the SCSI layer
+ * retry it after re-establishing link.
+ */
+ pring = &psli->ring[psli->fcp_ring];
+ lpfc_sli_abort_iocb_ring(phba, pring);
+}
+
+/**
+ * lpfc_sli_prep_dev_for_perm_failure - Prepare SLI3 dev for pci slot disable
+ * @phba: pointer to lpfc hba data structure.
+ *
+ * This routine is called to prepare the SLI3 device for PCI slot permanently
+ * disabling. It blocks the SCSI transport layer traffic and flushes the FCP
+ * pending I/Os.
+ **/
+static void
+lpfc_prep_dev_for_perm_failure(struct lpfc_hba *phba)
+{
+ lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+ "2711 PCI channel permanent disable for failure\n");
+ /* Block all SCSI devices' I/Os on the host */
+ lpfc_scsi_dev_block(phba);
+ /* Clean up all driver's outstanding SCSI I/Os */
+ lpfc_sli_flush_fcp_rings(phba);
+}
+
+/**
* lpfc_io_error_detected_s3 - Method for handling SLI-3 device PCI I/O error
* @pdev: pointer to PCI device.
* @state: the current PCI connection state.
@@ -7083,6 +7238,7 @@ lpfc_pci_resume_one_s3(struct pci_dev *pdev)
* as desired.
*
* Return codes
+ * PCI_ERS_RESULT_CAN_RECOVER - can be recovered with reset_link
* PCI_ERS_RESULT_NEED_RESET - need to reset before recovery
* PCI_ERS_RESULT_DISCONNECT - device could not be recovered
**/
@@ -7091,33 +7247,27 @@ lpfc_io_error_detected_s3(struct pci_dev *pdev, pci_channel_state_t state)
{
struct Scsi_Host *shost = pci_get_drvdata(pdev);
struct lpfc_hba *phba = ((struct lpfc_vport *)shost->hostdata)->phba;
- struct lpfc_sli *psli = &phba->sli;
- struct lpfc_sli_ring *pring;
- if (state == pci_channel_io_perm_failure) {
- lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
- "0472 PCI channel I/O permanent failure\n");
- /* Block all SCSI devices' I/Os on the host */
- lpfc_scsi_dev_block(phba);
- /* Clean up all driver's outstanding SCSI I/Os */
- lpfc_sli_flush_fcp_rings(phba);
+ switch (state) {
+ case pci_channel_io_normal:
+ /* Non-fatal error, prepare for recovery */
+ lpfc_sli_prep_dev_for_recover(phba);
+ return PCI_ERS_RESULT_CAN_RECOVER;
+ case pci_channel_io_frozen:
+ /* Fatal error, prepare for slot reset */
+ lpfc_sli_prep_dev_for_reset(phba);
+ return PCI_ERS_RESULT_NEED_RESET;
+ case pci_channel_io_perm_failure:
+ /* Permanent failure, prepare for device down */
+ lpfc_prep_dev_for_perm_failure(phba);
return PCI_ERS_RESULT_DISCONNECT;
+ default:
+ /* Unknown state, prepare and request slot reset */
+ lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+ "0472 Unknown PCI error state: x%x\n", state);
+ lpfc_sli_prep_dev_for_reset(phba);
+ return PCI_ERS_RESULT_NEED_RESET;
}
-
- pci_disable_device(pdev);
- /*
- * There may be I/Os dropped by the firmware.
- * Error iocb (I/O) on txcmplq and let the SCSI layer
- * retry it after re-establishing link.
- */
- pring = &psli->ring[psli->fcp_ring];
- lpfc_sli_abort_iocb_ring(phba, pring);
-
- /* Disable interrupt */
- lpfc_sli_disable_intr(phba);
-
- /* Request a slot reset. */
- return PCI_ERS_RESULT_NEED_RESET;
}
/**
@@ -7197,7 +7347,12 @@ lpfc_io_resume_s3(struct pci_dev *pdev)
struct Scsi_Host *shost = pci_get_drvdata(pdev);
struct lpfc_hba *phba = ((struct lpfc_vport *)shost->hostdata)->phba;
+ /* Bring the device online */
lpfc_online(phba);
+
+ /* Clean up Advanced Error Reporting (AER) if needed */
+ if (phba->hba_flag & HBA_AER_ENABLED)
+ pci_cleanup_aer_uncorrect_error_status(pdev);
}
/**
@@ -7213,15 +7368,15 @@ lpfc_sli4_get_els_iocb_cnt(struct lpfc_hba *phba)
if (phba->sli_rev == LPFC_SLI_REV4) {
if (max_xri <= 100)
- return 4;
+ return 10;
else if (max_xri <= 256)
- return 8;
+ return 25;
else if (max_xri <= 512)
- return 16;
+ return 50;
else if (max_xri <= 1024)
- return 32;
+ return 100;
else
- return 48;
+ return 150;
} else
return 0;
}
@@ -7249,6 +7404,7 @@ lpfc_pci_probe_one_s4(struct pci_dev *pdev, const struct pci_device_id *pid)
{
struct lpfc_hba *phba;
struct lpfc_vport *vport = NULL;
+ struct Scsi_Host *shost = NULL;
int error;
uint32_t cfg_mode, intr_mode;
int mcnt;
@@ -7329,6 +7485,7 @@ lpfc_pci_probe_one_s4(struct pci_dev *pdev, const struct pci_device_id *pid)
goto out_destroy_shost;
}
+ shost = lpfc_shost_from_vport(vport); /* save shost for error cleanup */
/* Now, trying to enable interrupt and bring up the device */
cfg_mode = phba->cfg_use_msi;
while (true) {
@@ -7397,6 +7554,8 @@ out_unset_pci_mem_s4:
lpfc_sli4_pci_mem_unset(phba);
out_disable_pci_dev:
lpfc_disable_pci_dev(phba);
+ if (shost)
+ scsi_host_put(shost);
out_free_phba:
lpfc_hba_free(phba);
return error;
@@ -7971,6 +8130,10 @@ static struct pci_device_id lpfc_id_table[] = {
PCI_ANY_ID, PCI_ANY_ID, },
{PCI_VENDOR_ID_SERVERENGINE, PCI_DEVICE_ID_TIGERSHARK,
PCI_ANY_ID, PCI_ANY_ID, },
+ {PCI_VENDOR_ID_SERVERENGINE, PCI_DEVICE_ID_TOMCAT,
+ PCI_ANY_ID, PCI_ANY_ID, },
+ {PCI_VENDOR_ID_EMULEX, PCI_DEVICE_ID_FALCON,
+ PCI_ANY_ID, PCI_ANY_ID, },
{ 0 }
};
@@ -8053,15 +8216,15 @@ lpfc_exit(void)
if (lpfc_enable_npiv)
fc_release_transport(lpfc_vport_transport_template);
if (_dump_buf_data) {
- printk(KERN_ERR "BLKGRD freeing %lu pages for _dump_buf_data "
- "at 0x%p\n",
+ printk(KERN_ERR "9062 BLKGRD: freeing %lu pages for "
+ "_dump_buf_data at 0x%p\n",
(1L << _dump_buf_data_order), _dump_buf_data);
free_pages((unsigned long)_dump_buf_data, _dump_buf_data_order);
}
if (_dump_buf_dif) {
- printk(KERN_ERR "BLKGRD freeing %lu pages for _dump_buf_dif "
- "at 0x%p\n",
+ printk(KERN_ERR "9049 BLKGRD: freeing %lu pages for "
+ "_dump_buf_dif at 0x%p\n",
(1L << _dump_buf_dif_order), _dump_buf_dif);
free_pages((unsigned long)_dump_buf_dif, _dump_buf_dif_order);
}
diff --git a/drivers/scsi/lpfc/lpfc_mbox.c b/drivers/scsi/lpfc/lpfc_mbox.c
index 1ab405902a1..a9afd8b94b6 100644
--- a/drivers/scsi/lpfc/lpfc_mbox.c
+++ b/drivers/scsi/lpfc/lpfc_mbox.c
@@ -25,8 +25,8 @@
#include <scsi/scsi_device.h>
#include <scsi/scsi_transport_fc.h>
-
#include <scsi/scsi.h>
+#include <scsi/fc/fc_fs.h>
#include "lpfc_hw4.h"
#include "lpfc_hw.h"
@@ -820,6 +820,10 @@ lpfc_reg_vpi(struct lpfc_vport *vport, LPFC_MBOXQ_t *pmb)
mb->un.varRegVpi.vpi = vport->vpi + vport->phba->vpi_base;
mb->un.varRegVpi.sid = vport->fc_myDID;
mb->un.varRegVpi.vfi = vport->vfi + vport->phba->vfi_base;
+ memcpy(mb->un.varRegVpi.wwn, &vport->fc_portname,
+ sizeof(struct lpfc_name));
+ mb->un.varRegVpi.wwn[0] = cpu_to_le32(mb->un.varRegVpi.wwn[0]);
+ mb->un.varRegVpi.wwn[1] = cpu_to_le32(mb->un.varRegVpi.wwn[1]);
mb->mbxCommand = MBX_REG_VPI;
mb->mbxOwner = OWN_HOST;
@@ -849,7 +853,10 @@ lpfc_unreg_vpi(struct lpfc_hba *phba, uint16_t vpi, LPFC_MBOXQ_t *pmb)
MAILBOX_t *mb = &pmb->u.mb;
memset(pmb, 0, sizeof (LPFC_MBOXQ_t));
- mb->un.varUnregVpi.vpi = vpi + phba->vpi_base;
+ if (phba->sli_rev < LPFC_SLI_REV4)
+ mb->un.varUnregVpi.vpi = vpi + phba->vpi_base;
+ else
+ mb->un.varUnregVpi.sli4_vpi = vpi + phba->vpi_base;
mb->mbxCommand = MBX_UNREG_VPI;
mb->mbxOwner = OWN_HOST;
@@ -1132,7 +1139,7 @@ lpfc_config_ring(struct lpfc_hba * phba, int ring, LPFC_MBOXQ_t * pmb)
/* Otherwise we setup specific rctl / type masks for this ring */
for (i = 0; i < pring->num_mask; i++) {
mb->un.varCfgRing.rrRegs[i].rval = pring->prt[i].rctl;
- if (mb->un.varCfgRing.rrRegs[i].rval != FC_ELS_REQ)
+ if (mb->un.varCfgRing.rrRegs[i].rval != FC_RCTL_ELS_REQ)
mb->un.varCfgRing.rrRegs[i].rmask = 0xff;
else
mb->un.varCfgRing.rrRegs[i].rmask = 0xfe;
@@ -1654,9 +1661,12 @@ lpfc_sli4_config(struct lpfc_hba *phba, struct lpfcMboxq *mbox,
/* Allocate record for keeping SGE virtual addresses */
mbox->sge_array = kmalloc(sizeof(struct lpfc_mbx_nembed_sge_virt),
GFP_KERNEL);
- if (!mbox->sge_array)
+ if (!mbox->sge_array) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_MBOX,
+ "2527 Failed to allocate non-embedded SGE "
+ "array.\n");
return 0;
-
+ }
for (pagen = 0, alloc_len = 0; pagen < pcount; pagen++) {
/* The DMA memory is always allocated in the length of a
* page even though the last SGE might not fill up to a
@@ -1753,11 +1763,6 @@ lpfc_request_features(struct lpfc_hba *phba, struct lpfcMboxq *mboxq)
/* Set up host requested features. */
bf_set(lpfc_mbx_rq_ftr_rq_fcpi, &mboxq->u.mqe.un.req_ftrs, 1);
- if (phba->cfg_enable_fip)
- bf_set(lpfc_mbx_rq_ftr_rq_ifip, &mboxq->u.mqe.un.req_ftrs, 0);
- else
- bf_set(lpfc_mbx_rq_ftr_rq_ifip, &mboxq->u.mqe.un.req_ftrs, 1);
-
/* Enable DIF (block guard) only if configured to do so. */
if (phba->cfg_enable_bg)
bf_set(lpfc_mbx_rq_ftr_rq_dif, &mboxq->u.mqe.un.req_ftrs, 1);
@@ -1817,6 +1822,9 @@ lpfc_reg_vfi(struct lpfcMboxq *mbox, struct lpfc_vport *vport, dma_addr_t phys)
bf_set(lpfc_reg_vfi_vfi, reg_vfi, vport->vfi + vport->phba->vfi_base);
bf_set(lpfc_reg_vfi_fcfi, reg_vfi, vport->phba->fcf.fcfi);
bf_set(lpfc_reg_vfi_vpi, reg_vfi, vport->vpi + vport->phba->vpi_base);
+ memcpy(reg_vfi->wwn, &vport->fc_portname, sizeof(struct lpfc_name));
+ reg_vfi->wwn[0] = cpu_to_le32(reg_vfi->wwn[0]);
+ reg_vfi->wwn[1] = cpu_to_le32(reg_vfi->wwn[1]);
reg_vfi->bde.addrHigh = putPaddrHigh(phys);
reg_vfi->bde.addrLow = putPaddrLow(phys);
reg_vfi->bde.tus.f.bdeSize = sizeof(vport->fc_sparam);
@@ -1850,7 +1858,7 @@ lpfc_init_vpi(struct lpfc_hba *phba, struct lpfcMboxq *mbox, uint16_t vpi)
/**
* lpfc_unreg_vfi - Initialize the UNREG_VFI mailbox command
* @mbox: pointer to lpfc mbox command to initialize.
- * @vfi: VFI to be unregistered.
+ * @vport: vport associated with the VF.
*
* The UNREG_VFI mailbox command causes the SLI Host to put a virtual fabric
* (logical NPort) into the inactive state. The SLI Host must have logged out
@@ -1859,11 +1867,12 @@ lpfc_init_vpi(struct lpfc_hba *phba, struct lpfcMboxq *mbox, uint16_t vpi)
* fabric inactive.
**/
void
-lpfc_unreg_vfi(struct lpfcMboxq *mbox, uint16_t vfi)
+lpfc_unreg_vfi(struct lpfcMboxq *mbox, struct lpfc_vport *vport)
{
memset(mbox, 0, sizeof(*mbox));
bf_set(lpfc_mqe_command, &mbox->u.mqe, MBX_UNREG_VFI);
- bf_set(lpfc_unreg_vfi_vfi, &mbox->u.mqe.un.unreg_vfi, vfi);
+ bf_set(lpfc_unreg_vfi_vfi, &mbox->u.mqe.un.unreg_vfi,
+ vport->vfi + vport->phba->vfi_base);
}
/**
diff --git a/drivers/scsi/lpfc/lpfc_nportdisc.c b/drivers/scsi/lpfc/lpfc_nportdisc.c
index 3e74136f1ed..2ed6af19493 100644
--- a/drivers/scsi/lpfc/lpfc_nportdisc.c
+++ b/drivers/scsi/lpfc/lpfc_nportdisc.c
@@ -1223,6 +1223,12 @@ lpfc_rcv_logo_reglogin_issue(struct lpfc_vport *vport,
list_for_each_entry_safe(mb, nextmb, &phba->sli.mboxq, list) {
if ((mb->u.mb.mbxCommand == MBX_REG_LOGIN64) &&
(ndlp == (struct lpfc_nodelist *) mb->context2)) {
+ if (phba->sli_rev == LPFC_SLI_REV4) {
+ spin_unlock_irq(&phba->hbalock);
+ lpfc_sli4_free_rpi(phba,
+ mb->u.mb.un.varRegLogin.rpi);
+ spin_lock_irq(&phba->hbalock);
+ }
mp = (struct lpfc_dmabuf *) (mb->context1);
if (mp) {
__lpfc_mbuf_free(phba, mp->virt, mp->phys);
@@ -1230,6 +1236,7 @@ lpfc_rcv_logo_reglogin_issue(struct lpfc_vport *vport,
}
lpfc_nlp_put(ndlp);
list_del(&mb->list);
+ phba->sli.mboxq_cnt--;
mempool_free(mb, phba->mbox_mem_pool);
}
}
diff --git a/drivers/scsi/lpfc/lpfc_scsi.c b/drivers/scsi/lpfc/lpfc_scsi.c
index c88f59f0ce3..a246410ce9d 100644
--- a/drivers/scsi/lpfc/lpfc_scsi.c
+++ b/drivers/scsi/lpfc/lpfc_scsi.c
@@ -59,22 +59,26 @@ static char *dif_op_str[] = {
};
static void
lpfc_release_scsi_buf_s4(struct lpfc_hba *phba, struct lpfc_scsi_buf *psb);
+static void
+lpfc_release_scsi_buf_s3(struct lpfc_hba *phba, struct lpfc_scsi_buf *psb);
static void
-lpfc_debug_save_data(struct scsi_cmnd *cmnd)
+lpfc_debug_save_data(struct lpfc_hba *phba, struct scsi_cmnd *cmnd)
{
void *src, *dst;
struct scatterlist *sgde = scsi_sglist(cmnd);
if (!_dump_buf_data) {
- printk(KERN_ERR "BLKGRD ERROR %s _dump_buf_data is NULL\n",
+ lpfc_printf_log(phba, KERN_ERR, LOG_BG,
+ "9050 BLKGRD: ERROR %s _dump_buf_data is NULL\n",
__func__);
return;
}
if (!sgde) {
- printk(KERN_ERR "BLKGRD ERROR: data scatterlist is null\n");
+ lpfc_printf_log(phba, KERN_ERR, LOG_BG,
+ "9051 BLKGRD: ERROR: data scatterlist is null\n");
return;
}
@@ -88,19 +92,21 @@ lpfc_debug_save_data(struct scsi_cmnd *cmnd)
}
static void
-lpfc_debug_save_dif(struct scsi_cmnd *cmnd)
+lpfc_debug_save_dif(struct lpfc_hba *phba, struct scsi_cmnd *cmnd)
{
void *src, *dst;
struct scatterlist *sgde = scsi_prot_sglist(cmnd);
if (!_dump_buf_dif) {
- printk(KERN_ERR "BLKGRD ERROR %s _dump_buf_data is NULL\n",
+ lpfc_printf_log(phba, KERN_ERR, LOG_BG,
+ "9052 BLKGRD: ERROR %s _dump_buf_data is NULL\n",
__func__);
return;
}
if (!sgde) {
- printk(KERN_ERR "BLKGRD ERROR: prot scatterlist is null\n");
+ lpfc_printf_log(phba, KERN_ERR, LOG_BG,
+ "9053 BLKGRD: ERROR: prot scatterlist is null\n");
return;
}
@@ -242,6 +248,36 @@ lpfc_send_sdev_queuedepth_change_event(struct lpfc_hba *phba,
}
/**
+ * lpfc_change_queue_depth - Alter scsi device queue depth
+ * @sdev: Pointer the scsi device on which to change the queue depth.
+ * @qdepth: New queue depth to set the sdev to.
+ * @reason: The reason for the queue depth change.
+ *
+ * This function is called by the midlayer and the LLD to alter the queue
+ * depth for a scsi device. This function sets the queue depth to the new
+ * value and sends an event out to log the queue depth change.
+ **/
+int
+lpfc_change_queue_depth(struct scsi_device *sdev, int qdepth, int reason)
+{
+ struct lpfc_vport *vport = (struct lpfc_vport *) sdev->host->hostdata;
+ struct lpfc_hba *phba = vport->phba;
+ struct lpfc_rport_data *rdata;
+ unsigned long new_queue_depth, old_queue_depth;
+
+ old_queue_depth = sdev->queue_depth;
+ scsi_adjust_queue_depth(sdev, scsi_get_tag_type(sdev), qdepth);
+ new_queue_depth = sdev->queue_depth;
+ rdata = sdev->hostdata;
+ if (rdata)
+ lpfc_send_sdev_queuedepth_change_event(phba, vport,
+ rdata->pnode, sdev->lun,
+ old_queue_depth,
+ new_queue_depth);
+ return sdev->queue_depth;
+}
+
+/**
* lpfc_rampdown_queue_depth - Post RAMP_DOWN_QUEUE event to worker thread
* @phba: The Hba for which this call is being executed.
*
@@ -305,8 +341,10 @@ lpfc_rampup_queue_depth(struct lpfc_vport *vport,
if (vport->cfg_lun_queue_depth <= queue_depth)
return;
spin_lock_irqsave(&phba->hbalock, flags);
- if (((phba->last_ramp_up_time + QUEUE_RAMP_UP_INTERVAL) > jiffies) ||
- ((phba->last_rsrc_error_time + QUEUE_RAMP_UP_INTERVAL ) > jiffies)) {
+ if (time_before(jiffies,
+ phba->last_ramp_up_time + QUEUE_RAMP_UP_INTERVAL) ||
+ time_before(jiffies,
+ phba->last_rsrc_error_time + QUEUE_RAMP_UP_INTERVAL)) {
spin_unlock_irqrestore(&phba->hbalock, flags);
return;
}
@@ -338,10 +376,9 @@ lpfc_ramp_down_queue_handler(struct lpfc_hba *phba)
struct lpfc_vport **vports;
struct Scsi_Host *shost;
struct scsi_device *sdev;
- unsigned long new_queue_depth, old_queue_depth;
+ unsigned long new_queue_depth;
unsigned long num_rsrc_err, num_cmd_success;
int i;
- struct lpfc_rport_data *rdata;
num_rsrc_err = atomic_read(&phba->num_rsrc_err);
num_cmd_success = atomic_read(&phba->num_cmd_success);
@@ -359,22 +396,8 @@ lpfc_ramp_down_queue_handler(struct lpfc_hba *phba)
else
new_queue_depth = sdev->queue_depth -
new_queue_depth;
- old_queue_depth = sdev->queue_depth;
- if (sdev->ordered_tags)
- scsi_adjust_queue_depth(sdev,
- MSG_ORDERED_TAG,
- new_queue_depth);
- else
- scsi_adjust_queue_depth(sdev,
- MSG_SIMPLE_TAG,
- new_queue_depth);
- rdata = sdev->hostdata;
- if (rdata)
- lpfc_send_sdev_queuedepth_change_event(
- phba, vports[i],
- rdata->pnode,
- sdev->lun, old_queue_depth,
- new_queue_depth);
+ lpfc_change_queue_depth(sdev, new_queue_depth,
+ SCSI_QDEPTH_DEFAULT);
}
}
lpfc_destroy_vport_work_array(phba, vports);
@@ -398,7 +421,6 @@ lpfc_ramp_up_queue_handler(struct lpfc_hba *phba)
struct Scsi_Host *shost;
struct scsi_device *sdev;
int i;
- struct lpfc_rport_data *rdata;
vports = lpfc_create_vport_work_array(phba);
if (vports != NULL)
@@ -408,22 +430,9 @@ lpfc_ramp_up_queue_handler(struct lpfc_hba *phba)
if (vports[i]->cfg_lun_queue_depth <=
sdev->queue_depth)
continue;
- if (sdev->ordered_tags)
- scsi_adjust_queue_depth(sdev,
- MSG_ORDERED_TAG,
- sdev->queue_depth+1);
- else
- scsi_adjust_queue_depth(sdev,
- MSG_SIMPLE_TAG,
- sdev->queue_depth+1);
- rdata = sdev->hostdata;
- if (rdata)
- lpfc_send_sdev_queuedepth_change_event(
- phba, vports[i],
- rdata->pnode,
- sdev->lun,
- sdev->queue_depth - 1,
- sdev->queue_depth);
+ lpfc_change_queue_depth(sdev,
+ sdev->queue_depth+1,
+ SCSI_QDEPTH_RAMP_UP);
}
}
lpfc_destroy_vport_work_array(phba, vports);
@@ -589,7 +598,7 @@ lpfc_new_scsi_buf_s3(struct lpfc_vport *vport, int num_to_alloc)
iocb->ulpClass = CLASS3;
psb->status = IOSTAT_SUCCESS;
/* Put it back into the SCSI buffer list */
- lpfc_release_scsi_buf_s4(phba, psb);
+ lpfc_release_scsi_buf_s3(phba, psb);
}
@@ -1024,7 +1033,8 @@ lpfc_scsi_prep_dma_buf_s3(struct lpfc_hba *phba, struct lpfc_scsi_buf *lpfc_cmd)
lpfc_cmd->seg_cnt = nseg;
if (lpfc_cmd->seg_cnt > phba->cfg_sg_seg_cnt) {
- printk(KERN_ERR "%s: Too many sg segments from "
+ lpfc_printf_log(phba, KERN_ERR, LOG_BG,
+ "9064 BLKGRD: %s: Too many sg segments from "
"dma_map_sg. Config %d, seg_cnt %d\n",
__func__, phba->cfg_sg_seg_cnt,
lpfc_cmd->seg_cnt);
@@ -1112,7 +1122,7 @@ lpfc_scsi_prep_dma_buf_s3(struct lpfc_hba *phba, struct lpfc_scsi_buf *lpfc_cmd)
* with the cmd
*/
static int
-lpfc_sc_to_sli_prof(struct scsi_cmnd *sc)
+lpfc_sc_to_sli_prof(struct lpfc_hba *phba, struct scsi_cmnd *sc)
{
uint8_t guard_type = scsi_host_get_guard(sc->device->host);
uint8_t ret_prof = LPFC_PROF_INVALID;
@@ -1136,7 +1146,8 @@ lpfc_sc_to_sli_prof(struct scsi_cmnd *sc)
case SCSI_PROT_NORMAL:
default:
- printk(KERN_ERR "Bad op/guard:%d/%d combination\n",
+ lpfc_printf_log(phba, KERN_ERR, LOG_BG,
+ "9063 BLKGRD:Bad op/guard:%d/%d combination\n",
scsi_get_prot_op(sc), guard_type);
break;
@@ -1157,7 +1168,8 @@ lpfc_sc_to_sli_prof(struct scsi_cmnd *sc)
case SCSI_PROT_WRITE_STRIP:
case SCSI_PROT_NORMAL:
default:
- printk(KERN_ERR "Bad op/guard:%d/%d combination\n",
+ lpfc_printf_log(phba, KERN_ERR, LOG_BG,
+ "9075 BLKGRD: Bad op/guard:%d/%d combination\n",
scsi_get_prot_op(sc), guard_type);
break;
}
@@ -1259,7 +1271,7 @@ lpfc_bg_setup_bpl(struct lpfc_hba *phba, struct scsi_cmnd *sc,
uint16_t apptagmask, apptagval;
pde1 = (struct lpfc_pde *) bpl;
- prof = lpfc_sc_to_sli_prof(sc);
+ prof = lpfc_sc_to_sli_prof(phba, sc);
if (prof == LPFC_PROF_INVALID)
goto out;
@@ -1359,7 +1371,7 @@ lpfc_bg_setup_bpl_prot(struct lpfc_hba *phba, struct scsi_cmnd *sc,
return 0;
}
- prof = lpfc_sc_to_sli_prof(sc);
+ prof = lpfc_sc_to_sli_prof(phba, sc);
if (prof == LPFC_PROF_INVALID)
goto out;
@@ -1408,7 +1420,8 @@ lpfc_bg_setup_bpl_prot(struct lpfc_hba *phba, struct scsi_cmnd *sc,
subtotal = 0; /* total bytes processed for current prot grp */
while (!pgdone) {
if (!sgde) {
- printk(KERN_ERR "%s Invalid data segment\n",
+ lpfc_printf_log(phba, KERN_ERR, LOG_BG,
+ "9065 BLKGRD:%s Invalid data segment\n",
__func__);
return 0;
}
@@ -1462,7 +1475,8 @@ lpfc_bg_setup_bpl_prot(struct lpfc_hba *phba, struct scsi_cmnd *sc,
reftag += protgrp_blks;
} else {
/* if we're here, we have a bug */
- printk(KERN_ERR "BLKGRD: bug in %s\n", __func__);
+ lpfc_printf_log(phba, KERN_ERR, LOG_BG,
+ "9054 BLKGRD: bug in %s\n", __func__);
}
} while (!alldone);
@@ -1544,8 +1558,10 @@ lpfc_bg_scsi_prep_dma_buf(struct lpfc_hba *phba,
lpfc_cmd->seg_cnt = datasegcnt;
if (lpfc_cmd->seg_cnt > phba->cfg_sg_seg_cnt) {
- printk(KERN_ERR "%s: Too many sg segments from "
- "dma_map_sg. Config %d, seg_cnt %d\n",
+ lpfc_printf_log(phba, KERN_ERR, LOG_BG,
+ "9067 BLKGRD: %s: Too many sg segments"
+ " from dma_map_sg. Config %d, seg_cnt"
+ " %d\n",
__func__, phba->cfg_sg_seg_cnt,
lpfc_cmd->seg_cnt);
scsi_dma_unmap(scsi_cmnd);
@@ -1579,8 +1595,9 @@ lpfc_bg_scsi_prep_dma_buf(struct lpfc_hba *phba,
lpfc_cmd->prot_seg_cnt = protsegcnt;
if (lpfc_cmd->prot_seg_cnt
> phba->cfg_prot_sg_seg_cnt) {
- printk(KERN_ERR "%s: Too many prot sg segments "
- "from dma_map_sg. Config %d,"
+ lpfc_printf_log(phba, KERN_ERR, LOG_BG,
+ "9068 BLKGRD: %s: Too many prot sg "
+ "segments from dma_map_sg. Config %d,"
"prot_seg_cnt %d\n", __func__,
phba->cfg_prot_sg_seg_cnt,
lpfc_cmd->prot_seg_cnt);
@@ -1671,23 +1688,26 @@ lpfc_parse_bg_err(struct lpfc_hba *phba, struct lpfc_scsi_buf *lpfc_cmd,
uint32_t bgstat = bgf->bgstat;
uint64_t failing_sector = 0;
- printk(KERN_ERR "BG ERROR in cmd 0x%x lba 0x%llx blk cnt 0x%x "
+ lpfc_printf_log(phba, KERN_ERR, LOG_BG, "9069 BLKGRD: BG ERROR in cmd"
+ " 0x%x lba 0x%llx blk cnt 0x%x "
"bgstat=0x%x bghm=0x%x\n",
cmd->cmnd[0], (unsigned long long)scsi_get_lba(cmd),
blk_rq_sectors(cmd->request), bgstat, bghm);
spin_lock(&_dump_buf_lock);
if (!_dump_buf_done) {
- printk(KERN_ERR "Saving Data for %u blocks to debugfs\n",
+ lpfc_printf_log(phba, KERN_ERR, LOG_BG, "9070 BLKGRD: Saving"
+ " Data for %u blocks to debugfs\n",
(cmd->cmnd[7] << 8 | cmd->cmnd[8]));
- lpfc_debug_save_data(cmd);
+ lpfc_debug_save_data(phba, cmd);
/* If we have a prot sgl, save the DIF buffer */
if (lpfc_prot_group_type(phba, cmd) ==
LPFC_PG_TYPE_DIF_BUF) {
- printk(KERN_ERR "Saving DIF for %u blocks to debugfs\n",
- (cmd->cmnd[7] << 8 | cmd->cmnd[8]));
- lpfc_debug_save_dif(cmd);
+ lpfc_printf_log(phba, KERN_ERR, LOG_BG, "9071 BLKGRD: "
+ "Saving DIF for %u blocks to debugfs\n",
+ (cmd->cmnd[7] << 8 | cmd->cmnd[8]));
+ lpfc_debug_save_dif(phba, cmd);
}
_dump_buf_done = 1;
@@ -1696,15 +1716,17 @@ lpfc_parse_bg_err(struct lpfc_hba *phba, struct lpfc_scsi_buf *lpfc_cmd,
if (lpfc_bgs_get_invalid_prof(bgstat)) {
cmd->result = ScsiResult(DID_ERROR, 0);
- printk(KERN_ERR "Invalid BlockGuard profile. bgstat:0x%x\n",
- bgstat);
+ lpfc_printf_log(phba, KERN_ERR, LOG_BG, "9072 BLKGRD: Invalid"
+ " BlockGuard profile. bgstat:0x%x\n",
+ bgstat);
ret = (-1);
goto out;
}
if (lpfc_bgs_get_uninit_dif_block(bgstat)) {
cmd->result = ScsiResult(DID_ERROR, 0);
- printk(KERN_ERR "Invalid BlockGuard DIF Block. bgstat:0x%x\n",
+ lpfc_printf_log(phba, KERN_ERR, LOG_BG, "9073 BLKGRD: "
+ "Invalid BlockGuard DIF Block. bgstat:0x%x\n",
bgstat);
ret = (-1);
goto out;
@@ -1718,7 +1740,8 @@ lpfc_parse_bg_err(struct lpfc_hba *phba, struct lpfc_scsi_buf *lpfc_cmd,
cmd->result = DRIVER_SENSE << 24
| ScsiResult(DID_ABORT, SAM_STAT_CHECK_CONDITION);
phba->bg_guard_err_cnt++;
- printk(KERN_ERR "BLKGRD: guard_tag error\n");
+ lpfc_printf_log(phba, KERN_ERR, LOG_BG,
+ "9055 BLKGRD: guard_tag error\n");
}
if (lpfc_bgs_get_reftag_err(bgstat)) {
@@ -1730,7 +1753,8 @@ lpfc_parse_bg_err(struct lpfc_hba *phba, struct lpfc_scsi_buf *lpfc_cmd,
| ScsiResult(DID_ABORT, SAM_STAT_CHECK_CONDITION);
phba->bg_reftag_err_cnt++;
- printk(KERN_ERR "BLKGRD: ref_tag error\n");
+ lpfc_printf_log(phba, KERN_ERR, LOG_BG,
+ "9056 BLKGRD: ref_tag error\n");
}
if (lpfc_bgs_get_apptag_err(bgstat)) {
@@ -1742,7 +1766,8 @@ lpfc_parse_bg_err(struct lpfc_hba *phba, struct lpfc_scsi_buf *lpfc_cmd,
| ScsiResult(DID_ABORT, SAM_STAT_CHECK_CONDITION);
phba->bg_apptag_err_cnt++;
- printk(KERN_ERR "BLKGRD: app_tag error\n");
+ lpfc_printf_log(phba, KERN_ERR, LOG_BG,
+ "9061 BLKGRD: app_tag error\n");
}
if (lpfc_bgs_get_hi_water_mark_present(bgstat)) {
@@ -1763,7 +1788,8 @@ lpfc_parse_bg_err(struct lpfc_hba *phba, struct lpfc_scsi_buf *lpfc_cmd,
if (!ret) {
/* No error was reported - problem in FW? */
cmd->result = ScsiResult(DID_ERROR, 0);
- printk(KERN_ERR "BLKGRD: no errors reported!\n");
+ lpfc_printf_log(phba, KERN_ERR, LOG_BG,
+ "9057 BLKGRD: no errors reported!\n");
}
out:
@@ -1822,9 +1848,10 @@ lpfc_scsi_prep_dma_buf_s4(struct lpfc_hba *phba, struct lpfc_scsi_buf *lpfc_cmd)
lpfc_cmd->seg_cnt = nseg;
if (lpfc_cmd->seg_cnt > phba->cfg_sg_seg_cnt) {
- printk(KERN_ERR "%s: Too many sg segments from "
- "dma_map_sg. Config %d, seg_cnt %d\n",
- __func__, phba->cfg_sg_seg_cnt,
+ lpfc_printf_log(phba, KERN_ERR, LOG_BG, "9074 BLKGRD:"
+ " %s: Too many sg segments from "
+ "dma_map_sg. Config %d, seg_cnt %d\n",
+ __func__, phba->cfg_sg_seg_cnt,
lpfc_cmd->seg_cnt);
scsi_dma_unmap(scsi_cmnd);
return 1;
@@ -2050,6 +2077,21 @@ lpfc_handle_fcp_err(struct lpfc_vport *vport, struct lpfc_scsi_buf *lpfc_cmd,
goto out;
}
+ if (resp_info & RSP_LEN_VALID) {
+ rsplen = be32_to_cpu(fcprsp->rspRspLen);
+ if ((rsplen != 0 && rsplen != 4 && rsplen != 8) ||
+ (fcprsp->rspInfo3 != RSP_NO_FAILURE)) {
+ lpfc_printf_vlog(vport, KERN_ERR, LOG_FCP,
+ "2719 Invalid response length: "
+ "tgt x%x lun x%x cmnd x%x rsplen x%x\n",
+ cmnd->device->id,
+ cmnd->device->lun, cmnd->cmnd[0],
+ rsplen);
+ host_status = DID_ERROR;
+ goto out;
+ }
+ }
+
if ((resp_info & SNS_LEN_VALID) && fcprsp->rspSnsLen) {
uint32_t snslen = be32_to_cpu(fcprsp->rspSnsLen);
if (snslen > SCSI_SENSE_BUFFERSIZE)
@@ -2074,15 +2116,6 @@ lpfc_handle_fcp_err(struct lpfc_vport *vport, struct lpfc_scsi_buf *lpfc_cmd,
be32_to_cpu(fcprsp->rspRspLen),
fcprsp->rspInfo3);
- if (resp_info & RSP_LEN_VALID) {
- rsplen = be32_to_cpu(fcprsp->rspRspLen);
- if ((rsplen != 0 && rsplen != 4 && rsplen != 8) ||
- (fcprsp->rspInfo3 != RSP_NO_FAILURE)) {
- host_status = DID_ERROR;
- goto out;
- }
- }
-
scsi_set_resid(cmnd, 0);
if (resp_info & RESID_UNDER) {
scsi_set_resid(cmnd, be32_to_cpu(fcprsp->rspResId));
@@ -2180,7 +2213,7 @@ lpfc_scsi_cmd_iocb_cmpl(struct lpfc_hba *phba, struct lpfc_iocbq *pIocbIn,
struct scsi_cmnd *cmd = lpfc_cmd->pCmd;
int result;
struct scsi_device *tmp_sdev;
- int depth = 0;
+ int depth;
unsigned long flags;
struct lpfc_fast_path_event *fast_path_evt;
struct Scsi_Host *shost = cmd->device->host;
@@ -2264,7 +2297,7 @@ lpfc_scsi_cmd_iocb_cmpl(struct lpfc_hba *phba, struct lpfc_iocbq *pIocbIn,
lpfc_printf_vlog(vport, KERN_WARNING,
LOG_BG,
"9031 non-zero BGSTAT "
- "on unprotected cmd");
+ "on unprotected cmd\n");
}
}
@@ -2347,67 +2380,29 @@ lpfc_scsi_cmd_iocb_cmpl(struct lpfc_hba *phba, struct lpfc_iocbq *pIocbIn,
return;
}
-
if (!result)
lpfc_rampup_queue_depth(vport, queue_depth);
- if (!result && pnode && NLP_CHK_NODE_ACT(pnode) &&
- ((jiffies - pnode->last_ramp_up_time) >
- LPFC_Q_RAMP_UP_INTERVAL * HZ) &&
- ((jiffies - pnode->last_q_full_time) >
- LPFC_Q_RAMP_UP_INTERVAL * HZ) &&
- (vport->cfg_lun_queue_depth > queue_depth)) {
- shost_for_each_device(tmp_sdev, shost) {
- if (vport->cfg_lun_queue_depth > tmp_sdev->queue_depth){
- if (tmp_sdev->id != scsi_id)
- continue;
- if (tmp_sdev->ordered_tags)
- scsi_adjust_queue_depth(tmp_sdev,
- MSG_ORDERED_TAG,
- tmp_sdev->queue_depth+1);
- else
- scsi_adjust_queue_depth(tmp_sdev,
- MSG_SIMPLE_TAG,
- tmp_sdev->queue_depth+1);
-
- pnode->last_ramp_up_time = jiffies;
- }
- }
- lpfc_send_sdev_queuedepth_change_event(phba, vport, pnode,
- 0xFFFFFFFF,
- queue_depth , queue_depth + 1);
- }
-
/*
* Check for queue full. If the lun is reporting queue full, then
* back off the lun queue depth to prevent target overloads.
*/
if (result == SAM_STAT_TASK_SET_FULL && pnode &&
NLP_CHK_NODE_ACT(pnode)) {
- pnode->last_q_full_time = jiffies;
-
shost_for_each_device(tmp_sdev, shost) {
if (tmp_sdev->id != scsi_id)
continue;
depth = scsi_track_queue_full(tmp_sdev,
- tmp_sdev->queue_depth - 1);
- }
- /*
- * The queue depth cannot be lowered any more.
- * Modify the returned error code to store
- * the final depth value set by
- * scsi_track_queue_full.
- */
- if (depth == -1)
- depth = shost->cmd_per_lun;
-
- if (depth) {
+ tmp_sdev->queue_depth-1);
+ if (depth <= 0)
+ continue;
lpfc_printf_vlog(vport, KERN_WARNING, LOG_FCP,
"0711 detected queue full - lun queue "
"depth adjusted to %d.\n", depth);
lpfc_send_sdev_queuedepth_change_event(phba, vport,
- pnode, 0xFFFFFFFF,
- depth+1, depth);
+ pnode,
+ tmp_sdev->lun,
+ depth+1, depth);
}
}
@@ -2745,7 +2740,9 @@ void lpfc_poll_timeout(unsigned long ptr)
struct lpfc_hba *phba = (struct lpfc_hba *) ptr;
if (phba->cfg_poll & ENABLE_FCP_RING_POLLING) {
- lpfc_sli_poll_fcp_ring (phba);
+ lpfc_sli_handle_fast_ring_event(phba,
+ &phba->sli.ring[LPFC_FCP_RING], HA_R0RE_REQ);
+
if (phba->cfg_poll & DISABLE_FCP_RING_INT)
lpfc_poll_rearm_timer(phba);
}
@@ -2771,7 +2768,7 @@ lpfc_queuecommand(struct scsi_cmnd *cmnd, void (*done) (struct scsi_cmnd *))
struct lpfc_vport *vport = (struct lpfc_vport *) shost->hostdata;
struct lpfc_hba *phba = vport->phba;
struct lpfc_rport_data *rdata = cmnd->device->hostdata;
- struct lpfc_nodelist *ndlp = rdata->pnode;
+ struct lpfc_nodelist *ndlp;
struct lpfc_scsi_buf *lpfc_cmd;
struct fc_rport *rport = starget_to_rport(scsi_target(cmnd->device));
int err;
@@ -2781,13 +2778,15 @@ lpfc_queuecommand(struct scsi_cmnd *cmnd, void (*done) (struct scsi_cmnd *))
cmnd->result = err;
goto out_fail_command;
}
+ ndlp = rdata->pnode;
if (!(phba->sli3_options & LPFC_SLI3_BG_ENABLED) &&
scsi_get_prot_op(cmnd) != SCSI_PROT_NORMAL) {
- printk(KERN_ERR "BLKGRD ERROR: rcvd protected cmd:%02x op:%02x "
- "str=%s without registering for BlockGuard - "
- "Rejecting command\n",
+ lpfc_printf_log(phba, KERN_ERR, LOG_BG,
+ "9058 BLKGRD: ERROR: rcvd protected cmd:%02x"
+ " op:%02x str=%s without registering for"
+ " BlockGuard - Rejecting command\n",
cmnd->cmnd[0], scsi_get_prot_op(cmnd),
dif_op_str[scsi_get_prot_op(cmnd)]);
goto out_fail_command;
@@ -2827,61 +2826,66 @@ lpfc_queuecommand(struct scsi_cmnd *cmnd, void (*done) (struct scsi_cmnd *))
cmnd->scsi_done = done;
if (scsi_get_prot_op(cmnd) != SCSI_PROT_NORMAL) {
- lpfc_printf_vlog(vport, KERN_WARNING, LOG_BG,
+ if (vport->phba->cfg_enable_bg) {
+ lpfc_printf_vlog(vport, KERN_WARNING, LOG_BG,
"9033 BLKGRD: rcvd protected cmd:%02x op:%02x "
"str=%s\n",
cmnd->cmnd[0], scsi_get_prot_op(cmnd),
dif_op_str[scsi_get_prot_op(cmnd)]);
- lpfc_printf_vlog(vport, KERN_WARNING, LOG_BG,
+ lpfc_printf_vlog(vport, KERN_WARNING, LOG_BG,
"9034 BLKGRD: CDB: %02x %02x %02x %02x %02x "
"%02x %02x %02x %02x %02x\n",
cmnd->cmnd[0], cmnd->cmnd[1], cmnd->cmnd[2],
cmnd->cmnd[3], cmnd->cmnd[4], cmnd->cmnd[5],
cmnd->cmnd[6], cmnd->cmnd[7], cmnd->cmnd[8],
cmnd->cmnd[9]);
- if (cmnd->cmnd[0] == READ_10)
- lpfc_printf_vlog(vport, KERN_WARNING, LOG_BG,
+ if (cmnd->cmnd[0] == READ_10)
+ lpfc_printf_vlog(vport, KERN_WARNING, LOG_BG,
"9035 BLKGRD: READ @ sector %llu, "
"count %u\n",
(unsigned long long)scsi_get_lba(cmnd),
blk_rq_sectors(cmnd->request));
- else if (cmnd->cmnd[0] == WRITE_10)
- lpfc_printf_vlog(vport, KERN_WARNING, LOG_BG,
+ else if (cmnd->cmnd[0] == WRITE_10)
+ lpfc_printf_vlog(vport, KERN_WARNING, LOG_BG,
"9036 BLKGRD: WRITE @ sector %llu, "
"count %u cmd=%p\n",
(unsigned long long)scsi_get_lba(cmnd),
blk_rq_sectors(cmnd->request),
cmnd);
+ }
err = lpfc_bg_scsi_prep_dma_buf(phba, lpfc_cmd);
} else {
- lpfc_printf_vlog(vport, KERN_WARNING, LOG_BG,
- "9038 BLKGRD: rcvd unprotected cmd:%02x op:%02x"
- " str=%s\n",
- cmnd->cmnd[0], scsi_get_prot_op(cmnd),
- dif_op_str[scsi_get_prot_op(cmnd)]);
- lpfc_printf_vlog(vport, KERN_WARNING, LOG_BG,
- "9039 BLKGRD: CDB: %02x %02x %02x %02x %02x "
- "%02x %02x %02x %02x %02x\n",
- cmnd->cmnd[0], cmnd->cmnd[1], cmnd->cmnd[2],
- cmnd->cmnd[3], cmnd->cmnd[4], cmnd->cmnd[5],
- cmnd->cmnd[6], cmnd->cmnd[7], cmnd->cmnd[8],
- cmnd->cmnd[9]);
- if (cmnd->cmnd[0] == READ_10)
+ if (vport->phba->cfg_enable_bg) {
lpfc_printf_vlog(vport, KERN_WARNING, LOG_BG,
- "9040 dbg: READ @ sector %llu, "
- "count %u\n",
- (unsigned long long)scsi_get_lba(cmnd),
+ "9038 BLKGRD: rcvd unprotected cmd:"
+ "%02x op:%02x str=%s\n",
+ cmnd->cmnd[0], scsi_get_prot_op(cmnd),
+ dif_op_str[scsi_get_prot_op(cmnd)]);
+ lpfc_printf_vlog(vport, KERN_WARNING, LOG_BG,
+ "9039 BLKGRD: CDB: %02x %02x %02x "
+ "%02x %02x %02x %02x %02x %02x %02x\n",
+ cmnd->cmnd[0], cmnd->cmnd[1],
+ cmnd->cmnd[2], cmnd->cmnd[3],
+ cmnd->cmnd[4], cmnd->cmnd[5],
+ cmnd->cmnd[6], cmnd->cmnd[7],
+ cmnd->cmnd[8], cmnd->cmnd[9]);
+ if (cmnd->cmnd[0] == READ_10)
+ lpfc_printf_vlog(vport, KERN_WARNING, LOG_BG,
+ "9040 dbg: READ @ sector %llu, "
+ "count %u\n",
+ (unsigned long long)scsi_get_lba(cmnd),
blk_rq_sectors(cmnd->request));
- else if (cmnd->cmnd[0] == WRITE_10)
- lpfc_printf_vlog(vport, KERN_WARNING, LOG_BG,
+ else if (cmnd->cmnd[0] == WRITE_10)
+ lpfc_printf_vlog(vport, KERN_WARNING, LOG_BG,
"9041 dbg: WRITE @ sector %llu, "
"count %u cmd=%p\n",
(unsigned long long)scsi_get_lba(cmnd),
blk_rq_sectors(cmnd->request), cmnd);
- else
- lpfc_printf_vlog(vport, KERN_WARNING, LOG_BG,
+ else
+ lpfc_printf_vlog(vport, KERN_WARNING, LOG_BG,
"9042 dbg: parser not implemented\n");
+ }
err = lpfc_scsi_prep_dma_buf(phba, lpfc_cmd);
}
@@ -2898,7 +2902,11 @@ lpfc_queuecommand(struct scsi_cmnd *cmnd, void (*done) (struct scsi_cmnd *))
goto out_host_busy_free_buf;
}
if (phba->cfg_poll & ENABLE_FCP_RING_POLLING) {
- lpfc_sli_poll_fcp_ring(phba);
+ spin_unlock(shost->host_lock);
+ lpfc_sli_handle_fast_ring_event(phba,
+ &phba->sli.ring[LPFC_FCP_RING], HA_R0RE_REQ);
+
+ spin_lock(shost->host_lock);
if (phba->cfg_poll & DISABLE_FCP_RING_INT)
lpfc_poll_rearm_timer(phba);
}
@@ -2917,28 +2925,6 @@ lpfc_queuecommand(struct scsi_cmnd *cmnd, void (*done) (struct scsi_cmnd *))
}
/**
- * lpfc_block_error_handler - Routine to block error handler
- * @cmnd: Pointer to scsi_cmnd data structure.
- *
- * This routine blocks execution till fc_rport state is not FC_PORSTAT_BLCOEKD.
- **/
-static void
-lpfc_block_error_handler(struct scsi_cmnd *cmnd)
-{
- struct Scsi_Host *shost = cmnd->device->host;
- struct fc_rport *rport = starget_to_rport(scsi_target(cmnd->device));
-
- spin_lock_irq(shost->host_lock);
- while (rport->port_state == FC_PORTSTATE_BLOCKED) {
- spin_unlock_irq(shost->host_lock);
- msleep(1000);
- spin_lock_irq(shost->host_lock);
- }
- spin_unlock_irq(shost->host_lock);
- return;
-}
-
-/**
* lpfc_abort_handler - scsi_host_template eh_abort_handler entry point
* @cmnd: Pointer to scsi_cmnd data structure.
*
@@ -2961,7 +2947,7 @@ lpfc_abort_handler(struct scsi_cmnd *cmnd)
int ret = SUCCESS;
DECLARE_WAIT_QUEUE_HEAD_ONSTACK(waitq);
- lpfc_block_error_handler(cmnd);
+ fc_block_scsi_eh(cmnd);
lpfc_cmd = (struct lpfc_scsi_buf *)cmnd->host_scribble;
BUG_ON(!lpfc_cmd);
@@ -3001,6 +2987,10 @@ lpfc_abort_handler(struct scsi_cmnd *cmnd)
icmd->ulpLe = 1;
icmd->ulpClass = cmd->ulpClass;
+
+ /* ABTS WQE must go to the same WQ as the WQE to be aborted */
+ abtsiocb->fcp_wqidx = iocb->fcp_wqidx;
+
if (lpfc_is_link_up(phba))
icmd->ulpCommand = CMD_ABORT_XRI_CN;
else
@@ -3016,7 +3006,8 @@ lpfc_abort_handler(struct scsi_cmnd *cmnd)
}
if (phba->cfg_poll & DISABLE_FCP_RING_INT)
- lpfc_sli_poll_fcp_ring (phba);
+ lpfc_sli_handle_fast_ring_event(phba,
+ &phba->sli.ring[LPFC_FCP_RING], HA_R0RE_REQ);
lpfc_cmd->waitq = &waitq;
/* Wait for abort to complete */
@@ -3166,9 +3157,15 @@ static int
lpfc_chk_tgt_mapped(struct lpfc_vport *vport, struct scsi_cmnd *cmnd)
{
struct lpfc_rport_data *rdata = cmnd->device->hostdata;
- struct lpfc_nodelist *pnode = rdata->pnode;
+ struct lpfc_nodelist *pnode;
unsigned long later;
+ if (!rdata) {
+ lpfc_printf_vlog(vport, KERN_INFO, LOG_FCP,
+ "0797 Tgt Map rport failure: rdata x%p\n", rdata);
+ return FAILED;
+ }
+ pnode = rdata->pnode;
/*
* If target is not in a MAPPED state, delay until
* target is rediscovered or devloss timeout expires.
@@ -3253,13 +3250,19 @@ lpfc_device_reset_handler(struct scsi_cmnd *cmnd)
struct Scsi_Host *shost = cmnd->device->host;
struct lpfc_vport *vport = (struct lpfc_vport *) shost->hostdata;
struct lpfc_rport_data *rdata = cmnd->device->hostdata;
- struct lpfc_nodelist *pnode = rdata->pnode;
+ struct lpfc_nodelist *pnode;
unsigned tgt_id = cmnd->device->id;
unsigned int lun_id = cmnd->device->lun;
struct lpfc_scsi_event_header scsi_event;
int status;
- lpfc_block_error_handler(cmnd);
+ if (!rdata) {
+ lpfc_printf_vlog(vport, KERN_ERR, LOG_FCP,
+ "0798 Device Reset rport failure: rdata x%p\n", rdata);
+ return FAILED;
+ }
+ pnode = rdata->pnode;
+ fc_block_scsi_eh(cmnd);
status = lpfc_chk_tgt_mapped(vport, cmnd);
if (status == FAILED) {
@@ -3312,13 +3315,19 @@ lpfc_target_reset_handler(struct scsi_cmnd *cmnd)
struct Scsi_Host *shost = cmnd->device->host;
struct lpfc_vport *vport = (struct lpfc_vport *) shost->hostdata;
struct lpfc_rport_data *rdata = cmnd->device->hostdata;
- struct lpfc_nodelist *pnode = rdata->pnode;
+ struct lpfc_nodelist *pnode;
unsigned tgt_id = cmnd->device->id;
unsigned int lun_id = cmnd->device->lun;
struct lpfc_scsi_event_header scsi_event;
int status;
- lpfc_block_error_handler(cmnd);
+ if (!rdata) {
+ lpfc_printf_vlog(vport, KERN_ERR, LOG_FCP,
+ "0799 Target Reset rport failure: rdata x%p\n", rdata);
+ return FAILED;
+ }
+ pnode = rdata->pnode;
+ fc_block_scsi_eh(cmnd);
status = lpfc_chk_tgt_mapped(vport, cmnd);
if (status == FAILED) {
@@ -3384,7 +3393,7 @@ lpfc_bus_reset_handler(struct scsi_cmnd *cmnd)
fc_host_post_vendor_event(shost, fc_get_event_number(),
sizeof(scsi_event), (char *)&scsi_event, LPFC_NL_VENDOR_ID);
- lpfc_block_error_handler(cmnd);
+ fc_block_scsi_eh(cmnd);
/*
* Since the driver manages a single bus device, reset all
@@ -3498,6 +3507,8 @@ lpfc_slave_alloc(struct scsi_device *sdev)
"Allocated %d buffers.\n",
num_to_alloc, num_allocated);
}
+ if (num_allocated > 0)
+ phba->total_scsi_bufs += num_allocated;
return 0;
}
@@ -3534,7 +3545,8 @@ lpfc_slave_configure(struct scsi_device *sdev)
rport->dev_loss_tmo = vport->cfg_devloss_tmo;
if (phba->cfg_poll & ENABLE_FCP_RING_POLLING) {
- lpfc_sli_poll_fcp_ring(phba);
+ lpfc_sli_handle_fast_ring_event(phba,
+ &phba->sli.ring[LPFC_FCP_RING], HA_R0RE_REQ);
if (phba->cfg_poll & DISABLE_FCP_RING_INT)
lpfc_poll_rearm_timer(phba);
}
@@ -3576,6 +3588,7 @@ struct scsi_host_template lpfc_template = {
.shost_attrs = lpfc_hba_attrs,
.max_sectors = 0xFFFF,
.vendor_id = LPFC_NL_VENDOR_ID,
+ .change_queue_depth = lpfc_change_queue_depth,
};
struct scsi_host_template lpfc_vport_template = {
@@ -3597,4 +3610,5 @@ struct scsi_host_template lpfc_vport_template = {
.use_clustering = ENABLE_CLUSTERING,
.shost_attrs = lpfc_vport_attrs,
.max_sectors = 0xFFFF,
+ .change_queue_depth = lpfc_change_queue_depth,
};
diff --git a/drivers/scsi/lpfc/lpfc_sli.c b/drivers/scsi/lpfc/lpfc_sli.c
index 42d4f3dae1d..7935667b81a 100644
--- a/drivers/scsi/lpfc/lpfc_sli.c
+++ b/drivers/scsi/lpfc/lpfc_sli.c
@@ -30,6 +30,7 @@
#include <scsi/scsi_host.h>
#include <scsi/scsi_transport_fc.h>
#include <scsi/fc/fc_fs.h>
+#include <linux/aer.h>
#include "lpfc_hw4.h"
#include "lpfc_hw.h"
@@ -58,8 +59,11 @@ typedef enum _lpfc_iocb_type {
static int lpfc_sli_issue_mbox_s4(struct lpfc_hba *, LPFC_MBOXQ_t *,
uint32_t);
static int lpfc_sli4_read_rev(struct lpfc_hba *, LPFC_MBOXQ_t *,
- uint8_t *, uint32_t *);
-
+ uint8_t *, uint32_t *);
+static struct lpfc_iocbq *lpfc_sli4_els_wcqe_to_rspiocbq(struct lpfc_hba *,
+ struct lpfc_iocbq *);
+static void lpfc_sli4_send_seq_to_ulp(struct lpfc_vport *,
+ struct hbq_dmabuf *);
static IOCB_t *
lpfc_get_iocb_from_iocbq(struct lpfc_iocbq *iocbq)
{
@@ -259,6 +263,9 @@ lpfc_sli4_eq_release(struct lpfc_queue *q, bool arm)
bf_set(lpfc_eqcq_doorbell_qt, &doorbell, LPFC_QUEUE_TYPE_EVENT);
bf_set(lpfc_eqcq_doorbell_eqid, &doorbell, q->queue_id);
writel(doorbell.word0, q->phba->sli4_hba.EQCQDBregaddr);
+ /* PCI read to flush PCI pipeline on re-arming for INTx mode */
+ if ((q->phba->intr_type == INTx) && (arm == LPFC_QUEUE_REARM))
+ readl(q->phba->sli4_hba.EQCQDBregaddr);
return released;
}
@@ -515,6 +522,8 @@ __lpfc_sli_get_sglq(struct lpfc_hba *phba)
struct lpfc_sglq *sglq = NULL;
uint16_t adj_xri;
list_remove_head(lpfc_sgl_list, sglq, struct lpfc_sglq, list);
+ if (!sglq)
+ return NULL;
adj_xri = sglq->sli4_xritag - phba->sli4_hba.max_cfg_param.xri_base;
phba->sli4_hba.lpfc_sglq_active_list[adj_xri] = sglq;
return sglq;
@@ -572,9 +581,9 @@ __lpfc_sli_release_iocbq_s4(struct lpfc_hba *phba, struct lpfc_iocbq *iocbq)
sglq = __lpfc_clear_active_sglq(phba, iocbq->sli4_xritag);
if (sglq) {
if (iocbq->iocb_flag & LPFC_DRIVER_ABORTED
- || ((iocbq->iocb.ulpStatus == IOSTAT_LOCAL_REJECT)
+ && ((iocbq->iocb.ulpStatus == IOSTAT_LOCAL_REJECT)
&& (iocbq->iocb.un.ulpWord[4]
- == IOERR_SLI_ABORTED))) {
+ == IOERR_ABORT_REQUESTED))) {
spin_lock_irqsave(&phba->sli4_hba.abts_sgl_list_lock,
iflag);
list_add(&sglq->list,
@@ -767,6 +776,7 @@ lpfc_sli_iocb_cmd_type(uint8_t iocb_cmnd)
case CMD_CLOSE_XRI_CX:
case CMD_XRI_ABORTED_CX:
case CMD_ABORT_MXRI64_CN:
+ case CMD_XMIT_BLS_RSP64_CX:
type = LPFC_ABORT_IOCB;
break;
case CMD_RCV_SEQUENCE_CX:
@@ -2068,8 +2078,8 @@ lpfc_sli_process_unsol_iocb(struct lpfc_hba *phba, struct lpfc_sli_ring *pring,
if ((irsp->ulpCommand == CMD_RCV_ELS_REQ64_CX) ||
(irsp->ulpCommand == CMD_RCV_ELS_REQ_CX) ||
(irsp->ulpCommand == CMD_IOCB_RCV_ELS64_CX)) {
- Rctl = FC_ELS_REQ;
- Type = FC_ELS_DATA;
+ Rctl = FC_RCTL_ELS_REQ;
+ Type = FC_TYPE_ELS;
} else {
w5p = (WORD5 *)&(saveq->iocb.un.ulpWord[5]);
Rctl = w5p->hcsw.Rctl;
@@ -2079,8 +2089,8 @@ lpfc_sli_process_unsol_iocb(struct lpfc_hba *phba, struct lpfc_sli_ring *pring,
if ((Rctl == 0) && (pring->ringno == LPFC_ELS_RING) &&
(irsp->ulpCommand == CMD_RCV_SEQUENCE64_CX ||
irsp->ulpCommand == CMD_IOCB_RCV_SEQ64_CX)) {
- Rctl = FC_ELS_REQ;
- Type = FC_ELS_DATA;
+ Rctl = FC_RCTL_ELS_REQ;
+ Type = FC_TYPE_ELS;
w5p->hcsw.Rctl = Rctl;
w5p->hcsw.Type = Type;
}
@@ -2324,168 +2334,6 @@ void lpfc_poll_eratt(unsigned long ptr)
return;
}
-/**
- * lpfc_sli_poll_fcp_ring - Handle FCP ring completion in polling mode
- * @phba: Pointer to HBA context object.
- *
- * This function is called from lpfc_queuecommand, lpfc_poll_timeout,
- * lpfc_abort_handler and lpfc_slave_configure when FCP_RING_POLLING
- * is enabled.
- *
- * The caller does not hold any lock.
- * The function processes each response iocb in the response ring until it
- * finds an iocb with LE bit set and chains all the iocbs upto the iocb with
- * LE bit set. The function will call the completion handler of the command iocb
- * if the response iocb indicates a completion for a command iocb or it is
- * an abort completion.
- **/
-void lpfc_sli_poll_fcp_ring(struct lpfc_hba *phba)
-{
- struct lpfc_sli *psli = &phba->sli;
- struct lpfc_sli_ring *pring = &psli->ring[LPFC_FCP_RING];
- IOCB_t *irsp = NULL;
- IOCB_t *entry = NULL;
- struct lpfc_iocbq *cmdiocbq = NULL;
- struct lpfc_iocbq rspiocbq;
- struct lpfc_pgp *pgp = &phba->port_gp[pring->ringno];
- uint32_t status;
- uint32_t portRspPut, portRspMax;
- int type;
- uint32_t rsp_cmpl = 0;
- uint32_t ha_copy;
- unsigned long iflags;
-
- pring->stats.iocb_event++;
-
- /*
- * The next available response entry should never exceed the maximum
- * entries. If it does, treat it as an adapter hardware error.
- */
- portRspMax = pring->numRiocb;
- portRspPut = le32_to_cpu(pgp->rspPutInx);
- if (unlikely(portRspPut >= portRspMax)) {
- lpfc_sli_rsp_pointers_error(phba, pring);
- return;
- }
-
- rmb();
- while (pring->rspidx != portRspPut) {
- entry = lpfc_resp_iocb(phba, pring);
- if (++pring->rspidx >= portRspMax)
- pring->rspidx = 0;
-
- lpfc_sli_pcimem_bcopy((uint32_t *) entry,
- (uint32_t *) &rspiocbq.iocb,
- phba->iocb_rsp_size);
- irsp = &rspiocbq.iocb;
- type = lpfc_sli_iocb_cmd_type(irsp->ulpCommand & CMD_IOCB_MASK);
- pring->stats.iocb_rsp++;
- rsp_cmpl++;
-
- if (unlikely(irsp->ulpStatus)) {
- /* Rsp ring <ringno> error: IOCB */
- lpfc_printf_log(phba, KERN_WARNING, LOG_SLI,
- "0326 Rsp Ring %d error: IOCB Data: "
- "x%x x%x x%x x%x x%x x%x x%x x%x\n",
- pring->ringno,
- irsp->un.ulpWord[0],
- irsp->un.ulpWord[1],
- irsp->un.ulpWord[2],
- irsp->un.ulpWord[3],
- irsp->un.ulpWord[4],
- irsp->un.ulpWord[5],
- *(uint32_t *)&irsp->un1,
- *((uint32_t *)&irsp->un1 + 1));
- }
-
- switch (type) {
- case LPFC_ABORT_IOCB:
- case LPFC_SOL_IOCB:
- /*
- * Idle exchange closed via ABTS from port. No iocb
- * resources need to be recovered.
- */
- if (unlikely(irsp->ulpCommand == CMD_XRI_ABORTED_CX)) {
- lpfc_printf_log(phba, KERN_INFO, LOG_SLI,
- "0314 IOCB cmd 0x%x "
- "processed. Skipping "
- "completion",
- irsp->ulpCommand);
- break;
- }
-
- spin_lock_irqsave(&phba->hbalock, iflags);
- cmdiocbq = lpfc_sli_iocbq_lookup(phba, pring,
- &rspiocbq);
- spin_unlock_irqrestore(&phba->hbalock, iflags);
- if ((cmdiocbq) && (cmdiocbq->iocb_cmpl)) {
- (cmdiocbq->iocb_cmpl)(phba, cmdiocbq,
- &rspiocbq);
- }
- break;
- default:
- if (irsp->ulpCommand == CMD_ADAPTER_MSG) {
- char adaptermsg[LPFC_MAX_ADPTMSG];
- memset(adaptermsg, 0, LPFC_MAX_ADPTMSG);
- memcpy(&adaptermsg[0], (uint8_t *) irsp,
- MAX_MSG_DATA);
- dev_warn(&((phba->pcidev)->dev),
- "lpfc%d: %s\n",
- phba->brd_no, adaptermsg);
- } else {
- /* Unknown IOCB command */
- lpfc_printf_log(phba, KERN_ERR, LOG_SLI,
- "0321 Unknown IOCB command "
- "Data: x%x, x%x x%x x%x x%x\n",
- type, irsp->ulpCommand,
- irsp->ulpStatus,
- irsp->ulpIoTag,
- irsp->ulpContext);
- }
- break;
- }
-
- /*
- * The response IOCB has been processed. Update the ring
- * pointer in SLIM. If the port response put pointer has not
- * been updated, sync the pgp->rspPutInx and fetch the new port
- * response put pointer.
- */
- writel(pring->rspidx, &phba->host_gp[pring->ringno].rspGetInx);
-
- if (pring->rspidx == portRspPut)
- portRspPut = le32_to_cpu(pgp->rspPutInx);
- }
-
- ha_copy = readl(phba->HAregaddr);
- ha_copy >>= (LPFC_FCP_RING * 4);
-
- if ((rsp_cmpl > 0) && (ha_copy & HA_R0RE_REQ)) {
- spin_lock_irqsave(&phba->hbalock, iflags);
- pring->stats.iocb_rsp_full++;
- status = ((CA_R0ATT | CA_R0RE_RSP) << (LPFC_FCP_RING * 4));
- writel(status, phba->CAregaddr);
- readl(phba->CAregaddr);
- spin_unlock_irqrestore(&phba->hbalock, iflags);
- }
- if ((ha_copy & HA_R0CE_RSP) &&
- (pring->flag & LPFC_CALL_RING_AVAILABLE)) {
- spin_lock_irqsave(&phba->hbalock, iflags);
- pring->flag &= ~LPFC_CALL_RING_AVAILABLE;
- pring->stats.iocb_cmd_empty++;
-
- /* Force update of the local copy of cmdGetInx */
- pring->local_getidx = le32_to_cpu(pgp->cmdGetInx);
- lpfc_sli_resume_iocb(phba, pring);
-
- if ((pring->lpfc_sli_cmd_available))
- (pring->lpfc_sli_cmd_available) (phba, pring);
-
- spin_unlock_irqrestore(&phba->hbalock, iflags);
- }
-
- return;
-}
/**
* lpfc_sli_handle_fast_ring_event - Handle ring events on FCP ring
@@ -2502,9 +2350,9 @@ void lpfc_sli_poll_fcp_ring(struct lpfc_hba *phba)
* an abort completion. The function will call lpfc_sli_process_unsol_iocb
* function if this is an unsolicited iocb.
* This routine presumes LPFC_FCP_RING handling and doesn't bother
- * to check it explicitly. This function always returns 1.
- **/
-static int
+ * to check it explicitly.
+ */
+int
lpfc_sli_handle_fast_ring_event(struct lpfc_hba *phba,
struct lpfc_sli_ring *pring, uint32_t mask)
{
@@ -2534,6 +2382,11 @@ lpfc_sli_handle_fast_ring_event(struct lpfc_hba *phba,
spin_unlock_irqrestore(&phba->hbalock, iflag);
return 1;
}
+ if (phba->fcp_ring_in_use) {
+ spin_unlock_irqrestore(&phba->hbalock, iflag);
+ return 1;
+ } else
+ phba->fcp_ring_in_use = 1;
rmb();
while (pring->rspidx != portRspPut) {
@@ -2604,10 +2457,6 @@ lpfc_sli_handle_fast_ring_event(struct lpfc_hba *phba,
cmdiocbq = lpfc_sli_iocbq_lookup(phba, pring,
&rspiocbq);
if ((cmdiocbq) && (cmdiocbq->iocb_cmpl)) {
- if (phba->cfg_poll & ENABLE_FCP_RING_POLLING) {
- (cmdiocbq->iocb_cmpl)(phba, cmdiocbq,
- &rspiocbq);
- } else {
spin_unlock_irqrestore(&phba->hbalock,
iflag);
(cmdiocbq->iocb_cmpl)(phba, cmdiocbq,
@@ -2615,7 +2464,6 @@ lpfc_sli_handle_fast_ring_event(struct lpfc_hba *phba,
spin_lock_irqsave(&phba->hbalock,
iflag);
}
- }
break;
case LPFC_UNSOL_IOCB:
spin_unlock_irqrestore(&phba->hbalock, iflag);
@@ -2675,6 +2523,7 @@ lpfc_sli_handle_fast_ring_event(struct lpfc_hba *phba,
}
+ phba->fcp_ring_in_use = 0;
spin_unlock_irqrestore(&phba->hbalock, iflag);
return rc;
}
@@ -3018,16 +2867,39 @@ lpfc_sli_handle_slow_ring_event_s4(struct lpfc_hba *phba,
struct lpfc_sli_ring *pring, uint32_t mask)
{
struct lpfc_iocbq *irspiocbq;
+ struct hbq_dmabuf *dmabuf;
+ struct lpfc_cq_event *cq_event;
unsigned long iflag;
- while (!list_empty(&phba->sli4_hba.sp_rspiocb_work_queue)) {
+ spin_lock_irqsave(&phba->hbalock, iflag);
+ phba->hba_flag &= ~HBA_SP_QUEUE_EVT;
+ spin_unlock_irqrestore(&phba->hbalock, iflag);
+ while (!list_empty(&phba->sli4_hba.sp_queue_event)) {
/* Get the response iocb from the head of work queue */
spin_lock_irqsave(&phba->hbalock, iflag);
- list_remove_head(&phba->sli4_hba.sp_rspiocb_work_queue,
- irspiocbq, struct lpfc_iocbq, list);
+ list_remove_head(&phba->sli4_hba.sp_queue_event,
+ cq_event, struct lpfc_cq_event, list);
spin_unlock_irqrestore(&phba->hbalock, iflag);
- /* Process the response iocb */
- lpfc_sli_sp_handle_rspiocb(phba, pring, irspiocbq);
+
+ switch (bf_get(lpfc_wcqe_c_code, &cq_event->cqe.wcqe_cmpl)) {
+ case CQE_CODE_COMPL_WQE:
+ irspiocbq = container_of(cq_event, struct lpfc_iocbq,
+ cq_event);
+ /* Translate ELS WCQE to response IOCBQ */
+ irspiocbq = lpfc_sli4_els_wcqe_to_rspiocbq(phba,
+ irspiocbq);
+ if (irspiocbq)
+ lpfc_sli_sp_handle_rspiocb(phba, pring,
+ irspiocbq);
+ break;
+ case CQE_CODE_RECEIVE:
+ dmabuf = container_of(cq_event, struct hbq_dmabuf,
+ cq_event);
+ lpfc_sli4_handle_received_buffer(phba, dmabuf);
+ break;
+ default:
+ break;
+ }
}
}
@@ -3416,6 +3288,7 @@ lpfc_sli_brdreset(struct lpfc_hba *phba)
/* perform board reset */
phba->fc_eventTag = 0;
+ phba->link_events = 0;
phba->pport->fc_myDID = 0;
phba->pport->fc_prevDID = 0;
@@ -3476,6 +3349,7 @@ lpfc_sli4_brdreset(struct lpfc_hba *phba)
/* perform board reset */
phba->fc_eventTag = 0;
+ phba->link_events = 0;
phba->pport->fc_myDID = 0;
phba->pport->fc_prevDID = 0;
@@ -3495,7 +3369,6 @@ lpfc_sli4_brdreset(struct lpfc_hba *phba)
list_del_init(&phba->sli4_hba.dat_rq->list);
list_del_init(&phba->sli4_hba.mbx_cq->list);
list_del_init(&phba->sli4_hba.els_cq->list);
- list_del_init(&phba->sli4_hba.rxq_cq->list);
for (qindx = 0; qindx < phba->cfg_fcp_wq_count; qindx++)
list_del_init(&phba->sli4_hba.fcp_wq[qindx]->list);
for (qindx = 0; qindx < phba->cfg_fcp_eq_count; qindx++)
@@ -3531,9 +3404,13 @@ lpfc_sli_brdrestart_s3(struct lpfc_hba *phba)
struct lpfc_sli *psli;
volatile uint32_t word0;
void __iomem *to_slim;
+ uint32_t hba_aer_enabled;
spin_lock_irq(&phba->hbalock);
+ /* Take PCIe device Advanced Error Reporting (AER) state */
+ hba_aer_enabled = phba->hba_flag & HBA_AER_ENABLED;
+
psli = &phba->sli;
/* Restart HBA */
@@ -3573,6 +3450,10 @@ lpfc_sli_brdrestart_s3(struct lpfc_hba *phba)
/* Give the INITFF and Post time to settle. */
mdelay(100);
+ /* Reset HBA AER if it was enabled, note hba_flag was reset above */
+ if (hba_aer_enabled)
+ pci_disable_pcie_error_reporting(phba->pcidev);
+
lpfc_hba_down_post(phba);
return 0;
@@ -4042,6 +3923,24 @@ lpfc_sli_hba_setup(struct lpfc_hba *phba)
if (rc)
goto lpfc_sli_hba_setup_error;
+ /* Enable PCIe device Advanced Error Reporting (AER) if configured */
+ if (phba->cfg_aer_support == 1 && !(phba->hba_flag & HBA_AER_ENABLED)) {
+ rc = pci_enable_pcie_error_reporting(phba->pcidev);
+ if (!rc) {
+ lpfc_printf_log(phba, KERN_INFO, LOG_INIT,
+ "2709 This device supports "
+ "Advanced Error Reporting (AER)\n");
+ spin_lock_irq(&phba->hbalock);
+ phba->hba_flag |= HBA_AER_ENABLED;
+ spin_unlock_irq(&phba->hbalock);
+ } else {
+ lpfc_printf_log(phba, KERN_INFO, LOG_INIT,
+ "2708 This device does not support "
+ "Advanced Error Reporting (AER)\n");
+ phba->cfg_aer_support = 0;
+ }
+ }
+
if (phba->sli_rev == 3) {
phba->iocb_cmd_size = SLI3_IOCB_CMD_SIZE;
phba->iocb_rsp_size = SLI3_IOCB_RSP_SIZE;
@@ -4243,7 +4142,6 @@ lpfc_sli4_arm_cqeq_intr(struct lpfc_hba *phba)
lpfc_sli4_cq_release(phba->sli4_hba.mbx_cq, LPFC_QUEUE_REARM);
lpfc_sli4_cq_release(phba->sli4_hba.els_cq, LPFC_QUEUE_REARM);
- lpfc_sli4_cq_release(phba->sli4_hba.rxq_cq, LPFC_QUEUE_REARM);
for (fcp_eqidx = 0; fcp_eqidx < phba->cfg_fcp_eq_count; fcp_eqidx++)
lpfc_sli4_cq_release(phba->sli4_hba.fcp_cq[fcp_eqidx],
LPFC_QUEUE_REARM);
@@ -4322,6 +4220,13 @@ lpfc_sli4_hba_setup(struct lpfc_hba *phba)
phba->sli_rev = bf_get(lpfc_mbx_rd_rev_sli_lvl, &mqe->un.read_rev);
if (bf_get(lpfc_mbx_rd_rev_fcoe, &mqe->un.read_rev))
phba->hba_flag |= HBA_FCOE_SUPPORT;
+
+ if (bf_get(lpfc_mbx_rd_rev_cee_ver, &mqe->un.read_rev) ==
+ LPFC_DCBX_CEE_MODE)
+ phba->hba_flag |= HBA_FIP_SUPPORT;
+ else
+ phba->hba_flag &= ~HBA_FIP_SUPPORT;
+
if (phba->sli_rev != LPFC_SLI_REV4 ||
!(phba->hba_flag & HBA_FCOE_SUPPORT)) {
lpfc_printf_log(phba, KERN_ERR, LOG_MBOX | LOG_SLI,
@@ -4468,7 +4373,8 @@ lpfc_sli4_hba_setup(struct lpfc_hba *phba)
rc = lpfc_sli4_post_sgl_list(phba);
if (unlikely(rc)) {
lpfc_printf_log(phba, KERN_ERR, LOG_MBOX | LOG_SLI,
- "0582 Error %d during sgl post operation", rc);
+ "0582 Error %d during sgl post operation\n",
+ rc);
rc = -ENODEV;
goto out_free_vpd;
}
@@ -4477,8 +4383,8 @@ lpfc_sli4_hba_setup(struct lpfc_hba *phba)
rc = lpfc_sli4_repost_scsi_sgl_list(phba);
if (unlikely(rc)) {
lpfc_printf_log(phba, KERN_WARNING, LOG_MBOX | LOG_SLI,
- "0383 Error %d during scsi sgl post opeation",
- rc);
+ "0383 Error %d during scsi sgl post "
+ "operation\n", rc);
/* Some Scsi buffers were moved to the abort scsi list */
/* A pci function reset will repost them */
rc = -ENODEV;
@@ -4494,10 +4400,6 @@ lpfc_sli4_hba_setup(struct lpfc_hba *phba)
rc = -ENODEV;
goto out_free_vpd;
}
- if (phba->cfg_enable_fip)
- bf_set(lpfc_fip_flag, &phba->sli4_hba.sli4_flags, 1);
- else
- bf_set(lpfc_fip_flag, &phba->sli4_hba.sli4_flags, 0);
/* Set up all the queues to the device */
rc = lpfc_sli4_queue_setup(phba);
@@ -5669,7 +5571,7 @@ __lpfc_sli_issue_iocb_s3(struct lpfc_hba *phba, uint32_t ring_number,
case CMD_GEN_REQUEST64_CX:
if (!(phba->sli.sli_flag & LPFC_MENLO_MAINT) ||
(piocb->iocb.un.genreq64.w5.hcsw.Rctl !=
- FC_FCP_CMND) ||
+ FC_RCTL_DD_UNSOL_CMD) ||
(piocb->iocb.un.genreq64.w5.hcsw.Type !=
MENLO_TRANSPORT_TYPE))
@@ -5849,7 +5751,7 @@ static int
lpfc_sli4_iocb2wqe(struct lpfc_hba *phba, struct lpfc_iocbq *iocbq,
union lpfc_wqe *wqe)
{
- uint32_t payload_len = 0;
+ uint32_t xmit_len = 0, total_len = 0;
uint8_t ct = 0;
uint32_t fip;
uint32_t abort_tag;
@@ -5857,12 +5759,15 @@ lpfc_sli4_iocb2wqe(struct lpfc_hba *phba, struct lpfc_iocbq *iocbq,
uint8_t cmnd;
uint16_t xritag;
struct ulp_bde64 *bpl = NULL;
+ uint32_t els_id = ELS_ID_DEFAULT;
+ int numBdes, i;
+ struct ulp_bde64 bde;
- fip = bf_get(lpfc_fip_flag, &phba->sli4_hba.sli4_flags);
+ fip = phba->hba_flag & HBA_FIP_SUPPORT;
/* The fcp commands will set command type */
if (iocbq->iocb_flag & LPFC_IO_FCP)
command_type = FCP_COMMAND;
- else if (fip && (iocbq->iocb_flag & LPFC_FIP_ELS))
+ else if (fip && (iocbq->iocb_flag & LPFC_FIP_ELS_ID_MASK))
command_type = ELS_COMMAND_FIP;
else
command_type = ELS_COMMAND_NON_FIP;
@@ -5874,6 +5779,8 @@ lpfc_sli4_iocb2wqe(struct lpfc_hba *phba, struct lpfc_iocbq *iocbq,
wqe->words[7] = 0; /* The ct field has moved so reset */
/* words0-2 bpl convert bde */
if (iocbq->iocb.un.genreq64.bdl.bdeFlags == BUFF_TYPE_BLP_64) {
+ numBdes = iocbq->iocb.un.genreq64.bdl.bdeSize /
+ sizeof(struct ulp_bde64);
bpl = (struct ulp_bde64 *)
((struct lpfc_dmabuf *)iocbq->context3)->virt;
if (!bpl)
@@ -5886,9 +5793,14 @@ lpfc_sli4_iocb2wqe(struct lpfc_hba *phba, struct lpfc_iocbq *iocbq,
* can assign it to the sgl.
*/
wqe->generic.bde.tus.w = le32_to_cpu(bpl->tus.w);
- payload_len = wqe->generic.bde.tus.f.bdeSize;
+ xmit_len = wqe->generic.bde.tus.f.bdeSize;
+ total_len = 0;
+ for (i = 0; i < numBdes; i++) {
+ bde.tus.w = le32_to_cpu(bpl[i].tus.w);
+ total_len += bde.tus.f.bdeSize;
+ }
} else
- payload_len = iocbq->iocb.un.fcpi64.bdl.bdeSize;
+ xmit_len = iocbq->iocb.un.fcpi64.bdl.bdeSize;
iocbq->iocb.ulpIoTag = iocbq->iotag;
cmnd = iocbq->iocb.ulpCommand;
@@ -5902,7 +5814,7 @@ lpfc_sli4_iocb2wqe(struct lpfc_hba *phba, struct lpfc_iocbq *iocbq,
iocbq->iocb.ulpCommand);
return IOCB_ERROR;
}
- wqe->els_req.payload_len = payload_len;
+ wqe->els_req.payload_len = xmit_len;
/* Els_reguest64 has a TMO */
bf_set(wqe_tmo, &wqe->els_req.wqe_com,
iocbq->iocb.ulpTimeout);
@@ -5923,7 +5835,23 @@ lpfc_sli4_iocb2wqe(struct lpfc_hba *phba, struct lpfc_iocbq *iocbq,
bf_set(lpfc_wqe_gen_ct, &wqe->generic, ct);
bf_set(lpfc_wqe_gen_pu, &wqe->generic, 0);
/* CCP CCPE PV PRI in word10 were set in the memcpy */
+
+ if (command_type == ELS_COMMAND_FIP) {
+ els_id = ((iocbq->iocb_flag & LPFC_FIP_ELS_ID_MASK)
+ >> LPFC_FIP_ELS_ID_SHIFT);
+ }
+ bf_set(lpfc_wqe_gen_els_id, &wqe->generic, els_id);
+
break;
+ case CMD_XMIT_SEQUENCE64_CX:
+ bf_set(lpfc_wqe_gen_context, &wqe->generic,
+ iocbq->iocb.un.ulpWord[3]);
+ wqe->generic.word3 = 0;
+ bf_set(wqe_rcvoxid, &wqe->generic, iocbq->iocb.ulpContext);
+ bf_set(wqe_xc, &wqe->generic, 1);
+ /* The entire sequence is transmitted for this IOCB */
+ xmit_len = total_len;
+ cmnd = CMD_XMIT_SEQUENCE64_CR;
case CMD_XMIT_SEQUENCE64_CR:
/* word3 iocb=io_tag32 wqe=payload_offset */
/* payload offset used for multilpe outstanding
@@ -5933,7 +5861,8 @@ lpfc_sli4_iocb2wqe(struct lpfc_hba *phba, struct lpfc_iocbq *iocbq,
/* word4 relative_offset memcpy */
/* word5 r_ctl/df_ctl memcpy */
bf_set(lpfc_wqe_gen_pu, &wqe->generic, 0);
- wqe->xmit_sequence.xmit_len = payload_len;
+ wqe->xmit_sequence.xmit_len = xmit_len;
+ command_type = OTHER_COMMAND;
break;
case CMD_XMIT_BCAST64_CN:
/* word3 iocb=iotag32 wqe=payload_len */
@@ -5962,7 +5891,7 @@ lpfc_sli4_iocb2wqe(struct lpfc_hba *phba, struct lpfc_iocbq *iocbq,
case CMD_FCP_IREAD64_CR:
/* FCP_CMD is always the 1st sgl entry */
wqe->fcp_iread.payload_len =
- payload_len + sizeof(struct fcp_rsp);
+ xmit_len + sizeof(struct fcp_rsp);
/* word 4 (xfer length) should have been set on the memcpy */
@@ -5999,7 +5928,7 @@ lpfc_sli4_iocb2wqe(struct lpfc_hba *phba, struct lpfc_iocbq *iocbq,
* sgl[1] = rsp.
*
*/
- wqe->gen_req.command_len = payload_len;
+ wqe->gen_req.command_len = xmit_len;
/* Word4 parameter copied in the memcpy */
/* Word5 [rctl, type, df_ctl, la] copied in memcpy */
/* word6 context tag copied in memcpy */
@@ -6066,6 +5995,38 @@ lpfc_sli4_iocb2wqe(struct lpfc_hba *phba, struct lpfc_iocbq *iocbq,
command_type = OTHER_COMMAND;
xritag = 0;
break;
+ case CMD_XMIT_BLS_RSP64_CX:
+ /* As BLS ABTS-ACC WQE is very different from other WQEs,
+ * we re-construct this WQE here based on information in
+ * iocbq from scratch.
+ */
+ memset(wqe, 0, sizeof(union lpfc_wqe));
+ /* OX_ID is invariable to who sent ABTS to CT exchange */
+ bf_set(xmit_bls_rsp64_oxid, &wqe->xmit_bls_rsp,
+ bf_get(lpfc_abts_oxid, &iocbq->iocb.un.bls_acc));
+ if (bf_get(lpfc_abts_orig, &iocbq->iocb.un.bls_acc) ==
+ LPFC_ABTS_UNSOL_INT) {
+ /* ABTS sent by initiator to CT exchange, the
+ * RX_ID field will be filled with the newly
+ * allocated responder XRI.
+ */
+ bf_set(xmit_bls_rsp64_rxid, &wqe->xmit_bls_rsp,
+ iocbq->sli4_xritag);
+ } else {
+ /* ABTS sent by responder to CT exchange, the
+ * RX_ID field will be filled with the responder
+ * RX_ID from ABTS.
+ */
+ bf_set(xmit_bls_rsp64_rxid, &wqe->xmit_bls_rsp,
+ bf_get(lpfc_abts_rxid, &iocbq->iocb.un.bls_acc));
+ }
+ bf_set(xmit_bls_rsp64_seqcnthi, &wqe->xmit_bls_rsp, 0xffff);
+ bf_set(wqe_xmit_bls_pt, &wqe->xmit_bls_rsp.wqe_dest, 0x1);
+ bf_set(wqe_ctxt_tag, &wqe->xmit_bls_rsp.wqe_com,
+ iocbq->iocb.ulpContext);
+ /* Overwrite the pre-set comnd type with OTHER_COMMAND */
+ command_type = OTHER_COMMAND;
+ break;
case CMD_XRI_ABORTED_CX:
case CMD_CREATE_XRI_CR: /* Do we expect to use this? */
/* words0-2 are all 0's no bde */
@@ -6120,11 +6081,10 @@ __lpfc_sli_issue_iocb_s4(struct lpfc_hba *phba, uint32_t ring_number,
uint16_t xritag;
union lpfc_wqe wqe;
struct lpfc_sli_ring *pring = &phba->sli.ring[ring_number];
- uint32_t fcp_wqidx;
if (piocb->sli4_xritag == NO_XRI) {
if (piocb->iocb.ulpCommand == CMD_ABORT_XRI_CN ||
- piocb->iocb.ulpCommand == CMD_CLOSE_XRI_CN)
+ piocb->iocb.ulpCommand == CMD_CLOSE_XRI_CN)
sglq = NULL;
else {
sglq = __lpfc_sli_get_sglq(phba);
@@ -6155,8 +6115,17 @@ __lpfc_sli_issue_iocb_s4(struct lpfc_hba *phba, uint32_t ring_number,
return IOCB_ERROR;
if (piocb->iocb_flag & LPFC_IO_FCP) {
- fcp_wqidx = lpfc_sli4_scmd_to_wqidx_distr(phba);
- if (lpfc_sli4_wq_put(phba->sli4_hba.fcp_wq[fcp_wqidx], &wqe))
+ /*
+ * For FCP command IOCB, get a new WQ index to distribute
+ * WQE across the WQsr. On the other hand, for abort IOCB,
+ * it carries the same WQ index to the original command
+ * IOCB.
+ */
+ if ((piocb->iocb.ulpCommand != CMD_ABORT_XRI_CN) &&
+ (piocb->iocb.ulpCommand != CMD_CLOSE_XRI_CN))
+ piocb->fcp_wqidx = lpfc_sli4_scmd_to_wqidx_distr(phba);
+ if (lpfc_sli4_wq_put(phba->sli4_hba.fcp_wq[piocb->fcp_wqidx],
+ &wqe))
return IOCB_ERROR;
} else {
if (lpfc_sli4_wq_put(phba->sli4_hba.els_wq, &wqe))
@@ -6449,31 +6418,37 @@ lpfc_sli_setup(struct lpfc_hba *phba)
pring->iotag_max = 4096;
pring->lpfc_sli_rcv_async_status =
lpfc_sli_async_event_handler;
- pring->num_mask = 4;
+ pring->num_mask = LPFC_MAX_RING_MASK;
pring->prt[0].profile = 0; /* Mask 0 */
- pring->prt[0].rctl = FC_ELS_REQ;
- pring->prt[0].type = FC_ELS_DATA;
+ pring->prt[0].rctl = FC_RCTL_ELS_REQ;
+ pring->prt[0].type = FC_TYPE_ELS;
pring->prt[0].lpfc_sli_rcv_unsol_event =
lpfc_els_unsol_event;
pring->prt[1].profile = 0; /* Mask 1 */
- pring->prt[1].rctl = FC_ELS_RSP;
- pring->prt[1].type = FC_ELS_DATA;
+ pring->prt[1].rctl = FC_RCTL_ELS_REP;
+ pring->prt[1].type = FC_TYPE_ELS;
pring->prt[1].lpfc_sli_rcv_unsol_event =
lpfc_els_unsol_event;
pring->prt[2].profile = 0; /* Mask 2 */
/* NameServer Inquiry */
- pring->prt[2].rctl = FC_UNSOL_CTL;
+ pring->prt[2].rctl = FC_RCTL_DD_UNSOL_CTL;
/* NameServer */
- pring->prt[2].type = FC_COMMON_TRANSPORT_ULP;
+ pring->prt[2].type = FC_TYPE_CT;
pring->prt[2].lpfc_sli_rcv_unsol_event =
lpfc_ct_unsol_event;
pring->prt[3].profile = 0; /* Mask 3 */
/* NameServer response */
- pring->prt[3].rctl = FC_SOL_CTL;
+ pring->prt[3].rctl = FC_RCTL_DD_SOL_CTL;
/* NameServer */
- pring->prt[3].type = FC_COMMON_TRANSPORT_ULP;
+ pring->prt[3].type = FC_TYPE_CT;
pring->prt[3].lpfc_sli_rcv_unsol_event =
lpfc_ct_unsol_event;
+ /* abort unsolicited sequence */
+ pring->prt[4].profile = 0; /* Mask 4 */
+ pring->prt[4].rctl = FC_RCTL_BA_ABTS;
+ pring->prt[4].type = FC_TYPE_BLS;
+ pring->prt[4].lpfc_sli_rcv_unsol_event =
+ lpfc_sli4_ct_abort_unsol_event;
break;
}
totiocbsize += (pring->numCiocb * pring->sizeCiocb) +
@@ -6976,8 +6951,18 @@ lpfc_sli_abort_els_cmpl(struct lpfc_hba *phba, struct lpfc_iocbq *cmdiocb,
abort_iotag = cmdiocb->iocb.un.acxri.abortIoTag;
spin_lock_irq(&phba->hbalock);
- if (abort_iotag != 0 && abort_iotag <= phba->sli.last_iotag)
- abort_iocb = phba->sli.iocbq_lookup[abort_iotag];
+ if (phba->sli_rev < LPFC_SLI_REV4) {
+ if (abort_iotag != 0 &&
+ abort_iotag <= phba->sli.last_iotag)
+ abort_iocb =
+ phba->sli.iocbq_lookup[abort_iotag];
+ } else
+ /* For sli4 the abort_tag is the XRI,
+ * so the abort routine puts the iotag of the iocb
+ * being aborted in the context field of the abort
+ * IOCB.
+ */
+ abort_iocb = phba->sli.iocbq_lookup[abort_context];
lpfc_printf_log(phba, KERN_INFO, LOG_ELS | LOG_SLI,
"0327 Cannot abort els iocb %p "
@@ -6991,9 +6976,18 @@ lpfc_sli_abort_els_cmpl(struct lpfc_hba *phba, struct lpfc_iocbq *cmdiocb,
* might have completed already. Do not free it again.
*/
if (irsp->ulpStatus == IOSTAT_LOCAL_REJECT) {
- spin_unlock_irq(&phba->hbalock);
- lpfc_sli_release_iocbq(phba, cmdiocb);
- return;
+ if (irsp->un.ulpWord[4] != IOERR_NO_XRI) {
+ spin_unlock_irq(&phba->hbalock);
+ lpfc_sli_release_iocbq(phba, cmdiocb);
+ return;
+ }
+ /* For SLI4 the ulpContext field for abort IOCB
+ * holds the iotag of the IOCB being aborted so
+ * the local abort_context needs to be reset to
+ * match the aborted IOCBs ulpContext.
+ */
+ if (abort_iocb && phba->sli_rev == LPFC_SLI_REV4)
+ abort_context = abort_iocb->iocb.ulpContext;
}
/*
* make sure we have the right iocbq before taking it
@@ -7112,13 +7106,18 @@ lpfc_sli_issue_abort_iotag(struct lpfc_hba *phba, struct lpfc_sli_ring *pring,
iabt = &abtsiocbp->iocb;
iabt->un.acxri.abortType = ABORT_TYPE_ABTS;
iabt->un.acxri.abortContextTag = icmd->ulpContext;
- if (phba->sli_rev == LPFC_SLI_REV4)
+ if (phba->sli_rev == LPFC_SLI_REV4) {
iabt->un.acxri.abortIoTag = cmdiocb->sli4_xritag;
+ iabt->un.acxri.abortContextTag = cmdiocb->iotag;
+ }
else
iabt->un.acxri.abortIoTag = icmd->ulpIoTag;
iabt->ulpLe = 1;
iabt->ulpClass = icmd->ulpClass;
+ /* ABTS WQE must go to the same WQ as the WQE to be aborted */
+ abtsiocbp->fcp_wqidx = cmdiocb->fcp_wqidx;
+
if (phba->link_state >= LPFC_LINK_UP)
iabt->ulpCommand = CMD_ABORT_XRI_CN;
else
@@ -7322,6 +7321,9 @@ lpfc_sli_abort_iocb(struct lpfc_vport *vport, struct lpfc_sli_ring *pring,
abtsiocb->iocb.ulpClass = cmd->ulpClass;
abtsiocb->vport = phba->pport;
+ /* ABTS WQE must go to the same WQ as the WQE to be aborted */
+ abtsiocb->fcp_wqidx = iocbq->fcp_wqidx;
+
if (lpfc_is_link_up(phba))
abtsiocb->iocb.ulpCommand = CMD_ABORT_XRI_CN;
else
@@ -7687,31 +7689,28 @@ static int
lpfc_sli4_eratt_read(struct lpfc_hba *phba)
{
uint32_t uerr_sta_hi, uerr_sta_lo;
- uint32_t onlnreg0, onlnreg1;
/* For now, use the SLI4 device internal unrecoverable error
* registers for error attention. This can be changed later.
*/
- onlnreg0 = readl(phba->sli4_hba.ONLINE0regaddr);
- onlnreg1 = readl(phba->sli4_hba.ONLINE1regaddr);
- if ((onlnreg0 != LPFC_ONLINE_NERR) || (onlnreg1 != LPFC_ONLINE_NERR)) {
- uerr_sta_lo = readl(phba->sli4_hba.UERRLOregaddr);
- uerr_sta_hi = readl(phba->sli4_hba.UERRHIregaddr);
- if (uerr_sta_lo || uerr_sta_hi) {
- lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
- "1423 HBA Unrecoverable error: "
- "uerr_lo_reg=0x%x, uerr_hi_reg=0x%x, "
- "online0_reg=0x%x, online1_reg=0x%x\n",
- uerr_sta_lo, uerr_sta_hi,
- onlnreg0, onlnreg1);
- phba->work_status[0] = uerr_sta_lo;
- phba->work_status[1] = uerr_sta_hi;
- /* Set the driver HA work bitmap */
- phba->work_ha |= HA_ERATT;
- /* Indicate polling handles this ERATT */
- phba->hba_flag |= HBA_ERATT_HANDLED;
- return 1;
- }
+ uerr_sta_lo = readl(phba->sli4_hba.UERRLOregaddr);
+ uerr_sta_hi = readl(phba->sli4_hba.UERRHIregaddr);
+ if ((~phba->sli4_hba.ue_mask_lo & uerr_sta_lo) ||
+ (~phba->sli4_hba.ue_mask_hi & uerr_sta_hi)) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+ "1423 HBA Unrecoverable error: "
+ "uerr_lo_reg=0x%x, uerr_hi_reg=0x%x, "
+ "ue_mask_lo_reg=0x%x, ue_mask_hi_reg=0x%x\n",
+ uerr_sta_lo, uerr_sta_hi,
+ phba->sli4_hba.ue_mask_lo,
+ phba->sli4_hba.ue_mask_hi);
+ phba->work_status[0] = uerr_sta_lo;
+ phba->work_status[1] = uerr_sta_hi;
+ /* Set the driver HA work bitmap */
+ phba->work_ha |= HA_ERATT;
+ /* Indicate polling handles this ERATT */
+ phba->hba_flag |= HBA_ERATT_HANDLED;
+ return 1;
}
return 0;
}
@@ -7834,7 +7833,7 @@ irqreturn_t
lpfc_sli_sp_intr_handler(int irq, void *dev_id)
{
struct lpfc_hba *phba;
- uint32_t ha_copy;
+ uint32_t ha_copy, hc_copy;
uint32_t work_ha_copy;
unsigned long status;
unsigned long iflag;
@@ -7892,8 +7891,13 @@ lpfc_sli_sp_intr_handler(int irq, void *dev_id)
}
/* Clear up only attention source related to slow-path */
+ hc_copy = readl(phba->HCregaddr);
+ writel(hc_copy & ~(HC_MBINT_ENA | HC_R2INT_ENA |
+ HC_LAINT_ENA | HC_ERINT_ENA),
+ phba->HCregaddr);
writel((ha_copy & (HA_MBATT | HA_R2_CLR_MSK)),
phba->HAregaddr);
+ writel(hc_copy, phba->HCregaddr);
readl(phba->HAregaddr); /* flush */
spin_unlock_irqrestore(&phba->hbalock, iflag);
} else
@@ -8049,7 +8053,7 @@ lpfc_sli_sp_intr_handler(int irq, void *dev_id)
KERN_ERR,
LOG_MBOX | LOG_SLI,
"0350 rc should have"
- "been MBX_BUSY");
+ "been MBX_BUSY\n");
if (rc != MBX_NOT_FINISHED)
goto send_current_mbox;
}
@@ -8078,7 +8082,7 @@ send_current_mbox:
if (rc != MBX_SUCCESS)
lpfc_printf_log(phba, KERN_ERR, LOG_MBOX |
LOG_SLI, "0349 rc should be "
- "MBX_SUCCESS");
+ "MBX_SUCCESS\n");
}
spin_lock_irqsave(&phba->hbalock, iflag);
@@ -8203,6 +8207,7 @@ lpfc_sli_intr_handler(int irq, void *dev_id)
struct lpfc_hba *phba;
irqreturn_t sp_irq_rc, fp_irq_rc;
unsigned long status1, status2;
+ uint32_t hc_copy;
/*
* Get the driver's phba structure from the dev_id and
@@ -8240,7 +8245,12 @@ lpfc_sli_intr_handler(int irq, void *dev_id)
}
/* Clear attention sources except link and error attentions */
+ hc_copy = readl(phba->HCregaddr);
+ writel(hc_copy & ~(HC_MBINT_ENA | HC_R0INT_ENA | HC_R1INT_ENA
+ | HC_R2INT_ENA | HC_LAINT_ENA | HC_ERINT_ENA),
+ phba->HCregaddr);
writel((phba->ha_copy & ~(HA_LATT | HA_ERATT)), phba->HAregaddr);
+ writel(hc_copy, phba->HCregaddr);
readl(phba->HAregaddr); /* flush */
spin_unlock(&phba->hbalock);
@@ -8351,8 +8361,6 @@ lpfc_sli4_iocb_param_transfer(struct lpfc_iocbq *pIocbIn,
memcpy((char *)pIocbIn + offset, (char *)pIocbOut + offset,
sizeof(struct lpfc_iocbq) - offset);
- memset(&pIocbIn->sli4_info, 0,
- sizeof(struct lpfc_sli4_rspiocb_info));
/* Map WCQE parameters into irspiocb parameters */
pIocbIn->iocb.ulpStatus = bf_get(lpfc_wcqe_c_status, wcqe);
if (pIocbOut->iocb_flag & LPFC_IO_FCP)
@@ -8364,16 +8372,49 @@ lpfc_sli4_iocb_param_transfer(struct lpfc_iocbq *pIocbIn,
pIocbIn->iocb.un.ulpWord[4] = wcqe->parameter;
else
pIocbIn->iocb.un.ulpWord[4] = wcqe->parameter;
- /* Load in additional WCQE parameters */
- pIocbIn->sli4_info.hw_status = bf_get(lpfc_wcqe_c_hw_status, wcqe);
- pIocbIn->sli4_info.bfield = 0;
- if (bf_get(lpfc_wcqe_c_xb, wcqe))
- pIocbIn->sli4_info.bfield |= LPFC_XB;
- if (bf_get(lpfc_wcqe_c_pv, wcqe)) {
- pIocbIn->sli4_info.bfield |= LPFC_PV;
- pIocbIn->sli4_info.priority =
- bf_get(lpfc_wcqe_c_priority, wcqe);
+}
+
+/**
+ * lpfc_sli4_els_wcqe_to_rspiocbq - Get response iocbq from els wcqe
+ * @phba: Pointer to HBA context object.
+ * @wcqe: Pointer to work-queue completion queue entry.
+ *
+ * This routine handles an ELS work-queue completion event and construct
+ * a pseudo response ELS IODBQ from the SLI4 ELS WCQE for the common
+ * discovery engine to handle.
+ *
+ * Return: Pointer to the receive IOCBQ, NULL otherwise.
+ **/
+static struct lpfc_iocbq *
+lpfc_sli4_els_wcqe_to_rspiocbq(struct lpfc_hba *phba,
+ struct lpfc_iocbq *irspiocbq)
+{
+ struct lpfc_sli_ring *pring = &phba->sli.ring[LPFC_ELS_RING];
+ struct lpfc_iocbq *cmdiocbq;
+ struct lpfc_wcqe_complete *wcqe;
+ unsigned long iflags;
+
+ wcqe = &irspiocbq->cq_event.cqe.wcqe_cmpl;
+ spin_lock_irqsave(&phba->hbalock, iflags);
+ pring->stats.iocb_event++;
+ /* Look up the ELS command IOCB and create pseudo response IOCB */
+ cmdiocbq = lpfc_sli_iocbq_lookup_by_tag(phba, pring,
+ bf_get(lpfc_wcqe_c_request_tag, wcqe));
+ spin_unlock_irqrestore(&phba->hbalock, iflags);
+
+ if (unlikely(!cmdiocbq)) {
+ lpfc_printf_log(phba, KERN_WARNING, LOG_SLI,
+ "0386 ELS complete with no corresponding "
+ "cmdiocb: iotag (%d)\n",
+ bf_get(lpfc_wcqe_c_request_tag, wcqe));
+ lpfc_sli_release_iocbq(phba, irspiocbq);
+ return NULL;
}
+
+ /* Fake the irspiocbq and copy necessary response information */
+ lpfc_sli4_iocb_param_transfer(irspiocbq, cmdiocbq, wcqe);
+
+ return irspiocbq;
}
/**
@@ -8566,45 +8607,26 @@ static bool
lpfc_sli4_sp_handle_els_wcqe(struct lpfc_hba *phba,
struct lpfc_wcqe_complete *wcqe)
{
- struct lpfc_sli_ring *pring = &phba->sli.ring[LPFC_ELS_RING];
- struct lpfc_iocbq *cmdiocbq;
struct lpfc_iocbq *irspiocbq;
unsigned long iflags;
- bool workposted = false;
-
- spin_lock_irqsave(&phba->hbalock, iflags);
- pring->stats.iocb_event++;
- /* Look up the ELS command IOCB and create pseudo response IOCB */
- cmdiocbq = lpfc_sli_iocbq_lookup_by_tag(phba, pring,
- bf_get(lpfc_wcqe_c_request_tag, wcqe));
- spin_unlock_irqrestore(&phba->hbalock, iflags);
- if (unlikely(!cmdiocbq)) {
- lpfc_printf_log(phba, KERN_WARNING, LOG_SLI,
- "0386 ELS complete with no corresponding "
- "cmdiocb: iotag (%d)\n",
- bf_get(lpfc_wcqe_c_request_tag, wcqe));
- return workposted;
- }
-
- /* Fake the irspiocbq and copy necessary response information */
+ /* Get an irspiocbq for later ELS response processing use */
irspiocbq = lpfc_sli_get_iocbq(phba);
if (!irspiocbq) {
lpfc_printf_log(phba, KERN_ERR, LOG_SLI,
"0387 Failed to allocate an iocbq\n");
- return workposted;
+ return false;
}
- lpfc_sli4_iocb_param_transfer(irspiocbq, cmdiocbq, wcqe);
- /* Add the irspiocb to the response IOCB work list */
+ /* Save off the slow-path queue event for work thread to process */
+ memcpy(&irspiocbq->cq_event.cqe.wcqe_cmpl, wcqe, sizeof(*wcqe));
spin_lock_irqsave(&phba->hbalock, iflags);
- list_add_tail(&irspiocbq->list, &phba->sli4_hba.sp_rspiocb_work_queue);
- /* Indicate ELS ring attention */
- phba->work_ha |= (HA_R0ATT << (4*LPFC_ELS_RING));
+ list_add_tail(&irspiocbq->cq_event.list,
+ &phba->sli4_hba.sp_queue_event);
+ phba->hba_flag |= HBA_SP_QUEUE_EVT;
spin_unlock_irqrestore(&phba->hbalock, iflags);
- workposted = true;
- return workposted;
+ return true;
}
/**
@@ -8690,52 +8712,6 @@ lpfc_sli4_sp_handle_abort_xri_wcqe(struct lpfc_hba *phba,
}
/**
- * lpfc_sli4_sp_handle_wcqe - Process a work-queue completion queue entry
- * @phba: Pointer to HBA context object.
- * @cq: Pointer to the completion queue.
- * @wcqe: Pointer to a completion queue entry.
- *
- * This routine process a slow-path work-queue completion queue entry.
- *
- * Return: true if work posted to worker thread, otherwise false.
- **/
-static bool
-lpfc_sli4_sp_handle_wcqe(struct lpfc_hba *phba, struct lpfc_queue *cq,
- struct lpfc_cqe *cqe)
-{
- struct lpfc_wcqe_complete wcqe;
- bool workposted = false;
-
- /* Copy the work queue CQE and convert endian order if needed */
- lpfc_sli_pcimem_bcopy(cqe, &wcqe, sizeof(struct lpfc_cqe));
-
- /* Check and process for different type of WCQE and dispatch */
- switch (bf_get(lpfc_wcqe_c_code, &wcqe)) {
- case CQE_CODE_COMPL_WQE:
- /* Process the WQ complete event */
- workposted = lpfc_sli4_sp_handle_els_wcqe(phba,
- (struct lpfc_wcqe_complete *)&wcqe);
- break;
- case CQE_CODE_RELEASE_WQE:
- /* Process the WQ release event */
- lpfc_sli4_sp_handle_rel_wcqe(phba,
- (struct lpfc_wcqe_release *)&wcqe);
- break;
- case CQE_CODE_XRI_ABORTED:
- /* Process the WQ XRI abort event */
- workposted = lpfc_sli4_sp_handle_abort_xri_wcqe(phba, cq,
- (struct sli4_wcqe_xri_aborted *)&wcqe);
- break;
- default:
- lpfc_printf_log(phba, KERN_ERR, LOG_SLI,
- "0388 Not a valid WCQE code: x%x\n",
- bf_get(lpfc_wcqe_c_code, &wcqe));
- break;
- }
- return workposted;
-}
-
-/**
* lpfc_sli4_sp_handle_rcqe - Process a receive-queue completion queue entry
* @phba: Pointer to HBA context object.
* @rcqe: Pointer to receive-queue completion queue entry.
@@ -8745,9 +8721,8 @@ lpfc_sli4_sp_handle_wcqe(struct lpfc_hba *phba, struct lpfc_queue *cq,
* Return: true if work posted to worker thread, otherwise false.
**/
static bool
-lpfc_sli4_sp_handle_rcqe(struct lpfc_hba *phba, struct lpfc_cqe *cqe)
+lpfc_sli4_sp_handle_rcqe(struct lpfc_hba *phba, struct lpfc_rcqe *rcqe)
{
- struct lpfc_rcqe rcqe;
bool workposted = false;
struct lpfc_queue *hrq = phba->sli4_hba.hdr_rq;
struct lpfc_queue *drq = phba->sli4_hba.dat_rq;
@@ -8755,31 +8730,28 @@ lpfc_sli4_sp_handle_rcqe(struct lpfc_hba *phba, struct lpfc_cqe *cqe)
uint32_t status;
unsigned long iflags;
- /* Copy the receive queue CQE and convert endian order if needed */
- lpfc_sli_pcimem_bcopy(cqe, &rcqe, sizeof(struct lpfc_rcqe));
- lpfc_sli4_rq_release(hrq, drq);
- if (bf_get(lpfc_rcqe_code, &rcqe) != CQE_CODE_RECEIVE)
- goto out;
- if (bf_get(lpfc_rcqe_rq_id, &rcqe) != hrq->queue_id)
+ if (bf_get(lpfc_rcqe_rq_id, rcqe) != hrq->queue_id)
goto out;
- status = bf_get(lpfc_rcqe_status, &rcqe);
+ status = bf_get(lpfc_rcqe_status, rcqe);
switch (status) {
case FC_STATUS_RQ_BUF_LEN_EXCEEDED:
lpfc_printf_log(phba, KERN_ERR, LOG_SLI,
"2537 Receive Frame Truncated!!\n");
case FC_STATUS_RQ_SUCCESS:
+ lpfc_sli4_rq_release(hrq, drq);
spin_lock_irqsave(&phba->hbalock, iflags);
dma_buf = lpfc_sli_hbqbuf_get(&phba->hbqs[0].hbq_buffer_list);
if (!dma_buf) {
spin_unlock_irqrestore(&phba->hbalock, iflags);
goto out;
}
- memcpy(&dma_buf->rcqe, &rcqe, sizeof(rcqe));
+ memcpy(&dma_buf->cq_event.cqe.rcqe_cmpl, rcqe, sizeof(*rcqe));
/* save off the frame for the word thread to process */
- list_add_tail(&dma_buf->dbuf.list, &phba->rb_pend_list);
+ list_add_tail(&dma_buf->cq_event.list,
+ &phba->sli4_hba.sp_queue_event);
/* Frame received */
- phba->hba_flag |= HBA_RECEIVE_BUFFER;
+ phba->hba_flag |= HBA_SP_QUEUE_EVT;
spin_unlock_irqrestore(&phba->hbalock, iflags);
workposted = true;
break;
@@ -8794,7 +8766,58 @@ lpfc_sli4_sp_handle_rcqe(struct lpfc_hba *phba, struct lpfc_cqe *cqe)
}
out:
return workposted;
+}
+
+/**
+ * lpfc_sli4_sp_handle_cqe - Process a slow path completion queue entry
+ * @phba: Pointer to HBA context object.
+ * @cq: Pointer to the completion queue.
+ * @wcqe: Pointer to a completion queue entry.
+ *
+ * This routine process a slow-path work-queue or recieve queue completion queue
+ * entry.
+ *
+ * Return: true if work posted to worker thread, otherwise false.
+ **/
+static bool
+lpfc_sli4_sp_handle_cqe(struct lpfc_hba *phba, struct lpfc_queue *cq,
+ struct lpfc_cqe *cqe)
+{
+ struct lpfc_cqe cqevt;
+ bool workposted = false;
+
+ /* Copy the work queue CQE and convert endian order if needed */
+ lpfc_sli_pcimem_bcopy(cqe, &cqevt, sizeof(struct lpfc_cqe));
+ /* Check and process for different type of WCQE and dispatch */
+ switch (bf_get(lpfc_cqe_code, &cqevt)) {
+ case CQE_CODE_COMPL_WQE:
+ /* Process the WQ/RQ complete event */
+ workposted = lpfc_sli4_sp_handle_els_wcqe(phba,
+ (struct lpfc_wcqe_complete *)&cqevt);
+ break;
+ case CQE_CODE_RELEASE_WQE:
+ /* Process the WQ release event */
+ lpfc_sli4_sp_handle_rel_wcqe(phba,
+ (struct lpfc_wcqe_release *)&cqevt);
+ break;
+ case CQE_CODE_XRI_ABORTED:
+ /* Process the WQ XRI abort event */
+ workposted = lpfc_sli4_sp_handle_abort_xri_wcqe(phba, cq,
+ (struct sli4_wcqe_xri_aborted *)&cqevt);
+ break;
+ case CQE_CODE_RECEIVE:
+ /* Process the RQ event */
+ workposted = lpfc_sli4_sp_handle_rcqe(phba,
+ (struct lpfc_rcqe *)&cqevt);
+ break;
+ default:
+ lpfc_printf_log(phba, KERN_ERR, LOG_SLI,
+ "0388 Not a valid WCQE code: x%x\n",
+ bf_get(lpfc_cqe_code, &cqevt));
+ break;
+ }
+ return workposted;
}
/**
@@ -8858,14 +8881,7 @@ lpfc_sli4_sp_handle_eqe(struct lpfc_hba *phba, struct lpfc_eqe *eqe)
break;
case LPFC_WCQ:
while ((cqe = lpfc_sli4_cq_get(cq))) {
- workposted |= lpfc_sli4_sp_handle_wcqe(phba, cq, cqe);
- if (!(++ecount % LPFC_GET_QE_REL_INT))
- lpfc_sli4_cq_release(cq, LPFC_QUEUE_NOARM);
- }
- break;
- case LPFC_RCQ:
- while ((cqe = lpfc_sli4_cq_get(cq))) {
- workposted |= lpfc_sli4_sp_handle_rcqe(phba, cqe);
+ workposted |= lpfc_sli4_sp_handle_cqe(phba, cq, cqe);
if (!(++ecount % LPFC_GET_QE_REL_INT))
lpfc_sli4_cq_release(cq, LPFC_QUEUE_NOARM);
}
@@ -10427,8 +10443,7 @@ lpfc_sli4_next_xritag(struct lpfc_hba *phba)
return xritag;
}
spin_unlock_irq(&phba->hbalock);
-
- lpfc_printf_log(phba, KERN_ERR, LOG_SLI,
+ lpfc_printf_log(phba, KERN_WARNING, LOG_SLI,
"2004 Failed to allocate XRI.last XRITAG is %d"
" Max XRI is %d, Used XRI is %d\n",
phba->sli4_hba.next_xri,
@@ -10492,15 +10507,7 @@ lpfc_sli4_post_sgl_list(struct lpfc_hba *phba)
lpfc_sli4_mbox_cmd_free(phba, mbox);
return -ENOMEM;
}
-
/* Get the first SGE entry from the non-embedded DMA memory */
- if (unlikely(!mbox->sge_array)) {
- lpfc_printf_log(phba, KERN_ERR, LOG_MBOX,
- "2525 Failed to get the non-embedded SGE "
- "virtual address\n");
- lpfc_sli4_mbox_cmd_free(phba, mbox);
- return -ENOMEM;
- }
viraddr = mbox->sge_array->addr[0];
/* Set up the SGL pages in the non-embedded DMA pages */
@@ -10524,8 +10531,7 @@ lpfc_sli4_post_sgl_list(struct lpfc_hba *phba)
sgl_pg_pairs++;
}
bf_set(lpfc_post_sgl_pages_xri, sgl, xritag_start);
- pg_pairs = (pg_pairs > 0) ? (pg_pairs - 1) : pg_pairs;
- bf_set(lpfc_post_sgl_pages_xricnt, sgl, pg_pairs);
+ bf_set(lpfc_post_sgl_pages_xricnt, sgl, els_xri_cnt);
/* Perform endian conversion if necessary */
sgl->word0 = cpu_to_le32(sgl->word0);
@@ -10607,15 +10613,7 @@ lpfc_sli4_post_scsi_sgl_block(struct lpfc_hba *phba, struct list_head *sblist,
lpfc_sli4_mbox_cmd_free(phba, mbox);
return -ENOMEM;
}
-
/* Get the first SGE entry from the non-embedded DMA memory */
- if (unlikely(!mbox->sge_array)) {
- lpfc_printf_log(phba, KERN_ERR, LOG_MBOX,
- "2565 Failed to get the non-embedded SGE "
- "virtual address\n");
- lpfc_sli4_mbox_cmd_free(phba, mbox);
- return -ENOMEM;
- }
viraddr = mbox->sge_array->addr[0];
/* Set up the SGL pages in the non-embedded DMA pages */
@@ -10802,6 +10800,105 @@ lpfc_fc_frame_to_vport(struct lpfc_hba *phba, struct fc_frame_header *fc_hdr,
}
/**
+ * lpfc_update_rcv_time_stamp - Update vport's rcv seq time stamp
+ * @vport: The vport to work on.
+ *
+ * This function updates the receive sequence time stamp for this vport. The
+ * receive sequence time stamp indicates the time that the last frame of the
+ * the sequence that has been idle for the longest amount of time was received.
+ * the driver uses this time stamp to indicate if any received sequences have
+ * timed out.
+ **/
+void
+lpfc_update_rcv_time_stamp(struct lpfc_vport *vport)
+{
+ struct lpfc_dmabuf *h_buf;
+ struct hbq_dmabuf *dmabuf = NULL;
+
+ /* get the oldest sequence on the rcv list */
+ h_buf = list_get_first(&vport->rcv_buffer_list,
+ struct lpfc_dmabuf, list);
+ if (!h_buf)
+ return;
+ dmabuf = container_of(h_buf, struct hbq_dmabuf, hbuf);
+ vport->rcv_buffer_time_stamp = dmabuf->time_stamp;
+}
+
+/**
+ * lpfc_cleanup_rcv_buffers - Cleans up all outstanding receive sequences.
+ * @vport: The vport that the received sequences were sent to.
+ *
+ * This function cleans up all outstanding received sequences. This is called
+ * by the driver when a link event or user action invalidates all the received
+ * sequences.
+ **/
+void
+lpfc_cleanup_rcv_buffers(struct lpfc_vport *vport)
+{
+ struct lpfc_dmabuf *h_buf, *hnext;
+ struct lpfc_dmabuf *d_buf, *dnext;
+ struct hbq_dmabuf *dmabuf = NULL;
+
+ /* start with the oldest sequence on the rcv list */
+ list_for_each_entry_safe(h_buf, hnext, &vport->rcv_buffer_list, list) {
+ dmabuf = container_of(h_buf, struct hbq_dmabuf, hbuf);
+ list_del_init(&dmabuf->hbuf.list);
+ list_for_each_entry_safe(d_buf, dnext,
+ &dmabuf->dbuf.list, list) {
+ list_del_init(&d_buf->list);
+ lpfc_in_buf_free(vport->phba, d_buf);
+ }
+ lpfc_in_buf_free(vport->phba, &dmabuf->dbuf);
+ }
+}
+
+/**
+ * lpfc_rcv_seq_check_edtov - Cleans up timed out receive sequences.
+ * @vport: The vport that the received sequences were sent to.
+ *
+ * This function determines whether any received sequences have timed out by
+ * first checking the vport's rcv_buffer_time_stamp. If this time_stamp
+ * indicates that there is at least one timed out sequence this routine will
+ * go through the received sequences one at a time from most inactive to most
+ * active to determine which ones need to be cleaned up. Once it has determined
+ * that a sequence needs to be cleaned up it will simply free up the resources
+ * without sending an abort.
+ **/
+void
+lpfc_rcv_seq_check_edtov(struct lpfc_vport *vport)
+{
+ struct lpfc_dmabuf *h_buf, *hnext;
+ struct lpfc_dmabuf *d_buf, *dnext;
+ struct hbq_dmabuf *dmabuf = NULL;
+ unsigned long timeout;
+ int abort_count = 0;
+
+ timeout = (msecs_to_jiffies(vport->phba->fc_edtov) +
+ vport->rcv_buffer_time_stamp);
+ if (list_empty(&vport->rcv_buffer_list) ||
+ time_before(jiffies, timeout))
+ return;
+ /* start with the oldest sequence on the rcv list */
+ list_for_each_entry_safe(h_buf, hnext, &vport->rcv_buffer_list, list) {
+ dmabuf = container_of(h_buf, struct hbq_dmabuf, hbuf);
+ timeout = (msecs_to_jiffies(vport->phba->fc_edtov) +
+ dmabuf->time_stamp);
+ if (time_before(jiffies, timeout))
+ break;
+ abort_count++;
+ list_del_init(&dmabuf->hbuf.list);
+ list_for_each_entry_safe(d_buf, dnext,
+ &dmabuf->dbuf.list, list) {
+ list_del_init(&d_buf->list);
+ lpfc_in_buf_free(vport->phba, d_buf);
+ }
+ lpfc_in_buf_free(vport->phba, &dmabuf->dbuf);
+ }
+ if (abort_count)
+ lpfc_update_rcv_time_stamp(vport);
+}
+
+/**
* lpfc_fc_frame_add - Adds a frame to the vport's list of received sequences
* @dmabuf: pointer to a dmabuf that describes the hdr and data of the FC frame
*
@@ -10823,6 +10920,8 @@ lpfc_fc_frame_add(struct lpfc_vport *vport, struct hbq_dmabuf *dmabuf)
struct hbq_dmabuf *seq_dmabuf = NULL;
struct hbq_dmabuf *temp_dmabuf = NULL;
+ INIT_LIST_HEAD(&dmabuf->dbuf.list);
+ dmabuf->time_stamp = jiffies;
new_hdr = (struct fc_frame_header *)dmabuf->hbuf.virt;
/* Use the hdr_buf to find the sequence that this frame belongs to */
list_for_each_entry(h_buf, &vport->rcv_buffer_list, list) {
@@ -10841,13 +10940,21 @@ lpfc_fc_frame_add(struct lpfc_vport *vport, struct hbq_dmabuf *dmabuf)
* Queue the buffer on the vport's rcv_buffer_list.
*/
list_add_tail(&dmabuf->hbuf.list, &vport->rcv_buffer_list);
+ lpfc_update_rcv_time_stamp(vport);
return dmabuf;
}
temp_hdr = seq_dmabuf->hbuf.virt;
if (new_hdr->fh_seq_cnt < temp_hdr->fh_seq_cnt) {
- list_add(&seq_dmabuf->dbuf.list, &dmabuf->dbuf.list);
+ list_del_init(&seq_dmabuf->hbuf.list);
+ list_add_tail(&dmabuf->hbuf.list, &vport->rcv_buffer_list);
+ list_add_tail(&dmabuf->dbuf.list, &seq_dmabuf->dbuf.list);
+ lpfc_update_rcv_time_stamp(vport);
return dmabuf;
}
+ /* move this sequence to the tail to indicate a young sequence */
+ list_move_tail(&seq_dmabuf->hbuf.list, &vport->rcv_buffer_list);
+ seq_dmabuf->time_stamp = jiffies;
+ lpfc_update_rcv_time_stamp(vport);
/* find the correct place in the sequence to insert this frame */
list_for_each_entry_reverse(d_buf, &seq_dmabuf->dbuf.list, list) {
temp_dmabuf = container_of(d_buf, struct hbq_dmabuf, dbuf);
@@ -10865,6 +10972,210 @@ lpfc_fc_frame_add(struct lpfc_vport *vport, struct hbq_dmabuf *dmabuf)
}
/**
+ * lpfc_sli4_abort_partial_seq - Abort partially assembled unsol sequence
+ * @vport: pointer to a vitural port
+ * @dmabuf: pointer to a dmabuf that describes the FC sequence
+ *
+ * This function tries to abort from the partially assembed sequence, described
+ * by the information from basic abbort @dmabuf. It checks to see whether such
+ * partially assembled sequence held by the driver. If so, it shall free up all
+ * the frames from the partially assembled sequence.
+ *
+ * Return
+ * true -- if there is matching partially assembled sequence present and all
+ * the frames freed with the sequence;
+ * false -- if there is no matching partially assembled sequence present so
+ * nothing got aborted in the lower layer driver
+ **/
+static bool
+lpfc_sli4_abort_partial_seq(struct lpfc_vport *vport,
+ struct hbq_dmabuf *dmabuf)
+{
+ struct fc_frame_header *new_hdr;
+ struct fc_frame_header *temp_hdr;
+ struct lpfc_dmabuf *d_buf, *n_buf, *h_buf;
+ struct hbq_dmabuf *seq_dmabuf = NULL;
+
+ /* Use the hdr_buf to find the sequence that matches this frame */
+ INIT_LIST_HEAD(&dmabuf->dbuf.list);
+ INIT_LIST_HEAD(&dmabuf->hbuf.list);
+ new_hdr = (struct fc_frame_header *)dmabuf->hbuf.virt;
+ list_for_each_entry(h_buf, &vport->rcv_buffer_list, list) {
+ temp_hdr = (struct fc_frame_header *)h_buf->virt;
+ if ((temp_hdr->fh_seq_id != new_hdr->fh_seq_id) ||
+ (temp_hdr->fh_ox_id != new_hdr->fh_ox_id) ||
+ (memcmp(&temp_hdr->fh_s_id, &new_hdr->fh_s_id, 3)))
+ continue;
+ /* found a pending sequence that matches this frame */
+ seq_dmabuf = container_of(h_buf, struct hbq_dmabuf, hbuf);
+ break;
+ }
+
+ /* Free up all the frames from the partially assembled sequence */
+ if (seq_dmabuf) {
+ list_for_each_entry_safe(d_buf, n_buf,
+ &seq_dmabuf->dbuf.list, list) {
+ list_del_init(&d_buf->list);
+ lpfc_in_buf_free(vport->phba, d_buf);
+ }
+ return true;
+ }
+ return false;
+}
+
+/**
+ * lpfc_sli4_seq_abort_acc_cmpl - Accept seq abort iocb complete handler
+ * @phba: Pointer to HBA context object.
+ * @cmd_iocbq: pointer to the command iocbq structure.
+ * @rsp_iocbq: pointer to the response iocbq structure.
+ *
+ * This function handles the sequence abort accept iocb command complete
+ * event. It properly releases the memory allocated to the sequence abort
+ * accept iocb.
+ **/
+static void
+lpfc_sli4_seq_abort_acc_cmpl(struct lpfc_hba *phba,
+ struct lpfc_iocbq *cmd_iocbq,
+ struct lpfc_iocbq *rsp_iocbq)
+{
+ if (cmd_iocbq)
+ lpfc_sli_release_iocbq(phba, cmd_iocbq);
+}
+
+/**
+ * lpfc_sli4_seq_abort_acc - Accept sequence abort
+ * @phba: Pointer to HBA context object.
+ * @fc_hdr: pointer to a FC frame header.
+ *
+ * This function sends a basic accept to a previous unsol sequence abort
+ * event after aborting the sequence handling.
+ **/
+static void
+lpfc_sli4_seq_abort_acc(struct lpfc_hba *phba,
+ struct fc_frame_header *fc_hdr)
+{
+ struct lpfc_iocbq *ctiocb = NULL;
+ struct lpfc_nodelist *ndlp;
+ uint16_t oxid, rxid;
+ uint32_t sid, fctl;
+ IOCB_t *icmd;
+
+ if (!lpfc_is_link_up(phba))
+ return;
+
+ sid = sli4_sid_from_fc_hdr(fc_hdr);
+ oxid = be16_to_cpu(fc_hdr->fh_ox_id);
+ rxid = be16_to_cpu(fc_hdr->fh_rx_id);
+
+ ndlp = lpfc_findnode_did(phba->pport, sid);
+ if (!ndlp) {
+ lpfc_printf_log(phba, KERN_WARNING, LOG_ELS,
+ "1268 Find ndlp returned NULL for oxid:x%x "
+ "SID:x%x\n", oxid, sid);
+ return;
+ }
+
+ /* Allocate buffer for acc iocb */
+ ctiocb = lpfc_sli_get_iocbq(phba);
+ if (!ctiocb)
+ return;
+
+ /* Extract the F_CTL field from FC_HDR */
+ fctl = sli4_fctl_from_fc_hdr(fc_hdr);
+
+ icmd = &ctiocb->iocb;
+ icmd->un.xseq64.bdl.bdeSize = 0;
+ icmd->un.xseq64.bdl.ulpIoTag32 = 0;
+ icmd->un.xseq64.w5.hcsw.Dfctl = 0;
+ icmd->un.xseq64.w5.hcsw.Rctl = FC_RCTL_BA_ACC;
+ icmd->un.xseq64.w5.hcsw.Type = FC_TYPE_BLS;
+
+ /* Fill in the rest of iocb fields */
+ icmd->ulpCommand = CMD_XMIT_BLS_RSP64_CX;
+ icmd->ulpBdeCount = 0;
+ icmd->ulpLe = 1;
+ icmd->ulpClass = CLASS3;
+ icmd->ulpContext = ndlp->nlp_rpi;
+
+ ctiocb->iocb_cmpl = NULL;
+ ctiocb->vport = phba->pport;
+ ctiocb->iocb_cmpl = lpfc_sli4_seq_abort_acc_cmpl;
+
+ if (fctl & FC_FC_EX_CTX) {
+ /* ABTS sent by responder to CT exchange, construction
+ * of BA_ACC will use OX_ID from ABTS for the XRI_TAG
+ * field and RX_ID from ABTS for RX_ID field.
+ */
+ bf_set(lpfc_abts_orig, &icmd->un.bls_acc, LPFC_ABTS_UNSOL_RSP);
+ bf_set(lpfc_abts_rxid, &icmd->un.bls_acc, rxid);
+ ctiocb->sli4_xritag = oxid;
+ } else {
+ /* ABTS sent by initiator to CT exchange, construction
+ * of BA_ACC will need to allocate a new XRI as for the
+ * XRI_TAG and RX_ID fields.
+ */
+ bf_set(lpfc_abts_orig, &icmd->un.bls_acc, LPFC_ABTS_UNSOL_INT);
+ bf_set(lpfc_abts_rxid, &icmd->un.bls_acc, NO_XRI);
+ ctiocb->sli4_xritag = NO_XRI;
+ }
+ bf_set(lpfc_abts_oxid, &icmd->un.bls_acc, oxid);
+
+ /* Xmit CT abts accept on exchange <xid> */
+ lpfc_printf_log(phba, KERN_INFO, LOG_ELS,
+ "1200 Xmit CT ABTS ACC on exchange x%x Data: x%x\n",
+ CMD_XMIT_BLS_RSP64_CX, phba->link_state);
+ lpfc_sli_issue_iocb(phba, LPFC_ELS_RING, ctiocb, 0);
+}
+
+/**
+ * lpfc_sli4_handle_unsol_abort - Handle sli-4 unsolicited abort event
+ * @vport: Pointer to the vport on which this sequence was received
+ * @dmabuf: pointer to a dmabuf that describes the FC sequence
+ *
+ * This function handles an SLI-4 unsolicited abort event. If the unsolicited
+ * receive sequence is only partially assembed by the driver, it shall abort
+ * the partially assembled frames for the sequence. Otherwise, if the
+ * unsolicited receive sequence has been completely assembled and passed to
+ * the Upper Layer Protocol (UPL), it then mark the per oxid status for the
+ * unsolicited sequence has been aborted. After that, it will issue a basic
+ * accept to accept the abort.
+ **/
+void
+lpfc_sli4_handle_unsol_abort(struct lpfc_vport *vport,
+ struct hbq_dmabuf *dmabuf)
+{
+ struct lpfc_hba *phba = vport->phba;
+ struct fc_frame_header fc_hdr;
+ uint32_t fctl;
+ bool abts_par;
+
+ /* Make a copy of fc_hdr before the dmabuf being released */
+ memcpy(&fc_hdr, dmabuf->hbuf.virt, sizeof(struct fc_frame_header));
+ fctl = sli4_fctl_from_fc_hdr(&fc_hdr);
+
+ if (fctl & FC_FC_EX_CTX) {
+ /*
+ * ABTS sent by responder to exchange, just free the buffer
+ */
+ lpfc_in_buf_free(phba, &dmabuf->dbuf);
+ } else {
+ /*
+ * ABTS sent by initiator to exchange, need to do cleanup
+ */
+ /* Try to abort partially assembled seq */
+ abts_par = lpfc_sli4_abort_partial_seq(vport, dmabuf);
+
+ /* Send abort to ULP if partially seq abort failed */
+ if (abts_par == false)
+ lpfc_sli4_send_seq_to_ulp(vport, dmabuf);
+ else
+ lpfc_in_buf_free(phba, &dmabuf->dbuf);
+ }
+ /* Send basic accept (BA_ACC) to the abort requester */
+ lpfc_sli4_seq_abort_acc(phba, &fc_hdr);
+}
+
+/**
* lpfc_seq_complete - Indicates if a sequence is complete
* @dmabuf: pointer to a dmabuf that describes the FC sequence
*
@@ -10935,10 +11246,9 @@ lpfc_prep_seq(struct lpfc_vport *vport, struct hbq_dmabuf *seq_dmabuf)
fc_hdr = (struct fc_frame_header *)seq_dmabuf->hbuf.virt;
/* remove from receive buffer list */
list_del_init(&seq_dmabuf->hbuf.list);
+ lpfc_update_rcv_time_stamp(vport);
/* get the Remote Port's SID */
- sid = (fc_hdr->fh_s_id[0] << 16 |
- fc_hdr->fh_s_id[1] << 8 |
- fc_hdr->fh_s_id[2]);
+ sid = sli4_sid_from_fc_hdr(fc_hdr);
/* Get an iocbq struct to fill in. */
first_iocbq = lpfc_sli_get_iocbq(vport->phba);
if (first_iocbq) {
@@ -10957,7 +11267,8 @@ lpfc_prep_seq(struct lpfc_vport *vport, struct hbq_dmabuf *seq_dmabuf)
LPFC_DATA_BUF_SIZE;
first_iocbq->iocb.un.rcvels.remoteID = sid;
first_iocbq->iocb.unsli3.rcvsli3.acc_len +=
- bf_get(lpfc_rcqe_length, &seq_dmabuf->rcqe);
+ bf_get(lpfc_rcqe_length,
+ &seq_dmabuf->cq_event.cqe.rcqe_cmpl);
}
iocbq = first_iocbq;
/*
@@ -10975,7 +11286,8 @@ lpfc_prep_seq(struct lpfc_vport *vport, struct hbq_dmabuf *seq_dmabuf)
iocbq->iocb.unsli3.rcvsli3.bde2.tus.f.bdeSize =
LPFC_DATA_BUF_SIZE;
first_iocbq->iocb.unsli3.rcvsli3.acc_len +=
- bf_get(lpfc_rcqe_length, &seq_dmabuf->rcqe);
+ bf_get(lpfc_rcqe_length,
+ &seq_dmabuf->cq_event.cqe.rcqe_cmpl);
} else {
iocbq = lpfc_sli_get_iocbq(vport->phba);
if (!iocbq) {
@@ -10994,7 +11306,8 @@ lpfc_prep_seq(struct lpfc_vport *vport, struct hbq_dmabuf *seq_dmabuf)
iocbq->iocb.un.cont64[0].tus.f.bdeSize =
LPFC_DATA_BUF_SIZE;
first_iocbq->iocb.unsli3.rcvsli3.acc_len +=
- bf_get(lpfc_rcqe_length, &seq_dmabuf->rcqe);
+ bf_get(lpfc_rcqe_length,
+ &seq_dmabuf->cq_event.cqe.rcqe_cmpl);
iocbq->iocb.un.rcvels.remoteID = sid;
list_add_tail(&iocbq->list, &first_iocbq->list);
}
@@ -11002,6 +11315,43 @@ lpfc_prep_seq(struct lpfc_vport *vport, struct hbq_dmabuf *seq_dmabuf)
return first_iocbq;
}
+static void
+lpfc_sli4_send_seq_to_ulp(struct lpfc_vport *vport,
+ struct hbq_dmabuf *seq_dmabuf)
+{
+ struct fc_frame_header *fc_hdr;
+ struct lpfc_iocbq *iocbq, *curr_iocb, *next_iocb;
+ struct lpfc_hba *phba = vport->phba;
+
+ fc_hdr = (struct fc_frame_header *)seq_dmabuf->hbuf.virt;
+ iocbq = lpfc_prep_seq(vport, seq_dmabuf);
+ if (!iocbq) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_SLI,
+ "2707 Ring %d handler: Failed to allocate "
+ "iocb Rctl x%x Type x%x received\n",
+ LPFC_ELS_RING,
+ fc_hdr->fh_r_ctl, fc_hdr->fh_type);
+ return;
+ }
+ if (!lpfc_complete_unsol_iocb(phba,
+ &phba->sli.ring[LPFC_ELS_RING],
+ iocbq, fc_hdr->fh_r_ctl,
+ fc_hdr->fh_type))
+ lpfc_printf_log(phba, KERN_WARNING, LOG_SLI,
+ "2540 Ring %d handler: unexpected Rctl "
+ "x%x Type x%x received\n",
+ LPFC_ELS_RING,
+ fc_hdr->fh_r_ctl, fc_hdr->fh_type);
+
+ /* Free iocb created in lpfc_prep_seq */
+ list_for_each_entry_safe(curr_iocb, next_iocb,
+ &iocbq->list, list) {
+ list_del_init(&curr_iocb->list);
+ lpfc_sli_release_iocbq(phba, curr_iocb);
+ }
+ lpfc_sli_release_iocbq(phba, iocbq);
+}
+
/**
* lpfc_sli4_handle_received_buffer - Handle received buffers from firmware
* @phba: Pointer to HBA context object.
@@ -11014,67 +11364,54 @@ lpfc_prep_seq(struct lpfc_vport *vport, struct hbq_dmabuf *seq_dmabuf)
* Worker thread calls lpfc_sli4_handle_received_buffer, which will call the
* appropriate receive function when the final frame in a sequence is received.
**/
-int
-lpfc_sli4_handle_received_buffer(struct lpfc_hba *phba)
+void
+lpfc_sli4_handle_received_buffer(struct lpfc_hba *phba,
+ struct hbq_dmabuf *dmabuf)
{
- LIST_HEAD(cmplq);
- struct hbq_dmabuf *dmabuf, *seq_dmabuf;
+ struct hbq_dmabuf *seq_dmabuf;
struct fc_frame_header *fc_hdr;
struct lpfc_vport *vport;
uint32_t fcfi;
- struct lpfc_iocbq *iocbq;
-
- /* Clear hba flag and get all received buffers into the cmplq */
- spin_lock_irq(&phba->hbalock);
- phba->hba_flag &= ~HBA_RECEIVE_BUFFER;
- list_splice_init(&phba->rb_pend_list, &cmplq);
- spin_unlock_irq(&phba->hbalock);
/* Process each received buffer */
- while ((dmabuf = lpfc_sli_hbqbuf_get(&cmplq)) != NULL) {
- fc_hdr = (struct fc_frame_header *)dmabuf->hbuf.virt;
- /* check to see if this a valid type of frame */
- if (lpfc_fc_frame_check(phba, fc_hdr)) {
- lpfc_in_buf_free(phba, &dmabuf->dbuf);
- continue;
- }
- fcfi = bf_get(lpfc_rcqe_fcf_id, &dmabuf->rcqe);
- vport = lpfc_fc_frame_to_vport(phba, fc_hdr, fcfi);
- if (!vport) {
- /* throw out the frame */
- lpfc_in_buf_free(phba, &dmabuf->dbuf);
- continue;
- }
- /* Link this frame */
- seq_dmabuf = lpfc_fc_frame_add(vport, dmabuf);
- if (!seq_dmabuf) {
- /* unable to add frame to vport - throw it out */
- lpfc_in_buf_free(phba, &dmabuf->dbuf);
- continue;
- }
- /* If not last frame in sequence continue processing frames. */
- if (!lpfc_seq_complete(seq_dmabuf)) {
- /*
- * When saving off frames post a new one and mark this
- * frame to be freed when it is finished.
- **/
- lpfc_sli_hbqbuf_fill_hbqs(phba, LPFC_ELS_HBQ, 1);
- dmabuf->tag = -1;
- continue;
- }
- fc_hdr = (struct fc_frame_header *)seq_dmabuf->hbuf.virt;
- iocbq = lpfc_prep_seq(vport, seq_dmabuf);
- if (!lpfc_complete_unsol_iocb(phba,
- &phba->sli.ring[LPFC_ELS_RING],
- iocbq, fc_hdr->fh_r_ctl,
- fc_hdr->fh_type))
- lpfc_printf_log(phba, KERN_WARNING, LOG_SLI,
- "2540 Ring %d handler: unexpected Rctl "
- "x%x Type x%x received\n",
- LPFC_ELS_RING,
- fc_hdr->fh_r_ctl, fc_hdr->fh_type);
- };
- return 0;
+ fc_hdr = (struct fc_frame_header *)dmabuf->hbuf.virt;
+ /* check to see if this a valid type of frame */
+ if (lpfc_fc_frame_check(phba, fc_hdr)) {
+ lpfc_in_buf_free(phba, &dmabuf->dbuf);
+ return;
+ }
+ fcfi = bf_get(lpfc_rcqe_fcf_id, &dmabuf->cq_event.cqe.rcqe_cmpl);
+ vport = lpfc_fc_frame_to_vport(phba, fc_hdr, fcfi);
+ if (!vport || !(vport->vpi_state & LPFC_VPI_REGISTERED)) {
+ /* throw out the frame */
+ lpfc_in_buf_free(phba, &dmabuf->dbuf);
+ return;
+ }
+ /* Handle the basic abort sequence (BA_ABTS) event */
+ if (fc_hdr->fh_r_ctl == FC_RCTL_BA_ABTS) {
+ lpfc_sli4_handle_unsol_abort(vport, dmabuf);
+ return;
+ }
+
+ /* Link this frame */
+ seq_dmabuf = lpfc_fc_frame_add(vport, dmabuf);
+ if (!seq_dmabuf) {
+ /* unable to add frame to vport - throw it out */
+ lpfc_in_buf_free(phba, &dmabuf->dbuf);
+ return;
+ }
+ /* If not last frame in sequence continue processing frames. */
+ if (!lpfc_seq_complete(seq_dmabuf)) {
+ /*
+ * When saving off frames post a new one and mark this
+ * frame to be freed when it is finished.
+ **/
+ lpfc_sli_hbqbuf_fill_hbqs(phba, LPFC_ELS_HBQ, 1);
+ dmabuf->tag = -1;
+ return;
+ }
+ /* Send the complete sequence to the upper layer protocol */
+ lpfc_sli4_send_seq_to_ulp(vport, seq_dmabuf);
}
/**
@@ -11334,6 +11671,7 @@ lpfc_sli4_init_vpi(struct lpfc_hba *phba, uint16_t vpi)
{
LPFC_MBOXQ_t *mboxq;
int rc = 0;
+ int retval = MBX_SUCCESS;
uint32_t mbox_tmo;
if (vpi == 0)
@@ -11344,16 +11682,17 @@ lpfc_sli4_init_vpi(struct lpfc_hba *phba, uint16_t vpi)
lpfc_init_vpi(phba, mboxq, vpi);
mbox_tmo = lpfc_mbox_tmo_val(phba, MBX_INIT_VPI);
rc = lpfc_sli_issue_mbox_wait(phba, mboxq, mbox_tmo);
- if (rc != MBX_TIMEOUT)
- mempool_free(mboxq, phba->mbox_mem_pool);
if (rc != MBX_SUCCESS) {
lpfc_printf_log(phba, KERN_ERR, LOG_SLI,
"2022 INIT VPI Mailbox failed "
"status %d, mbxStatus x%x\n", rc,
bf_get(lpfc_mqe_status, &mboxq->u.mqe));
- rc = -EIO;
+ retval = -EIO;
}
- return rc;
+ if (rc != MBX_TIMEOUT)
+ mempool_free(mboxq, phba->mbox_mem_pool);
+
+ return retval;
}
/**
@@ -11438,13 +11777,6 @@ lpfc_sli4_add_fcf_record(struct lpfc_hba *phba, struct fcf_record *fcf_record)
*/
lpfc_sli4_mbx_sge_get(mboxq, 0, &sge);
phys_addr = getPaddr(sge.pa_hi, sge.pa_lo);
- if (unlikely(!mboxq->sge_array)) {
- lpfc_printf_log(phba, KERN_ERR, LOG_MBOX,
- "2526 Failed to get the non-embedded SGE "
- "virtual address\n");
- lpfc_sli4_mbox_cmd_free(phba, mboxq);
- return -ENOMEM;
- }
virt_addr = mboxq->sge_array->addr[0];
/*
* Configure the FCF record for FCFI 0. This is the driver's
@@ -11542,7 +11874,8 @@ lpfc_sli4_read_fcf_record(struct lpfc_hba *phba, uint16_t fcf_index)
lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
"2000 Failed to allocate mbox for "
"READ_FCF cmd\n");
- return -ENOMEM;
+ error = -ENOMEM;
+ goto fail_fcfscan;
}
req_len = sizeof(struct fcf_record) +
@@ -11558,8 +11891,8 @@ lpfc_sli4_read_fcf_record(struct lpfc_hba *phba, uint16_t fcf_index)
"0291 Allocated DMA memory size (x%x) is "
"less than the requested DMA memory "
"size (x%x)\n", alloc_len, req_len);
- lpfc_sli4_mbox_cmd_free(phba, mboxq);
- return -ENOMEM;
+ error = -ENOMEM;
+ goto fail_fcfscan;
}
/* Get the first SGE entry from the non-embedded DMA memory. This
@@ -11567,13 +11900,6 @@ lpfc_sli4_read_fcf_record(struct lpfc_hba *phba, uint16_t fcf_index)
*/
lpfc_sli4_mbx_sge_get(mboxq, 0, &sge);
phys_addr = getPaddr(sge.pa_hi, sge.pa_lo);
- if (unlikely(!mboxq->sge_array)) {
- lpfc_printf_log(phba, KERN_ERR, LOG_MBOX,
- "2527 Failed to get the non-embedded SGE "
- "virtual address\n");
- lpfc_sli4_mbox_cmd_free(phba, mboxq);
- return -ENOMEM;
- }
virt_addr = mboxq->sge_array->addr[0];
read_fcf = (struct lpfc_mbx_read_fcf_tbl *)virt_addr;
@@ -11586,7 +11912,6 @@ lpfc_sli4_read_fcf_record(struct lpfc_hba *phba, uint16_t fcf_index)
mboxq->mbox_cmpl = lpfc_mbx_cmpl_read_fcf_record;
rc = lpfc_sli_issue_mbox(phba, mboxq, MBX_NOWAIT);
if (rc == MBX_NOT_FINISHED) {
- lpfc_sli4_mbox_cmd_free(phba, mboxq);
error = -EIO;
} else {
spin_lock_irq(&phba->hbalock);
@@ -11594,6 +11919,15 @@ lpfc_sli4_read_fcf_record(struct lpfc_hba *phba, uint16_t fcf_index)
spin_unlock_irq(&phba->hbalock);
error = 0;
}
+fail_fcfscan:
+ if (error) {
+ if (mboxq)
+ lpfc_sli4_mbox_cmd_free(phba, mboxq);
+ /* FCF scan failed, clear FCF_DISC_INPROGRESS flag */
+ spin_lock_irq(&phba->hbalock);
+ phba->hba_flag &= ~FCF_DISC_INPROGRESS;
+ spin_unlock_irq(&phba->hbalock);
+ }
return error;
}
diff --git a/drivers/scsi/lpfc/lpfc_sli.h b/drivers/scsi/lpfc/lpfc_sli.h
index 3c53316cf6d..ba38de3c28f 100644
--- a/drivers/scsi/lpfc/lpfc_sli.h
+++ b/drivers/scsi/lpfc/lpfc_sli.h
@@ -29,14 +29,17 @@ typedef enum _lpfc_ctx_cmd {
LPFC_CTX_HOST
} lpfc_ctx_cmd;
-/* This structure is used to carry the needed response IOCB states */
-struct lpfc_sli4_rspiocb_info {
- uint8_t hw_status;
- uint8_t bfield;
-#define LPFC_XB 0x1
-#define LPFC_PV 0x2
- uint8_t priority;
- uint8_t reserved;
+struct lpfc_cq_event {
+ struct list_head list;
+ union {
+ struct lpfc_mcqe mcqe_cmpl;
+ struct lpfc_acqe_link acqe_link;
+ struct lpfc_acqe_fcoe acqe_fcoe;
+ struct lpfc_acqe_dcbx acqe_dcbx;
+ struct lpfc_rcqe rcqe_cmpl;
+ struct sli4_wcqe_xri_aborted wcqe_axri;
+ struct lpfc_wcqe_complete wcqe_cmpl;
+ } cqe;
};
/* This structure is used to handle IOCB requests / responses */
@@ -46,6 +49,7 @@ struct lpfc_iocbq {
struct list_head clist;
uint16_t iotag; /* pre-assigned IO tag */
uint16_t sli4_xritag; /* pre-assigned XRI, (OXID) tag. */
+ struct lpfc_cq_event cq_event;
IOCB_t iocb; /* IOCB cmd */
uint8_t retry; /* retry counter for IOCB cmd - if needed */
@@ -56,11 +60,13 @@ struct lpfc_iocbq {
#define LPFC_DRIVER_ABORTED 8 /* driver aborted this request */
#define LPFC_IO_FABRIC 0x10 /* Iocb send using fabric scheduler */
#define LPFC_DELAY_MEM_FREE 0x20 /* Defer free'ing of FC data */
-#define LPFC_FIP_ELS 0x40
+#define LPFC_FIP_ELS_ID_MASK 0xc0 /* ELS_ID range 0-3 */
+#define LPFC_FIP_ELS_ID_SHIFT 6
uint8_t abort_count;
uint8_t rsvd2;
uint32_t drvrTimeout; /* driver timeout in seconds */
+ uint32_t fcp_wqidx; /* index to FCP work queue */
struct lpfc_vport *vport;/* virtual port pointer */
void *context1; /* caller context information */
void *context2; /* caller context information */
@@ -76,7 +82,6 @@ struct lpfc_iocbq {
struct lpfc_iocbq *);
void (*iocb_cmpl) (struct lpfc_hba *, struct lpfc_iocbq *,
struct lpfc_iocbq *);
- struct lpfc_sli4_rspiocb_info sli4_info;
};
#define SLI_IOCB_RET_IOCB 1 /* Return IOCB if cmd ring full */
@@ -110,7 +115,7 @@ typedef struct lpfcMboxq {
return */
#define MBX_NOWAIT 2 /* issue command then return immediately */
-#define LPFC_MAX_RING_MASK 4 /* max num of rctl/type masks allowed per
+#define LPFC_MAX_RING_MASK 5 /* max num of rctl/type masks allowed per
ring */
#define LPFC_MAX_RING 4 /* max num of SLI rings used by driver */
diff --git a/drivers/scsi/lpfc/lpfc_sli4.h b/drivers/scsi/lpfc/lpfc_sli4.h
index b5f4ba1a5c2..25d66d070cf 100644
--- a/drivers/scsi/lpfc/lpfc_sli4.h
+++ b/drivers/scsi/lpfc/lpfc_sli4.h
@@ -58,6 +58,16 @@
#define LPFC_FCOE_FKA_ADV_PER 0
#define LPFC_FCOE_FIP_PRIORITY 0x80
+#define sli4_sid_from_fc_hdr(fc_hdr) \
+ ((fc_hdr)->fh_s_id[0] << 16 | \
+ (fc_hdr)->fh_s_id[1] << 8 | \
+ (fc_hdr)->fh_s_id[2])
+
+#define sli4_fctl_from_fc_hdr(fc_hdr) \
+ ((fc_hdr)->fh_f_ctl[0] << 16 | \
+ (fc_hdr)->fh_f_ctl[1] << 8 | \
+ (fc_hdr)->fh_f_ctl[2])
+
enum lpfc_sli4_queue_type {
LPFC_EQ,
LPFC_GCQ,
@@ -110,18 +120,6 @@ struct lpfc_queue {
union sli4_qe qe[1]; /* array to index entries (must be last) */
};
-struct lpfc_cq_event {
- struct list_head list;
- union {
- struct lpfc_mcqe mcqe_cmpl;
- struct lpfc_acqe_link acqe_link;
- struct lpfc_acqe_fcoe acqe_fcoe;
- struct lpfc_acqe_dcbx acqe_dcbx;
- struct lpfc_rcqe rcqe_cmpl;
- struct sli4_wcqe_xri_aborted wcqe_axri;
- } cqe;
-};
-
struct lpfc_sli4_link {
uint8_t speed;
uint8_t duplex;
@@ -166,7 +164,7 @@ struct lpfc_fip_param_hdr {
#define lpfc_fip_param_hdr_fipp_mode_SHIFT 6
#define lpfc_fip_param_hdr_fipp_mode_MASK 0x3
#define lpfc_fip_param_hdr_fipp_mode_WORD parm_flags
-#define FIPP_MODE_ON 0x2
+#define FIPP_MODE_ON 0x1
#define FIPP_MODE_OFF 0x0
#define FIPP_VLAN_VALID 0x1
};
@@ -295,9 +293,8 @@ struct lpfc_sli4_hba {
/* BAR0 PCI config space register memory map */
void __iomem *UERRLOregaddr; /* Address to UERR_STATUS_LO register */
void __iomem *UERRHIregaddr; /* Address to UERR_STATUS_HI register */
- void __iomem *ONLINE0regaddr; /* Address to components of internal UE */
- void __iomem *ONLINE1regaddr; /* Address to components of internal UE */
-#define LPFC_ONLINE_NERR 0xFFFFFFFF
+ void __iomem *UEMASKLOregaddr; /* Address to UE_MASK_LO register */
+ void __iomem *UEMASKHIregaddr; /* Address to UE_MASK_HI register */
void __iomem *SCRATCHPADregaddr; /* Address to scratchpad register */
/* BAR1 FCoE function CSR register memory map */
void __iomem *STAregaddr; /* Address to HST_STATE register */
@@ -311,6 +308,8 @@ struct lpfc_sli4_hba {
void __iomem *MQDBregaddr; /* Address to MQ_DOORBELL register */
void __iomem *BMBXregaddr; /* Address to BootStrap MBX register */
+ uint32_t ue_mask_lo;
+ uint32_t ue_mask_hi;
struct msix_entry *msix_entries;
uint32_t cfg_eqn;
struct lpfc_fcp_eq_hdl *fcp_eq_hdl; /* FCP per-WQ handle */
@@ -325,7 +324,6 @@ struct lpfc_sli4_hba {
struct lpfc_queue **fcp_cq;/* Fast-path FCP compl queue */
struct lpfc_queue *mbx_cq; /* Slow-path mailbox complete queue */
struct lpfc_queue *els_cq; /* Slow-path ELS response complete queue */
- struct lpfc_queue *rxq_cq; /* Slow-path unsolicited complete queue */
/* Setup information for various queue parameters */
int eq_esize;
@@ -360,7 +358,7 @@ struct lpfc_sli4_hba {
unsigned long *rpi_bmask;
uint16_t rpi_count;
struct lpfc_sli4_flags sli4_flags;
- struct list_head sp_rspiocb_work_queue;
+ struct list_head sp_queue_event;
struct list_head sp_cqe_event_pool;
struct list_head sp_asynce_work_queue;
struct list_head sp_fcp_xri_aborted_work_queue;
diff --git a/drivers/scsi/lpfc/lpfc_version.h b/drivers/scsi/lpfc/lpfc_version.h
index 9ae20af4bdb..c7f3aed2aab 100644
--- a/drivers/scsi/lpfc/lpfc_version.h
+++ b/drivers/scsi/lpfc/lpfc_version.h
@@ -18,8 +18,7 @@
* included with this package. *
*******************************************************************/
-#define LPFC_DRIVER_VERSION "8.3.4"
-
+#define LPFC_DRIVER_VERSION "8.3.6"
#define LPFC_DRIVER_NAME "lpfc"
#define LPFC_SP_DRIVER_HANDLER_NAME "lpfc:sp"
#define LPFC_FP_DRIVER_HANDLER_NAME "lpfc:fp"
diff --git a/drivers/scsi/lpfc/lpfc_vport.c b/drivers/scsi/lpfc/lpfc_vport.c
index 606efa76754..7d6dd83d359 100644
--- a/drivers/scsi/lpfc/lpfc_vport.c
+++ b/drivers/scsi/lpfc/lpfc_vport.c
@@ -389,7 +389,7 @@ lpfc_vport_create(struct fc_vport *fc_vport, bool disable)
* by the port.
*/
if ((phba->sli_rev == LPFC_SLI_REV4) &&
- (pport->vfi_state & LPFC_VFI_REGISTERED)) {
+ (pport->vpi_state & LPFC_VPI_REGISTERED)) {
rc = lpfc_sli4_init_vpi(phba, vpi);
if (rc) {
lpfc_printf_log(phba, KERN_ERR, LOG_VPORT,
@@ -700,6 +700,8 @@ lpfc_vport_delete(struct fc_vport *fc_vport)
}
spin_unlock_irq(&phba->ndlp_lock);
}
+ if (vport->vpi_state != LPFC_VPI_REGISTERED)
+ goto skip_logo;
vport->unreg_vpi_cmpl = VPORT_INVAL;
timeout = msecs_to_jiffies(phba->fc_ratov * 2000);
if (!lpfc_issue_els_npiv_logo(vport, ndlp))
diff --git a/drivers/scsi/megaraid/megaraid_mbox.c b/drivers/scsi/megaraid/megaraid_mbox.c
index f9ae8037a71..7f977967b88 100644
--- a/drivers/scsi/megaraid/megaraid_mbox.c
+++ b/drivers/scsi/megaraid/megaraid_mbox.c
@@ -335,12 +335,17 @@ static struct device_attribute *megaraid_sdev_attrs[] = {
* megaraid_change_queue_depth - Change the device's queue depth
* @sdev: scsi device struct
* @qdepth: depth to set
+ * @reason: calling context
*
* Return value:
* actual depth set
*/
-static int megaraid_change_queue_depth(struct scsi_device *sdev, int qdepth)
+static int megaraid_change_queue_depth(struct scsi_device *sdev, int qdepth,
+ int reason)
{
+ if (reason != SCSI_QDEPTH_DEFAULT)
+ return -EOPNOTSUPP;
+
if (qdepth > MBOX_MAX_SCSI_CMDS)
qdepth = MBOX_MAX_SCSI_CMDS;
scsi_adjust_queue_depth(sdev, 0, qdepth);
diff --git a/drivers/scsi/megaraid/megaraid_sas.c b/drivers/scsi/megaraid/megaraid_sas.c
index a39addc3a59..134c63ef6d3 100644
--- a/drivers/scsi/megaraid/megaraid_sas.c
+++ b/drivers/scsi/megaraid/megaraid_sas.c
@@ -10,7 +10,7 @@
* 2 of the License, or (at your option) any later version.
*
* FILE : megaraid_sas.c
- * Version : v00.00.04.01-rc1
+ * Version : v00.00.04.12-rc1
*
* Authors:
* (email-id : megaraidlinux@lsi.com)
@@ -40,6 +40,7 @@
#include <linux/compat.h>
#include <linux/blkdev.h>
#include <linux/mutex.h>
+#include <linux/poll.h>
#include <scsi/scsi.h>
#include <scsi/scsi_cmnd.h>
@@ -75,6 +76,10 @@ static struct pci_device_id megasas_pci_table[] = {
/* gen2*/
{PCI_DEVICE(PCI_VENDOR_ID_LSI_LOGIC, PCI_DEVICE_ID_LSI_SAS0079GEN2)},
/* gen2*/
+ {PCI_DEVICE(PCI_VENDOR_ID_LSI_LOGIC, PCI_DEVICE_ID_LSI_SAS0073SKINNY)},
+ /* skinny*/
+ {PCI_DEVICE(PCI_VENDOR_ID_LSI_LOGIC, PCI_DEVICE_ID_LSI_SAS0071SKINNY)},
+ /* skinny*/
{PCI_DEVICE(PCI_VENDOR_ID_LSI_LOGIC, PCI_DEVICE_ID_LSI_VERDE_ZCR)},
/* xscale IOP, vega */
{PCI_DEVICE(PCI_VENDOR_ID_DELL, PCI_DEVICE_ID_DELL_PERC5)},
@@ -89,8 +94,14 @@ static struct megasas_mgmt_info megasas_mgmt_info;
static struct fasync_struct *megasas_async_queue;
static DEFINE_MUTEX(megasas_async_queue_mutex);
+static int megasas_poll_wait_aen;
+static DECLARE_WAIT_QUEUE_HEAD(megasas_poll_wait);
+static u32 support_poll_for_event;
static u32 megasas_dbg_lvl;
+/* define lock for aen poll */
+spinlock_t poll_aen_lock;
+
static void
megasas_complete_cmd(struct megasas_instance *instance, struct megasas_cmd *cmd,
u8 alt_status);
@@ -215,7 +226,10 @@ megasas_clear_intr_xscale(struct megasas_register_set __iomem * regs)
* @regs : MFI register set
*/
static inline void
-megasas_fire_cmd_xscale(dma_addr_t frame_phys_addr,u32 frame_count, struct megasas_register_set __iomem *regs)
+megasas_fire_cmd_xscale(struct megasas_instance *instance,
+ dma_addr_t frame_phys_addr,
+ u32 frame_count,
+ struct megasas_register_set __iomem *regs)
{
writel((frame_phys_addr >> 3)|(frame_count),
&(regs)->inbound_queue_port);
@@ -312,7 +326,10 @@ megasas_clear_intr_ppc(struct megasas_register_set __iomem * regs)
* @regs : MFI register set
*/
static inline void
-megasas_fire_cmd_ppc(dma_addr_t frame_phys_addr, u32 frame_count, struct megasas_register_set __iomem *regs)
+megasas_fire_cmd_ppc(struct megasas_instance *instance,
+ dma_addr_t frame_phys_addr,
+ u32 frame_count,
+ struct megasas_register_set __iomem *regs)
{
writel((frame_phys_addr | (frame_count<<1))|1,
&(regs)->inbound_queue_port);
@@ -328,6 +345,104 @@ static struct megasas_instance_template megasas_instance_template_ppc = {
};
/**
+ * megasas_enable_intr_skinny - Enables interrupts
+ * @regs: MFI register set
+ */
+static inline void
+megasas_enable_intr_skinny(struct megasas_register_set __iomem *regs)
+{
+ writel(0xFFFFFFFF, &(regs)->outbound_intr_mask);
+
+ writel(~MFI_SKINNY_ENABLE_INTERRUPT_MASK, &(regs)->outbound_intr_mask);
+
+ /* Dummy readl to force pci flush */
+ readl(&regs->outbound_intr_mask);
+}
+
+/**
+ * megasas_disable_intr_skinny - Disables interrupt
+ * @regs: MFI register set
+ */
+static inline void
+megasas_disable_intr_skinny(struct megasas_register_set __iomem *regs)
+{
+ u32 mask = 0xFFFFFFFF;
+ writel(mask, &regs->outbound_intr_mask);
+ /* Dummy readl to force pci flush */
+ readl(&regs->outbound_intr_mask);
+}
+
+/**
+ * megasas_read_fw_status_reg_skinny - returns the current FW status value
+ * @regs: MFI register set
+ */
+static u32
+megasas_read_fw_status_reg_skinny(struct megasas_register_set __iomem *regs)
+{
+ return readl(&(regs)->outbound_scratch_pad);
+}
+
+/**
+ * megasas_clear_interrupt_skinny - Check & clear interrupt
+ * @regs: MFI register set
+ */
+static int
+megasas_clear_intr_skinny(struct megasas_register_set __iomem *regs)
+{
+ u32 status;
+ /*
+ * Check if it is our interrupt
+ */
+ status = readl(&regs->outbound_intr_status);
+
+ if (!(status & MFI_SKINNY_ENABLE_INTERRUPT_MASK)) {
+ return 1;
+ }
+
+ /*
+ * Clear the interrupt by writing back the same value
+ */
+ writel(status, &regs->outbound_intr_status);
+
+ /*
+ * dummy read to flush PCI
+ */
+ readl(&regs->outbound_intr_status);
+
+ return 0;
+}
+
+/**
+ * megasas_fire_cmd_skinny - Sends command to the FW
+ * @frame_phys_addr : Physical address of cmd
+ * @frame_count : Number of frames for the command
+ * @regs : MFI register set
+ */
+static inline void
+megasas_fire_cmd_skinny(struct megasas_instance *instance,
+ dma_addr_t frame_phys_addr,
+ u32 frame_count,
+ struct megasas_register_set __iomem *regs)
+{
+ unsigned long flags;
+ spin_lock_irqsave(&instance->fire_lock, flags);
+ writel(0, &(regs)->inbound_high_queue_port);
+ writel((frame_phys_addr | (frame_count<<1))|1,
+ &(regs)->inbound_low_queue_port);
+ spin_unlock_irqrestore(&instance->fire_lock, flags);
+}
+
+static struct megasas_instance_template megasas_instance_template_skinny = {
+
+ .fire_cmd = megasas_fire_cmd_skinny,
+ .enable_intr = megasas_enable_intr_skinny,
+ .disable_intr = megasas_disable_intr_skinny,
+ .clear_intr = megasas_clear_intr_skinny,
+ .read_fw_status_reg = megasas_read_fw_status_reg_skinny,
+};
+
+
+/**
* The following functions are defined for gen2 (deviceid : 0x78 0x79)
* controllers
*/
@@ -404,7 +519,9 @@ megasas_clear_intr_gen2(struct megasas_register_set __iomem *regs)
* @regs : MFI register set
*/
static inline void
-megasas_fire_cmd_gen2(dma_addr_t frame_phys_addr, u32 frame_count,
+megasas_fire_cmd_gen2(struct megasas_instance *instance,
+ dma_addr_t frame_phys_addr,
+ u32 frame_count,
struct megasas_register_set __iomem *regs)
{
writel((frame_phys_addr | (frame_count<<1))|1,
@@ -446,7 +563,8 @@ megasas_issue_polled(struct megasas_instance *instance, struct megasas_cmd *cmd)
/*
* Issue the frame using inbound queue port
*/
- instance->instancet->fire_cmd(cmd->frame_phys_addr ,0,instance->reg_set);
+ instance->instancet->fire_cmd(instance,
+ cmd->frame_phys_addr, 0, instance->reg_set);
/*
* Wait for cmd_status to change
@@ -477,7 +595,8 @@ megasas_issue_blocked_cmd(struct megasas_instance *instance,
{
cmd->cmd_status = ENODATA;
- instance->instancet->fire_cmd(cmd->frame_phys_addr ,0,instance->reg_set);
+ instance->instancet->fire_cmd(instance,
+ cmd->frame_phys_addr, 0, instance->reg_set);
wait_event_timeout(instance->int_cmd_wait_q, (cmd->cmd_status != ENODATA),
MEGASAS_INTERNAL_CMD_WAIT_TIME*HZ);
@@ -522,7 +641,8 @@ megasas_issue_blocked_abort_cmd(struct megasas_instance *instance,
cmd->sync_cmd = 1;
cmd->cmd_status = 0xFF;
- instance->instancet->fire_cmd(cmd->frame_phys_addr ,0,instance->reg_set);
+ instance->instancet->fire_cmd(instance,
+ cmd->frame_phys_addr, 0, instance->reg_set);
/*
* Wait for this cmd to complete
@@ -592,6 +712,35 @@ megasas_make_sgl64(struct megasas_instance *instance, struct scsi_cmnd *scp,
return sge_count;
}
+/**
+ * megasas_make_sgl_skinny - Prepares IEEE SGL
+ * @instance: Adapter soft state
+ * @scp: SCSI command from the mid-layer
+ * @mfi_sgl: SGL to be filled in
+ *
+ * If successful, this function returns the number of SG elements. Otherwise,
+ * it returnes -1.
+ */
+static int
+megasas_make_sgl_skinny(struct megasas_instance *instance,
+ struct scsi_cmnd *scp, union megasas_sgl *mfi_sgl)
+{
+ int i;
+ int sge_count;
+ struct scatterlist *os_sgl;
+
+ sge_count = scsi_dma_map(scp);
+
+ if (sge_count) {
+ scsi_for_each_sg(scp, os_sgl, sge_count, i) {
+ mfi_sgl->sge_skinny[i].length = sg_dma_len(os_sgl);
+ mfi_sgl->sge_skinny[i].phys_addr =
+ sg_dma_address(os_sgl);
+ }
+ }
+ return sge_count;
+}
+
/**
* megasas_get_frame_count - Computes the number of frames
* @frame_type : type of frame- io or pthru frame
@@ -600,7 +749,8 @@ megasas_make_sgl64(struct megasas_instance *instance, struct scsi_cmnd *scp,
* Returns the number of frames required for numnber of sge's (sge_count)
*/
-static u32 megasas_get_frame_count(u8 sge_count, u8 frame_type)
+static u32 megasas_get_frame_count(struct megasas_instance *instance,
+ u8 sge_count, u8 frame_type)
{
int num_cnt;
int sge_bytes;
@@ -610,6 +760,10 @@ static u32 megasas_get_frame_count(u8 sge_count, u8 frame_type)
sge_sz = (IS_DMA64) ? sizeof(struct megasas_sge64) :
sizeof(struct megasas_sge32);
+ if (instance->flag_ieee) {
+ sge_sz = sizeof(struct megasas_sge_skinny);
+ }
+
/*
* Main frame can contain 2 SGEs for 64-bit SGLs and
* 3 SGEs for 32-bit SGLs for ldio &
@@ -617,12 +771,16 @@ static u32 megasas_get_frame_count(u8 sge_count, u8 frame_type)
* 2 SGEs for 32-bit SGLs for pthru frame
*/
if (unlikely(frame_type == PTHRU_FRAME)) {
- if (IS_DMA64)
+ if (instance->flag_ieee == 1) {
+ num_cnt = sge_count - 1;
+ } else if (IS_DMA64)
num_cnt = sge_count - 1;
else
num_cnt = sge_count - 2;
} else {
- if (IS_DMA64)
+ if (instance->flag_ieee == 1) {
+ num_cnt = sge_count - 1;
+ } else if (IS_DMA64)
num_cnt = sge_count - 2;
else
num_cnt = sge_count - 3;
@@ -671,6 +829,10 @@ megasas_build_dcdb(struct megasas_instance *instance, struct scsi_cmnd *scp,
else if (scp->sc_data_direction == PCI_DMA_NONE)
flags = MFI_FRAME_DIR_NONE;
+ if (instance->flag_ieee == 1) {
+ flags |= MFI_FRAME_IEEE;
+ }
+
/*
* Prepare the DCDB frame
*/
@@ -687,9 +849,24 @@ megasas_build_dcdb(struct megasas_instance *instance, struct scsi_cmnd *scp,
memcpy(pthru->cdb, scp->cmnd, scp->cmd_len);
/*
+ * If the command is for the tape device, set the
+ * pthru timeout to the os layer timeout value.
+ */
+ if (scp->device->type == TYPE_TAPE) {
+ if ((scp->request->timeout / HZ) > 0xFFFF)
+ pthru->timeout = 0xFFFF;
+ else
+ pthru->timeout = scp->request->timeout / HZ;
+ }
+
+ /*
* Construct SGL
*/
- if (IS_DMA64) {
+ if (instance->flag_ieee == 1) {
+ pthru->flags |= MFI_FRAME_SGL64;
+ pthru->sge_count = megasas_make_sgl_skinny(instance, scp,
+ &pthru->sgl);
+ } else if (IS_DMA64) {
pthru->flags |= MFI_FRAME_SGL64;
pthru->sge_count = megasas_make_sgl64(instance, scp,
&pthru->sgl);
@@ -708,7 +885,7 @@ megasas_build_dcdb(struct megasas_instance *instance, struct scsi_cmnd *scp,
* Compute the total number of frames this command consumes. FW uses
* this number to pull sufficient number of frames from host memory.
*/
- cmd->frame_count = megasas_get_frame_count(pthru->sge_count,
+ cmd->frame_count = megasas_get_frame_count(instance, pthru->sge_count,
PTHRU_FRAME);
return cmd->frame_count;
@@ -739,6 +916,10 @@ megasas_build_ldio(struct megasas_instance *instance, struct scsi_cmnd *scp,
else if (scp->sc_data_direction == PCI_DMA_FROMDEVICE)
flags = MFI_FRAME_DIR_READ;
+ if (instance->flag_ieee == 1) {
+ flags |= MFI_FRAME_IEEE;
+ }
+
/*
* Prepare the Logical IO frame: 2nd bit is zero for all read cmds
*/
@@ -809,7 +990,11 @@ megasas_build_ldio(struct megasas_instance *instance, struct scsi_cmnd *scp,
/*
* Construct SGL
*/
- if (IS_DMA64) {
+ if (instance->flag_ieee) {
+ ldio->flags |= MFI_FRAME_SGL64;
+ ldio->sge_count = megasas_make_sgl_skinny(instance, scp,
+ &ldio->sgl);
+ } else if (IS_DMA64) {
ldio->flags |= MFI_FRAME_SGL64;
ldio->sge_count = megasas_make_sgl64(instance, scp, &ldio->sgl);
} else
@@ -826,7 +1011,8 @@ megasas_build_ldio(struct megasas_instance *instance, struct scsi_cmnd *scp,
* Compute the total number of frames this command consumes. FW uses
* this number to pull sufficient number of frames from host memory.
*/
- cmd->frame_count = megasas_get_frame_count(ldio->sge_count, IO_FRAME);
+ cmd->frame_count = megasas_get_frame_count(instance,
+ ldio->sge_count, IO_FRAME);
return cmd->frame_count;
}
@@ -983,7 +1169,8 @@ megasas_queue_command(struct scsi_cmnd *scmd, void (*done) (struct scsi_cmnd *))
*/
atomic_inc(&instance->fw_outstanding);
- instance->instancet->fire_cmd(cmd->frame_phys_addr ,cmd->frame_count-1,instance->reg_set);
+ instance->instancet->fire_cmd(instance, cmd->frame_phys_addr,
+ cmd->frame_count-1, instance->reg_set);
/*
* Check if we have pend cmds to be completed
*/
@@ -1000,24 +1187,76 @@ megasas_queue_command(struct scsi_cmnd *scmd, void (*done) (struct scsi_cmnd *))
return 0;
}
+static struct megasas_instance *megasas_lookup_instance(u16 host_no)
+{
+ int i;
+
+ for (i = 0; i < megasas_mgmt_info.max_index; i++) {
+
+ if ((megasas_mgmt_info.instance[i]) &&
+ (megasas_mgmt_info.instance[i]->host->host_no == host_no))
+ return megasas_mgmt_info.instance[i];
+ }
+
+ return NULL;
+}
+
static int megasas_slave_configure(struct scsi_device *sdev)
{
+ u16 pd_index = 0;
+ struct megasas_instance *instance ;
+
+ instance = megasas_lookup_instance(sdev->host->host_no);
+
/*
- * Don't export physical disk devices to the disk driver.
- *
- * FIXME: Currently we don't export them to the midlayer at all.
- * That will be fixed once LSI engineers have audited the
- * firmware for possible issues.
- */
- if (sdev->channel < MEGASAS_MAX_PD_CHANNELS && sdev->type == TYPE_DISK)
+ * Don't export physical disk devices to the disk driver.
+ *
+ * FIXME: Currently we don't export them to the midlayer at all.
+ * That will be fixed once LSI engineers have audited the
+ * firmware for possible issues.
+ */
+ if (sdev->channel < MEGASAS_MAX_PD_CHANNELS &&
+ sdev->type == TYPE_DISK) {
+ pd_index = (sdev->channel * MEGASAS_MAX_DEV_PER_CHANNEL) +
+ sdev->id;
+ if (instance->pd_list[pd_index].driveState ==
+ MR_PD_STATE_SYSTEM) {
+ blk_queue_rq_timeout(sdev->request_queue,
+ MEGASAS_DEFAULT_CMD_TIMEOUT * HZ);
+ return 0;
+ }
return -ENXIO;
+ }
/*
- * The RAID firmware may require extended timeouts.
- */
- if (sdev->channel >= MEGASAS_MAX_PD_CHANNELS)
- blk_queue_rq_timeout(sdev->request_queue,
- MEGASAS_DEFAULT_CMD_TIMEOUT * HZ);
+ * The RAID firmware may require extended timeouts.
+ */
+ blk_queue_rq_timeout(sdev->request_queue,
+ MEGASAS_DEFAULT_CMD_TIMEOUT * HZ);
+ return 0;
+}
+
+static int megasas_slave_alloc(struct scsi_device *sdev)
+{
+ u16 pd_index = 0;
+ struct megasas_instance *instance ;
+ instance = megasas_lookup_instance(sdev->host->host_no);
+ if ((sdev->channel < MEGASAS_MAX_PD_CHANNELS) &&
+ (sdev->type == TYPE_DISK)) {
+ /*
+ * Open the OS scan to the SYSTEM PD
+ */
+ pd_index =
+ (sdev->channel * MEGASAS_MAX_DEV_PER_CHANNEL) +
+ sdev->id;
+ if ((instance->pd_list[pd_index].driveState ==
+ MR_PD_STATE_SYSTEM) &&
+ (instance->pd_list[pd_index].driveType ==
+ TYPE_DISK)) {
+ return 0;
+ }
+ return -ENXIO;
+ }
return 0;
}
@@ -1072,7 +1311,14 @@ static void megasas_complete_cmd_dpc(unsigned long instance_addr)
spin_lock_irqsave(instance->host->host_lock, flags);
instance->flag &= ~MEGASAS_FW_BUSY;
- instance->host->can_queue =
+ if ((instance->pdev->device ==
+ PCI_DEVICE_ID_LSI_SAS0073SKINNY) ||
+ (instance->pdev->device ==
+ PCI_DEVICE_ID_LSI_SAS0071SKINNY)) {
+ instance->host->can_queue =
+ instance->max_fw_cmds - MEGASAS_SKINNY_INT_CMDS;
+ } else
+ instance->host->can_queue =
instance->max_fw_cmds - MEGASAS_INT_CMDS;
spin_unlock_irqrestore(instance->host->host_lock, flags);
@@ -1117,8 +1363,16 @@ static int megasas_wait_for_outstanding(struct megasas_instance *instance)
* Send signal to FW to stop processing any pending cmds.
* The controller will be taken offline by the OS now.
*/
- writel(MFI_STOP_ADP,
+ if ((instance->pdev->device ==
+ PCI_DEVICE_ID_LSI_SAS0073SKINNY) ||
+ (instance->pdev->device ==
+ PCI_DEVICE_ID_LSI_SAS0071SKINNY)) {
+ writel(MFI_STOP_ADP,
+ &instance->reg_set->reserved_0[0]);
+ } else {
+ writel(MFI_STOP_ADP,
&instance->reg_set->inbound_doorbell);
+ }
megasas_dump_pending_frames(instance);
instance->hw_crit_error = 1;
return FAILED;
@@ -1266,6 +1520,8 @@ megasas_bios_param(struct scsi_device *sdev, struct block_device *bdev,
return 0;
}
+static void megasas_aen_polling(struct work_struct *work);
+
/**
* megasas_service_aen - Processes an event notification
* @instance: Adapter soft state
@@ -1281,16 +1537,36 @@ megasas_bios_param(struct scsi_device *sdev, struct block_device *bdev,
static void
megasas_service_aen(struct megasas_instance *instance, struct megasas_cmd *cmd)
{
+ unsigned long flags;
/*
* Don't signal app if it is just an aborted previously registered aen
*/
- if (!cmd->abort_aen)
+ if ((!cmd->abort_aen) && (instance->unload == 0)) {
+ spin_lock_irqsave(&poll_aen_lock, flags);
+ megasas_poll_wait_aen = 1;
+ spin_unlock_irqrestore(&poll_aen_lock, flags);
+ wake_up(&megasas_poll_wait);
kill_fasync(&megasas_async_queue, SIGIO, POLL_IN);
+ }
else
cmd->abort_aen = 0;
instance->aen_cmd = NULL;
megasas_return_cmd(instance, cmd);
+
+ if (instance->unload == 0) {
+ struct megasas_aen_event *ev;
+ ev = kzalloc(sizeof(*ev), GFP_ATOMIC);
+ if (!ev) {
+ printk(KERN_ERR "megasas_service_aen: out of memory\n");
+ } else {
+ ev->instance = instance;
+ instance->ev = ev;
+ INIT_WORK(&ev->hotplug_work, megasas_aen_polling);
+ schedule_delayed_work(
+ (struct delayed_work *)&ev->hotplug_work, 0);
+ }
+ }
}
/*
@@ -1302,6 +1578,7 @@ static struct scsi_host_template megasas_template = {
.name = "LSI SAS based MegaRAID driver",
.proc_name = "megaraid_sas",
.slave_configure = megasas_slave_configure,
+ .slave_alloc = megasas_slave_alloc,
.queuecommand = megasas_queue_command,
.eh_device_reset_handler = megasas_reset_device,
.eh_bus_reset_handler = megasas_reset_bus_host,
@@ -1370,6 +1647,7 @@ megasas_complete_cmd(struct megasas_instance *instance, struct megasas_cmd *cmd,
{
int exception = 0;
struct megasas_header *hdr = &cmd->frame->hdr;
+ unsigned long flags;
if (cmd->scmd)
cmd->scmd->SCp.ptr = NULL;
@@ -1459,6 +1737,12 @@ megasas_complete_cmd(struct megasas_instance *instance, struct megasas_cmd *cmd,
case MFI_CMD_SMP:
case MFI_CMD_STP:
case MFI_CMD_DCMD:
+ if (cmd->frame->dcmd.opcode == MR_DCMD_CTRL_EVENT_GET_INFO ||
+ cmd->frame->dcmd.opcode == MR_DCMD_CTRL_EVENT_GET) {
+ spin_lock_irqsave(&poll_aen_lock, flags);
+ megasas_poll_wait_aen = 0;
+ spin_unlock_irqrestore(&poll_aen_lock, flags);
+ }
/*
* See if got an event notification
@@ -1536,6 +1820,7 @@ megasas_transition_to_ready(struct megasas_instance* instance)
u8 max_wait;
u32 fw_state;
u32 cur_state;
+ u32 abs_state, curr_abs_state;
fw_state = instance->instancet->read_fw_status_reg(instance->reg_set) & MFI_STATE_MASK;
@@ -1545,6 +1830,9 @@ megasas_transition_to_ready(struct megasas_instance* instance)
while (fw_state != MFI_STATE_READY) {
+ abs_state =
+ instance->instancet->read_fw_status_reg(instance->reg_set);
+
switch (fw_state) {
case MFI_STATE_FAULT:
@@ -1556,18 +1844,36 @@ megasas_transition_to_ready(struct megasas_instance* instance)
/*
* Set the CLR bit in inbound doorbell
*/
- writel(MFI_INIT_CLEAR_HANDSHAKE|MFI_INIT_HOTPLUG,
- &instance->reg_set->inbound_doorbell);
+ if ((instance->pdev->device ==
+ PCI_DEVICE_ID_LSI_SAS0073SKINNY) ||
+ (instance->pdev->device ==
+ PCI_DEVICE_ID_LSI_SAS0071SKINNY)) {
+
+ writel(
+ MFI_INIT_CLEAR_HANDSHAKE|MFI_INIT_HOTPLUG,
+ &instance->reg_set->reserved_0[0]);
+ } else {
+ writel(
+ MFI_INIT_CLEAR_HANDSHAKE|MFI_INIT_HOTPLUG,
+ &instance->reg_set->inbound_doorbell);
+ }
- max_wait = 2;
+ max_wait = MEGASAS_RESET_WAIT_TIME;
cur_state = MFI_STATE_WAIT_HANDSHAKE;
break;
case MFI_STATE_BOOT_MESSAGE_PENDING:
- writel(MFI_INIT_HOTPLUG,
- &instance->reg_set->inbound_doorbell);
+ if ((instance->pdev->device ==
+ PCI_DEVICE_ID_LSI_SAS0073SKINNY) ||
+ (instance->pdev->device ==
+ PCI_DEVICE_ID_LSI_SAS0071SKINNY)) {
+ writel(MFI_INIT_HOTPLUG,
+ &instance->reg_set->reserved_0[0]);
+ } else
+ writel(MFI_INIT_HOTPLUG,
+ &instance->reg_set->inbound_doorbell);
- max_wait = 10;
+ max_wait = MEGASAS_RESET_WAIT_TIME;
cur_state = MFI_STATE_BOOT_MESSAGE_PENDING;
break;
@@ -1576,9 +1882,17 @@ megasas_transition_to_ready(struct megasas_instance* instance)
* Bring it to READY state; assuming max wait 10 secs
*/
instance->instancet->disable_intr(instance->reg_set);
- writel(MFI_RESET_FLAGS, &instance->reg_set->inbound_doorbell);
+ if ((instance->pdev->device ==
+ PCI_DEVICE_ID_LSI_SAS0073SKINNY) ||
+ (instance->pdev->device ==
+ PCI_DEVICE_ID_LSI_SAS0071SKINNY)) {
+ writel(MFI_RESET_FLAGS,
+ &instance->reg_set->reserved_0[0]);
+ } else
+ writel(MFI_RESET_FLAGS,
+ &instance->reg_set->inbound_doorbell);
- max_wait = 60;
+ max_wait = MEGASAS_RESET_WAIT_TIME;
cur_state = MFI_STATE_OPERATIONAL;
break;
@@ -1586,32 +1900,32 @@ megasas_transition_to_ready(struct megasas_instance* instance)
/*
* This state should not last for more than 2 seconds
*/
- max_wait = 2;
+ max_wait = MEGASAS_RESET_WAIT_TIME;
cur_state = MFI_STATE_UNDEFINED;
break;
case MFI_STATE_BB_INIT:
- max_wait = 2;
+ max_wait = MEGASAS_RESET_WAIT_TIME;
cur_state = MFI_STATE_BB_INIT;
break;
case MFI_STATE_FW_INIT:
- max_wait = 20;
+ max_wait = MEGASAS_RESET_WAIT_TIME;
cur_state = MFI_STATE_FW_INIT;
break;
case MFI_STATE_FW_INIT_2:
- max_wait = 20;
+ max_wait = MEGASAS_RESET_WAIT_TIME;
cur_state = MFI_STATE_FW_INIT_2;
break;
case MFI_STATE_DEVICE_SCAN:
- max_wait = 20;
+ max_wait = MEGASAS_RESET_WAIT_TIME;
cur_state = MFI_STATE_DEVICE_SCAN;
break;
case MFI_STATE_FLUSH_CACHE:
- max_wait = 20;
+ max_wait = MEGASAS_RESET_WAIT_TIME;
cur_state = MFI_STATE_FLUSH_CACHE;
break;
@@ -1627,8 +1941,10 @@ megasas_transition_to_ready(struct megasas_instance* instance)
for (i = 0; i < (max_wait * 1000); i++) {
fw_state = instance->instancet->read_fw_status_reg(instance->reg_set) &
MFI_STATE_MASK ;
+ curr_abs_state =
+ instance->instancet->read_fw_status_reg(instance->reg_set);
- if (fw_state == cur_state) {
+ if (abs_state == curr_abs_state) {
msleep(1);
} else
break;
@@ -1637,7 +1953,7 @@ megasas_transition_to_ready(struct megasas_instance* instance)
/*
* Return error if fw_state hasn't changed after max_wait
*/
- if (fw_state == cur_state) {
+ if (curr_abs_state == abs_state) {
printk(KERN_DEBUG "FW state [%d] hasn't changed "
"in %d secs\n", fw_state, max_wait);
return -ENODEV;
@@ -1715,6 +2031,10 @@ static int megasas_create_frame_pool(struct megasas_instance *instance)
sge_sz = (IS_DMA64) ? sizeof(struct megasas_sge64) :
sizeof(struct megasas_sge32);
+ if (instance->flag_ieee) {
+ sge_sz = sizeof(struct megasas_sge_skinny);
+ }
+
/*
* Calculated the number of 64byte frames required for SGL
*/
@@ -1777,6 +2097,7 @@ static int megasas_create_frame_pool(struct megasas_instance *instance)
}
cmd->frame->io.context = cmd->index;
+ cmd->frame->io.pad_0 = 0;
}
return 0;
@@ -1882,6 +2203,97 @@ static int megasas_alloc_cmds(struct megasas_instance *instance)
return 0;
}
+/*
+ * megasas_get_pd_list_info - Returns FW's pd_list structure
+ * @instance: Adapter soft state
+ * @pd_list: pd_list structure
+ *
+ * Issues an internal command (DCMD) to get the FW's controller PD
+ * list structure. This information is mainly used to find out SYSTEM
+ * supported by the FW.
+ */
+static int
+megasas_get_pd_list(struct megasas_instance *instance)
+{
+ int ret = 0, pd_index = 0;
+ struct megasas_cmd *cmd;
+ struct megasas_dcmd_frame *dcmd;
+ struct MR_PD_LIST *ci;
+ struct MR_PD_ADDRESS *pd_addr;
+ dma_addr_t ci_h = 0;
+
+ cmd = megasas_get_cmd(instance);
+
+ if (!cmd) {
+ printk(KERN_DEBUG "megasas (get_pd_list): Failed to get cmd\n");
+ return -ENOMEM;
+ }
+
+ dcmd = &cmd->frame->dcmd;
+
+ ci = pci_alloc_consistent(instance->pdev,
+ MEGASAS_MAX_PD * sizeof(struct MR_PD_LIST), &ci_h);
+
+ if (!ci) {
+ printk(KERN_DEBUG "Failed to alloc mem for pd_list\n");
+ megasas_return_cmd(instance, cmd);
+ return -ENOMEM;
+ }
+
+ memset(ci, 0, sizeof(*ci));
+ memset(dcmd->mbox.b, 0, MFI_MBOX_SIZE);
+
+ dcmd->mbox.b[0] = MR_PD_QUERY_TYPE_EXPOSED_TO_HOST;
+ dcmd->mbox.b[1] = 0;
+ dcmd->cmd = MFI_CMD_DCMD;
+ dcmd->cmd_status = 0xFF;
+ dcmd->sge_count = 1;
+ dcmd->flags = MFI_FRAME_DIR_READ;
+ dcmd->timeout = 0;
+ dcmd->data_xfer_len = MEGASAS_MAX_PD * sizeof(struct MR_PD_LIST);
+ dcmd->opcode = MR_DCMD_PD_LIST_QUERY;
+ dcmd->sgl.sge32[0].phys_addr = ci_h;
+ dcmd->sgl.sge32[0].length = MEGASAS_MAX_PD * sizeof(struct MR_PD_LIST);
+
+ if (!megasas_issue_polled(instance, cmd)) {
+ ret = 0;
+ } else {
+ ret = -1;
+ }
+
+ /*
+ * the following function will get the instance PD LIST.
+ */
+
+ pd_addr = ci->addr;
+
+ if ( ret == 0 &&
+ (ci->count <
+ (MEGASAS_MAX_PD_CHANNELS * MEGASAS_MAX_DEV_PER_CHANNEL))) {
+
+ memset(instance->pd_list, 0,
+ MEGASAS_MAX_PD * sizeof(struct megasas_pd_list));
+
+ for (pd_index = 0; pd_index < ci->count; pd_index++) {
+
+ instance->pd_list[pd_addr->deviceId].tid =
+ pd_addr->deviceId;
+ instance->pd_list[pd_addr->deviceId].driveType =
+ pd_addr->scsiDevType;
+ instance->pd_list[pd_addr->deviceId].driveState =
+ MR_PD_STATE_SYSTEM;
+ pd_addr++;
+ }
+ }
+
+ pci_free_consistent(instance->pdev,
+ MEGASAS_MAX_PD * sizeof(struct MR_PD_LIST),
+ ci, ci_h);
+ megasas_return_cmd(instance, cmd);
+
+ return ret;
+}
+
/**
* megasas_get_controller_info - Returns FW's controller structure
* @instance: Adapter soft state
@@ -2081,6 +2493,8 @@ static int megasas_init_mfi(struct megasas_instance *instance)
* Map the message registers
*/
if ((instance->pdev->device == PCI_DEVICE_ID_LSI_SAS1078GEN2) ||
+ (instance->pdev->device == PCI_DEVICE_ID_LSI_SAS0071SKINNY) ||
+ (instance->pdev->device == PCI_DEVICE_ID_LSI_SAS0073SKINNY) ||
(instance->pdev->device == PCI_DEVICE_ID_LSI_SAS0079GEN2)) {
instance->base_addr = pci_resource_start(instance->pdev, 1);
} else {
@@ -2111,6 +2525,10 @@ static int megasas_init_mfi(struct megasas_instance *instance)
case PCI_DEVICE_ID_LSI_SAS0079GEN2:
instance->instancet = &megasas_instance_template_gen2;
break;
+ case PCI_DEVICE_ID_LSI_SAS0073SKINNY:
+ case PCI_DEVICE_ID_LSI_SAS0071SKINNY:
+ instance->instancet = &megasas_instance_template_skinny;
+ break;
case PCI_DEVICE_ID_LSI_SAS1064R:
case PCI_DEVICE_ID_DELL_PERC5:
default:
@@ -2166,6 +2584,10 @@ static int megasas_init_mfi(struct megasas_instance *instance)
if (megasas_issue_init_mfi(instance))
goto fail_fw_init;
+ memset(instance->pd_list, 0 ,
+ (MEGASAS_MAX_PD * sizeof(struct megasas_pd_list)));
+ megasas_get_pd_list(instance);
+
ctrl_info = kmalloc(sizeof(struct megasas_ctrl_info), GFP_KERNEL);
/*
@@ -2409,6 +2831,11 @@ megasas_register_aen(struct megasas_instance *instance, u32 seq_num,
dcmd->sgl.sge32[0].phys_addr = (u32) instance->evt_detail_h;
dcmd->sgl.sge32[0].length = sizeof(struct megasas_evt_detail);
+ if (instance->aen_cmd != NULL) {
+ megasas_return_cmd(instance, cmd);
+ return 0;
+ }
+
/*
* Store reference to the cmd used to register for AEN. When an
* application wants us to register for AEN, we have to abort this
@@ -2419,7 +2846,8 @@ megasas_register_aen(struct megasas_instance *instance, u32 seq_num,
/*
* Issue the aen registration frame
*/
- instance->instancet->fire_cmd(cmd->frame_phys_addr ,0,instance->reg_set);
+ instance->instancet->fire_cmd(instance,
+ cmd->frame_phys_addr, 0, instance->reg_set);
return 0;
}
@@ -2465,7 +2893,13 @@ static int megasas_io_attach(struct megasas_instance *instance)
*/
host->irq = instance->pdev->irq;
host->unique_id = instance->unique_id;
- host->can_queue = instance->max_fw_cmds - MEGASAS_INT_CMDS;
+ if ((instance->pdev->device == PCI_DEVICE_ID_LSI_SAS0073SKINNY) ||
+ (instance->pdev->device == PCI_DEVICE_ID_LSI_SAS0071SKINNY)) {
+ host->can_queue =
+ instance->max_fw_cmds - MEGASAS_SKINNY_INT_CMDS;
+ } else
+ host->can_queue =
+ instance->max_fw_cmds - MEGASAS_INT_CMDS;
host->this_id = instance->init_id;
host->sg_tablesize = instance->max_num_sge;
host->max_sectors = instance->max_sectors_per_req;
@@ -2572,6 +3006,9 @@ megasas_probe_one(struct pci_dev *pdev, const struct pci_device_id *id)
*instance->producer = 0;
*instance->consumer = 0;
+ megasas_poll_wait_aen = 0;
+ instance->flag_ieee = 0;
+ instance->ev = NULL;
instance->evt_detail = pci_alloc_consistent(pdev,
sizeof(struct
@@ -2595,10 +3032,11 @@ megasas_probe_one(struct pci_dev *pdev, const struct pci_device_id *id)
init_waitqueue_head(&instance->abort_cmd_wait_q);
spin_lock_init(&instance->cmd_pool_lock);
+ spin_lock_init(&instance->fire_lock);
spin_lock_init(&instance->completion_lock);
+ spin_lock_init(&poll_aen_lock);
mutex_init(&instance->aen_mutex);
- sema_init(&instance->ioctl_sem, MEGASAS_INT_CMDS);
/*
* Initialize PCI related and misc parameters
@@ -2608,8 +3046,16 @@ megasas_probe_one(struct pci_dev *pdev, const struct pci_device_id *id)
instance->unique_id = pdev->bus->number << 8 | pdev->devfn;
instance->init_id = MEGASAS_DEFAULT_INIT_ID;
+ if ((instance->pdev->device == PCI_DEVICE_ID_LSI_SAS0073SKINNY) ||
+ (instance->pdev->device == PCI_DEVICE_ID_LSI_SAS0071SKINNY)) {
+ instance->flag_ieee = 1;
+ sema_init(&instance->ioctl_sem, MEGASAS_SKINNY_INT_CMDS);
+ } else
+ sema_init(&instance->ioctl_sem, MEGASAS_INT_CMDS);
+
megasas_dbg_lvl = 0;
instance->flag = 0;
+ instance->unload = 1;
instance->last_time = 0;
/*
@@ -2655,6 +3101,7 @@ megasas_probe_one(struct pci_dev *pdev, const struct pci_device_id *id)
if (megasas_io_attach(instance))
goto fail_io_attach;
+ instance->unload = 0;
return 0;
fail_start_aen:
@@ -2778,12 +3225,23 @@ megasas_suspend(struct pci_dev *pdev, pm_message_t state)
instance = pci_get_drvdata(pdev);
host = instance->host;
+ instance->unload = 1;
if (poll_mode_io)
del_timer_sync(&instance->io_completion_timer);
megasas_flush_cache(instance);
megasas_shutdown_controller(instance, MR_DCMD_HIBERNATE_SHUTDOWN);
+
+ /* cancel the delayed work if this work still in queue */
+ if (instance->ev != NULL) {
+ struct megasas_aen_event *ev = instance->ev;
+ cancel_delayed_work(
+ (struct delayed_work *)&ev->hotplug_work);
+ flush_scheduled_work();
+ instance->ev = NULL;
+ }
+
tasklet_kill(&instance->isr_tasklet);
pci_set_drvdata(instance->pdev, instance);
@@ -2873,6 +3331,8 @@ megasas_resume(struct pci_dev *pdev)
megasas_start_timer(instance, &instance->io_completion_timer,
megasas_io_completion_timer,
MEGASAS_COMPLETION_TIMER_INTERVAL);
+ instance->unload = 0;
+
return 0;
fail_irq:
@@ -2913,6 +3373,7 @@ static void __devexit megasas_detach_one(struct pci_dev *pdev)
struct megasas_instance *instance;
instance = pci_get_drvdata(pdev);
+ instance->unload = 1;
host = instance->host;
if (poll_mode_io)
@@ -2921,6 +3382,16 @@ static void __devexit megasas_detach_one(struct pci_dev *pdev)
scsi_remove_host(instance->host);
megasas_flush_cache(instance);
megasas_shutdown_controller(instance, MR_DCMD_CTRL_SHUTDOWN);
+
+ /* cancel the delayed work if this work still in queue*/
+ if (instance->ev != NULL) {
+ struct megasas_aen_event *ev = instance->ev;
+ cancel_delayed_work(
+ (struct delayed_work *)&ev->hotplug_work);
+ flush_scheduled_work();
+ instance->ev = NULL;
+ }
+
tasklet_kill(&instance->isr_tasklet);
/*
@@ -2969,6 +3440,7 @@ static void __devexit megasas_detach_one(struct pci_dev *pdev)
static void megasas_shutdown(struct pci_dev *pdev)
{
struct megasas_instance *instance = pci_get_drvdata(pdev);
+ instance->unload = 1;
megasas_flush_cache(instance);
megasas_shutdown_controller(instance, MR_DCMD_CTRL_SHUTDOWN);
}
@@ -3016,6 +3488,23 @@ static int megasas_mgmt_fasync(int fd, struct file *filep, int mode)
}
/**
+ * megasas_mgmt_poll - char node "poll" entry point
+ * */
+static unsigned int megasas_mgmt_poll(struct file *file, poll_table *wait)
+{
+ unsigned int mask;
+ unsigned long flags;
+ poll_wait(file, &megasas_poll_wait, wait);
+ spin_lock_irqsave(&poll_aen_lock, flags);
+ if (megasas_poll_wait_aen)
+ mask = (POLLIN | POLLRDNORM);
+ else
+ mask = 0;
+ spin_unlock_irqrestore(&poll_aen_lock, flags);
+ return mask;
+}
+
+/**
* megasas_mgmt_fw_ioctl - Issues management ioctls to FW
* @instance: Adapter soft state
* @argp: User's ioctl packet
@@ -3032,7 +3521,7 @@ megasas_mgmt_fw_ioctl(struct megasas_instance *instance,
int error = 0, i;
void *sense = NULL;
dma_addr_t sense_handle;
- u32 *sense_ptr;
+ unsigned long *sense_ptr;
memset(kbuff_arr, 0, sizeof(kbuff_arr));
@@ -3056,6 +3545,7 @@ megasas_mgmt_fw_ioctl(struct megasas_instance *instance,
*/
memcpy(cmd->frame, ioc->frame.raw, 2 * MEGAMFI_FRAME_SIZE);
cmd->frame->hdr.context = cmd->index;
+ cmd->frame->hdr.pad_0 = 0;
/*
* The management interface between applications and the fw uses
@@ -3109,7 +3599,7 @@ megasas_mgmt_fw_ioctl(struct megasas_instance *instance,
}
sense_ptr =
- (u32 *) ((unsigned long)cmd->frame + ioc->sense_off);
+ (unsigned long *) ((unsigned long)cmd->frame + ioc->sense_off);
*sense_ptr = sense_handle;
}
@@ -3140,8 +3630,8 @@ megasas_mgmt_fw_ioctl(struct megasas_instance *instance,
* sense_ptr points to the location that has the user
* sense buffer address
*/
- sense_ptr = (u32 *) ((unsigned long)ioc->frame.raw +
- ioc->sense_off);
+ sense_ptr = (unsigned long *) ((unsigned long)ioc->frame.raw +
+ ioc->sense_off);
if (copy_to_user((void __user *)((unsigned long)(*sense_ptr)),
sense, ioc->sense_len)) {
@@ -3177,20 +3667,6 @@ megasas_mgmt_fw_ioctl(struct megasas_instance *instance,
return error;
}
-static struct megasas_instance *megasas_lookup_instance(u16 host_no)
-{
- int i;
-
- for (i = 0; i < megasas_mgmt_info.max_index; i++) {
-
- if ((megasas_mgmt_info.instance[i]) &&
- (megasas_mgmt_info.instance[i]->host->host_no == host_no))
- return megasas_mgmt_info.instance[i];
- }
-
- return NULL;
-}
-
static int megasas_mgmt_ioctl_fw(struct file *file, unsigned long arg)
{
struct megasas_iocpacket __user *user_ioc =
@@ -3214,6 +3690,17 @@ static int megasas_mgmt_ioctl_fw(struct file *file, unsigned long arg)
goto out_kfree_ioc;
}
+ if (instance->hw_crit_error == 1) {
+ printk(KERN_DEBUG "Controller in Crit ERROR\n");
+ error = -ENODEV;
+ goto out_kfree_ioc;
+ }
+
+ if (instance->unload == 1) {
+ error = -ENODEV;
+ goto out_kfree_ioc;
+ }
+
/*
* We will allow only MEGASAS_INT_CMDS number of parallel ioctl cmds
*/
@@ -3249,6 +3736,14 @@ static int megasas_mgmt_ioctl_aen(struct file *file, unsigned long arg)
if (!instance)
return -ENODEV;
+ if (instance->hw_crit_error == 1) {
+ error = -ENODEV;
+ }
+
+ if (instance->unload == 1) {
+ return -ENODEV;
+ }
+
mutex_lock(&instance->aen_mutex);
error = megasas_register_aen(instance, aen.seq_num,
aen.class_locale_word);
@@ -3337,6 +3832,7 @@ static const struct file_operations megasas_mgmt_fops = {
.open = megasas_mgmt_open,
.fasync = megasas_mgmt_fasync,
.unlocked_ioctl = megasas_mgmt_ioctl,
+ .poll = megasas_mgmt_poll,
#ifdef CONFIG_COMPAT
.compat_ioctl = megasas_mgmt_compat_ioctl,
#endif
@@ -3378,6 +3874,15 @@ static DRIVER_ATTR(release_date, S_IRUGO, megasas_sysfs_show_release_date,
NULL);
static ssize_t
+megasas_sysfs_show_support_poll_for_event(struct device_driver *dd, char *buf)
+{
+ return sprintf(buf, "%u\n", support_poll_for_event);
+}
+
+static DRIVER_ATTR(support_poll_for_event, S_IRUGO,
+ megasas_sysfs_show_support_poll_for_event, NULL);
+
+static ssize_t
megasas_sysfs_show_dbg_lvl(struct device_driver *dd, char *buf)
{
return sprintf(buf, "%u\n", megasas_dbg_lvl);
@@ -3451,6 +3956,92 @@ out:
return retval;
}
+static void
+megasas_aen_polling(struct work_struct *work)
+{
+ struct megasas_aen_event *ev =
+ container_of(work, struct megasas_aen_event, hotplug_work);
+ struct megasas_instance *instance = ev->instance;
+ union megasas_evt_class_locale class_locale;
+ struct Scsi_Host *host;
+ struct scsi_device *sdev1;
+ u16 pd_index = 0;
+ int i, j, doscan = 0;
+ u32 seq_num;
+ int error;
+
+ if (!instance) {
+ printk(KERN_ERR "invalid instance!\n");
+ kfree(ev);
+ return;
+ }
+ instance->ev = NULL;
+ host = instance->host;
+ if (instance->evt_detail) {
+
+ switch (instance->evt_detail->code) {
+ case MR_EVT_PD_INSERTED:
+ case MR_EVT_PD_REMOVED:
+ case MR_EVT_CTRL_HOST_BUS_SCAN_REQUESTED:
+ doscan = 1;
+ break;
+ default:
+ doscan = 0;
+ break;
+ }
+ } else {
+ printk(KERN_ERR "invalid evt_detail!\n");
+ kfree(ev);
+ return;
+ }
+
+ if (doscan) {
+ printk(KERN_INFO "scanning ...\n");
+ megasas_get_pd_list(instance);
+ for (i = 0; i < MEGASAS_MAX_PD_CHANNELS; i++) {
+ for (j = 0; j < MEGASAS_MAX_DEV_PER_CHANNEL; j++) {
+ pd_index = i*MEGASAS_MAX_DEV_PER_CHANNEL + j;
+ sdev1 = scsi_device_lookup(host, i, j, 0);
+ if (instance->pd_list[pd_index].driveState ==
+ MR_PD_STATE_SYSTEM) {
+ if (!sdev1) {
+ scsi_add_device(host, i, j, 0);
+ }
+ if (sdev1)
+ scsi_device_put(sdev1);
+ } else {
+ if (sdev1) {
+ scsi_remove_device(sdev1);
+ scsi_device_put(sdev1);
+ }
+ }
+ }
+ }
+ }
+
+ if ( instance->aen_cmd != NULL ) {
+ kfree(ev);
+ return ;
+ }
+
+ seq_num = instance->evt_detail->seq_num + 1;
+
+ /* Register AEN with FW for latest sequence number plus 1 */
+ class_locale.members.reserved = 0;
+ class_locale.members.locale = MR_EVT_LOCALE_ALL;
+ class_locale.members.class = MR_EVT_CLASS_DEBUG;
+ mutex_lock(&instance->aen_mutex);
+ error = megasas_register_aen(instance, seq_num,
+ class_locale.word);
+ mutex_unlock(&instance->aen_mutex);
+
+ if (error)
+ printk(KERN_ERR "register aen failed error %x\n", error);
+
+ kfree(ev);
+}
+
+
static DRIVER_ATTR(poll_mode_io, S_IRUGO|S_IWUGO,
megasas_sysfs_show_poll_mode_io,
megasas_sysfs_set_poll_mode_io);
@@ -3468,6 +4059,8 @@ static int __init megasas_init(void)
printk(KERN_INFO "megasas: %s %s\n", MEGASAS_VERSION,
MEGASAS_EXT_VERSION);
+ support_poll_for_event = 2;
+
memset(&megasas_mgmt_info, 0, sizeof(megasas_mgmt_info));
/*
@@ -3500,6 +4093,12 @@ static int __init megasas_init(void)
&driver_attr_release_date);
if (rval)
goto err_dcf_rel_date;
+
+ rval = driver_create_file(&megasas_pci_driver.driver,
+ &driver_attr_support_poll_for_event);
+ if (rval)
+ goto err_dcf_support_poll_for_event;
+
rval = driver_create_file(&megasas_pci_driver.driver,
&driver_attr_dbg_lvl);
if (rval)
@@ -3516,7 +4115,12 @@ err_dcf_poll_mode_io:
&driver_attr_dbg_lvl);
err_dcf_dbg_lvl:
driver_remove_file(&megasas_pci_driver.driver,
+ &driver_attr_support_poll_for_event);
+
+err_dcf_support_poll_for_event:
+ driver_remove_file(&megasas_pci_driver.driver,
&driver_attr_release_date);
+
err_dcf_rel_date:
driver_remove_file(&megasas_pci_driver.driver, &driver_attr_version);
err_dcf_attr_ver:
diff --git a/drivers/scsi/megaraid/megaraid_sas.h b/drivers/scsi/megaraid/megaraid_sas.h
index 0d033248fdf..72b28e436e3 100644
--- a/drivers/scsi/megaraid/megaraid_sas.h
+++ b/drivers/scsi/megaraid/megaraid_sas.h
@@ -18,9 +18,9 @@
/*
* MegaRAID SAS Driver meta data
*/
-#define MEGASAS_VERSION "00.00.04.01"
-#define MEGASAS_RELDATE "July 24, 2008"
-#define MEGASAS_EXT_VERSION "Thu July 24 11:41:51 PST 2008"
+#define MEGASAS_VERSION "00.00.04.12-rc1"
+#define MEGASAS_RELDATE "Sep. 17, 2009"
+#define MEGASAS_EXT_VERSION "Thu Sep. 17 11:41:51 PST 2009"
/*
* Device IDs
@@ -30,6 +30,8 @@
#define PCI_DEVICE_ID_LSI_VERDE_ZCR 0x0413
#define PCI_DEVICE_ID_LSI_SAS1078GEN2 0x0078
#define PCI_DEVICE_ID_LSI_SAS0079GEN2 0x0079
+#define PCI_DEVICE_ID_LSI_SAS0073SKINNY 0x0073
+#define PCI_DEVICE_ID_LSI_SAS0071SKINNY 0x0071
/*
* =====================================
@@ -94,6 +96,7 @@
#define MFI_FRAME_DIR_WRITE 0x0008
#define MFI_FRAME_DIR_READ 0x0010
#define MFI_FRAME_DIR_BOTH 0x0018
+#define MFI_FRAME_IEEE 0x0020
/*
* Definition for cmd_status
@@ -131,6 +134,7 @@
#define MR_DCMD_CLUSTER 0x08000000
#define MR_DCMD_CLUSTER_RESET_ALL 0x08010100
#define MR_DCMD_CLUSTER_RESET_LD 0x08010200
+#define MR_DCMD_PD_LIST_QUERY 0x02010100
/*
* MFI command completion codes
@@ -251,9 +255,100 @@ enum MR_EVT_ARGS {
MR_EVT_ARGS_STR,
MR_EVT_ARGS_TIME,
MR_EVT_ARGS_ECC,
+ MR_EVT_ARGS_LD_PROP,
+ MR_EVT_ARGS_PD_SPARE,
+ MR_EVT_ARGS_PD_INDEX,
+ MR_EVT_ARGS_DIAG_PASS,
+ MR_EVT_ARGS_DIAG_FAIL,
+ MR_EVT_ARGS_PD_LBA_LBA,
+ MR_EVT_ARGS_PORT_PHY,
+ MR_EVT_ARGS_PD_MISSING,
+ MR_EVT_ARGS_PD_ADDRESS,
+ MR_EVT_ARGS_BITMAP,
+ MR_EVT_ARGS_CONNECTOR,
+ MR_EVT_ARGS_PD_PD,
+ MR_EVT_ARGS_PD_FRU,
+ MR_EVT_ARGS_PD_PATHINFO,
+ MR_EVT_ARGS_PD_POWER_STATE,
+ MR_EVT_ARGS_GENERIC,
+};
+/*
+ * define constants for device list query options
+ */
+enum MR_PD_QUERY_TYPE {
+ MR_PD_QUERY_TYPE_ALL = 0,
+ MR_PD_QUERY_TYPE_STATE = 1,
+ MR_PD_QUERY_TYPE_POWER_STATE = 2,
+ MR_PD_QUERY_TYPE_MEDIA_TYPE = 3,
+ MR_PD_QUERY_TYPE_SPEED = 4,
+ MR_PD_QUERY_TYPE_EXPOSED_TO_HOST = 5,
};
+#define MR_EVT_CFG_CLEARED 0x0004
+#define MR_EVT_LD_STATE_CHANGE 0x0051
+#define MR_EVT_PD_INSERTED 0x005b
+#define MR_EVT_PD_REMOVED 0x0070
+#define MR_EVT_LD_CREATED 0x008a
+#define MR_EVT_LD_DELETED 0x008b
+#define MR_EVT_FOREIGN_CFG_IMPORTED 0x00db
+#define MR_EVT_LD_OFFLINE 0x00fc
+#define MR_EVT_CTRL_HOST_BUS_SCAN_REQUESTED 0x0152
+#define MAX_LOGICAL_DRIVES 64
+
+enum MR_PD_STATE {
+ MR_PD_STATE_UNCONFIGURED_GOOD = 0x00,
+ MR_PD_STATE_UNCONFIGURED_BAD = 0x01,
+ MR_PD_STATE_HOT_SPARE = 0x02,
+ MR_PD_STATE_OFFLINE = 0x10,
+ MR_PD_STATE_FAILED = 0x11,
+ MR_PD_STATE_REBUILD = 0x14,
+ MR_PD_STATE_ONLINE = 0x18,
+ MR_PD_STATE_COPYBACK = 0x20,
+ MR_PD_STATE_SYSTEM = 0x40,
+ };
+
+
+ /*
+ * defines the physical drive address structure
+ */
+struct MR_PD_ADDRESS {
+ u16 deviceId;
+ u16 enclDeviceId;
+
+ union {
+ struct {
+ u8 enclIndex;
+ u8 slotNumber;
+ } mrPdAddress;
+ struct {
+ u8 enclPosition;
+ u8 enclConnectorIndex;
+ } mrEnclAddress;
+ };
+ u8 scsiDevType;
+ union {
+ u8 connectedPortBitmap;
+ u8 connectedPortNumbers;
+ };
+ u64 sasAddr[2];
+} __packed;
+
+/*
+ * defines the physical drive list structure
+ */
+struct MR_PD_LIST {
+ u32 size;
+ u32 count;
+ struct MR_PD_ADDRESS addr[1];
+} __packed;
+
+struct megasas_pd_list {
+ u16 tid;
+ u8 driveType;
+ u8 driveState;
+} __packed;
+
/*
* SAS controller properties
*/
@@ -282,7 +377,7 @@ struct megasas_ctrl_prop {
u8 expose_encl_devices;
u8 reserved[38];
-} __attribute__ ((packed));
+} __packed;
/*
* SAS controller information
@@ -525,7 +620,7 @@ struct megasas_ctrl_info {
u8 pad[0x800 - 0x6a0];
-} __attribute__ ((packed));
+} __packed;
/*
* ===============================
@@ -540,6 +635,8 @@ struct megasas_ctrl_info {
#define MEGASAS_DEFAULT_INIT_ID -1
#define MEGASAS_MAX_LUN 8
#define MEGASAS_MAX_LD 64
+#define MEGASAS_MAX_PD (MEGASAS_MAX_PD_CHANNELS * \
+ MEGASAS_MAX_DEV_PER_CHANNEL)
#define MEGASAS_DBG_LVL 1
@@ -570,6 +667,7 @@ struct megasas_ctrl_info {
* is shown below
*/
#define MEGASAS_INT_CMDS 32
+#define MEGASAS_SKINNY_INT_CMDS 5
/*
* FW can accept both 32 and 64 bit SGLs. We want to allocate 32/64 bit
@@ -584,6 +682,8 @@ struct megasas_ctrl_info {
#define MFI_REPLY_1078_MESSAGE_INTERRUPT 0x80000000
#define MFI_REPLY_GEN2_MESSAGE_INTERRUPT 0x00000001
#define MFI_GEN2_ENABLE_INTERRUPT_MASK (0x00000001 | 0x00000004)
+#define MFI_REPLY_SKINNY_MESSAGE_INTERRUPT 0x40000000
+#define MFI_SKINNY_ENABLE_INTERRUPT_MASK (0x00000001)
/*
* register set for both 1068 and 1078 controllers
@@ -644,10 +744,17 @@ struct megasas_sge64 {
} __attribute__ ((packed));
+struct megasas_sge_skinny {
+ u64 phys_addr;
+ u32 length;
+ u32 flag;
+} __packed;
+
union megasas_sgl {
struct megasas_sge32 sge32[1];
struct megasas_sge64 sge64[1];
+ struct megasas_sge_skinny sge_skinny[1];
} __attribute__ ((packed));
@@ -1061,16 +1168,10 @@ struct megasas_evt_detail {
} __attribute__ ((packed));
- struct megasas_instance_template {
- void (*fire_cmd)(dma_addr_t ,u32 ,struct megasas_register_set __iomem *);
-
- void (*enable_intr)(struct megasas_register_set __iomem *) ;
- void (*disable_intr)(struct megasas_register_set __iomem *);
-
- int (*clear_intr)(struct megasas_register_set __iomem *);
-
- u32 (*read_fw_status_reg)(struct megasas_register_set __iomem *);
- };
+struct megasas_aen_event {
+ struct work_struct hotplug_work;
+ struct megasas_instance *instance;
+};
struct megasas_instance {
@@ -1085,17 +1186,21 @@ struct megasas_instance {
unsigned long base_addr;
struct megasas_register_set __iomem *reg_set;
+ struct megasas_pd_list pd_list[MEGASAS_MAX_PD];
s8 init_id;
u16 max_num_sge;
u16 max_fw_cmds;
u32 max_sectors_per_req;
+ struct megasas_aen_event *ev;
struct megasas_cmd **cmd_list;
struct list_head cmd_pool;
spinlock_t cmd_pool_lock;
/* used to synch producer, consumer ptrs in dpc */
spinlock_t completion_lock;
+ /* used to sync fire the cmd to fw */
+ spinlock_t fire_lock;
struct dma_pool *frame_dma_pool;
struct dma_pool *sense_dma_pool;
@@ -1120,11 +1225,25 @@ struct megasas_instance {
struct tasklet_struct isr_tasklet;
u8 flag;
+ u8 unload;
+ u8 flag_ieee;
unsigned long last_time;
struct timer_list io_completion_timer;
};
+struct megasas_instance_template {
+ void (*fire_cmd)(struct megasas_instance *, dma_addr_t, \
+ u32, struct megasas_register_set __iomem *);
+
+ void (*enable_intr)(struct megasas_register_set __iomem *) ;
+ void (*disable_intr)(struct megasas_register_set __iomem *);
+
+ int (*clear_intr)(struct megasas_register_set __iomem *);
+
+ u32 (*read_fw_status_reg)(struct megasas_register_set __iomem *);
+};
+
#define MEGASAS_IS_LOGICAL(scp) \
(scp->device->channel < MEGASAS_MAX_PD_CHANNELS) ? 0 : 1
diff --git a/drivers/scsi/mpt2sas/mpi/mpi2.h b/drivers/scsi/mpt2sas/mpi/mpi2.h
index f9f6c083927..91416810529 100644
--- a/drivers/scsi/mpt2sas/mpi/mpi2.h
+++ b/drivers/scsi/mpt2sas/mpi/mpi2.h
@@ -8,7 +8,7 @@
* scatter/gather formats.
* Creation Date: June 21, 2006
*
- * mpi2.h Version: 02.00.12
+ * mpi2.h Version: 02.00.13
*
* Version History
* ---------------
@@ -52,6 +52,7 @@
* MPI2_SCSI_IO_SUCCESS_REPLY_DESCRIPTOR and made those
* bytes reserved.
* Added RAID Accelerator functionality.
+ * 07-30-09 02.00.13 Bumped MPI2_HEADER_VERSION_UNIT.
* --------------------------------------------------------------------------
*/
@@ -77,7 +78,7 @@
#define MPI2_VERSION_02_00 (0x0200)
/* versioning for this MPI header set */
-#define MPI2_HEADER_VERSION_UNIT (0x0C)
+#define MPI2_HEADER_VERSION_UNIT (0x0D)
#define MPI2_HEADER_VERSION_DEV (0x00)
#define MPI2_HEADER_VERSION_UNIT_MASK (0xFF00)
#define MPI2_HEADER_VERSION_UNIT_SHIFT (8)
diff --git a/drivers/scsi/mpt2sas/mpi/mpi2_cnfg.h b/drivers/scsi/mpt2sas/mpi/mpi2_cnfg.h
index ab47c467964..1611c57a6fd 100644
--- a/drivers/scsi/mpt2sas/mpi/mpi2_cnfg.h
+++ b/drivers/scsi/mpt2sas/mpi/mpi2_cnfg.h
@@ -6,7 +6,7 @@
* Title: MPI Configuration messages and pages
* Creation Date: November 10, 2006
*
- * mpi2_cnfg.h Version: 02.00.11
+ * mpi2_cnfg.h Version: 02.00.12
*
* Version History
* ---------------
@@ -100,6 +100,13 @@
* Added expander reduced functionality data to SAS
* Expander Page 0.
* Added SAS PHY Page 2 and SAS PHY Page 3.
+ * 07-30-09 02.00.12 Added IO Unit Page 7.
+ * Added new device ids.
+ * Added SAS IO Unit Page 5.
+ * Added partial and slumber power management capable flags
+ * to SAS Device Page 0 Flags field.
+ * Added PhyInfo defines for power condition.
+ * Added Ethernet configuration pages.
* --------------------------------------------------------------------------
*/
@@ -182,6 +189,7 @@ typedef union _MPI2_CONFIG_EXT_PAGE_HEADER_UNION
#define MPI2_CONFIG_EXTPAGETYPE_RAID_CONFIG (0x16)
#define MPI2_CONFIG_EXTPAGETYPE_DRIVER_MAPPING (0x17)
#define MPI2_CONFIG_EXTPAGETYPE_SAS_PORT (0x18)
+#define MPI2_CONFIG_EXTPAGETYPE_ETHERNET (0x19)
/*****************************************************************************
@@ -268,6 +276,14 @@ typedef union _MPI2_CONFIG_EXT_PAGE_HEADER_UNION
#define MPI2_DPM_PGAD_START_ENTRY_MASK (0x0000FFFF)
+/* Ethernet PageAddress format */
+#define MPI2_ETHERNET_PGAD_FORM_MASK (0xF0000000)
+#define MPI2_ETHERNET_PGAD_FORM_IF_NUM (0x00000000)
+
+#define MPI2_ETHERNET_PGAD_IF_NUMBER_MASK (0x000000FF)
+
+
+
/****************************************************************************
* Configuration messages
****************************************************************************/
@@ -349,6 +365,15 @@ typedef struct _MPI2_CONFIG_REPLY
#define MPI2_MFGPAGE_DEVID_SAS2116_1 (0x0064)
#define MPI2_MFGPAGE_DEVID_SAS2116_2 (0x0065)
+#define MPI2_MFGPAGE_DEVID_SAS2208_1 (0x0080)
+#define MPI2_MFGPAGE_DEVID_SAS2208_2 (0x0081)
+#define MPI2_MFGPAGE_DEVID_SAS2208_3 (0x0082)
+#define MPI2_MFGPAGE_DEVID_SAS2208_4 (0x0083)
+#define MPI2_MFGPAGE_DEVID_SAS2208_5 (0x0084)
+#define MPI2_MFGPAGE_DEVID_SAS2208_6 (0x0085)
+#define MPI2_MFGPAGE_DEVID_SAS2208_7 (0x0086)
+#define MPI2_MFGPAGE_DEVID_SAS2208_8 (0x0087)
+
/* Manufacturing Page 0 */
@@ -787,6 +812,56 @@ typedef struct _MPI2_CONFIG_PAGE_IO_UNIT_6 {
#define MPI2_IOUNITPAGE6_FLAGS_ENABLE_RAID_ACCELERATOR (0x0001)
+/* IO Unit Page 7 */
+
+typedef struct _MPI2_CONFIG_PAGE_IO_UNIT_7 {
+ MPI2_CONFIG_PAGE_HEADER Header; /* 0x00 */
+ U16 Reserved1; /* 0x04 */
+ U8 PCIeWidth; /* 0x06 */
+ U8 PCIeSpeed; /* 0x07 */
+ U32 ProcessorState; /* 0x08 */
+ U32 Reserved2; /* 0x0C */
+ U16 IOCTemperature; /* 0x10 */
+ U8 IOCTemperatureUnits; /* 0x12 */
+ U8 IOCSpeed; /* 0x13 */
+ U32 Reserved3; /* 0x14 */
+} MPI2_CONFIG_PAGE_IO_UNIT_7, MPI2_POINTER PTR_MPI2_CONFIG_PAGE_IO_UNIT_7,
+ Mpi2IOUnitPage7_t, MPI2_POINTER pMpi2IOUnitPage7_t;
+
+#define MPI2_IOUNITPAGE7_PAGEVERSION (0x00)
+
+/* defines for IO Unit Page 7 PCIeWidth field */
+#define MPI2_IOUNITPAGE7_PCIE_WIDTH_X1 (0x01)
+#define MPI2_IOUNITPAGE7_PCIE_WIDTH_X2 (0x02)
+#define MPI2_IOUNITPAGE7_PCIE_WIDTH_X4 (0x04)
+#define MPI2_IOUNITPAGE7_PCIE_WIDTH_X8 (0x08)
+
+/* defines for IO Unit Page 7 PCIeSpeed field */
+#define MPI2_IOUNITPAGE7_PCIE_SPEED_2_5_GBPS (0x00)
+#define MPI2_IOUNITPAGE7_PCIE_SPEED_5_0_GBPS (0x01)
+#define MPI2_IOUNITPAGE7_PCIE_SPEED_8_0_GBPS (0x02)
+
+/* defines for IO Unit Page 7 ProcessorState field */
+#define MPI2_IOUNITPAGE7_PSTATE_MASK_SECOND (0x0000000F)
+#define MPI2_IOUNITPAGE7_PSTATE_SHIFT_SECOND (0)
+
+#define MPI2_IOUNITPAGE7_PSTATE_NOT_PRESENT (0x00)
+#define MPI2_IOUNITPAGE7_PSTATE_DISABLED (0x01)
+#define MPI2_IOUNITPAGE7_PSTATE_ENABLED (0x02)
+
+/* defines for IO Unit Page 7 IOCTemperatureUnits field */
+#define MPI2_IOUNITPAGE7_IOC_TEMP_NOT_PRESENT (0x00)
+#define MPI2_IOUNITPAGE7_IOC_TEMP_FAHRENHEIT (0x01)
+#define MPI2_IOUNITPAGE7_IOC_TEMP_CELSIUS (0x02)
+
+/* defines for IO Unit Page 7 IOCSpeed field */
+#define MPI2_IOUNITPAGE7_IOC_SPEED_FULL (0x01)
+#define MPI2_IOUNITPAGE7_IOC_SPEED_HALF (0x02)
+#define MPI2_IOUNITPAGE7_IOC_SPEED_QUARTER (0x04)
+#define MPI2_IOUNITPAGE7_IOC_SPEED_EIGHTH (0x08)
+
+
+
/****************************************************************************
* IOC Config Pages
****************************************************************************/
@@ -1470,6 +1545,12 @@ typedef struct _MPI2_CONFIG_PAGE_RD_PDISK_1
/* values for PhyInfo fields */
#define MPI2_SAS_PHYINFO_PHY_VACANT (0x80000000)
+
+#define MPI2_SAS_PHYINFO_PHY_POWER_CONDITION_MASK (0x18000000)
+#define MPI2_SAS_PHYINFO_PHY_POWER_ACTIVE (0x00000000)
+#define MPI2_SAS_PHYINFO_PHY_POWER_PARTIAL (0x08000000)
+#define MPI2_SAS_PHYINFO_PHY_POWER_SLUMBER (0x10000000)
+
#define MPI2_SAS_PHYINFO_CHANGED_REQ_INSIDE_ZPSDS (0x04000000)
#define MPI2_SAS_PHYINFO_INSIDE_ZPSDS_PERSISTENT (0x02000000)
#define MPI2_SAS_PHYINFO_REQ_INSIDE_ZPSDS (0x01000000)
@@ -1682,11 +1763,11 @@ typedef struct _MPI2_CONFIG_PAGE_SASIOUNIT_1
/* values for SAS IO Unit Page 1 PortFlags */
#define MPI2_SASIOUNIT1_PORT_FLAGS_AUTO_PORT_CONFIG (0x01)
-/* values for SAS IO Unit Page 2 PhyFlags */
+/* values for SAS IO Unit Page 1 PhyFlags */
#define MPI2_SASIOUNIT1_PHYFLAGS_ZONING_ENABLE (0x10)
#define MPI2_SASIOUNIT1_PHYFLAGS_PHY_DISABLE (0x08)
-/* values for SAS IO Unit Page 0 MaxMinLinkRate */
+/* values for SAS IO Unit Page 1 MaxMinLinkRate */
#define MPI2_SASIOUNIT1_MAX_RATE_MASK (0xF0)
#define MPI2_SASIOUNIT1_MAX_RATE_1_5 (0x80)
#define MPI2_SASIOUNIT1_MAX_RATE_3_0 (0x90)
@@ -1745,6 +1826,74 @@ typedef struct _MPI2_CONFIG_PAGE_SASIOUNIT_4
#define MPI2_SASIOUNIT4_PHY_SPINUP_GROUP_MASK (0x03)
+/* SAS IO Unit Page 5 */
+
+typedef struct _MPI2_SAS_IO_UNIT5_PHY_PM_SETTINGS {
+ U8 ControlFlags; /* 0x00 */
+ U8 Reserved1; /* 0x01 */
+ U16 InactivityTimerExponent; /* 0x02 */
+ U8 SATAPartialTimeout; /* 0x04 */
+ U8 Reserved2; /* 0x05 */
+ U8 SATASlumberTimeout; /* 0x06 */
+ U8 Reserved3; /* 0x07 */
+ U8 SASPartialTimeout; /* 0x08 */
+ U8 Reserved4; /* 0x09 */
+ U8 SASSlumberTimeout; /* 0x0A */
+ U8 Reserved5; /* 0x0B */
+} MPI2_SAS_IO_UNIT5_PHY_PM_SETTINGS,
+ MPI2_POINTER PTR_MPI2_SAS_IO_UNIT5_PHY_PM_SETTINGS,
+ Mpi2SasIOUnit5PhyPmSettings_t, MPI2_POINTER pMpi2SasIOUnit5PhyPmSettings_t;
+
+/* defines for ControlFlags field */
+#define MPI2_SASIOUNIT5_CONTROL_SAS_SLUMBER_ENABLE (0x08)
+#define MPI2_SASIOUNIT5_CONTROL_SAS_PARTIAL_ENABLE (0x04)
+#define MPI2_SASIOUNIT5_CONTROL_SATA_SLUMBER_ENABLE (0x02)
+#define MPI2_SASIOUNIT5_CONTROL_SATA_PARTIAL_ENABLE (0x01)
+
+/* defines for InactivityTimerExponent field */
+#define MPI2_SASIOUNIT5_ITE_MASK_SAS_SLUMBER (0x7000)
+#define MPI2_SASIOUNIT5_ITE_SHIFT_SAS_SLUMBER (12)
+#define MPI2_SASIOUNIT5_ITE_MASK_SAS_PARTIAL (0x0700)
+#define MPI2_SASIOUNIT5_ITE_SHIFT_SAS_PARTIAL (8)
+#define MPI2_SASIOUNIT5_ITE_MASK_SATA_SLUMBER (0x0070)
+#define MPI2_SASIOUNIT5_ITE_SHIFT_SATA_SLUMBER (4)
+#define MPI2_SASIOUNIT5_ITE_MASK_SATA_PARTIAL (0x0007)
+#define MPI2_SASIOUNIT5_ITE_SHIFT_SATA_PARTIAL (0)
+
+#define MPI2_SASIOUNIT5_ITE_TEN_SECONDS (7)
+#define MPI2_SASIOUNIT5_ITE_ONE_SECOND (6)
+#define MPI2_SASIOUNIT5_ITE_HUNDRED_MILLISECONDS (5)
+#define MPI2_SASIOUNIT5_ITE_TEN_MILLISECONDS (4)
+#define MPI2_SASIOUNIT5_ITE_ONE_MILLISECOND (3)
+#define MPI2_SASIOUNIT5_ITE_HUNDRED_MICROSECONDS (2)
+#define MPI2_SASIOUNIT5_ITE_TEN_MICROSECONDS (1)
+#define MPI2_SASIOUNIT5_ITE_ONE_MICROSECOND (0)
+
+/*
+ * Host code (drivers, BIOS, utilities, etc.) should leave this define set to
+ * one and check Header.ExtPageLength or NumPhys at runtime.
+ */
+#ifndef MPI2_SAS_IOUNIT5_PHY_MAX
+#define MPI2_SAS_IOUNIT5_PHY_MAX (1)
+#endif
+
+typedef struct _MPI2_CONFIG_PAGE_SASIOUNIT_5 {
+ MPI2_CONFIG_EXTENDED_PAGE_HEADER Header; /* 0x00 */
+ U8 NumPhys; /* 0x08 */
+ U8 Reserved1; /* 0x09 */
+ U16 Reserved2; /* 0x0A */
+ U32 Reserved3; /* 0x0C */
+ MPI2_SAS_IO_UNIT5_PHY_PM_SETTINGS SASPhyPowerManagementSettings
+ [MPI2_SAS_IOUNIT5_PHY_MAX]; /* 0x10 */
+} MPI2_CONFIG_PAGE_SASIOUNIT_5,
+ MPI2_POINTER PTR_MPI2_CONFIG_PAGE_SASIOUNIT_5,
+ Mpi2SasIOUnitPage5_t, MPI2_POINTER pMpi2SasIOUnitPage5_t;
+
+#define MPI2_SASIOUNITPAGE5_PAGEVERSION (0x00)
+
+
+
+
/****************************************************************************
* SAS Expander Config Pages
****************************************************************************/
@@ -1927,6 +2076,8 @@ typedef struct _MPI2_CONFIG_PAGE_SAS_DEV_0
/* see mpi2_sas.h for values for SAS Device Page 0 DeviceInfo values */
/* values for SAS Device Page 0 Flags field */
+#define MPI2_SAS_DEVICE0_FLAGS_SLUMBER_PM_CAPABLE (0x1000)
+#define MPI2_SAS_DEVICE0_FLAGS_PARTIAL_PM_CAPABLE (0x0800)
#define MPI2_SAS_DEVICE0_FLAGS_SATA_ASYNCHRONOUS_NOTIFY (0x0400)
#define MPI2_SAS_DEVICE0_FLAGS_SATA_SW_PRESERVE (0x0200)
#define MPI2_SAS_DEVICE0_FLAGS_UNSUPPORTED_DEVICE (0x0100)
@@ -2343,5 +2494,122 @@ typedef struct _MPI2_CONFIG_PAGE_DRIVER_MAPPING_0
#define MPI2_DRVMAP0_MAPINFO_MISSING_MASK (0x000F)
+/****************************************************************************
+* Ethernet Config Pages
+****************************************************************************/
+
+/* Ethernet Page 0 */
+
+/* IP address (union of IPv4 and IPv6) */
+typedef union _MPI2_ETHERNET_IP_ADDR {
+ U32 IPv4Addr;
+ U32 IPv6Addr[4];
+} MPI2_ETHERNET_IP_ADDR, MPI2_POINTER PTR_MPI2_ETHERNET_IP_ADDR,
+ Mpi2EthernetIpAddr_t, MPI2_POINTER pMpi2EthernetIpAddr_t;
+
+#define MPI2_ETHERNET_HOST_NAME_LENGTH (32)
+
+typedef struct _MPI2_CONFIG_PAGE_ETHERNET_0 {
+ MPI2_CONFIG_EXTENDED_PAGE_HEADER Header; /* 0x00 */
+ U8 NumInterfaces; /* 0x08 */
+ U8 Reserved0; /* 0x09 */
+ U16 Reserved1; /* 0x0A */
+ U32 Status; /* 0x0C */
+ U8 MediaState; /* 0x10 */
+ U8 Reserved2; /* 0x11 */
+ U16 Reserved3; /* 0x12 */
+ U8 MacAddress[6]; /* 0x14 */
+ U8 Reserved4; /* 0x1A */
+ U8 Reserved5; /* 0x1B */
+ MPI2_ETHERNET_IP_ADDR IpAddress; /* 0x1C */
+ MPI2_ETHERNET_IP_ADDR SubnetMask; /* 0x2C */
+ MPI2_ETHERNET_IP_ADDR GatewayIpAddress; /* 0x3C */
+ MPI2_ETHERNET_IP_ADDR DNS1IpAddress; /* 0x4C */
+ MPI2_ETHERNET_IP_ADDR DNS2IpAddress; /* 0x5C */
+ MPI2_ETHERNET_IP_ADDR DhcpIpAddress; /* 0x6C */
+ U8 HostName
+ [MPI2_ETHERNET_HOST_NAME_LENGTH];/* 0x7C */
+} MPI2_CONFIG_PAGE_ETHERNET_0, MPI2_POINTER PTR_MPI2_CONFIG_PAGE_ETHERNET_0,
+ Mpi2EthernetPage0_t, MPI2_POINTER pMpi2EthernetPage0_t;
+
+#define MPI2_ETHERNETPAGE0_PAGEVERSION (0x00)
+
+/* values for Ethernet Page 0 Status field */
+#define MPI2_ETHPG0_STATUS_IPV6_CAPABLE (0x80000000)
+#define MPI2_ETHPG0_STATUS_IPV4_CAPABLE (0x40000000)
+#define MPI2_ETHPG0_STATUS_CONSOLE_CONNECTED (0x20000000)
+#define MPI2_ETHPG0_STATUS_DEFAULT_IF (0x00000100)
+#define MPI2_ETHPG0_STATUS_FW_DWNLD_ENABLED (0x00000080)
+#define MPI2_ETHPG0_STATUS_TELNET_ENABLED (0x00000040)
+#define MPI2_ETHPG0_STATUS_SSH2_ENABLED (0x00000020)
+#define MPI2_ETHPG0_STATUS_DHCP_CLIENT_ENABLED (0x00000010)
+#define MPI2_ETHPG0_STATUS_IPV6_ENABLED (0x00000008)
+#define MPI2_ETHPG0_STATUS_IPV4_ENABLED (0x00000004)
+#define MPI2_ETHPG0_STATUS_IPV6_ADDRESSES (0x00000002)
+#define MPI2_ETHPG0_STATUS_ETH_IF_ENABLED (0x00000001)
+
+/* values for Ethernet Page 0 MediaState field */
+#define MPI2_ETHPG0_MS_DUPLEX_MASK (0x80)
+#define MPI2_ETHPG0_MS_HALF_DUPLEX (0x00)
+#define MPI2_ETHPG0_MS_FULL_DUPLEX (0x80)
+
+#define MPI2_ETHPG0_MS_CONNECT_SPEED_MASK (0x07)
+#define MPI2_ETHPG0_MS_NOT_CONNECTED (0x00)
+#define MPI2_ETHPG0_MS_10MBIT (0x01)
+#define MPI2_ETHPG0_MS_100MBIT (0x02)
+#define MPI2_ETHPG0_MS_1GBIT (0x03)
+
+
+/* Ethernet Page 1 */
+
+typedef struct _MPI2_CONFIG_PAGE_ETHERNET_1 {
+ MPI2_CONFIG_EXTENDED_PAGE_HEADER Header; /* 0x00 */
+ U32 Reserved0; /* 0x08 */
+ U32 Flags; /* 0x0C */
+ U8 MediaState; /* 0x10 */
+ U8 Reserved1; /* 0x11 */
+ U16 Reserved2; /* 0x12 */
+ U8 MacAddress[6]; /* 0x14 */
+ U8 Reserved3; /* 0x1A */
+ U8 Reserved4; /* 0x1B */
+ MPI2_ETHERNET_IP_ADDR StaticIpAddress; /* 0x1C */
+ MPI2_ETHERNET_IP_ADDR StaticSubnetMask; /* 0x2C */
+ MPI2_ETHERNET_IP_ADDR StaticGatewayIpAddress; /* 0x3C */
+ MPI2_ETHERNET_IP_ADDR StaticDNS1IpAddress; /* 0x4C */
+ MPI2_ETHERNET_IP_ADDR StaticDNS2IpAddress; /* 0x5C */
+ U32 Reserved5; /* 0x6C */
+ U32 Reserved6; /* 0x70 */
+ U32 Reserved7; /* 0x74 */
+ U32 Reserved8; /* 0x78 */
+ U8 HostName
+ [MPI2_ETHERNET_HOST_NAME_LENGTH];/* 0x7C */
+} MPI2_CONFIG_PAGE_ETHERNET_1, MPI2_POINTER PTR_MPI2_CONFIG_PAGE_ETHERNET_1,
+ Mpi2EthernetPage1_t, MPI2_POINTER pMpi2EthernetPage1_t;
+
+#define MPI2_ETHERNETPAGE1_PAGEVERSION (0x00)
+
+/* values for Ethernet Page 1 Flags field */
+#define MPI2_ETHPG1_FLAG_SET_DEFAULT_IF (0x00000100)
+#define MPI2_ETHPG1_FLAG_ENABLE_FW_DOWNLOAD (0x00000080)
+#define MPI2_ETHPG1_FLAG_ENABLE_TELNET (0x00000040)
+#define MPI2_ETHPG1_FLAG_ENABLE_SSH2 (0x00000020)
+#define MPI2_ETHPG1_FLAG_ENABLE_DHCP_CLIENT (0x00000010)
+#define MPI2_ETHPG1_FLAG_ENABLE_IPV6 (0x00000008)
+#define MPI2_ETHPG1_FLAG_ENABLE_IPV4 (0x00000004)
+#define MPI2_ETHPG1_FLAG_USE_IPV6_ADDRESSES (0x00000002)
+#define MPI2_ETHPG1_FLAG_ENABLE_ETH_IF (0x00000001)
+
+/* values for Ethernet Page 1 MediaState field */
+#define MPI2_ETHPG1_MS_DUPLEX_MASK (0x80)
+#define MPI2_ETHPG1_MS_HALF_DUPLEX (0x00)
+#define MPI2_ETHPG1_MS_FULL_DUPLEX (0x80)
+
+#define MPI2_ETHPG1_MS_DATA_RATE_MASK (0x07)
+#define MPI2_ETHPG1_MS_DATA_RATE_AUTO (0x00)
+#define MPI2_ETHPG1_MS_DATA_RATE_10MBIT (0x01)
+#define MPI2_ETHPG1_MS_DATA_RATE_100MBIT (0x02)
+#define MPI2_ETHPG1_MS_DATA_RATE_1GBIT (0x03)
+
+
#endif
diff --git a/drivers/scsi/mpt2sas/mpi/mpi2_ioc.h b/drivers/scsi/mpt2sas/mpi/mpi2_ioc.h
index c294128bdeb..ea51ce86869 100644
--- a/drivers/scsi/mpt2sas/mpi/mpi2_ioc.h
+++ b/drivers/scsi/mpt2sas/mpi/mpi2_ioc.h
@@ -6,7 +6,7 @@
* Title: MPI IOC, Port, Event, FW Download, and FW Upload messages
* Creation Date: October 11, 2006
*
- * mpi2_ioc.h Version: 02.00.11
+ * mpi2_ioc.h Version: 02.00.12
*
* Version History
* ---------------
@@ -84,6 +84,9 @@
* Added two new reason codes for SAS Device Status Change
* Event.
* Added new event: SAS PHY Counter.
+ * 07-30-09 02.00.12 Added GPIO Interrupt event define and structure.
+ * Added MPI2_IOCFACTS_CAPABILITY_EXTENDED_BUFFER define.
+ * Added new product id family for 2208.
* --------------------------------------------------------------------------
*/
@@ -274,6 +277,7 @@ typedef struct _MPI2_IOC_FACTS_REPLY
#define MPI2_IOCFACTS_CAPABILITY_MULTICAST (0x00000100)
#define MPI2_IOCFACTS_CAPABILITY_BIDIRECTIONAL_TARGET (0x00000080)
#define MPI2_IOCFACTS_CAPABILITY_EEDP (0x00000040)
+#define MPI2_IOCFACTS_CAPABILITY_EXTENDED_BUFFER (0x00000020)
#define MPI2_IOCFACTS_CAPABILITY_SNAPSHOT_BUFFER (0x00000010)
#define MPI2_IOCFACTS_CAPABILITY_DIAG_TRACE_BUFFER (0x00000008)
#define MPI2_IOCFACTS_CAPABILITY_TASK_SET_FULL_HANDLING (0x00000004)
@@ -448,6 +452,7 @@ typedef struct _MPI2_EVENT_NOTIFICATION_REPLY
#define MPI2_EVENT_IR_CONFIGURATION_CHANGE_LIST (0x0020)
#define MPI2_EVENT_LOG_ENTRY_ADDED (0x0021)
#define MPI2_EVENT_SAS_PHY_COUNTER (0x0022)
+#define MPI2_EVENT_GPIO_INTERRUPT (0x0023)
/* Log Entry Added Event data */
@@ -469,6 +474,16 @@ typedef struct _MPI2_EVENT_DATA_LOG_ENTRY_ADDED
MPI2_POINTER PTR_MPI2_EVENT_DATA_LOG_ENTRY_ADDED,
Mpi2EventDataLogEntryAdded_t, MPI2_POINTER pMpi2EventDataLogEntryAdded_t;
+/* GPIO Interrupt Event data */
+
+typedef struct _MPI2_EVENT_DATA_GPIO_INTERRUPT {
+ U8 GPIONum; /* 0x00 */
+ U8 Reserved1; /* 0x01 */
+ U16 Reserved2; /* 0x02 */
+} MPI2_EVENT_DATA_GPIO_INTERRUPT,
+ MPI2_POINTER PTR_MPI2_EVENT_DATA_GPIO_INTERRUPT,
+ Mpi2EventDataGpioInterrupt_t, MPI2_POINTER pMpi2EventDataGpioInterrupt_t;
+
/* Hard Reset Received Event data */
typedef struct _MPI2_EVENT_DATA_HARD_RESET_RECEIVED
@@ -1117,6 +1132,7 @@ typedef struct _MPI2_FW_IMAGE_HEADER
#define MPI2_FW_HEADER_PID_FAMILY_MASK (0x00FF)
/* SAS */
#define MPI2_FW_HEADER_PID_FAMILY_2108_SAS (0x0010)
+#define MPI2_FW_HEADER_PID_FAMILY_2208_SAS (0x0011)
/* use MPI2_IOCFACTS_PROTOCOL_ defines for ProtocolFlags field */
diff --git a/drivers/scsi/mpt2sas/mpi/mpi2_raid.h b/drivers/scsi/mpt2sas/mpi/mpi2_raid.h
index 7134816d904..5160c33d2a0 100644
--- a/drivers/scsi/mpt2sas/mpi/mpi2_raid.h
+++ b/drivers/scsi/mpt2sas/mpi/mpi2_raid.h
@@ -6,7 +6,7 @@
* Title: MPI Integrated RAID messages and structures
* Creation Date: April 26, 2007
*
- * mpi2_raid.h Version: 02.00.03
+ * mpi2_raid.h Version: 02.00.04
*
* Version History
* ---------------
@@ -20,6 +20,8 @@
* 05-21-08 02.00.03 Added MPI2_RAID_VOL_CREATION_NUM_PHYSDISKS so that
* the PhysDisk array in MPI2_RAID_VOLUME_CREATION_STRUCT
* can be sized by the build environment.
+ * 07-30-09 02.00.04 Added proper define for the Use Default Settings bit of
+ * VolumeCreationFlags and marked the old one as obsolete.
* --------------------------------------------------------------------------
*/
@@ -217,10 +219,14 @@ typedef struct _MPI2_RAID_VOLUME_CREATION_STRUCT
/* use MPI2_RAID_VOL_TYPE_ defines from mpi2_cnfg.h for VolumeType */
/* defines for the VolumeCreationFlags field */
+#define MPI2_RAID_VOL_CREATION_DEFAULT_SETTINGS (0x80000000)
+#define MPI2_RAID_VOL_CREATION_BACKGROUND_INIT (0x00000004)
+#define MPI2_RAID_VOL_CREATION_LOW_LEVEL_INIT (0x00000002)
+#define MPI2_RAID_VOL_CREATION_MIGRATE_DATA (0x00000001)
+/* The following is an obsolete define.
+ * It must be shifted left 24 bits in order to set the proper bit.
+ */
#define MPI2_RAID_VOL_CREATION_USE_DEFAULT_SETTINGS (0x80)
-#define MPI2_RAID_VOL_CREATION_BACKGROUND_INIT (0x04)
-#define MPI2_RAID_VOL_CREATION_LOW_LEVEL_INIT (0x02)
-#define MPI2_RAID_VOL_CREATION_MIGRATE_DATA (0x01)
/* RAID Online Capacity Expansion Structure */
diff --git a/drivers/scsi/mpt2sas/mpi/mpi2_tool.h b/drivers/scsi/mpt2sas/mpi/mpi2_tool.h
index 007e950f7bf..73fcdbf9263 100644
--- a/drivers/scsi/mpt2sas/mpi/mpi2_tool.h
+++ b/drivers/scsi/mpt2sas/mpi/mpi2_tool.h
@@ -6,7 +6,7 @@
* Title: MPI diagnostic tool structures and definitions
* Creation Date: March 26, 2007
*
- * mpi2_tool.h Version: 02.00.03
+ * mpi2_tool.h Version: 02.00.04
*
* Version History
* ---------------
@@ -18,6 +18,10 @@
* structures and defines.
* 02-29-08 02.00.02 Modified various names to make them 32-character unique.
* 05-06-09 02.00.03 Added ISTWI Read Write Tool and Diagnostic CLI Tool.
+ * 07-30-09 02.00.04 Added ExtendedType field to DiagnosticBufferPost request
+ * and reply messages.
+ * Added MPI2_DIAG_BUF_TYPE_EXTENDED.
+ * Incremented MPI2_DIAG_BUF_TYPE_COUNT.
* --------------------------------------------------------------------------
*/
@@ -282,7 +286,7 @@ typedef struct _MPI2_TOOLBOX_DIAGNOSTIC_CLI_REPLY {
typedef struct _MPI2_DIAG_BUFFER_POST_REQUEST
{
- U8 Reserved1; /* 0x00 */
+ U8 ExtendedType; /* 0x00 */
U8 BufferType; /* 0x01 */
U8 ChainOffset; /* 0x02 */
U8 Function; /* 0x03 */
@@ -301,11 +305,15 @@ typedef struct _MPI2_DIAG_BUFFER_POST_REQUEST
} MPI2_DIAG_BUFFER_POST_REQUEST, MPI2_POINTER PTR_MPI2_DIAG_BUFFER_POST_REQUEST,
Mpi2DiagBufferPostRequest_t, MPI2_POINTER pMpi2DiagBufferPostRequest_t;
+/* values for the ExtendedType field */
+#define MPI2_DIAG_EXTENDED_TYPE_UTILIZATION (0x02)
+
/* values for the BufferType field */
#define MPI2_DIAG_BUF_TYPE_TRACE (0x00)
#define MPI2_DIAG_BUF_TYPE_SNAPSHOT (0x01)
+#define MPI2_DIAG_BUF_TYPE_EXTENDED (0x02)
/* count of the number of buffer types */
-#define MPI2_DIAG_BUF_TYPE_COUNT (0x02)
+#define MPI2_DIAG_BUF_TYPE_COUNT (0x03)
/****************************************************************************
@@ -314,7 +322,7 @@ typedef struct _MPI2_DIAG_BUFFER_POST_REQUEST
typedef struct _MPI2_DIAG_BUFFER_POST_REPLY
{
- U8 Reserved1; /* 0x00 */
+ U8 ExtendedType; /* 0x00 */
U8 BufferType; /* 0x01 */
U8 MsgLength; /* 0x02 */
U8 Function; /* 0x03 */
diff --git a/drivers/scsi/mpt2sas/mpt2sas_base.c b/drivers/scsi/mpt2sas/mpt2sas_base.c
index 670241efa4b..6422e258fd5 100644
--- a/drivers/scsi/mpt2sas/mpt2sas_base.c
+++ b/drivers/scsi/mpt2sas/mpt2sas_base.c
@@ -57,6 +57,7 @@
#include <linux/dma-mapping.h>
#include <linux/sort.h>
#include <linux/io.h>
+#include <linux/time.h>
#include "mpt2sas_base.h"
@@ -77,6 +78,44 @@ static int msix_disable = -1;
module_param(msix_disable, int, 0);
MODULE_PARM_DESC(msix_disable, " disable msix routed interrupts (default=0)");
+/* diag_buffer_enable is bitwise
+ * bit 0 set = TRACE
+ * bit 1 set = SNAPSHOT
+ * bit 2 set = EXTENDED
+ *
+ * Either bit can be set, or both
+ */
+static int diag_buffer_enable;
+module_param(diag_buffer_enable, int, 0);
+MODULE_PARM_DESC(diag_buffer_enable, " post diag buffers "
+ "(TRACE=1/SNAPSHOT=2/EXTENDED=4/default=0)");
+
+int mpt2sas_fwfault_debug;
+MODULE_PARM_DESC(mpt2sas_fwfault_debug, " enable detection of firmware fault "
+ "and halt firmware - (default=0)");
+
+/**
+ * _scsih_set_fwfault_debug - global setting of ioc->fwfault_debug.
+ *
+ */
+static int
+_scsih_set_fwfault_debug(const char *val, struct kernel_param *kp)
+{
+ int ret = param_set_int(val, kp);
+ struct MPT2SAS_ADAPTER *ioc;
+
+ if (ret)
+ return ret;
+
+ printk(KERN_INFO "setting logging_level(0x%08x)\n",
+ mpt2sas_fwfault_debug);
+ list_for_each_entry(ioc, &mpt2sas_ioc_list, list)
+ ioc->fwfault_debug = mpt2sas_fwfault_debug;
+ return 0;
+}
+module_param_call(mpt2sas_fwfault_debug, _scsih_set_fwfault_debug,
+ param_get_int, &mpt2sas_fwfault_debug, 0644);
+
/**
* _base_fault_reset_work - workq handling ioc fault conditions
* @work: input argument, used to derive ioc
@@ -121,7 +160,7 @@ _base_fault_reset_work(struct work_struct *work)
/**
* mpt2sas_base_start_watchdog - start the fault_reset_work_q
- * @ioc: pointer to scsi command object
+ * @ioc: per adapter object
* Context: sleep.
*
* Return nothing.
@@ -155,7 +194,7 @@ mpt2sas_base_start_watchdog(struct MPT2SAS_ADAPTER *ioc)
/**
* mpt2sas_base_stop_watchdog - stop the fault_reset_work_q
- * @ioc: pointer to scsi command object
+ * @ioc: per adapter object
* Context: sleep.
*
* Return nothing.
@@ -177,10 +216,55 @@ mpt2sas_base_stop_watchdog(struct MPT2SAS_ADAPTER *ioc)
}
}
+/**
+ * mpt2sas_base_fault_info - verbose translation of firmware FAULT code
+ * @ioc: per adapter object
+ * @fault_code: fault code
+ *
+ * Return nothing.
+ */
+void
+mpt2sas_base_fault_info(struct MPT2SAS_ADAPTER *ioc , u16 fault_code)
+{
+ printk(MPT2SAS_ERR_FMT "fault_state(0x%04x)!\n",
+ ioc->name, fault_code);
+}
+
+/**
+ * mpt2sas_halt_firmware - halt's mpt controller firmware
+ * @ioc: per adapter object
+ *
+ * For debugging timeout related issues. Writing 0xCOFFEE00
+ * to the doorbell register will halt controller firmware. With
+ * the purpose to stop both driver and firmware, the enduser can
+ * obtain a ring buffer from controller UART.
+ */
+void
+mpt2sas_halt_firmware(struct MPT2SAS_ADAPTER *ioc)
+{
+ u32 doorbell;
+
+ if (!ioc->fwfault_debug)
+ return;
+
+ dump_stack();
+
+ doorbell = readl(&ioc->chip->Doorbell);
+ if ((doorbell & MPI2_IOC_STATE_MASK) == MPI2_IOC_STATE_FAULT)
+ mpt2sas_base_fault_info(ioc , doorbell);
+ else {
+ writel(0xC0FFEE00, &ioc->chip->Doorbell);
+ printk(MPT2SAS_ERR_FMT "Firmware is halted due to command "
+ "timeout\n", ioc->name);
+ }
+
+ panic("panic in %s\n", __func__);
+}
+
#ifdef CONFIG_SCSI_MPT2SAS_LOGGING
/**
* _base_sas_ioc_info - verbose translation of the ioc status
- * @ioc: pointer to scsi command object
+ * @ioc: per adapter object
* @mpi_reply: reply mf payload returned from firmware
* @request_hdr: request mf
*
@@ -394,7 +478,7 @@ _base_sas_ioc_info(struct MPT2SAS_ADAPTER *ioc, MPI2DefaultReply_t *mpi_reply,
/**
* _base_display_event_data - verbose translation of firmware asyn events
- * @ioc: pointer to scsi command object
+ * @ioc: per adapter object
* @mpi_reply: reply mf payload returned from firmware
*
* Return nothing.
@@ -474,7 +558,7 @@ _base_display_event_data(struct MPT2SAS_ADAPTER *ioc,
/**
* _base_sas_log_info - verbose translation of firmware log info
- * @ioc: pointer to scsi command object
+ * @ioc: per adapter object
* @log_info: log info
*
* Return nothing.
@@ -526,22 +610,8 @@ _base_sas_log_info(struct MPT2SAS_ADAPTER *ioc , u32 log_info)
}
/**
- * mpt2sas_base_fault_info - verbose translation of firmware FAULT code
- * @ioc: pointer to scsi command object
- * @fault_code: fault code
- *
- * Return nothing.
- */
-void
-mpt2sas_base_fault_info(struct MPT2SAS_ADAPTER *ioc , u16 fault_code)
-{
- printk(MPT2SAS_ERR_FMT "fault_state(0x%04x)!\n",
- ioc->name, fault_code);
-}
-
-/**
* _base_display_reply_info -
- * @ioc: pointer to scsi command object
+ * @ioc: per adapter object
* @smid: system request message index
* @msix_index: MSIX table index supplied by the OS
* @reply: reply message frame(lower 32bit addr)
@@ -570,7 +640,7 @@ _base_display_reply_info(struct MPT2SAS_ADAPTER *ioc, u16 smid, u8 msix_index,
/**
* mpt2sas_base_done - base internal command completion routine
- * @ioc: pointer to scsi command object
+ * @ioc: per adapter object
* @smid: system request message index
* @msix_index: MSIX table index supplied by the OS
* @reply: reply message frame(lower 32bit addr)
@@ -603,7 +673,7 @@ mpt2sas_base_done(struct MPT2SAS_ADAPTER *ioc, u16 smid, u8 msix_index,
/**
* _base_async_event - main callback handler for firmware asyn events
- * @ioc: pointer to scsi command object
+ * @ioc: per adapter object
* @msix_index: MSIX table index supplied by the OS
* @reply: reply message frame(lower 32bit addr)
*
@@ -684,7 +754,7 @@ _base_get_cb_idx(struct MPT2SAS_ADAPTER *ioc, u16 smid)
/**
* _base_mask_interrupts - disable interrupts
- * @ioc: pointer to scsi command object
+ * @ioc: per adapter object
*
* Disabling ResetIRQ, Reply and Doorbell Interrupts
*
@@ -704,7 +774,7 @@ _base_mask_interrupts(struct MPT2SAS_ADAPTER *ioc)
/**
* _base_unmask_interrupts - enable interrupts
- * @ioc: pointer to scsi command object
+ * @ioc: per adapter object
*
* Enabling only Reply Interrupts
*
@@ -1258,12 +1328,13 @@ mpt2sas_base_get_sense_buffer(struct MPT2SAS_ADAPTER *ioc, u16 smid)
* @ioc: per adapter object
* @smid: system request message index
*
- * Returns phys pointer to sense buffer.
+ * Returns phys pointer to the low 32bit address of the sense buffer.
*/
-dma_addr_t
+__le32
mpt2sas_base_get_sense_buffer_dma(struct MPT2SAS_ADAPTER *ioc, u16 smid)
{
- return ioc->sense_dma + ((smid - 1) * SCSI_SENSE_BUFFERSIZE);
+ return cpu_to_le32(ioc->sense_dma +
+ ((smid - 1) * SCSI_SENSE_BUFFERSIZE));
}
/**
@@ -1697,6 +1768,12 @@ _base_display_ioc_capabilities(struct MPT2SAS_ADAPTER *ioc)
}
if (ioc->facts.IOCCapabilities &
+ MPI2_IOCFACTS_CAPABILITY_EXTENDED_BUFFER) {
+ printk(KERN_INFO "%sDiag Extended Buffer", i ? "," : "");
+ i++;
+ }
+
+ if (ioc->facts.IOCCapabilities &
MPI2_IOCFACTS_CAPABILITY_TASK_SET_FULL_HANDLING) {
printk("%sTask Set Full", i ? "," : "");
i++;
@@ -2871,6 +2948,8 @@ _base_send_ioc_init(struct MPT2SAS_ADAPTER *ioc, int sleep_flag)
Mpi2IOCInitRequest_t mpi_request;
Mpi2IOCInitReply_t mpi_reply;
int r;
+ struct timeval current_time;
+ u16 ioc_status;
dinitprintk(ioc, printk(MPT2SAS_DEBUG_FMT "%s\n", ioc->name,
__func__));
@@ -2921,6 +3000,13 @@ _base_send_ioc_init(struct MPT2SAS_ADAPTER *ioc, int sleep_flag)
cpu_to_le32(ioc->reply_post_free_dma);
#endif
+ /* This time stamp specifies number of milliseconds
+ * since epoch ~ midnight January 1, 1970.
+ */
+ do_gettimeofday(&current_time);
+ mpi_request.TimeStamp = (current_time.tv_sec * 1000) +
+ (current_time.tv_usec >> 3);
+
if (ioc->logging_level & MPT_DEBUG_INIT) {
u32 *mfp;
int i;
@@ -2943,7 +3029,8 @@ _base_send_ioc_init(struct MPT2SAS_ADAPTER *ioc, int sleep_flag)
return r;
}
- if (mpi_reply.IOCStatus != MPI2_IOCSTATUS_SUCCESS ||
+ ioc_status = le16_to_cpu(mpi_reply.IOCStatus) & MPI2_IOCSTATUS_MASK;
+ if (ioc_status != MPI2_IOCSTATUS_SUCCESS ||
mpi_reply.IOCLogInfo) {
printk(MPT2SAS_ERR_FMT "%s: failed\n", ioc->name, __func__);
r = -EIO;
@@ -3461,11 +3548,11 @@ mpt2sas_base_attach(struct MPT2SAS_ADAPTER *ioc)
return r;
pci_set_drvdata(ioc->pdev, ioc->shost);
- r = _base_make_ioc_ready(ioc, CAN_SLEEP, SOFT_RESET);
+ r = _base_get_ioc_facts(ioc, CAN_SLEEP);
if (r)
goto out_free_resources;
- r = _base_get_ioc_facts(ioc, CAN_SLEEP);
+ r = _base_make_ioc_ready(ioc, CAN_SLEEP, SOFT_RESET);
if (r)
goto out_free_resources;
@@ -3531,6 +3618,8 @@ mpt2sas_base_attach(struct MPT2SAS_ADAPTER *ioc)
goto out_free_resources;
mpt2sas_base_start_watchdog(ioc);
+ if (diag_buffer_enable != 0)
+ mpt2sas_enable_diag_buffer(ioc, diag_buffer_enable);
return 0;
out_free_resources:
@@ -3684,6 +3773,9 @@ mpt2sas_base_hard_reset_handler(struct MPT2SAS_ADAPTER *ioc, int sleep_flag,
dtmprintk(ioc, printk(MPT2SAS_DEBUG_FMT "%s: enter\n", ioc->name,
__func__));
+ if (mpt2sas_fwfault_debug)
+ mpt2sas_halt_firmware(ioc);
+
spin_lock_irqsave(&ioc->ioc_reset_in_progress_lock, flags);
if (ioc->shost_recovery) {
spin_unlock_irqrestore(&ioc->ioc_reset_in_progress_lock, flags);
diff --git a/drivers/scsi/mpt2sas/mpt2sas_base.h b/drivers/scsi/mpt2sas/mpt2sas_base.h
index 0cf6bc236e4..bb4f14656af 100644
--- a/drivers/scsi/mpt2sas/mpt2sas_base.h
+++ b/drivers/scsi/mpt2sas/mpt2sas_base.h
@@ -69,8 +69,8 @@
#define MPT2SAS_DRIVER_NAME "mpt2sas"
#define MPT2SAS_AUTHOR "LSI Corporation <DL-MPTFusionLinux@lsi.com>"
#define MPT2SAS_DESCRIPTION "LSI MPT Fusion SAS 2.0 Device Driver"
-#define MPT2SAS_DRIVER_VERSION "02.100.03.00"
-#define MPT2SAS_MAJOR_VERSION 02
+#define MPT2SAS_DRIVER_VERSION "03.100.03.00"
+#define MPT2SAS_MAJOR_VERSION 03
#define MPT2SAS_MINOR_VERSION 100
#define MPT2SAS_BUILD_VERSION 03
#define MPT2SAS_RELEASE_VERSION 00
@@ -278,7 +278,7 @@ struct _internal_cmd {
* @sas_address: device sas address
* @device_name: retrieved from the SAS IDENTIFY frame.
* @handle: device handle
- * @parent_handle: handle to parent device
+ * @sas_address_parent: sas address of parent expander or sas host
* @enclosure_handle: enclosure handle
* @enclosure_logical_id: enclosure logical identifier
* @volume_handle: volume handle (valid when hidden raid member)
@@ -296,7 +296,7 @@ struct _sas_device {
u64 sas_address;
u64 device_name;
u16 handle;
- u16 parent_handle;
+ u64 sas_address_parent;
u16 enclosure_handle;
u64 enclosure_logical_id;
u16 volume_handle;
@@ -352,8 +352,6 @@ struct _boot_device {
/**
* struct _sas_port - wide/narrow sas port information
* @port_list: list of ports belonging to expander
- * @handle: device handle for this port
- * @sas_address: sas address of this port
* @num_phys: number of phys belonging to this port
* @remote_identify: attached device identification
* @rphy: sas transport rphy object
@@ -362,8 +360,6 @@ struct _boot_device {
*/
struct _sas_port {
struct list_head port_list;
- u16 handle;
- u64 sas_address;
u8 num_phys;
struct sas_identify remote_identify;
struct sas_rphy *rphy;
@@ -398,7 +394,7 @@ struct _sas_phy {
* @num_phys: number phys belonging to this sas_host/expander
* @sas_address: sas address of this sas_host/expander
* @handle: handle for this sas_host/expander
- * @parent_handle: parent handle
+ * @sas_address_parent: sas address of parent expander or sas host
* @enclosure_handle: handle for this a member of an enclosure
* @device_info: bitwise defining capabilities of this sas_host/expander
* @responding: used in _scsih_expander_device_mark_responding
@@ -411,7 +407,7 @@ struct _sas_node {
u8 num_phys;
u64 sas_address;
u16 handle;
- u16 parent_handle;
+ u64 sas_address_parent;
u16 enclosure_handle;
u64 enclosure_logical_id;
u8 responding;
@@ -470,6 +466,7 @@ typedef void (*MPT_ADD_SGE)(void *paddr, u32 flags_length, dma_addr_t dma_addr);
* @chip_phys: physical addrss prior to mapping
* @pio_chip: I/O mapped register space
* @logging_level: see mpt2sas_debug.h
+ * @fwfault_debug: debuging FW timeouts
* @ir_firmware: IR firmware present
* @bars: bitmask of BAR's that must be configured
* @mask_interrupts: ignore interrupt
@@ -495,12 +492,14 @@ typedef void (*MPT_ADD_SGE)(void *paddr, u32 flags_length, dma_addr_t dma_addr);
* @msix_table_backup: backup msix table
* @scsi_io_cb_idx: shost generated commands
* @tm_cb_idx: task management commands
+ * @scsih_cb_idx: scsih internal commands
* @transport_cb_idx: transport internal commands
* @ctl_cb_idx: clt internal commands
* @base_cb_idx: base internal commands
* @config_cb_idx: base internal commands
* @base_cmds:
* @transport_cmds:
+ * @scsih_cmds:
* @tm_cmds:
* @ctl_cmds:
* @config_cmds:
@@ -591,6 +590,7 @@ struct MPT2SAS_ADAPTER {
unsigned long chip_phys;
unsigned long pio_chip;
int logging_level;
+ int fwfault_debug;
u8 ir_firmware;
int bars;
u8 mask_interrupts;
@@ -626,6 +626,7 @@ struct MPT2SAS_ADAPTER {
u8 scsi_io_cb_idx;
u8 tm_cb_idx;
u8 transport_cb_idx;
+ u8 scsih_cb_idx;
u8 ctl_cb_idx;
u8 base_cb_idx;
u8 config_cb_idx;
@@ -633,6 +634,7 @@ struct MPT2SAS_ADAPTER {
u8 tm_sas_control_cb_idx;
struct _internal_cmd base_cmds;
struct _internal_cmd transport_cmds;
+ struct _internal_cmd scsih_cmds;
struct _internal_cmd tm_cmds;
struct _internal_cmd ctl_cmds;
struct _internal_cmd config_cmds;
@@ -773,7 +775,7 @@ int mpt2sas_base_hard_reset_handler(struct MPT2SAS_ADAPTER *ioc, int sleep_flag,
void *mpt2sas_base_get_msg_frame(struct MPT2SAS_ADAPTER *ioc, u16 smid);
void *mpt2sas_base_get_sense_buffer(struct MPT2SAS_ADAPTER *ioc, u16 smid);
void mpt2sas_base_build_zero_len_sge(struct MPT2SAS_ADAPTER *ioc, void *paddr);
-dma_addr_t mpt2sas_base_get_sense_buffer_dma(struct MPT2SAS_ADAPTER *ioc,
+__le32 mpt2sas_base_get_sense_buffer_dma(struct MPT2SAS_ADAPTER *ioc,
u16 smid);
/* hi-priority queue */
@@ -807,6 +809,8 @@ int mpt2sas_base_scsi_enclosure_processor(struct MPT2SAS_ADAPTER *ioc,
Mpi2SepReply_t *mpi_reply, Mpi2SepRequest_t *mpi_request);
void mpt2sas_base_validate_event_type(struct MPT2SAS_ADAPTER *ioc, u32 *event_type);
+void mpt2sas_halt_firmware(struct MPT2SAS_ADAPTER *ioc);
+
/* scsih shared API */
u8 mpt2sas_scsih_event_callback(struct MPT2SAS_ADAPTER *ioc, u8 msix_index,
u32 reply);
@@ -886,19 +890,22 @@ u8 mpt2sas_ctl_event_callback(struct MPT2SAS_ADAPTER *ioc, u8 msix_index,
void mpt2sas_ctl_add_to_event_log(struct MPT2SAS_ADAPTER *ioc,
Mpi2EventNotificationReply_t *mpi_reply);
+void mpt2sas_enable_diag_buffer(struct MPT2SAS_ADAPTER *ioc,
+ u8 bits_to_regsiter);
+
/* transport shared API */
u8 mpt2sas_transport_done(struct MPT2SAS_ADAPTER *ioc, u16 smid, u8 msix_index,
u32 reply);
struct _sas_port *mpt2sas_transport_port_add(struct MPT2SAS_ADAPTER *ioc,
- u16 handle, u16 parent_handle);
+ u16 handle, u64 sas_address);
void mpt2sas_transport_port_remove(struct MPT2SAS_ADAPTER *ioc, u64 sas_address,
- u16 parent_handle);
+ u64 sas_address_parent);
int mpt2sas_transport_add_host_phy(struct MPT2SAS_ADAPTER *ioc, struct _sas_phy
*mpt2sas_phy, Mpi2SasPhyPage0_t phy_pg0, struct device *parent_dev);
int mpt2sas_transport_add_expander_phy(struct MPT2SAS_ADAPTER *ioc, struct _sas_phy
*mpt2sas_phy, Mpi2ExpanderPage1_t expander_pg1, struct device *parent_dev);
-void mpt2sas_transport_update_links(struct MPT2SAS_ADAPTER *ioc, u16 handle,
- u16 attached_handle, u8 phy_number, u8 link_rate);
+void mpt2sas_transport_update_links(struct MPT2SAS_ADAPTER *ioc,
+ u64 sas_address, u16 handle, u8 phy_number, u8 link_rate);
extern struct sas_function_template mpt2sas_transport_functions;
extern struct scsi_transport_template *mpt2sas_transport_template;
extern int scsi_internal_device_block(struct scsi_device *sdev);
diff --git a/drivers/scsi/mpt2sas/mpt2sas_ctl.c b/drivers/scsi/mpt2sas/mpt2sas_ctl.c
index 57d72463390..84a124f8e21 100644
--- a/drivers/scsi/mpt2sas/mpt2sas_ctl.c
+++ b/drivers/scsi/mpt2sas/mpt2sas_ctl.c
@@ -740,7 +740,7 @@ _ctl_do_mpt_command(struct MPT2SAS_ADAPTER *ioc,
Mpi2SCSIIORequest_t *scsiio_request =
(Mpi2SCSIIORequest_t *)mpi_request;
scsiio_request->SenseBufferLowAddress =
- (u32)mpt2sas_base_get_sense_buffer_dma(ioc, smid);
+ mpt2sas_base_get_sense_buffer_dma(ioc, smid);
priv_sense = mpt2sas_base_get_sense_buffer(ioc, smid);
memset(priv_sense, 0, SCSI_SENSE_BUFFERSIZE);
mpt2sas_base_put_smid_scsi_io(ioc, smid,
@@ -848,8 +848,9 @@ _ctl_do_mpt_command(struct MPT2SAS_ADAPTER *ioc,
printk(MPT2SAS_DEBUG_FMT "TASK_MGMT: "
"IOCStatus(0x%04x), IOCLogInfo(0x%08x), "
"TerminationCount(0x%08x)\n", ioc->name,
- tm_reply->IOCStatus, tm_reply->IOCLogInfo,
- tm_reply->TerminationCount);
+ le16_to_cpu(tm_reply->IOCStatus),
+ le32_to_cpu(tm_reply->IOCLogInfo),
+ le32_to_cpu(tm_reply->TerminationCount));
}
#endif
/* copy out xdata to user */
@@ -896,6 +897,7 @@ _ctl_do_mpt_command(struct MPT2SAS_ADAPTER *ioc,
printk(MPT2SAS_INFO_FMT "issue target reset: handle "
"= (0x%04x)\n", ioc->name,
mpi_request->FunctionDependent1);
+ mpt2sas_halt_firmware(ioc);
mutex_lock(&ioc->tm_cmds.mutex);
mpt2sas_scsih_issue_tm(ioc,
mpi_request->FunctionDependent1, 0,
@@ -1229,7 +1231,7 @@ _ctl_btdh_mapping(void __user *arg)
/**
* _ctl_diag_capability - return diag buffer capability
* @ioc: per adapter object
- * @buffer_type: specifies either TRACE or SNAPSHOT
+ * @buffer_type: specifies either TRACE, SNAPSHOT, or EXTENDED
*
* returns 1 when diag buffer support is enabled in firmware
*/
@@ -1249,24 +1251,25 @@ _ctl_diag_capability(struct MPT2SAS_ADAPTER *ioc, u8 buffer_type)
MPI2_IOCFACTS_CAPABILITY_SNAPSHOT_BUFFER)
rc = 1;
break;
+ case MPI2_DIAG_BUF_TYPE_EXTENDED:
+ if (ioc->facts.IOCCapabilities &
+ MPI2_IOCFACTS_CAPABILITY_EXTENDED_BUFFER)
+ rc = 1;
}
return rc;
}
/**
- * _ctl_diag_register - application register with driver
- * @arg - user space buffer containing ioctl content
- * @state - NON_BLOCKING or BLOCKING
+ * _ctl_diag_register_2 - wrapper for registering diag buffer support
+ * @ioc: per adapter object
+ * @diag_register: the diag_register struct passed in from user space
*
- * This will allow the driver to setup any required buffers that will be
- * needed by firmware to communicate with the driver.
*/
static long
-_ctl_diag_register(void __user *arg, enum block_state state)
+_ctl_diag_register_2(struct MPT2SAS_ADAPTER *ioc,
+ struct mpt2_diag_register *diag_register)
{
- struct mpt2_diag_register karg;
- struct MPT2SAS_ADAPTER *ioc;
int rc, i;
void *request_data = NULL;
dma_addr_t request_data_dma;
@@ -1279,18 +1282,17 @@ _ctl_diag_register(void __user *arg, enum block_state state)
u16 ioc_status;
u8 issue_reset = 0;
- if (copy_from_user(&karg, arg, sizeof(karg))) {
- printk(KERN_ERR "failure at %s:%d/%s()!\n",
- __FILE__, __LINE__, __func__);
- return -EFAULT;
- }
- if (_ctl_verify_adapter(karg.hdr.ioc_number, &ioc) == -1 || !ioc)
- return -ENODEV;
-
dctlprintk(ioc, printk(MPT2SAS_DEBUG_FMT "%s\n", ioc->name,
__func__));
- buffer_type = karg.buffer_type;
+ if (ioc->ctl_cmds.status != MPT2_CMD_NOT_USED) {
+ printk(MPT2SAS_ERR_FMT "%s: ctl_cmd in use\n",
+ ioc->name, __func__);
+ rc = -EAGAIN;
+ goto out;
+ }
+
+ buffer_type = diag_register->buffer_type;
if (!_ctl_diag_capability(ioc, buffer_type)) {
printk(MPT2SAS_ERR_FMT "%s: doesn't have capability for "
"buffer_type(0x%02x)\n", ioc->name, __func__, buffer_type);
@@ -1305,24 +1307,12 @@ _ctl_diag_register(void __user *arg, enum block_state state)
return -EINVAL;
}
- if (karg.requested_buffer_size % 4) {
+ if (diag_register->requested_buffer_size % 4) {
printk(MPT2SAS_ERR_FMT "%s: the requested_buffer_size "
"is not 4 byte aligned\n", ioc->name, __func__);
return -EINVAL;
}
- if (state == NON_BLOCKING && !mutex_trylock(&ioc->ctl_cmds.mutex))
- return -EAGAIN;
- else if (mutex_lock_interruptible(&ioc->ctl_cmds.mutex))
- return -ERESTARTSYS;
-
- if (ioc->ctl_cmds.status != MPT2_CMD_NOT_USED) {
- printk(MPT2SAS_ERR_FMT "%s: ctl_cmd in use\n",
- ioc->name, __func__);
- rc = -EAGAIN;
- goto out;
- }
-
smid = mpt2sas_base_get_smid(ioc, ioc->ctl_cb_idx);
if (!smid) {
printk(MPT2SAS_ERR_FMT "%s: failed obtaining a smid\n",
@@ -1338,12 +1328,12 @@ _ctl_diag_register(void __user *arg, enum block_state state)
ioc->ctl_cmds.smid = smid;
request_data = ioc->diag_buffer[buffer_type];
- request_data_sz = karg.requested_buffer_size;
- ioc->unique_id[buffer_type] = karg.unique_id;
+ request_data_sz = diag_register->requested_buffer_size;
+ ioc->unique_id[buffer_type] = diag_register->unique_id;
ioc->diag_buffer_status[buffer_type] = 0;
- memcpy(ioc->product_specific[buffer_type], karg.product_specific,
- MPT2_PRODUCT_SPECIFIC_DWORDS);
- ioc->diagnostic_flags[buffer_type] = karg.diagnostic_flags;
+ memcpy(ioc->product_specific[buffer_type],
+ diag_register->product_specific, MPT2_PRODUCT_SPECIFIC_DWORDS);
+ ioc->diagnostic_flags[buffer_type] = diag_register->diagnostic_flags;
if (request_data) {
request_data_dma = ioc->diag_buffer_dma[buffer_type];
@@ -1373,8 +1363,8 @@ _ctl_diag_register(void __user *arg, enum block_state state)
}
mpi_request->Function = MPI2_FUNCTION_DIAG_BUFFER_POST;
- mpi_request->BufferType = karg.buffer_type;
- mpi_request->Flags = cpu_to_le32(karg.diagnostic_flags);
+ mpi_request->BufferType = diag_register->buffer_type;
+ mpi_request->Flags = cpu_to_le32(diag_register->diagnostic_flags);
mpi_request->BufferAddress = cpu_to_le64(request_data_dma);
mpi_request->BufferLength = cpu_to_le32(request_data_sz);
mpi_request->VF_ID = 0; /* TODO */
@@ -1422,7 +1412,7 @@ _ctl_diag_register(void __user *arg, enum block_state state)
} else {
printk(MPT2SAS_DEBUG_FMT "%s: ioc_status(0x%04x) "
"log_info(0x%08x)\n", ioc->name, __func__,
- ioc_status, mpi_reply->IOCLogInfo);
+ ioc_status, le32_to_cpu(mpi_reply->IOCLogInfo));
rc = -EFAULT;
}
@@ -1438,6 +1428,83 @@ _ctl_diag_register(void __user *arg, enum block_state state)
request_data, request_data_dma);
ioc->ctl_cmds.status = MPT2_CMD_NOT_USED;
+ return rc;
+}
+
+/**
+ * mpt2sas_enable_diag_buffer - enabling diag_buffers support driver load time
+ * @ioc: per adapter object
+ * @bits_to_register: bitwise field where trace is bit 0, and snapshot is bit 1
+ *
+ * This is called when command line option diag_buffer_enable is enabled
+ * at driver load time.
+ */
+void
+mpt2sas_enable_diag_buffer(struct MPT2SAS_ADAPTER *ioc, u8 bits_to_register)
+{
+ struct mpt2_diag_register diag_register;
+
+ memset(&diag_register, 0, sizeof(struct mpt2_diag_register));
+
+ if (bits_to_register & 1) {
+ printk(MPT2SAS_INFO_FMT "registering trace buffer support\n",
+ ioc->name);
+ diag_register.buffer_type = MPI2_DIAG_BUF_TYPE_TRACE;
+ /* register for 1MB buffers */
+ diag_register.requested_buffer_size = (1024 * 1024);
+ diag_register.unique_id = 0x7075900;
+ _ctl_diag_register_2(ioc, &diag_register);
+ }
+
+ if (bits_to_register & 2) {
+ printk(MPT2SAS_INFO_FMT "registering snapshot buffer support\n",
+ ioc->name);
+ diag_register.buffer_type = MPI2_DIAG_BUF_TYPE_SNAPSHOT;
+ /* register for 2MB buffers */
+ diag_register.requested_buffer_size = 2 * (1024 * 1024);
+ diag_register.unique_id = 0x7075901;
+ _ctl_diag_register_2(ioc, &diag_register);
+ }
+
+ if (bits_to_register & 4) {
+ printk(MPT2SAS_INFO_FMT "registering extended buffer support\n",
+ ioc->name);
+ diag_register.buffer_type = MPI2_DIAG_BUF_TYPE_EXTENDED;
+ /* register for 2MB buffers */
+ diag_register.requested_buffer_size = 2 * (1024 * 1024);
+ diag_register.unique_id = 0x7075901;
+ _ctl_diag_register_2(ioc, &diag_register);
+ }
+}
+
+/**
+ * _ctl_diag_register - application register with driver
+ * @arg - user space buffer containing ioctl content
+ * @state - NON_BLOCKING or BLOCKING
+ *
+ * This will allow the driver to setup any required buffers that will be
+ * needed by firmware to communicate with the driver.
+ */
+static long
+_ctl_diag_register(void __user *arg, enum block_state state)
+{
+ struct mpt2_diag_register karg;
+ struct MPT2SAS_ADAPTER *ioc;
+ long rc;
+
+ if (copy_from_user(&karg, arg, sizeof(karg))) {
+ printk(KERN_ERR "failure at %s:%d/%s()!\n",
+ __FILE__, __LINE__, __func__);
+ return -EFAULT;
+ }
+ if (_ctl_verify_adapter(karg.hdr.ioc_number, &ioc) == -1 || !ioc)
+ return -ENODEV;
+
+ if (state == NON_BLOCKING && !mutex_trylock(&ioc->ctl_cmds.mutex))
+ return -EAGAIN;
+ else if (mutex_lock_interruptible(&ioc->ctl_cmds.mutex))
+ return -ERESTARTSYS;
+ rc = _ctl_diag_register_2(ioc, &karg);
mutex_unlock(&ioc->ctl_cmds.mutex);
return rc;
}
@@ -1600,7 +1667,7 @@ _ctl_diag_query(void __user *arg)
/**
* _ctl_send_release - Diag Release Message
* @ioc: per adapter object
- * @buffer_type - specifies either TRACE or SNAPSHOT
+ * @buffer_type - specifies either TRACE, SNAPSHOT, or EXTENDED
* @issue_reset - specifies whether host reset is required.
*
*/
@@ -1690,7 +1757,7 @@ _ctl_send_release(struct MPT2SAS_ADAPTER *ioc, u8 buffer_type, u8 *issue_reset)
} else {
printk(MPT2SAS_DEBUG_FMT "%s: ioc_status(0x%04x) "
"log_info(0x%08x)\n", ioc->name, __func__,
- ioc_status, mpi_reply->IOCLogInfo);
+ ioc_status, le32_to_cpu(mpi_reply->IOCLogInfo));
rc = -EFAULT;
}
@@ -1951,7 +2018,7 @@ _ctl_diag_read_buffer(void __user *arg, enum block_state state)
} else {
printk(MPT2SAS_DEBUG_FMT "%s: ioc_status(0x%04x) "
"log_info(0x%08x)\n", ioc->name, __func__,
- ioc_status, mpi_reply->IOCLogInfo);
+ ioc_status, le32_to_cpu(mpi_reply->IOCLogInfo));
rc = -EFAULT;
}
@@ -2474,6 +2541,43 @@ _ctl_logging_level_store(struct device *cdev, struct device_attribute *attr,
static DEVICE_ATTR(logging_level, S_IRUGO | S_IWUSR,
_ctl_logging_level_show, _ctl_logging_level_store);
+/* device attributes */
+/*
+ * _ctl_fwfault_debug_show - show/store fwfault_debug
+ * @cdev - pointer to embedded class device
+ * @buf - the buffer returned
+ *
+ * mpt2sas_fwfault_debug is command line option
+ * A sysfs 'read/write' shost attribute.
+ */
+static ssize_t
+_ctl_fwfault_debug_show(struct device *cdev,
+ struct device_attribute *attr, char *buf)
+{
+ struct Scsi_Host *shost = class_to_shost(cdev);
+ struct MPT2SAS_ADAPTER *ioc = shost_priv(shost);
+
+ return snprintf(buf, PAGE_SIZE, "%d\n", ioc->fwfault_debug);
+}
+static ssize_t
+_ctl_fwfault_debug_store(struct device *cdev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ struct Scsi_Host *shost = class_to_shost(cdev);
+ struct MPT2SAS_ADAPTER *ioc = shost_priv(shost);
+ int val = 0;
+
+ if (sscanf(buf, "%d", &val) != 1)
+ return -EINVAL;
+
+ ioc->fwfault_debug = val;
+ printk(MPT2SAS_INFO_FMT "fwfault_debug=%d\n", ioc->name,
+ ioc->fwfault_debug);
+ return strlen(buf);
+}
+static DEVICE_ATTR(fwfault_debug, S_IRUGO | S_IWUSR,
+ _ctl_fwfault_debug_show, _ctl_fwfault_debug_store);
+
struct device_attribute *mpt2sas_host_attrs[] = {
&dev_attr_version_fw,
&dev_attr_version_bios,
@@ -2487,13 +2591,12 @@ struct device_attribute *mpt2sas_host_attrs[] = {
&dev_attr_io_delay,
&dev_attr_device_delay,
&dev_attr_logging_level,
+ &dev_attr_fwfault_debug,
&dev_attr_fw_queue_depth,
&dev_attr_host_sas_address,
NULL,
};
-/* device attributes */
-
/**
* _ctl_device_sas_address_show - sas address
* @cdev - pointer to embedded class device
diff --git a/drivers/scsi/mpt2sas/mpt2sas_ctl.h b/drivers/scsi/mpt2sas/mpt2sas_ctl.h
index 211f296dd19..8a5eeb1a5c8 100644
--- a/drivers/scsi/mpt2sas/mpt2sas_ctl.h
+++ b/drivers/scsi/mpt2sas/mpt2sas_ctl.h
@@ -313,7 +313,7 @@ struct mpt2_ioctl_btdh_mapping {
* struct mpt2_diag_register - application register with driver
* @hdr - generic header
* @reserved -
- * @buffer_type - specifies either TRACE or SNAPSHOT
+ * @buffer_type - specifies either TRACE, SNAPSHOT, or EXTENDED
* @application_flags - misc flags
* @diagnostic_flags - specifies flags affecting command processing
* @product_specific - product specific information
@@ -352,7 +352,7 @@ struct mpt2_diag_unregister {
* struct mpt2_diag_query - query relevant info associated with diag buffers
* @hdr - generic header
* @reserved -
- * @buffer_type - specifies either TRACE or SNAPSHOT
+ * @buffer_type - specifies either TRACE, SNAPSHOT, or EXTENDED
* @application_flags - misc flags
* @diagnostic_flags - specifies flags affecting command processing
* @product_specific - product specific information
diff --git a/drivers/scsi/mpt2sas/mpt2sas_scsih.c b/drivers/scsi/mpt2sas/mpt2sas_scsih.c
index 756e509d495..efabea1a3ce 100644
--- a/drivers/scsi/mpt2sas/mpt2sas_scsih.c
+++ b/drivers/scsi/mpt2sas/mpt2sas_scsih.c
@@ -76,6 +76,7 @@ static u8 tm_cb_idx = -1;
static u8 ctl_cb_idx = -1;
static u8 base_cb_idx = -1;
static u8 transport_cb_idx = -1;
+static u8 scsih_cb_idx = -1;
static u8 config_cb_idx = -1;
static int mpt_ids;
@@ -196,10 +197,28 @@ static struct pci_device_id scsih_pci_table[] = {
PCI_ANY_ID, PCI_ANY_ID },
{ MPI2_MFGPAGE_VENDORID_LSI, MPI2_MFGPAGE_DEVID_SAS2108_3,
PCI_ANY_ID, PCI_ANY_ID },
+ /* Meteor ~ 2116 */
{ MPI2_MFGPAGE_VENDORID_LSI, MPI2_MFGPAGE_DEVID_SAS2116_1,
PCI_ANY_ID, PCI_ANY_ID },
{ MPI2_MFGPAGE_VENDORID_LSI, MPI2_MFGPAGE_DEVID_SAS2116_2,
PCI_ANY_ID, PCI_ANY_ID },
+ /* Thunderbolt ~ 2208 */
+ { MPI2_MFGPAGE_VENDORID_LSI, MPI2_MFGPAGE_DEVID_SAS2208_1,
+ PCI_ANY_ID, PCI_ANY_ID },
+ { MPI2_MFGPAGE_VENDORID_LSI, MPI2_MFGPAGE_DEVID_SAS2208_2,
+ PCI_ANY_ID, PCI_ANY_ID },
+ { MPI2_MFGPAGE_VENDORID_LSI, MPI2_MFGPAGE_DEVID_SAS2208_3,
+ PCI_ANY_ID, PCI_ANY_ID },
+ { MPI2_MFGPAGE_VENDORID_LSI, MPI2_MFGPAGE_DEVID_SAS2208_4,
+ PCI_ANY_ID, PCI_ANY_ID },
+ { MPI2_MFGPAGE_VENDORID_LSI, MPI2_MFGPAGE_DEVID_SAS2208_5,
+ PCI_ANY_ID, PCI_ANY_ID },
+ { MPI2_MFGPAGE_VENDORID_LSI, MPI2_MFGPAGE_DEVID_SAS2208_6,
+ PCI_ANY_ID, PCI_ANY_ID },
+ { MPI2_MFGPAGE_VENDORID_LSI, MPI2_MFGPAGE_DEVID_SAS2208_7,
+ PCI_ANY_ID, PCI_ANY_ID },
+ { MPI2_MFGPAGE_VENDORID_LSI, MPI2_MFGPAGE_DEVID_SAS2208_8,
+ PCI_ANY_ID, PCI_ANY_ID },
{0} /* Terminating entry */
};
MODULE_DEVICE_TABLE(pci, scsih_pci_table);
@@ -317,6 +336,47 @@ _scsih_is_boot_device(u64 sas_address, u64 device_name,
}
/**
+ * _scsih_get_sas_address - set the sas_address for given device handle
+ * @handle: device handle
+ * @sas_address: sas address
+ *
+ * Returns 0 success, non-zero when failure
+ */
+static int
+_scsih_get_sas_address(struct MPT2SAS_ADAPTER *ioc, u16 handle,
+ u64 *sas_address)
+{
+ Mpi2SasDevicePage0_t sas_device_pg0;
+ Mpi2ConfigReply_t mpi_reply;
+ u32 ioc_status;
+
+ if (handle <= ioc->sas_hba.num_phys) {
+ *sas_address = ioc->sas_hba.sas_address;
+ return 0;
+ } else
+ *sas_address = 0;
+
+ if ((mpt2sas_config_get_sas_device_pg0(ioc, &mpi_reply, &sas_device_pg0,
+ MPI2_SAS_DEVICE_PGAD_FORM_HANDLE, handle))) {
+ printk(MPT2SAS_ERR_FMT "failure at %s:%d/%s()!\n",
+ ioc->name, __FILE__, __LINE__, __func__);
+ return -ENXIO;
+ }
+
+ ioc_status = le16_to_cpu(mpi_reply.IOCStatus) &
+ MPI2_IOCSTATUS_MASK;
+ if (ioc_status != MPI2_IOCSTATUS_SUCCESS) {
+ printk(MPT2SAS_ERR_FMT "handle(0x%04x), ioc_status(0x%04x)"
+ "\nfailure at %s:%d/%s()!\n", ioc->name, handle, ioc_status,
+ __FILE__, __LINE__, __func__);
+ return -EIO;
+ }
+
+ *sas_address = le64_to_cpu(sas_device_pg0.SASAddress);
+ return 0;
+}
+
+/**
* _scsih_determine_boot_device - determine boot device.
* @ioc: per adapter object
* @device: either sas_device or raid_device object
@@ -510,8 +570,6 @@ _scsih_sas_device_add(struct MPT2SAS_ADAPTER *ioc,
struct _sas_device *sas_device)
{
unsigned long flags;
- u16 handle, parent_handle;
- u64 sas_address;
dewtprintk(ioc, printk(MPT2SAS_DEBUG_FMT "%s: handle"
"(0x%04x), sas_addr(0x%016llx)\n", ioc->name, __func__,
@@ -521,10 +579,8 @@ _scsih_sas_device_add(struct MPT2SAS_ADAPTER *ioc,
list_add_tail(&sas_device->list, &ioc->sas_device_list);
spin_unlock_irqrestore(&ioc->sas_device_lock, flags);
- handle = sas_device->handle;
- parent_handle = sas_device->parent_handle;
- sas_address = sas_device->sas_address;
- if (!mpt2sas_transport_port_add(ioc, handle, parent_handle))
+ if (!mpt2sas_transport_port_add(ioc, sas_device->handle,
+ sas_device->sas_address_parent))
_scsih_sas_device_remove(ioc, sas_device);
}
@@ -553,31 +609,6 @@ _scsih_sas_device_init_add(struct MPT2SAS_ADAPTER *ioc,
}
/**
- * mpt2sas_scsih_expander_find_by_handle - expander device search
- * @ioc: per adapter object
- * @handle: expander handle (assigned by firmware)
- * Context: Calling function should acquire ioc->sas_device_lock
- *
- * This searches for expander device based on handle, then returns the
- * sas_node object.
- */
-struct _sas_node *
-mpt2sas_scsih_expander_find_by_handle(struct MPT2SAS_ADAPTER *ioc, u16 handle)
-{
- struct _sas_node *sas_expander, *r;
-
- r = NULL;
- list_for_each_entry(sas_expander, &ioc->sas_expander_list, list) {
- if (sas_expander->handle != handle)
- continue;
- r = sas_expander;
- goto out;
- }
- out:
- return r;
-}
-
-/**
* _scsih_raid_device_find_by_id - raid device search
* @ioc: per adapter object
* @id: sas device target id
@@ -699,6 +730,31 @@ _scsih_raid_device_remove(struct MPT2SAS_ADAPTER *ioc,
}
/**
+ * mpt2sas_scsih_expander_find_by_handle - expander device search
+ * @ioc: per adapter object
+ * @handle: expander handle (assigned by firmware)
+ * Context: Calling function should acquire ioc->sas_device_lock
+ *
+ * This searches for expander device based on handle, then returns the
+ * sas_node object.
+ */
+struct _sas_node *
+mpt2sas_scsih_expander_find_by_handle(struct MPT2SAS_ADAPTER *ioc, u16 handle)
+{
+ struct _sas_node *sas_expander, *r;
+
+ r = NULL;
+ list_for_each_entry(sas_expander, &ioc->sas_expander_list, list) {
+ if (sas_expander->handle != handle)
+ continue;
+ r = sas_expander;
+ goto out;
+ }
+ out:
+ return r;
+}
+
+/**
* mpt2sas_scsih_expander_find_by_sas_address - expander device search
* @ioc: per adapter object
* @sas_address: sas address
@@ -1043,17 +1099,46 @@ _scsih_build_scatter_gather(struct MPT2SAS_ADAPTER *ioc,
* _scsih_change_queue_depth - setting device queue depth
* @sdev: scsi device struct
* @qdepth: requested queue depth
+ * @reason: calling context
*
* Returns queue depth.
*/
static int
-_scsih_change_queue_depth(struct scsi_device *sdev, int qdepth)
+_scsih_change_queue_depth(struct scsi_device *sdev, int qdepth, int reason)
{
struct Scsi_Host *shost = sdev->host;
int max_depth;
int tag_type;
+ struct MPT2SAS_ADAPTER *ioc = shost_priv(shost);
+ struct MPT2SAS_DEVICE *sas_device_priv_data;
+ struct MPT2SAS_TARGET *sas_target_priv_data;
+ struct _sas_device *sas_device;
+ unsigned long flags;
+
+ if (reason != SCSI_QDEPTH_DEFAULT)
+ return -EOPNOTSUPP;
max_depth = shost->can_queue;
+
+ /* limit max device queue for SATA to 32 */
+ sas_device_priv_data = sdev->hostdata;
+ if (!sas_device_priv_data)
+ goto not_sata;
+ sas_target_priv_data = sas_device_priv_data->sas_target;
+ if (!sas_target_priv_data)
+ goto not_sata;
+ if ((sas_target_priv_data->flags & MPT_TARGET_FLAGS_VOLUME))
+ goto not_sata;
+ spin_lock_irqsave(&ioc->sas_device_lock, flags);
+ sas_device = mpt2sas_scsih_sas_device_find_by_sas_address(ioc,
+ sas_device_priv_data->sas_target->sas_address);
+ spin_unlock_irqrestore(&ioc->sas_device_lock, flags);
+ if (sas_device && sas_device->device_info &
+ MPI2_SAS_DEVICE_INFO_SATA_DEVICE)
+ max_depth = MPT2SAS_SATA_QUEUE_DEPTH;
+
+ not_sata:
+
if (!sdev->tagged_supported)
max_depth = 1;
if (qdepth > max_depth)
@@ -1488,7 +1573,7 @@ _scsih_slave_configure(struct scsi_device *sdev)
r_level, raid_device->handle,
(unsigned long long)raid_device->wwid,
raid_device->num_pds, ds);
- _scsih_change_queue_depth(sdev, qdepth);
+ _scsih_change_queue_depth(sdev, qdepth, SCSI_QDEPTH_DEFAULT);
return 0;
}
@@ -1534,7 +1619,7 @@ _scsih_slave_configure(struct scsi_device *sdev)
_scsih_display_sata_capabilities(ioc, sas_device, sdev);
}
- _scsih_change_queue_depth(sdev, qdepth);
+ _scsih_change_queue_depth(sdev, qdepth, SCSI_QDEPTH_DEFAULT);
if (ssp_target)
sas_read_port_mode_page(sdev);
@@ -1874,6 +1959,8 @@ _scsih_abort(struct scsi_cmnd *scmd)
goto out;
}
+ mpt2sas_halt_firmware(ioc);
+
mutex_lock(&ioc->tm_cmds.mutex);
handle = sas_device_priv_data->sas_target->handle;
mpt2sas_scsih_issue_tm(ioc, handle, sas_device_priv_data->lun,
@@ -2297,7 +2384,6 @@ _scsih_block_io_to_children_attached_directly(struct MPT2SAS_ADAPTER *ioc,
u16 handle;
u16 reason_code;
u8 phy_number;
- u8 link_rate;
for (i = 0; i < event_data->NumEntries; i++) {
handle = le16_to_cpu(event_data->PHY[i].AttachedDevHandle);
@@ -2308,11 +2394,6 @@ _scsih_block_io_to_children_attached_directly(struct MPT2SAS_ADAPTER *ioc,
MPI2_EVENT_SAS_TOPO_RC_MASK;
if (reason_code == MPI2_EVENT_SAS_TOPO_RC_DELAY_NOT_RESPONDING)
_scsih_block_io_device(ioc, handle);
- if (reason_code == MPI2_EVENT_SAS_TOPO_RC_PHY_CHANGED) {
- link_rate = event_data->PHY[i].LinkRate >> 4;
- if (link_rate >= MPI2_SAS_NEG_LINK_RATE_1_5)
- _scsih_ublock_io_device(ioc, handle);
- }
}
}
@@ -2349,16 +2430,10 @@ _scsih_tm_tr_send(struct MPT2SAS_ADAPTER *ioc, u16 handle)
spin_lock_irqsave(&ioc->sas_device_lock, flags);
sas_device = _scsih_sas_device_find_by_handle(ioc, handle);
- if (!sas_device) {
- spin_unlock_irqrestore(&ioc->sas_device_lock, flags);
- printk(MPT2SAS_ERR_FMT "%s: failed finding sas_device\n",
- ioc->name, __func__);
- return;
- }
spin_unlock_irqrestore(&ioc->sas_device_lock, flags);
/* skip is hidden raid component */
- if (sas_device->hidden_raid_component)
+ if (sas_device && sas_device->hidden_raid_component)
return;
smid = mpt2sas_base_get_smid_hpr(ioc, ioc->tm_tr_cb_idx);
@@ -2371,18 +2446,31 @@ _scsih_tm_tr_send(struct MPT2SAS_ADAPTER *ioc, u16 handle)
delayed_tr->state = MPT2SAS_REQ_SAS_CNTRL;
list_add_tail(&delayed_tr->list,
&ioc->delayed_tr_list);
- if (sas_device->starget)
+ if (sas_device && sas_device->starget) {
dewtprintk(ioc, starget_printk(KERN_INFO,
sas_device->starget, "DELAYED:tr:handle(0x%04x), "
- "(open)\n", sas_device->handle));
+ "(open)\n", handle));
+ } else {
+ dewtprintk(ioc, printk(MPT2SAS_INFO_FMT
+ "DELAYED:tr:handle(0x%04x), (open)\n",
+ ioc->name, handle));
+ }
return;
}
- if (sas_device->starget && sas_device->starget->hostdata) {
- sas_target_priv_data = sas_device->starget->hostdata;
- sas_target_priv_data->tm_busy = 1;
- dewtprintk(ioc, starget_printk(KERN_INFO, sas_device->starget,
- "tr:handle(0x%04x), (open)\n", sas_device->handle));
+ if (sas_device) {
+ sas_device->state |= MPTSAS_STATE_TR_SEND;
+ sas_device->state |= MPT2SAS_REQ_SAS_CNTRL;
+ if (sas_device->starget && sas_device->starget->hostdata) {
+ sas_target_priv_data = sas_device->starget->hostdata;
+ sas_target_priv_data->tm_busy = 1;
+ dewtprintk(ioc, starget_printk(KERN_INFO,
+ sas_device->starget, "tr:handle(0x%04x), (open)\n",
+ handle));
+ }
+ } else {
+ dewtprintk(ioc, printk(MPT2SAS_INFO_FMT
+ "tr:handle(0x%04x), (open)\n", ioc->name, handle));
}
mpi_request = mpt2sas_base_get_msg_frame(ioc, smid);
@@ -2390,8 +2478,6 @@ _scsih_tm_tr_send(struct MPT2SAS_ADAPTER *ioc, u16 handle)
mpi_request->Function = MPI2_FUNCTION_SCSI_TASK_MGMT;
mpi_request->DevHandle = cpu_to_le16(handle);
mpi_request->TaskType = MPI2_SCSITASKMGMT_TASKTYPE_TARGET_RESET;
- sas_device->state |= MPTSAS_STATE_TR_SEND;
- sas_device->state |= MPT2SAS_REQ_SAS_CNTRL;
mpt2sas_base_put_smid_hi_priority(ioc, smid);
}
@@ -2426,21 +2512,25 @@ _scsih_sas_control_complete(struct MPT2SAS_ADAPTER *ioc, u16 smid,
spin_lock_irqsave(&ioc->sas_device_lock, flags);
sas_device = _scsih_sas_device_find_by_handle(ioc, handle);
- if (!sas_device) {
- spin_unlock_irqrestore(&ioc->sas_device_lock, flags);
- printk(MPT2SAS_ERR_FMT "%s: failed finding sas_device\n",
- ioc->name, __func__);
- return 1;
- }
- sas_device->state |= MPTSAS_STATE_CNTRL_COMPLETE;
spin_unlock_irqrestore(&ioc->sas_device_lock, flags);
- if (sas_device->starget)
- dewtprintk(ioc, starget_printk(KERN_INFO, sas_device->starget,
+ if (sas_device) {
+ sas_device->state |= MPTSAS_STATE_CNTRL_COMPLETE;
+ if (sas_device->starget)
+ dewtprintk(ioc, starget_printk(KERN_INFO,
+ sas_device->starget,
+ "sc_complete:handle(0x%04x), "
+ "ioc_status(0x%04x), loginfo(0x%08x)\n",
+ handle, le16_to_cpu(mpi_reply->IOCStatus),
+ le32_to_cpu(mpi_reply->IOCLogInfo)));
+ } else {
+ dewtprintk(ioc, printk(MPT2SAS_INFO_FMT
"sc_complete:handle(0x%04x), "
"ioc_status(0x%04x), loginfo(0x%08x)\n",
- handle, le16_to_cpu(mpi_reply->IOCStatus),
+ ioc->name, handle, le16_to_cpu(mpi_reply->IOCStatus),
le32_to_cpu(mpi_reply->IOCLogInfo)));
+ }
+
return 1;
}
@@ -2478,28 +2568,33 @@ _scsih_tm_tr_complete(struct MPT2SAS_ADAPTER *ioc, u16 smid, u8 msix_index,
handle = le16_to_cpu(mpi_reply->DevHandle);
spin_lock_irqsave(&ioc->sas_device_lock, flags);
sas_device = _scsih_sas_device_find_by_handle(ioc, handle);
- if (!sas_device) {
- spin_unlock_irqrestore(&ioc->sas_device_lock, flags);
- printk(MPT2SAS_ERR_FMT "%s: failed finding sas_device\n",
- ioc->name, __func__);
- return 1;
- }
- sas_device->state |= MPTSAS_STATE_TR_COMPLETE;
spin_unlock_irqrestore(&ioc->sas_device_lock, flags);
- if (sas_device->starget)
- dewtprintk(ioc, starget_printk(KERN_INFO, sas_device->starget,
- "tr_complete:handle(0x%04x), (%s) ioc_status(0x%04x), "
- "loginfo(0x%08x), completed(%d)\n",
- sas_device->handle, (sas_device->state &
- MPT2SAS_REQ_SAS_CNTRL) ? "open" : "active",
- le16_to_cpu(mpi_reply->IOCStatus),
+ if (sas_device) {
+ sas_device->state |= MPTSAS_STATE_TR_COMPLETE;
+ if (sas_device->starget) {
+ dewtprintk(ioc, starget_printk(KERN_INFO,
+ sas_device->starget, "tr_complete:handle(0x%04x), "
+ "(%s) ioc_status(0x%04x), loginfo(0x%08x), "
+ "completed(%d)\n", sas_device->handle,
+ (sas_device->state & MPT2SAS_REQ_SAS_CNTRL) ?
+ "open" : "active",
+ le16_to_cpu(mpi_reply->IOCStatus),
+ le32_to_cpu(mpi_reply->IOCLogInfo),
+ le32_to_cpu(mpi_reply->TerminationCount)));
+ if (sas_device->starget->hostdata) {
+ sas_target_priv_data =
+ sas_device->starget->hostdata;
+ sas_target_priv_data->tm_busy = 0;
+ }
+ }
+ } else {
+ dewtprintk(ioc, printk(MPT2SAS_INFO_FMT
+ "tr_complete:handle(0x%04x), (open) ioc_status(0x%04x), "
+ "loginfo(0x%08x), completed(%d)\n", ioc->name,
+ handle, le16_to_cpu(mpi_reply->IOCStatus),
le32_to_cpu(mpi_reply->IOCLogInfo),
le32_to_cpu(mpi_reply->TerminationCount)));
-
- if (sas_device->starget && sas_device->starget->hostdata) {
- sas_target_priv_data = sas_device->starget->hostdata;
- sas_target_priv_data->tm_busy = 0;
}
if (!list_empty(&ioc->delayed_tr_list)) {
@@ -2514,8 +2609,7 @@ _scsih_tm_tr_complete(struct MPT2SAS_ADAPTER *ioc, u16 smid, u8 msix_index,
} else
rc = 1;
-
- if (!(sas_device->state & MPT2SAS_REQ_SAS_CNTRL))
+ if (sas_device && !(sas_device->state & MPT2SAS_REQ_SAS_CNTRL))
return rc;
if (ioc->shost_recovery) {
@@ -2531,12 +2625,14 @@ _scsih_tm_tr_complete(struct MPT2SAS_ADAPTER *ioc, u16 smid, u8 msix_index,
return rc;
}
+ if (sas_device)
+ sas_device->state |= MPTSAS_STATE_CNTRL_SEND;
+
mpi_request = mpt2sas_base_get_msg_frame(ioc, smid_sas_ctrl);
memset(mpi_request, 0, sizeof(Mpi2SasIoUnitControlRequest_t));
mpi_request->Function = MPI2_FUNCTION_SAS_IO_UNIT_CONTROL;
mpi_request->Operation = MPI2_SAS_OP_REMOVE_DEVICE;
mpi_request->DevHandle = mpi_reply->DevHandle;
- sas_device->state |= MPTSAS_STATE_CNTRL_SEND;
mpt2sas_base_put_smid_default(ioc, smid_sas_ctrl);
return rc;
}
@@ -2678,8 +2774,6 @@ _scsih_setup_eedp(struct scsi_cmnd *scmd, Mpi2SCSIIORequest_t *mpi_request)
else
return;
- mpi_request->EEDPBlockSize = scmd->device->sector_size;
-
switch (prot_type) {
case SCSI_PROT_DIF_TYPE1:
@@ -2687,8 +2781,7 @@ _scsih_setup_eedp(struct scsi_cmnd *scmd, Mpi2SCSIIORequest_t *mpi_request)
* enable ref/guard checking
* auto increment ref tag
*/
- mpi_request->EEDPFlags = eedp_flags |
- MPI2_SCSIIO_EEDPFLAGS_INC_PRI_REFTAG |
+ eedp_flags |= MPI2_SCSIIO_EEDPFLAGS_INC_PRI_REFTAG |
MPI2_SCSIIO_EEDPFLAGS_CHECK_REFTAG |
MPI2_SCSIIO_EEDPFLAGS_CHECK_GUARD;
mpi_request->CDB.EEDP32.PrimaryReferenceTag =
@@ -2701,11 +2794,11 @@ _scsih_setup_eedp(struct scsi_cmnd *scmd, Mpi2SCSIIORequest_t *mpi_request)
/*
* enable guard checking
*/
- mpi_request->EEDPFlags = eedp_flags |
- MPI2_SCSIIO_EEDPFLAGS_CHECK_GUARD;
-
+ eedp_flags |= MPI2_SCSIIO_EEDPFLAGS_CHECK_GUARD;
break;
}
+ mpi_request->EEDPBlockSize = cpu_to_le32(scmd->device->sector_size);
+ mpi_request->EEDPFlags = cpu_to_le16(eedp_flags);
}
/**
@@ -2788,7 +2881,7 @@ _scsih_qcmd(struct scsi_cmnd *scmd, void (*done)(struct scsi_cmnd *))
}
/* see if we are busy with task managment stuff */
- if (sas_target_priv_data->tm_busy)
+ if (sas_device_priv_data->block || sas_target_priv_data->tm_busy)
return SCSI_MLQUEUE_DEVICE_BUSY;
else if (ioc->shost_recovery || ioc->ioc_link_reset_in_progress)
return SCSI_MLQUEUE_HOST_BUSY;
@@ -2842,7 +2935,7 @@ _scsih_qcmd(struct scsi_cmnd *scmd, void (*done)(struct scsi_cmnd *))
mpi_request->MsgFlags = MPI2_SCSIIO_MSGFLAGS_SYSTEM_SENSE_ADDR;
mpi_request->SenseBufferLength = SCSI_SENSE_BUFFERSIZE;
mpi_request->SenseBufferLowAddress =
- (u32)mpt2sas_base_get_sense_buffer_dma(ioc, smid);
+ mpt2sas_base_get_sense_buffer_dma(ioc, smid);
mpi_request->SGLOffset0 = offsetof(Mpi2SCSIIORequest_t, SGL) / 4;
mpi_request->SGLFlags = cpu_to_le16(MPI2_SCSIIO_SGLFLAGS_TYPE_MPI +
MPI2_SCSIIO_SGLFLAGS_SYSTEM_ADDR);
@@ -3059,7 +3152,7 @@ _scsih_scsi_ioc_info(struct MPT2SAS_ADAPTER *ioc, struct scsi_cmnd *scmd,
if (scsi_state & MPI2_SCSI_STATE_RESPONSE_INFO_VALID) {
response_info = le32_to_cpu(mpi_reply->ResponseInfo);
response_bytes = (u8 *)&response_info;
- _scsih_response_code(ioc, response_bytes[3]);
+ _scsih_response_code(ioc, response_bytes[0]);
}
}
#endif
@@ -3177,7 +3270,7 @@ _scsih_io_done(struct MPT2SAS_ADAPTER *ioc, u16 smid, u8 msix_index, u32 reply)
u8 scsi_status;
u32 log_info;
struct MPT2SAS_DEVICE *sas_device_priv_data;
- u32 response_code;
+ u32 response_code = 0;
mpi_reply = mpt2sas_base_get_reply_virt_addr(ioc, reply);
scmd = _scsih_scsi_lookup_get(ioc, smid);
@@ -3199,16 +3292,16 @@ _scsih_io_done(struct MPT2SAS_ADAPTER *ioc, u16 smid, u8 msix_index, u32 reply)
}
/* turning off TLR */
+ scsi_state = mpi_reply->SCSIState;
+ if (scsi_state & MPI2_SCSI_STATE_RESPONSE_INFO_VALID)
+ response_code =
+ le32_to_cpu(mpi_reply->ResponseInfo) & 0xFF;
if (!sas_device_priv_data->tlr_snoop_check) {
sas_device_priv_data->tlr_snoop_check++;
- if (sas_device_priv_data->flags & MPT_DEVICE_TLR_ON) {
- response_code = (le32_to_cpu(mpi_reply->ResponseInfo)
- >> 24);
- if (response_code ==
- MPI2_SCSITASKMGMT_RSP_INVALID_FRAME)
- sas_device_priv_data->flags &=
- ~MPT_DEVICE_TLR_ON;
- }
+ if ((sas_device_priv_data->flags & MPT_DEVICE_TLR_ON) &&
+ response_code == MPI2_SCSITASKMGMT_RSP_INVALID_FRAME)
+ sas_device_priv_data->flags &=
+ ~MPT_DEVICE_TLR_ON;
}
xfer_cnt = le32_to_cpu(mpi_reply->TransferCount);
@@ -3219,7 +3312,6 @@ _scsih_io_done(struct MPT2SAS_ADAPTER *ioc, u16 smid, u8 msix_index, u32 reply)
else
log_info = 0;
ioc_status &= MPI2_IOCSTATUS_MASK;
- scsi_state = mpi_reply->SCSIState;
scsi_status = mpi_reply->SCSIStatus;
if (ioc_status == MPI2_IOCSTATUS_SCSI_DATA_UNDERRUN && xfer_cnt == 0 &&
@@ -3255,10 +3347,9 @@ _scsih_io_done(struct MPT2SAS_ADAPTER *ioc, u16 smid, u8 msix_index, u32 reply)
case MPI2_IOCSTATUS_SCSI_IOC_TERMINATED:
if (sas_device_priv_data->block) {
- scmd->result = (DID_BUS_BUSY << 16);
- break;
+ scmd->result = DID_TRANSPORT_DISRUPTED << 16;
+ goto out;
}
-
case MPI2_IOCSTATUS_SCSI_TASK_TERMINATED:
case MPI2_IOCSTATUS_SCSI_EXT_TERMINATED:
scmd->result = DID_RESET << 16;
@@ -3304,8 +3395,10 @@ _scsih_io_done(struct MPT2SAS_ADAPTER *ioc, u16 smid, u8 msix_index, u32 reply)
case MPI2_IOCSTATUS_SCSI_RECOVERED_ERROR:
case MPI2_IOCSTATUS_SUCCESS:
scmd->result = (DID_OK << 16) | scsi_status;
- if (scsi_state & (MPI2_SCSI_STATE_AUTOSENSE_FAILED |
- MPI2_SCSI_STATE_NO_SCSI_STATUS))
+ if (response_code ==
+ MPI2_SCSITASKMGMT_RSP_INVALID_FRAME ||
+ (scsi_state & (MPI2_SCSI_STATE_AUTOSENSE_FAILED |
+ MPI2_SCSI_STATE_NO_SCSI_STATUS)))
scmd->result = DID_SOFT_ERROR << 16;
else if (scsi_state & MPI2_SCSI_STATE_TERMINATED)
scmd->result = DID_RESET << 16;
@@ -3344,7 +3437,6 @@ _scsih_io_done(struct MPT2SAS_ADAPTER *ioc, u16 smid, u8 msix_index, u32 reply)
/**
* _scsih_sas_host_refresh - refreshing sas host object contents
* @ioc: per adapter object
- * @update: update link information
* Context: user
*
* During port enable, fw will send topology events for every device. Its
@@ -3354,13 +3446,14 @@ _scsih_io_done(struct MPT2SAS_ADAPTER *ioc, u16 smid, u8 msix_index, u32 reply)
* Return nothing.
*/
static void
-_scsih_sas_host_refresh(struct MPT2SAS_ADAPTER *ioc, u8 update)
+_scsih_sas_host_refresh(struct MPT2SAS_ADAPTER *ioc)
{
u16 sz;
u16 ioc_status;
int i;
Mpi2ConfigReply_t mpi_reply;
Mpi2SasIOUnitPage0_t *sas_iounit_pg0 = NULL;
+ u16 attached_handle;
dtmprintk(ioc, printk(MPT2SAS_INFO_FMT
"updating handles for sas_host(0x%016llx)\n",
@@ -3374,27 +3467,24 @@ _scsih_sas_host_refresh(struct MPT2SAS_ADAPTER *ioc, u8 update)
ioc->name, __FILE__, __LINE__, __func__);
return;
}
- if (!(mpt2sas_config_get_sas_iounit_pg0(ioc, &mpi_reply,
- sas_iounit_pg0, sz))) {
- ioc_status = le16_to_cpu(mpi_reply.IOCStatus) &
- MPI2_IOCSTATUS_MASK;
- if (ioc_status != MPI2_IOCSTATUS_SUCCESS)
- goto out;
- for (i = 0; i < ioc->sas_hba.num_phys ; i++) {
- ioc->sas_hba.phy[i].handle =
- le16_to_cpu(sas_iounit_pg0->PhyData[i].
- ControllerDevHandle);
- if (update)
- mpt2sas_transport_update_links(
- ioc,
- ioc->sas_hba.phy[i].handle,
- le16_to_cpu(sas_iounit_pg0->PhyData[i].
- AttachedDevHandle), i,
- sas_iounit_pg0->PhyData[i].
- NegotiatedLinkRate >> 4);
- }
- }
+ if ((mpt2sas_config_get_sas_iounit_pg0(ioc, &mpi_reply,
+ sas_iounit_pg0, sz)) != 0)
+ goto out;
+ ioc_status = le16_to_cpu(mpi_reply.IOCStatus) & MPI2_IOCSTATUS_MASK;
+ if (ioc_status != MPI2_IOCSTATUS_SUCCESS)
+ goto out;
+ for (i = 0; i < ioc->sas_hba.num_phys ; i++) {
+ if (i == 0)
+ ioc->sas_hba.handle = le16_to_cpu(sas_iounit_pg0->
+ PhyData[0].ControllerDevHandle);
+ ioc->sas_hba.phy[i].handle = ioc->sas_hba.handle;
+ attached_handle = le16_to_cpu(sas_iounit_pg0->PhyData[i].
+ AttachedDevHandle);
+ mpt2sas_transport_update_links(ioc, ioc->sas_hba.sas_address,
+ attached_handle, i, sas_iounit_pg0->PhyData[i].
+ NegotiatedLinkRate >> 4);
+ }
out:
kfree(sas_iounit_pg0);
}
@@ -3507,19 +3597,21 @@ _scsih_sas_host_add(struct MPT2SAS_ADAPTER *ioc)
ioc->name, __FILE__, __LINE__, __func__);
goto out;
}
- ioc->sas_hba.phy[i].handle =
- le16_to_cpu(sas_iounit_pg0->PhyData[i].ControllerDevHandle);
+
+ if (i == 0)
+ ioc->sas_hba.handle = le16_to_cpu(sas_iounit_pg0->
+ PhyData[0].ControllerDevHandle);
+ ioc->sas_hba.phy[i].handle = ioc->sas_hba.handle;
ioc->sas_hba.phy[i].phy_id = i;
mpt2sas_transport_add_host_phy(ioc, &ioc->sas_hba.phy[i],
phy_pg0, ioc->sas_hba.parent_dev);
}
if ((mpt2sas_config_get_sas_device_pg0(ioc, &mpi_reply, &sas_device_pg0,
- MPI2_SAS_DEVICE_PGAD_FORM_HANDLE, ioc->sas_hba.phy[0].handle))) {
+ MPI2_SAS_DEVICE_PGAD_FORM_HANDLE, ioc->sas_hba.handle))) {
printk(MPT2SAS_ERR_FMT "failure at %s:%d/%s()!\n",
ioc->name, __FILE__, __LINE__, __func__);
goto out;
}
- ioc->sas_hba.handle = le16_to_cpu(sas_device_pg0.DevHandle);
ioc->sas_hba.enclosure_handle =
le16_to_cpu(sas_device_pg0.EnclosureHandle);
ioc->sas_hba.sas_address = le64_to_cpu(sas_device_pg0.SASAddress);
@@ -3562,7 +3654,7 @@ _scsih_expander_add(struct MPT2SAS_ADAPTER *ioc, u16 handle)
Mpi2SasEnclosurePage0_t enclosure_pg0;
u32 ioc_status;
u16 parent_handle;
- __le64 sas_address;
+ __le64 sas_address, sas_address_parent = 0;
int i;
unsigned long flags;
struct _sas_port *mpt2sas_port = NULL;
@@ -3591,10 +3683,16 @@ _scsih_expander_add(struct MPT2SAS_ADAPTER *ioc, u16 handle)
/* handle out of order topology events */
parent_handle = le16_to_cpu(expander_pg0.ParentDevHandle);
- if (parent_handle >= ioc->sas_hba.num_phys) {
+ if (_scsih_get_sas_address(ioc, parent_handle, &sas_address_parent)
+ != 0) {
+ printk(MPT2SAS_ERR_FMT "failure at %s:%d/%s()!\n",
+ ioc->name, __FILE__, __LINE__, __func__);
+ return -1;
+ }
+ if (sas_address_parent != ioc->sas_hba.sas_address) {
spin_lock_irqsave(&ioc->sas_node_lock, flags);
- sas_expander = mpt2sas_scsih_expander_find_by_handle(ioc,
- parent_handle);
+ sas_expander = mpt2sas_scsih_expander_find_by_sas_address(ioc,
+ sas_address_parent);
spin_unlock_irqrestore(&ioc->sas_node_lock, flags);
if (!sas_expander) {
rc = _scsih_expander_add(ioc, parent_handle);
@@ -3622,14 +3720,12 @@ _scsih_expander_add(struct MPT2SAS_ADAPTER *ioc, u16 handle)
sas_expander->handle = handle;
sas_expander->num_phys = expander_pg0.NumPhys;
- sas_expander->parent_handle = parent_handle;
- sas_expander->enclosure_handle =
- le16_to_cpu(expander_pg0.EnclosureHandle);
+ sas_expander->sas_address_parent = sas_address_parent;
sas_expander->sas_address = sas_address;
printk(MPT2SAS_INFO_FMT "expander_add: handle(0x%04x),"
" parent(0x%04x), sas_addr(0x%016llx), phys(%d)\n", ioc->name,
- handle, sas_expander->parent_handle, (unsigned long long)
+ handle, parent_handle, (unsigned long long)
sas_expander->sas_address, sas_expander->num_phys);
if (!sas_expander->num_phys)
@@ -3645,7 +3741,7 @@ _scsih_expander_add(struct MPT2SAS_ADAPTER *ioc, u16 handle)
INIT_LIST_HEAD(&sas_expander->sas_port_list);
mpt2sas_port = mpt2sas_transport_port_add(ioc, handle,
- sas_expander->parent_handle);
+ sas_address_parent);
if (!mpt2sas_port) {
printk(MPT2SAS_ERR_FMT "failure at %s:%d/%s()!\n",
ioc->name, __FILE__, __LINE__, __func__);
@@ -3691,20 +3787,54 @@ _scsih_expander_add(struct MPT2SAS_ADAPTER *ioc, u16 handle)
if (mpt2sas_port)
mpt2sas_transport_port_remove(ioc, sas_expander->sas_address,
- sas_expander->parent_handle);
+ sas_address_parent);
kfree(sas_expander);
return rc;
}
/**
+ * _scsih_done - scsih callback handler.
+ * @ioc: per adapter object
+ * @smid: system request message index
+ * @msix_index: MSIX table index supplied by the OS
+ * @reply: reply message frame(lower 32bit addr)
+ *
+ * Callback handler when sending internal generated message frames.
+ * The callback index passed is `ioc->scsih_cb_idx`
+ *
+ * Return 1 meaning mf should be freed from _base_interrupt
+ * 0 means the mf is freed from this function.
+ */
+static u8
+_scsih_done(struct MPT2SAS_ADAPTER *ioc, u16 smid, u8 msix_index, u32 reply)
+{
+ MPI2DefaultReply_t *mpi_reply;
+
+ mpi_reply = mpt2sas_base_get_reply_virt_addr(ioc, reply);
+ if (ioc->scsih_cmds.status == MPT2_CMD_NOT_USED)
+ return 1;
+ if (ioc->scsih_cmds.smid != smid)
+ return 1;
+ ioc->scsih_cmds.status |= MPT2_CMD_COMPLETE;
+ if (mpi_reply) {
+ memcpy(ioc->scsih_cmds.reply, mpi_reply,
+ mpi_reply->MsgLength*4);
+ ioc->scsih_cmds.status |= MPT2_CMD_REPLY_VALID;
+ }
+ ioc->scsih_cmds.status &= ~MPT2_CMD_PENDING;
+ complete(&ioc->scsih_cmds.done);
+ return 1;
+}
+
+/**
* _scsih_expander_remove - removing expander object
* @ioc: per adapter object
- * @handle: expander handle
+ * @sas_address: expander sas_address
*
* Return nothing.
*/
static void
-_scsih_expander_remove(struct MPT2SAS_ADAPTER *ioc, u16 handle)
+_scsih_expander_remove(struct MPT2SAS_ADAPTER *ioc, u64 sas_address)
{
struct _sas_node *sas_expander;
unsigned long flags;
@@ -3713,7 +3843,8 @@ _scsih_expander_remove(struct MPT2SAS_ADAPTER *ioc, u16 handle)
return;
spin_lock_irqsave(&ioc->sas_node_lock, flags);
- sas_expander = mpt2sas_scsih_expander_find_by_handle(ioc, handle);
+ sas_expander = mpt2sas_scsih_expander_find_by_sas_address(ioc,
+ sas_address);
spin_unlock_irqrestore(&ioc->sas_node_lock, flags);
_scsih_expander_node_remove(ioc, sas_expander);
}
@@ -3805,8 +3936,11 @@ _scsih_add_device(struct MPT2SAS_ADAPTER *ioc, u16 handle, u8 phy_num, u8 is_pd)
}
sas_device->handle = handle;
- sas_device->parent_handle =
- le16_to_cpu(sas_device_pg0.ParentDevHandle);
+ if (_scsih_get_sas_address(ioc, le16_to_cpu
+ (sas_device_pg0.ParentDevHandle),
+ &sas_device->sas_address_parent) != 0)
+ printk(MPT2SAS_ERR_FMT "failure at %s:%d/%s()!\n",
+ ioc->name, __FILE__, __LINE__, __func__);
sas_device->enclosure_handle =
le16_to_cpu(sas_device_pg0.EnclosureHandle);
sas_device->slot =
@@ -3836,43 +3970,39 @@ _scsih_add_device(struct MPT2SAS_ADAPTER *ioc, u16 handle, u8 phy_num, u8 is_pd)
/**
* _scsih_remove_device - removing sas device object
* @ioc: per adapter object
- * @handle: sas device handle
+ * @sas_device: the sas_device object
*
* Return nothing.
*/
static void
-_scsih_remove_device(struct MPT2SAS_ADAPTER *ioc, u16 handle)
+_scsih_remove_device(struct MPT2SAS_ADAPTER *ioc, struct _sas_device
+ *sas_device)
{
struct MPT2SAS_TARGET *sas_target_priv_data;
- struct _sas_device *sas_device;
- unsigned long flags;
Mpi2SasIoUnitControlReply_t mpi_reply;
Mpi2SasIoUnitControlRequest_t mpi_request;
- u16 device_handle;
+ u16 device_handle, handle;
- /* lookup sas_device */
- spin_lock_irqsave(&ioc->sas_device_lock, flags);
- sas_device = _scsih_sas_device_find_by_handle(ioc, handle);
- if (!sas_device) {
- spin_unlock_irqrestore(&ioc->sas_device_lock, flags);
+ if (!sas_device)
return;
- }
- dewtprintk(ioc, printk(MPT2SAS_INFO_FMT "%s: enter: handle"
- "(0x%04x)\n", ioc->name, __func__, handle));
+ handle = sas_device->handle;
+ dewtprintk(ioc, printk(MPT2SAS_INFO_FMT "%s: enter: handle(0x%04x),"
+ " sas_addr(0x%016llx)\n", ioc->name, __func__, handle,
+ (unsigned long long) sas_device->sas_address));
if (sas_device->starget && sas_device->starget->hostdata) {
sas_target_priv_data = sas_device->starget->hostdata;
sas_target_priv_data->deleted = 1;
}
- spin_unlock_irqrestore(&ioc->sas_device_lock, flags);
- if (ioc->remove_host)
+ if (ioc->remove_host || ioc->shost_recovery || !handle)
goto out;
if ((sas_device->state & MPTSAS_STATE_TR_COMPLETE)) {
dewtprintk(ioc, printk(MPT2SAS_INFO_FMT "\tskip "
- "target_reset handle(0x%04x)\n", ioc->name, handle));
+ "target_reset handle(0x%04x)\n", ioc->name,
+ handle));
goto skip_tr;
}
@@ -3925,10 +4055,10 @@ _scsih_remove_device(struct MPT2SAS_ADAPTER *ioc, u16 handle)
_scsih_ublock_io_device(ioc, handle);
mpt2sas_transport_port_remove(ioc, sas_device->sas_address,
- sas_device->parent_handle);
+ sas_device->sas_address_parent);
printk(MPT2SAS_INFO_FMT "removing handle(0x%04x), sas_addr"
- "(0x%016llx)\n", ioc->name, sas_device->handle,
+ "(0x%016llx)\n", ioc->name, handle,
(unsigned long long) sas_device->sas_address);
_scsih_sas_device_remove(ioc, sas_device);
@@ -3952,7 +4082,7 @@ _scsih_sas_topology_change_event_debug(struct MPT2SAS_ADAPTER *ioc,
u16 reason_code;
u8 phy_number;
char *status_str = NULL;
- char link_rate[25];
+ u8 link_rate, prev_link_rate;
switch (event_data->ExpStatus) {
case MPI2_EVENT_SAS_TOPO_ES_ADDED:
@@ -3962,6 +4092,7 @@ _scsih_sas_topology_change_event_debug(struct MPT2SAS_ADAPTER *ioc,
status_str = "remove";
break;
case MPI2_EVENT_SAS_TOPO_ES_RESPONDING:
+ case 0:
status_str = "responding";
break;
case MPI2_EVENT_SAS_TOPO_ES_DELAY_NOT_RESPONDING:
@@ -3987,30 +4118,30 @@ _scsih_sas_topology_change_event_debug(struct MPT2SAS_ADAPTER *ioc,
MPI2_EVENT_SAS_TOPO_RC_MASK;
switch (reason_code) {
case MPI2_EVENT_SAS_TOPO_RC_TARG_ADDED:
- snprintf(link_rate, 25, ": add, link(0x%02x)",
- (event_data->PHY[i].LinkRate >> 4));
- status_str = link_rate;
+ status_str = "target add";
break;
case MPI2_EVENT_SAS_TOPO_RC_TARG_NOT_RESPONDING:
- status_str = ": remove";
+ status_str = "target remove";
break;
case MPI2_EVENT_SAS_TOPO_RC_DELAY_NOT_RESPONDING:
- status_str = ": remove_delay";
+ status_str = "delay target remove";
break;
case MPI2_EVENT_SAS_TOPO_RC_PHY_CHANGED:
- snprintf(link_rate, 25, ": link(0x%02x)",
- (event_data->PHY[i].LinkRate >> 4));
- status_str = link_rate;
+ status_str = "link rate change";
break;
case MPI2_EVENT_SAS_TOPO_RC_NO_CHANGE:
- status_str = ": responding";
+ status_str = "target responding";
break;
default:
- status_str = ": unknown";
+ status_str = "unknown";
break;
}
- printk(KERN_DEBUG "\tphy(%02d), attached_handle(0x%04x)%s\n",
- phy_number, handle, status_str);
+ link_rate = event_data->PHY[i].LinkRate >> 4;
+ prev_link_rate = event_data->PHY[i].LinkRate & 0xF;
+ printk(KERN_DEBUG "\tphy(%02d), attached_handle(0x%04x): %s:"
+ " link rate: new(0x%02x), old(0x%02x)\n", phy_number,
+ handle, status_str, link_rate, prev_link_rate);
+
}
}
#endif
@@ -4031,8 +4162,10 @@ _scsih_sas_topology_change_event(struct MPT2SAS_ADAPTER *ioc,
u16 reason_code;
u8 phy_number;
struct _sas_node *sas_expander;
+ struct _sas_device *sas_device;
+ u64 sas_address;
unsigned long flags;
- u8 link_rate_;
+ u8 link_rate, prev_link_rate;
Mpi2EventDataSasTopologyChangeList_t *event_data = fw_event->event_data;
#ifdef CONFIG_SCSI_MPT2SAS_LOGGING
@@ -4040,10 +4173,13 @@ _scsih_sas_topology_change_event(struct MPT2SAS_ADAPTER *ioc,
_scsih_sas_topology_change_event_debug(ioc, event_data);
#endif
+ if (ioc->shost_recovery)
+ return;
+
if (!ioc->sas_hba.num_phys)
_scsih_sas_host_add(ioc);
else
- _scsih_sas_host_refresh(ioc, 0);
+ _scsih_sas_host_refresh(ioc);
if (fw_event->ignore) {
dewtprintk(ioc, printk(MPT2SAS_DEBUG_FMT "ignoring expander "
@@ -4058,6 +4194,17 @@ _scsih_sas_topology_change_event(struct MPT2SAS_ADAPTER *ioc,
if (_scsih_expander_add(ioc, parent_handle) != 0)
return;
+ spin_lock_irqsave(&ioc->sas_node_lock, flags);
+ sas_expander = mpt2sas_scsih_expander_find_by_handle(ioc,
+ parent_handle);
+ spin_unlock_irqrestore(&ioc->sas_node_lock, flags);
+ if (sas_expander)
+ sas_address = sas_expander->sas_address;
+ else if (parent_handle < ioc->sas_hba.num_phys)
+ sas_address = ioc->sas_hba.sas_address;
+ else
+ return;
+
/* handle siblings events */
for (i = 0; i < event_data->NumEntries; i++) {
if (fw_event->ignore) {
@@ -4077,48 +4224,47 @@ _scsih_sas_topology_change_event(struct MPT2SAS_ADAPTER *ioc,
handle = le16_to_cpu(event_data->PHY[i].AttachedDevHandle);
if (!handle)
continue;
- link_rate_ = event_data->PHY[i].LinkRate >> 4;
+ link_rate = event_data->PHY[i].LinkRate >> 4;
+ prev_link_rate = event_data->PHY[i].LinkRate & 0xF;
switch (reason_code) {
case MPI2_EVENT_SAS_TOPO_RC_PHY_CHANGED:
+
+ if (link_rate == prev_link_rate)
+ break;
+
+ mpt2sas_transport_update_links(ioc, sas_address,
+ handle, phy_number, link_rate);
+
+ if (link_rate >= MPI2_SAS_NEG_LINK_RATE_1_5)
+ _scsih_ublock_io_device(ioc, handle);
+ break;
case MPI2_EVENT_SAS_TOPO_RC_TARG_ADDED:
- if (!parent_handle) {
- if (phy_number < ioc->sas_hba.num_phys)
- mpt2sas_transport_update_links(
- ioc,
- ioc->sas_hba.phy[phy_number].handle,
- handle, phy_number, link_rate_);
- } else {
- spin_lock_irqsave(&ioc->sas_node_lock, flags);
- sas_expander =
- mpt2sas_scsih_expander_find_by_handle(ioc,
- parent_handle);
- spin_unlock_irqrestore(&ioc->sas_node_lock,
- flags);
- if (sas_expander) {
- if (phy_number < sas_expander->num_phys)
- mpt2sas_transport_update_links(
- ioc,
- sas_expander->
- phy[phy_number].handle,
- handle, phy_number,
- link_rate_);
- }
- }
- if (reason_code == MPI2_EVENT_SAS_TOPO_RC_TARG_ADDED) {
- if (link_rate_ < MPI2_SAS_NEG_LINK_RATE_1_5)
- break;
- _scsih_add_device(ioc, handle, phy_number, 0);
- }
+
+ mpt2sas_transport_update_links(ioc, sas_address,
+ handle, phy_number, link_rate);
+
+ _scsih_add_device(ioc, handle, phy_number, 0);
break;
case MPI2_EVENT_SAS_TOPO_RC_TARG_NOT_RESPONDING:
- _scsih_remove_device(ioc, handle);
+
+ spin_lock_irqsave(&ioc->sas_device_lock, flags);
+ sas_device = _scsih_sas_device_find_by_handle(ioc,
+ handle);
+ if (!sas_device) {
+ spin_unlock_irqrestore(&ioc->sas_device_lock,
+ flags);
+ break;
+ }
+ spin_unlock_irqrestore(&ioc->sas_device_lock, flags);
+ _scsih_remove_device(ioc, sas_device);
break;
}
}
/* handle expander removal */
- if (event_data->ExpStatus == MPI2_EVENT_SAS_TOPO_ES_NOT_RESPONDING)
- _scsih_expander_remove(ioc, parent_handle);
+ if (event_data->ExpStatus == MPI2_EVENT_SAS_TOPO_ES_NOT_RESPONDING &&
+ sas_expander)
+ _scsih_expander_remove(ioc, sas_address);
}
@@ -4170,6 +4316,12 @@ _scsih_sas_device_status_change_event_debug(struct MPT2SAS_ADAPTER *ioc,
case MPI2_EVENT_SAS_DEV_STAT_RC_ASYNC_NOTIFICATION:
reason_str = "internal async notification";
break;
+ case MPI2_EVENT_SAS_DEV_STAT_RC_EXPANDER_REDUCED_FUNCTIONALITY:
+ reason_str = "expander reduced functionality";
+ break;
+ case MPI2_EVENT_SAS_DEV_STAT_RC_CMP_EXPANDER_REDUCED_FUNCTIONALITY:
+ reason_str = "expander reduced functionality complete";
+ break;
default:
reason_str = "unknown reason";
break;
@@ -4197,11 +4349,43 @@ static void
_scsih_sas_device_status_change_event(struct MPT2SAS_ADAPTER *ioc,
struct fw_event_work *fw_event)
{
+ struct MPT2SAS_TARGET *target_priv_data;
+ struct _sas_device *sas_device;
+ __le64 sas_address;
+ unsigned long flags;
+ Mpi2EventDataSasDeviceStatusChange_t *event_data =
+ fw_event->event_data;
+
#ifdef CONFIG_SCSI_MPT2SAS_LOGGING
if (ioc->logging_level & MPT_DEBUG_EVENT_WORK_TASK)
_scsih_sas_device_status_change_event_debug(ioc,
- fw_event->event_data);
+ event_data);
#endif
+
+ if (!(event_data->ReasonCode ==
+ MPI2_EVENT_SAS_DEV_STAT_RC_INTERNAL_DEVICE_RESET &&
+ event_data->ReasonCode ==
+ MPI2_EVENT_SAS_DEV_STAT_RC_CMP_INTERNAL_DEV_RESET))
+ return;
+
+ spin_lock_irqsave(&ioc->sas_device_lock, flags);
+ sas_address = le64_to_cpu(event_data->SASAddress);
+ sas_device = mpt2sas_scsih_sas_device_find_by_sas_address(ioc,
+ sas_address);
+ spin_unlock_irqrestore(&ioc->sas_device_lock, flags);
+
+ if (!sas_device || !sas_device->starget)
+ return;
+
+ target_priv_data = sas_device->starget->hostdata;
+ if (!target_priv_data)
+ return;
+
+ if (event_data->ReasonCode ==
+ MPI2_EVENT_SAS_DEV_STAT_RC_INTERNAL_DEVICE_RESET)
+ target_priv_data->tm_busy = 1;
+ else
+ target_priv_data->tm_busy = 0;
}
#ifdef CONFIG_SCSI_MPT2SAS_LOGGING
@@ -4281,6 +4465,7 @@ _scsih_sas_broadcast_primative_event(struct MPT2SAS_ADAPTER *ioc,
#ifdef CONFIG_SCSI_MPT2SAS_LOGGING
Mpi2EventDataSasBroadcastPrimitive_t *event_data = fw_event->event_data;
#endif
+ u16 ioc_status;
dewtprintk(ioc, printk(MPT2SAS_DEBUG_FMT "broadcast primative: "
"phy number(%d), width(%d)\n", ioc->name, event_data->PhyNum,
event_data->PortWidth));
@@ -4314,8 +4499,9 @@ _scsih_sas_broadcast_primative_event(struct MPT2SAS_ADAPTER *ioc,
mpt2sas_scsih_issue_tm(ioc, handle, lun,
MPI2_SCSITASKMGMT_TASKTYPE_QUERY_TASK, smid, 30);
ioc->tm_cmds.status = MPT2_CMD_NOT_USED;
-
- if ((mpi_reply->IOCStatus == MPI2_IOCSTATUS_SUCCESS) &&
+ ioc_status = le16_to_cpu(mpi_reply->IOCStatus)
+ & MPI2_IOCSTATUS_MASK;
+ if ((ioc_status == MPI2_IOCSTATUS_SUCCESS) &&
(mpi_reply->ResponseCode ==
MPI2_SCSITASKMGMT_RSP_TM_SUCCEEDED ||
mpi_reply->ResponseCode ==
@@ -4570,7 +4756,7 @@ _scsih_sas_pd_delete(struct MPT2SAS_ADAPTER *ioc,
spin_unlock_irqrestore(&ioc->sas_device_lock, flags);
if (!sas_device)
return;
- _scsih_remove_device(ioc, handle);
+ _scsih_remove_device(ioc, sas_device);
}
/**
@@ -4591,6 +4777,8 @@ _scsih_sas_pd_add(struct MPT2SAS_ADAPTER *ioc,
Mpi2ConfigReply_t mpi_reply;
Mpi2SasDevicePage0_t sas_device_pg0;
u32 ioc_status;
+ u64 sas_address;
+ u16 parent_handle;
spin_lock_irqsave(&ioc->sas_device_lock, flags);
sas_device = _scsih_sas_device_find_by_handle(ioc, handle);
@@ -4615,9 +4803,10 @@ _scsih_sas_pd_add(struct MPT2SAS_ADAPTER *ioc,
return;
}
- mpt2sas_transport_update_links(ioc,
- le16_to_cpu(sas_device_pg0.ParentDevHandle),
- handle, sas_device_pg0.PhyNum, MPI2_SAS_NEG_LINK_RATE_1_5);
+ parent_handle = le16_to_cpu(sas_device_pg0.ParentDevHandle);
+ if (!_scsih_get_sas_address(ioc, parent_handle, &sas_address))
+ mpt2sas_transport_update_links(ioc, sas_address, handle,
+ sas_device_pg0.PhyNum, MPI2_SAS_NEG_LINK_RATE_1_5);
_scsih_add_device(ioc, handle, 0, 1);
}
@@ -4857,7 +5046,7 @@ static void
_scsih_sas_ir_physical_disk_event(struct MPT2SAS_ADAPTER *ioc,
struct fw_event_work *fw_event)
{
- u16 handle;
+ u16 handle, parent_handle;
u32 state;
struct _sas_device *sas_device;
unsigned long flags;
@@ -4865,6 +5054,7 @@ _scsih_sas_ir_physical_disk_event(struct MPT2SAS_ADAPTER *ioc,
Mpi2SasDevicePage0_t sas_device_pg0;
u32 ioc_status;
Mpi2EventDataIrPhysicalDisk_t *event_data = fw_event->event_data;
+ u64 sas_address;
if (event_data->ReasonCode != MPI2_EVENT_IR_PHYSDISK_RC_STATE_CHANGED)
return;
@@ -4906,9 +5096,10 @@ _scsih_sas_ir_physical_disk_event(struct MPT2SAS_ADAPTER *ioc,
return;
}
- mpt2sas_transport_update_links(ioc,
- le16_to_cpu(sas_device_pg0.ParentDevHandle),
- handle, sas_device_pg0.PhyNum, MPI2_SAS_NEG_LINK_RATE_1_5);
+ parent_handle = le16_to_cpu(sas_device_pg0.ParentDevHandle);
+ if (!_scsih_get_sas_address(ioc, parent_handle, &sas_address))
+ mpt2sas_transport_update_links(ioc, sas_address, handle,
+ sas_device_pg0.PhyNum, MPI2_SAS_NEG_LINK_RATE_1_5);
_scsih_add_device(ioc, handle, 0, 1);
@@ -4948,11 +5139,17 @@ _scsih_sas_ir_operation_status_event_debug(struct MPT2SAS_ADAPTER *ioc,
case MPI2_EVENT_IR_RAIDOP_CONSISTENCY_CHECK:
reason_str = "consistency check";
break;
- default:
- reason_str = "unknown reason";
+ case MPI2_EVENT_IR_RAIDOP_BACKGROUND_INIT:
+ reason_str = "background init";
+ break;
+ case MPI2_EVENT_IR_RAIDOP_MAKE_DATA_CONSISTENT:
+ reason_str = "make data consistent";
break;
}
+ if (!reason_str)
+ return;
+
printk(MPT2SAS_INFO_FMT "raid operational status: (%s)"
"\thandle(0x%04x), percent complete(%d)\n",
ioc->name, reason_str,
@@ -5252,18 +5449,23 @@ _scsih_mark_responding_expander(struct MPT2SAS_ADAPTER *ioc, u64 sas_address,
{
struct _sas_node *sas_expander;
unsigned long flags;
+ int i;
spin_lock_irqsave(&ioc->sas_node_lock, flags);
list_for_each_entry(sas_expander, &ioc->sas_expander_list, list) {
- if (sas_expander->sas_address == sas_address) {
- sas_expander->responding = 1;
- if (sas_expander->handle != handle) {
- printk(KERN_INFO "old handle(0x%04x)\n",
- sas_expander->handle);
- sas_expander->handle = handle;
- }
+ if (sas_expander->sas_address != sas_address)
+ continue;
+ sas_expander->responding = 1;
+ if (sas_expander->handle == handle)
goto out;
- }
+ printk(KERN_INFO "\texpander(0x%016llx): handle changed"
+ " from(0x%04x) to (0x%04x)!!!\n",
+ (unsigned long long)sas_expander->sas_address,
+ sas_expander->handle, handle);
+ sas_expander->handle = handle;
+ for (i = 0 ; i < sas_expander->num_phys ; i++)
+ sas_expander->phy[i].handle = handle;
+ goto out;
}
out:
spin_unlock_irqrestore(&ioc->sas_node_lock, flags);
@@ -5340,7 +5542,9 @@ _scsih_remove_unresponding_devices(struct MPT2SAS_ADAPTER *ioc)
(unsigned long long)
sas_device->enclosure_logical_id,
sas_device->slot);
- _scsih_remove_device(ioc, sas_device->handle);
+ /* invalidate the device handle */
+ sas_device->handle = 0;
+ _scsih_remove_device(ioc, sas_device);
}
list_for_each_entry_safe(raid_device, raid_device_next,
@@ -5366,7 +5570,7 @@ _scsih_remove_unresponding_devices(struct MPT2SAS_ADAPTER *ioc)
sas_expander->responding = 0;
continue;
}
- _scsih_expander_remove(ioc, sas_expander->handle);
+ _scsih_expander_remove(ioc, sas_expander->sas_address);
goto retry_expander_search;
}
}
@@ -5406,7 +5610,7 @@ mpt2sas_scsih_reset_handler(struct MPT2SAS_ADAPTER *ioc, int reset_phase)
case MPT2_IOC_DONE_RESET:
dtmprintk(ioc, printk(MPT2SAS_DEBUG_FMT "%s: "
"MPT2_IOC_DONE_RESET\n", ioc->name, __func__));
- _scsih_sas_host_refresh(ioc, 0);
+ _scsih_sas_host_refresh(ioc);
_scsih_search_responding_sas_devices(ioc);
_scsih_search_responding_raid_devices(ioc);
_scsih_search_responding_expanders(ioc);
@@ -5646,7 +5850,7 @@ _scsih_expander_node_remove(struct MPT2SAS_ADAPTER *ioc,
spin_unlock_irqrestore(&ioc->sas_device_lock, flags);
if (!sas_device)
continue;
- _scsih_remove_device(ioc, sas_device->handle);
+ _scsih_remove_device(ioc, sas_device);
if (ioc->shost_recovery)
return;
goto retry_device_search;
@@ -5669,7 +5873,8 @@ _scsih_expander_node_remove(struct MPT2SAS_ADAPTER *ioc,
spin_unlock_irqrestore(&ioc->sas_node_lock, flags);
if (!expander_sibling)
continue;
- _scsih_expander_remove(ioc, expander_sibling->handle);
+ _scsih_expander_remove(ioc,
+ expander_sibling->sas_address);
if (ioc->shost_recovery)
return;
goto retry_expander_search;
@@ -5677,7 +5882,7 @@ _scsih_expander_node_remove(struct MPT2SAS_ADAPTER *ioc,
}
mpt2sas_transport_port_remove(ioc, sas_expander->sas_address,
- sas_expander->parent_handle);
+ sas_expander->sas_address_parent);
printk(MPT2SAS_INFO_FMT "expander_remove: handle"
"(0x%04x), sas_addr(0x%016llx)\n", ioc->name,
@@ -5690,9 +5895,99 @@ _scsih_expander_node_remove(struct MPT2SAS_ADAPTER *ioc,
}
/**
+ * _scsih_ir_shutdown - IR shutdown notification
+ * @ioc: per adapter object
+ *
+ * Sending RAID Action to alert the Integrated RAID subsystem of the IOC that
+ * the host system is shutting down.
+ *
+ * Return nothing.
+ */
+static void
+_scsih_ir_shutdown(struct MPT2SAS_ADAPTER *ioc)
+{
+ Mpi2RaidActionRequest_t *mpi_request;
+ Mpi2RaidActionReply_t *mpi_reply;
+ u16 smid;
+
+ /* is IR firmware build loaded ? */
+ if (!ioc->ir_firmware)
+ return;
+
+ /* are there any volumes ? */
+ if (list_empty(&ioc->raid_device_list))
+ return;
+
+ mutex_lock(&ioc->scsih_cmds.mutex);
+
+ if (ioc->scsih_cmds.status != MPT2_CMD_NOT_USED) {
+ printk(MPT2SAS_ERR_FMT "%s: scsih_cmd in use\n",
+ ioc->name, __func__);
+ goto out;
+ }
+ ioc->scsih_cmds.status = MPT2_CMD_PENDING;
+
+ smid = mpt2sas_base_get_smid(ioc, ioc->scsih_cb_idx);
+ if (!smid) {
+ printk(MPT2SAS_ERR_FMT "%s: failed obtaining a smid\n",
+ ioc->name, __func__);
+ ioc->scsih_cmds.status = MPT2_CMD_NOT_USED;
+ goto out;
+ }
+
+ mpi_request = mpt2sas_base_get_msg_frame(ioc, smid);
+ ioc->scsih_cmds.smid = smid;
+ memset(mpi_request, 0, sizeof(Mpi2RaidActionRequest_t));
+
+ mpi_request->Function = MPI2_FUNCTION_RAID_ACTION;
+ mpi_request->Action = MPI2_RAID_ACTION_SYSTEM_SHUTDOWN_INITIATED;
+
+ printk(MPT2SAS_INFO_FMT "IR shutdown (sending)\n", ioc->name);
+ init_completion(&ioc->scsih_cmds.done);
+ mpt2sas_base_put_smid_default(ioc, smid);
+ wait_for_completion_timeout(&ioc->scsih_cmds.done, 10*HZ);
+
+ if (!(ioc->scsih_cmds.status & MPT2_CMD_COMPLETE)) {
+ printk(MPT2SAS_ERR_FMT "%s: timeout\n",
+ ioc->name, __func__);
+ goto out;
+ }
+
+ if (ioc->scsih_cmds.status & MPT2_CMD_REPLY_VALID) {
+ mpi_reply = ioc->scsih_cmds.reply;
+
+ printk(MPT2SAS_INFO_FMT "IR shutdown (complete): "
+ "ioc_status(0x%04x), loginfo(0x%08x)\n",
+ ioc->name, le16_to_cpu(mpi_reply->IOCStatus),
+ le32_to_cpu(mpi_reply->IOCLogInfo));
+ }
+
+ out:
+ ioc->scsih_cmds.status = MPT2_CMD_NOT_USED;
+ mutex_unlock(&ioc->scsih_cmds.mutex);
+}
+
+/**
+ * _scsih_shutdown - routine call during system shutdown
+ * @pdev: PCI device struct
+ *
+ * Return nothing.
+ */
+static void
+_scsih_shutdown(struct pci_dev *pdev)
+{
+ struct Scsi_Host *shost = pci_get_drvdata(pdev);
+ struct MPT2SAS_ADAPTER *ioc = shost_priv(shost);
+
+ _scsih_ir_shutdown(ioc);
+ mpt2sas_base_detach(ioc);
+}
+
+/**
* _scsih_remove - detach and remove add host
* @pdev: PCI device struct
*
+ * Routine called when unloading the driver.
* Return nothing.
*/
static void __devexit
@@ -5726,7 +6021,7 @@ _scsih_remove(struct pci_dev *pdev)
mpt2sas_scsih_sas_device_find_by_sas_address(ioc,
mpt2sas_port->remote_identify.sas_address);
if (sas_device) {
- _scsih_remove_device(ioc, sas_device->handle);
+ _scsih_remove_device(ioc, sas_device);
goto retry_again;
}
} else {
@@ -5735,7 +6030,7 @@ _scsih_remove(struct pci_dev *pdev)
mpt2sas_port->remote_identify.sas_address);
if (expander_sibling) {
_scsih_expander_remove(ioc,
- expander_sibling->handle);
+ expander_sibling->sas_address);
goto retry_again;
}
}
@@ -5749,7 +6044,7 @@ _scsih_remove(struct pci_dev *pdev)
}
sas_remove_host(shost);
- mpt2sas_base_detach(ioc);
+ _scsih_shutdown(pdev);
list_del(&ioc->list);
scsi_remove_host(shost);
scsi_host_put(shost);
@@ -5770,7 +6065,8 @@ _scsih_probe_boot_devices(struct MPT2SAS_ADAPTER *ioc)
void *device;
struct _sas_device *sas_device;
struct _raid_device *raid_device;
- u16 handle, parent_handle;
+ u16 handle;
+ u64 sas_address_parent;
u64 sas_address;
unsigned long flags;
int rc;
@@ -5799,17 +6095,17 @@ _scsih_probe_boot_devices(struct MPT2SAS_ADAPTER *ioc)
} else {
sas_device = device;
handle = sas_device->handle;
- parent_handle = sas_device->parent_handle;
+ sas_address_parent = sas_device->sas_address_parent;
sas_address = sas_device->sas_address;
spin_lock_irqsave(&ioc->sas_device_lock, flags);
list_move_tail(&sas_device->list, &ioc->sas_device_list);
spin_unlock_irqrestore(&ioc->sas_device_lock, flags);
if (!mpt2sas_transport_port_add(ioc, sas_device->handle,
- sas_device->parent_handle)) {
+ sas_device->sas_address_parent)) {
_scsih_sas_device_remove(ioc, sas_device);
} else if (!sas_device->starget) {
mpt2sas_transport_port_remove(ioc, sas_address,
- parent_handle);
+ sas_address_parent);
_scsih_sas_device_remove(ioc, sas_device);
}
}
@@ -5849,8 +6145,6 @@ _scsih_probe_sas(struct MPT2SAS_ADAPTER *ioc)
{
struct _sas_device *sas_device, *next;
unsigned long flags;
- u16 handle, parent_handle;
- u64 sas_address;
/* SAS Device List */
list_for_each_entry_safe(sas_device, next, &ioc->sas_device_init_list,
@@ -5859,14 +6153,13 @@ _scsih_probe_sas(struct MPT2SAS_ADAPTER *ioc)
list_move_tail(&sas_device->list, &ioc->sas_device_list);
spin_unlock_irqrestore(&ioc->sas_device_lock, flags);
- handle = sas_device->handle;
- parent_handle = sas_device->parent_handle;
- sas_address = sas_device->sas_address;
- if (!mpt2sas_transport_port_add(ioc, handle, parent_handle)) {
+ if (!mpt2sas_transport_port_add(ioc, sas_device->handle,
+ sas_device->sas_address_parent)) {
_scsih_sas_device_remove(ioc, sas_device);
} else if (!sas_device->starget) {
- mpt2sas_transport_port_remove(ioc, sas_address,
- parent_handle);
+ mpt2sas_transport_port_remove(ioc,
+ sas_device->sas_address,
+ sas_device->sas_address_parent);
_scsih_sas_device_remove(ioc, sas_device);
}
}
@@ -5935,6 +6228,7 @@ _scsih_probe(struct pci_dev *pdev, const struct pci_device_id *id)
ioc->ctl_cb_idx = ctl_cb_idx;
ioc->base_cb_idx = base_cb_idx;
ioc->transport_cb_idx = transport_cb_idx;
+ ioc->scsih_cb_idx = scsih_cb_idx;
ioc->config_cb_idx = config_cb_idx;
ioc->tm_tr_cb_idx = tm_tr_cb_idx;
ioc->tm_sas_control_cb_idx = tm_sas_control_cb_idx;
@@ -6072,6 +6366,7 @@ static struct pci_driver scsih_driver = {
.id_table = scsih_pci_table,
.probe = _scsih_probe,
.remove = __devexit_p(_scsih_remove),
+ .shutdown = _scsih_shutdown,
#ifdef CONFIG_PM
.suspend = _scsih_suspend,
.resume = _scsih_resume,
@@ -6113,6 +6408,9 @@ _scsih_init(void)
transport_cb_idx = mpt2sas_base_register_callback_handler(
mpt2sas_transport_done);
+ /* scsih internal commands callback handler */
+ scsih_cb_idx = mpt2sas_base_register_callback_handler(_scsih_done);
+
/* configuration page API internal commands callback handler */
config_cb_idx = mpt2sas_base_register_callback_handler(
mpt2sas_config_done);
@@ -6152,6 +6450,7 @@ _scsih_exit(void)
mpt2sas_base_release_callback_handler(tm_cb_idx);
mpt2sas_base_release_callback_handler(base_cb_idx);
mpt2sas_base_release_callback_handler(transport_cb_idx);
+ mpt2sas_base_release_callback_handler(scsih_cb_idx);
mpt2sas_base_release_callback_handler(config_cb_idx);
mpt2sas_base_release_callback_handler(ctl_cb_idx);
diff --git a/drivers/scsi/mpt2sas/mpt2sas_transport.c b/drivers/scsi/mpt2sas/mpt2sas_transport.c
index eb98188c7f3..3a82872bad4 100644
--- a/drivers/scsi/mpt2sas/mpt2sas_transport.c
+++ b/drivers/scsi/mpt2sas/mpt2sas_transport.c
@@ -59,24 +59,23 @@
#include "mpt2sas_base.h"
/**
- * _transport_sas_node_find_by_handle - sas node search
+ * _transport_sas_node_find_by_sas_address - sas node search
* @ioc: per adapter object
- * @handle: expander or hba handle (assigned by firmware)
+ * @sas_address: sas address of expander or sas host
* Context: Calling function should acquire ioc->sas_node_lock.
*
* Search for either hba phys or expander device based on handle, then returns
* the sas_node object.
*/
static struct _sas_node *
-_transport_sas_node_find_by_handle(struct MPT2SAS_ADAPTER *ioc, u16 handle)
+_transport_sas_node_find_by_sas_address(struct MPT2SAS_ADAPTER *ioc,
+ u64 sas_address)
{
- int i;
-
- for (i = 0; i < ioc->sas_hba.num_phys; i++)
- if (ioc->sas_hba.phy[i].handle == handle)
- return &ioc->sas_hba;
-
- return mpt2sas_scsih_expander_find_by_handle(ioc, handle);
+ if (ioc->sas_hba.sas_address == sas_address)
+ return &ioc->sas_hba;
+ else
+ return mpt2sas_scsih_expander_find_by_sas_address(ioc,
+ sas_address);
}
/**
@@ -259,8 +258,7 @@ struct rep_manu_reply{
u8 response_length;
u16 expander_change_count;
u8 reserved0[2];
- u8 sas_format:1;
- u8 reserved1:7;
+ u8 sas_format;
u8 reserved2[3];
u8 vendor_id[SAS_EXPANDER_VENDOR_ID_LEN];
u8 product_id[SAS_EXPANDER_PRODUCT_ID_LEN];
@@ -375,7 +373,8 @@ _transport_expander_report_manufacture(struct MPT2SAS_ADAPTER *ioc,
mpi_request->VP_ID = 0;
sas_address_le = (u64 *)&mpi_request->SASAddress;
*sas_address_le = cpu_to_le64(sas_address);
- mpi_request->RequestDataLength = sizeof(struct rep_manu_request);
+ mpi_request->RequestDataLength =
+ cpu_to_le16(sizeof(struct rep_manu_request));
psge = &mpi_request->SGL;
/* WRITE sgel first */
@@ -438,8 +437,8 @@ _transport_expander_report_manufacture(struct MPT2SAS_ADAPTER *ioc,
SAS_EXPANDER_PRODUCT_ID_LEN);
strncpy(edev->product_rev, manufacture_reply->product_rev,
SAS_EXPANDER_PRODUCT_REV_LEN);
- edev->level = manufacture_reply->sas_format;
- if (manufacture_reply->sas_format) {
+ edev->level = manufacture_reply->sas_format & 1;
+ if (edev->level) {
strncpy(edev->component_vendor_id,
manufacture_reply->component_vendor_id,
SAS_EXPANDER_COMPONENT_VENDOR_ID_LEN);
@@ -469,7 +468,7 @@ _transport_expander_report_manufacture(struct MPT2SAS_ADAPTER *ioc,
* mpt2sas_transport_port_add - insert port to the list
* @ioc: per adapter object
* @handle: handle of attached device
- * @parent_handle: parent handle(either hba or expander)
+ * @sas_address: sas address of parent expander or sas host
* Context: This function will acquire ioc->sas_node_lock.
*
* Adding new port object to the sas_node->sas_port_list.
@@ -478,7 +477,7 @@ _transport_expander_report_manufacture(struct MPT2SAS_ADAPTER *ioc,
*/
struct _sas_port *
mpt2sas_transport_port_add(struct MPT2SAS_ADAPTER *ioc, u16 handle,
- u16 parent_handle)
+ u64 sas_address)
{
struct _sas_phy *mpt2sas_phy, *next;
struct _sas_port *mpt2sas_port;
@@ -488,9 +487,6 @@ mpt2sas_transport_port_add(struct MPT2SAS_ADAPTER *ioc, u16 handle,
int i;
struct sas_port *port;
- if (!parent_handle)
- return NULL;
-
mpt2sas_port = kzalloc(sizeof(struct _sas_port),
GFP_KERNEL);
if (!mpt2sas_port) {
@@ -502,17 +498,16 @@ mpt2sas_transport_port_add(struct MPT2SAS_ADAPTER *ioc, u16 handle,
INIT_LIST_HEAD(&mpt2sas_port->port_list);
INIT_LIST_HEAD(&mpt2sas_port->phy_list);
spin_lock_irqsave(&ioc->sas_node_lock, flags);
- sas_node = _transport_sas_node_find_by_handle(ioc, parent_handle);
+ sas_node = _transport_sas_node_find_by_sas_address(ioc, sas_address);
spin_unlock_irqrestore(&ioc->sas_node_lock, flags);
if (!sas_node) {
- printk(MPT2SAS_ERR_FMT "%s: Could not find parent(0x%04x)!\n",
- ioc->name, __func__, parent_handle);
+ printk(MPT2SAS_ERR_FMT "%s: Could not find "
+ "parent sas_address(0x%016llx)!\n", ioc->name,
+ __func__, (unsigned long long)sas_address);
goto out_fail;
}
- mpt2sas_port->handle = parent_handle;
- mpt2sas_port->sas_address = sas_node->sas_address;
if ((_transport_set_identify(ioc, handle,
&mpt2sas_port->remote_identify))) {
printk(MPT2SAS_ERR_FMT "failure at %s:%d/%s()!\n",
@@ -604,7 +599,7 @@ mpt2sas_transport_port_add(struct MPT2SAS_ADAPTER *ioc, u16 handle,
* mpt2sas_transport_port_remove - remove port from the list
* @ioc: per adapter object
* @sas_address: sas address of attached device
- * @parent_handle: handle to the upstream parent(either hba or expander)
+ * @sas_address_parent: sas address of parent expander or sas host
* Context: This function will acquire ioc->sas_node_lock.
*
* Removing object and freeing associated memory from the
@@ -614,7 +609,7 @@ mpt2sas_transport_port_add(struct MPT2SAS_ADAPTER *ioc, u16 handle,
*/
void
mpt2sas_transport_port_remove(struct MPT2SAS_ADAPTER *ioc, u64 sas_address,
- u16 parent_handle)
+ u64 sas_address_parent)
{
int i;
unsigned long flags;
@@ -624,7 +619,8 @@ mpt2sas_transport_port_remove(struct MPT2SAS_ADAPTER *ioc, u64 sas_address,
struct _sas_phy *mpt2sas_phy, *next_phy;
spin_lock_irqsave(&ioc->sas_node_lock, flags);
- sas_node = _transport_sas_node_find_by_handle(ioc, parent_handle);
+ sas_node = _transport_sas_node_find_by_sas_address(ioc,
+ sas_address_parent);
spin_unlock_irqrestore(&ioc->sas_node_lock, flags);
if (!sas_node)
return;
@@ -650,8 +646,7 @@ mpt2sas_transport_port_remove(struct MPT2SAS_ADAPTER *ioc, u64 sas_address,
&mpt2sas_port->phy_list, port_siblings) {
if ((ioc->logging_level & MPT_DEBUG_TRANSPORT))
dev_printk(KERN_INFO, &mpt2sas_port->port->dev,
- "remove: parent_handle(0x%04x), "
- "sas_addr(0x%016llx), phy(%d)\n", parent_handle,
+ "remove: sas_addr(0x%016llx), phy(%d)\n",
(unsigned long long)
mpt2sas_port->remote_identify.sas_address,
mpt2sas_phy->phy_id);
@@ -799,8 +794,8 @@ mpt2sas_transport_add_expander_phy(struct MPT2SAS_ADAPTER *ioc, struct _sas_phy
/**
* mpt2sas_transport_update_links - refreshing phy link changes
* @ioc: per adapter object
- * @handle: handle to sas_host or expander
- * @attached_handle: attached device handle
+ * @sas_address: sas address of parent expander or sas host
+ * @handle: attached device handle
* @phy_numberv: phy number
* @link_rate: new link rate
*
@@ -808,28 +803,25 @@ mpt2sas_transport_add_expander_phy(struct MPT2SAS_ADAPTER *ioc, struct _sas_phy
*/
void
mpt2sas_transport_update_links(struct MPT2SAS_ADAPTER *ioc,
- u16 handle, u16 attached_handle, u8 phy_number, u8 link_rate)
+ u64 sas_address, u16 handle, u8 phy_number, u8 link_rate)
{
unsigned long flags;
struct _sas_node *sas_node;
struct _sas_phy *mpt2sas_phy;
- if (ioc->shost_recovery) {
- printk(MPT2SAS_INFO_FMT "%s: host reset in progress!\n",
- __func__, ioc->name);
+ if (ioc->shost_recovery)
return;
- }
spin_lock_irqsave(&ioc->sas_node_lock, flags);
- sas_node = _transport_sas_node_find_by_handle(ioc, handle);
+ sas_node = _transport_sas_node_find_by_sas_address(ioc, sas_address);
spin_unlock_irqrestore(&ioc->sas_node_lock, flags);
if (!sas_node)
return;
mpt2sas_phy = &sas_node->phy[phy_number];
- mpt2sas_phy->attached_handle = attached_handle;
- if (attached_handle && (link_rate >= MPI2_SAS_NEG_LINK_RATE_1_5))
- _transport_set_identify(ioc, mpt2sas_phy->attached_handle,
+ mpt2sas_phy->attached_handle = handle;
+ if (handle && (link_rate >= MPI2_SAS_NEG_LINK_RATE_1_5))
+ _transport_set_identify(ioc, handle,
&mpt2sas_phy->remote_identify);
else
memset(&mpt2sas_phy->remote_identify, 0 , sizeof(struct
@@ -841,13 +833,11 @@ mpt2sas_transport_update_links(struct MPT2SAS_ADAPTER *ioc,
if ((ioc->logging_level & MPT_DEBUG_TRANSPORT))
dev_printk(KERN_INFO, &mpt2sas_phy->phy->dev,
- "refresh: handle(0x%04x), sas_addr(0x%016llx),\n"
+ "refresh: parent sas_addr(0x%016llx),\n"
"\tlink_rate(0x%02x), phy(%d)\n"
"\tattached_handle(0x%04x), sas_addr(0x%016llx)\n",
- handle, (unsigned long long)
- mpt2sas_phy->identify.sas_address, link_rate,
- phy_number, attached_handle,
- (unsigned long long)
+ (unsigned long long)sas_address,
+ link_rate, phy_number, handle, (unsigned long long)
mpt2sas_phy->remote_identify.sas_address);
}
@@ -1126,7 +1116,7 @@ _transport_smp_handler(struct Scsi_Host *shost, struct sas_rphy *rphy,
dma_addr_out = pci_map_single(ioc->pdev, bio_data(req->bio),
blk_rq_bytes(req), PCI_DMA_BIDIRECTIONAL);
if (!dma_addr_out) {
- mpt2sas_base_free_smid(ioc, le16_to_cpu(smid));
+ mpt2sas_base_free_smid(ioc, smid);
goto unmap;
}
@@ -1144,7 +1134,7 @@ _transport_smp_handler(struct Scsi_Host *shost, struct sas_rphy *rphy,
dma_addr_in = pci_map_single(ioc->pdev, bio_data(rsp->bio),
blk_rq_bytes(rsp), PCI_DMA_BIDIRECTIONAL);
if (!dma_addr_in) {
- mpt2sas_base_free_smid(ioc, le16_to_cpu(smid));
+ mpt2sas_base_free_smid(ioc, smid);
goto unmap;
}
diff --git a/drivers/scsi/osd/osd_initiator.c b/drivers/scsi/osd/osd_initiator.c
index 7a117c18114..950202a70bc 100644
--- a/drivers/scsi/osd/osd_initiator.c
+++ b/drivers/scsi/osd/osd_initiator.c
@@ -73,7 +73,8 @@ static const char *_osd_ver_desc(struct osd_request *or)
#define ATTR_DEF_RI(id, len) ATTR_DEF(OSD_APAGE_ROOT_INFORMATION, id, len)
-static int _osd_print_system_info(struct osd_dev *od, void *caps)
+static int _osd_get_print_system_info(struct osd_dev *od,
+ void *caps, struct osd_dev_info *odi)
{
struct osd_request *or;
struct osd_attr get_attrs[] = {
@@ -137,8 +138,12 @@ static int _osd_print_system_info(struct osd_dev *od, void *caps)
OSD_INFO("PRODUCT_SERIAL_NUMBER [%s]\n",
(char *)pFirst);
- pFirst = get_attrs[a].val_ptr;
- OSD_INFO("OSD_NAME [%s]\n", (char *)pFirst);
+ odi->osdname_len = get_attrs[a].len;
+ /* Avoid NULL for memcmp optimization 0-length is good enough */
+ odi->osdname = kzalloc(odi->osdname_len + 1, GFP_KERNEL);
+ if (odi->osdname_len)
+ memcpy(odi->osdname, get_attrs[a].val_ptr, odi->osdname_len);
+ OSD_INFO("OSD_NAME [%s]\n", odi->osdname);
a++;
pFirst = get_attrs[a++].val_ptr;
@@ -171,6 +176,14 @@ static int _osd_print_system_info(struct osd_dev *od, void *caps)
sid_dump, sizeof(sid_dump), true);
OSD_INFO("OSD_SYSTEM_ID(%d)\n"
" [%s]\n", len, sid_dump);
+
+ if (unlikely(len > sizeof(odi->systemid))) {
+ OSD_ERR("OSD Target error: OSD_SYSTEM_ID too long(%d). "
+ "device idetification might not work\n", len);
+ len = sizeof(odi->systemid);
+ }
+ odi->systemid_len = len;
+ memcpy(odi->systemid, get_attrs[a].val_ptr, len);
a++;
}
out:
@@ -178,16 +191,17 @@ out:
return ret;
}
-int osd_auto_detect_ver(struct osd_dev *od, void *caps)
+int osd_auto_detect_ver(struct osd_dev *od,
+ void *caps, struct osd_dev_info *odi)
{
int ret;
/* Auto-detect the osd version */
- ret = _osd_print_system_info(od, caps);
+ ret = _osd_get_print_system_info(od, caps, odi);
if (ret) {
osd_dev_set_ver(od, OSD_VER1);
OSD_DEBUG("converting to OSD1\n");
- ret = _osd_print_system_info(od, caps);
+ ret = _osd_get_print_system_info(od, caps, odi);
}
return ret;
@@ -461,7 +475,8 @@ EXPORT_SYMBOL(osd_end_request);
int osd_execute_request(struct osd_request *or)
{
- return blk_execute_rq(or->request->q, NULL, or->request, 0);
+ return or->async_error =
+ blk_execute_rq(or->request->q, NULL, or->request, 0);
}
EXPORT_SYMBOL(osd_execute_request);
@@ -471,8 +486,12 @@ static void osd_request_async_done(struct request *req, int error)
or->async_error = error;
- if (error)
- OSD_DEBUG("osd_request_async_done error recieved %d\n", error);
+ if (unlikely(error)) {
+ OSD_DEBUG("osd_request_async_done error recieved %d "
+ "errors 0x%x\n", error, req->errors);
+ if (!req->errors) /* don't miss out on this one */
+ req->errors = error;
+ }
if (or->async_done)
or->async_done(or, or->async_private);
@@ -1153,6 +1172,7 @@ int osd_req_decode_get_attr_list(struct osd_request *or,
"c=%d r=%d n=%d\n",
cur_bytes, returned_bytes, n);
oa->val_ptr = NULL;
+ cur_bytes = returned_bytes; /* break the caller loop */
break;
}
@@ -1436,6 +1456,15 @@ int osd_finalize_request(struct osd_request *or,
}
EXPORT_SYMBOL(osd_finalize_request);
+static bool _is_osd_security_code(int code)
+{
+ return (code == osd_security_audit_value_frozen) ||
+ (code == osd_security_working_key_frozen) ||
+ (code == osd_nonce_not_unique) ||
+ (code == osd_nonce_timestamp_out_of_range) ||
+ (code == osd_invalid_dataout_buffer_integrity_check_value);
+}
+
#define OSD_SENSE_PRINT1(fmt, a...) \
do { \
if (__cur_sense_need_output) \
@@ -1458,9 +1487,16 @@ int osd_req_decode_sense_full(struct osd_request *or,
#else
bool __cur_sense_need_output = !silent;
#endif
+ int ret;
- if (!or->request->errors)
+ if (likely(!or->request->errors)) {
+ osi->out_resid = 0;
+ osi->in_resid = 0;
return 0;
+ }
+
+ osi = osi ? : &local_osi;
+ memset(osi, 0, sizeof(*osi));
ssdb = or->request->sense;
sense_len = or->request->sense_len;
@@ -1468,17 +1504,15 @@ int osd_req_decode_sense_full(struct osd_request *or,
OSD_ERR("Block-layer returned error(0x%x) but "
"sense_len(%u) || key(%d) is empty\n",
or->request->errors, sense_len, ssdb->sense_key);
- return -EIO;
+ goto analyze;
}
if ((ssdb->response_code != 0x72) && (ssdb->response_code != 0x73)) {
OSD_ERR("Unrecognized scsi sense: rcode=%x length=%d\n",
ssdb->response_code, sense_len);
- return -EIO;
+ goto analyze;
}
- osi = osi ? : &local_osi;
- memset(osi, 0, sizeof(*osi));
osi->key = ssdb->sense_key;
osi->additional_code = be16_to_cpu(ssdb->additional_sense_code);
original_sense_len = ssdb->additional_sense_length + 8;
@@ -1488,9 +1522,10 @@ int osd_req_decode_sense_full(struct osd_request *or,
__cur_sense_need_output = (osi->key > scsi_sk_recovered_error);
#endif
OSD_SENSE_PRINT1("Main Sense information key=0x%x length(%d, %d) "
- "additional_code=0x%x\n",
+ "additional_code=0x%x async_error=%d errors=0x%x\n",
osi->key, original_sense_len, sense_len,
- osi->additional_code);
+ osi->additional_code, or->async_error,
+ or->request->errors);
if (original_sense_len < sense_len)
sense_len = original_sense_len;
@@ -1569,15 +1604,14 @@ int osd_req_decode_sense_full(struct osd_request *or,
{
struct osd_sense_attributes_data_descriptor
*osadd = cur_descriptor;
- int len = min(cur_len, sense_len);
- int i = 0;
+ unsigned len = min(cur_len, sense_len);
struct osd_sense_attr *pattr = osadd->sense_attrs;
- while (len < 0) {
+ while (len >= sizeof(*pattr)) {
u32 attr_page = be32_to_cpu(pattr->attr_page);
u32 attr_id = be32_to_cpu(pattr->attr_id);
- if (i++ == 0) {
+ if (!osi->attr.attr_page) {
osi->attr.attr_page = attr_page;
osi->attr.attr_id = attr_id;
}
@@ -1588,6 +1622,8 @@ int osd_req_decode_sense_full(struct osd_request *or,
bad_attr_list++;
max_attr--;
}
+
+ len -= sizeof(*pattr);
OSD_SENSE_PRINT2(
"osd_sense_attribute_identification"
"attr_page=0x%x attr_id=0x%x\n",
@@ -1621,7 +1657,50 @@ int osd_req_decode_sense_full(struct osd_request *or,
cur_descriptor += cur_len;
}
- return (osi->key > scsi_sk_recovered_error) ? -EIO : 0;
+analyze:
+ if (!osi->key) {
+ /* scsi sense is Empty, the request was never issued to target
+ * linux return code might tell us what happened.
+ */
+ if (or->async_error == -ENOMEM)
+ osi->osd_err_pri = OSD_ERR_PRI_RESOURCE;
+ else
+ osi->osd_err_pri = OSD_ERR_PRI_UNREACHABLE;
+ ret = or->async_error;
+ } else if (osi->key <= scsi_sk_recovered_error) {
+ osi->osd_err_pri = 0;
+ ret = 0;
+ } else if (osi->additional_code == scsi_invalid_field_in_cdb) {
+ if (osi->cdb_field_offset == OSD_CFO_STARTING_BYTE) {
+ osi->osd_err_pri = OSD_ERR_PRI_CLEAR_PAGES;
+ ret = -EFAULT; /* caller should recover from this */
+ } else if (osi->cdb_field_offset == OSD_CFO_OBJECT_ID) {
+ osi->osd_err_pri = OSD_ERR_PRI_NOT_FOUND;
+ ret = -ENOENT;
+ } else if (osi->cdb_field_offset == OSD_CFO_PERMISSIONS) {
+ osi->osd_err_pri = OSD_ERR_PRI_NO_ACCESS;
+ ret = -EACCES;
+ } else {
+ osi->osd_err_pri = OSD_ERR_PRI_BAD_CRED;
+ ret = -EINVAL;
+ }
+ } else if (osi->additional_code == osd_quota_error) {
+ osi->osd_err_pri = OSD_ERR_PRI_NO_SPACE;
+ ret = -ENOSPC;
+ } else if (_is_osd_security_code(osi->additional_code)) {
+ osi->osd_err_pri = OSD_ERR_PRI_BAD_CRED;
+ ret = -EINVAL;
+ } else {
+ osi->osd_err_pri = OSD_ERR_PRI_EIO;
+ ret = -EIO;
+ }
+
+ if (or->out.req)
+ osi->out_resid = or->out.req->resid_len ?: or->out.total_bytes;
+ if (or->in.req)
+ osi->in_resid = or->in.req->resid_len ?: or->in.total_bytes;
+
+ return ret;
}
EXPORT_SYMBOL(osd_req_decode_sense_full);
diff --git a/drivers/scsi/osd/osd_uld.c b/drivers/scsi/osd/osd_uld.c
index 0bdef339090..0a90702b3d7 100644
--- a/drivers/scsi/osd/osd_uld.c
+++ b/drivers/scsi/osd/osd_uld.c
@@ -71,8 +71,7 @@
#define SCSI_OSD_MAX_MINOR 64
static const char osd_name[] = "osd";
-static const char *osd_version_string = "open-osd 0.1.0";
-const char osd_symlink[] = "scsi_osd";
+static const char *osd_version_string = "open-osd 0.2.0";
MODULE_AUTHOR("Boaz Harrosh <bharrosh@panasas.com>");
MODULE_DESCRIPTION("open-osd Upper-Layer-Driver osd.ko");
@@ -82,15 +81,25 @@ MODULE_ALIAS_SCSI_DEVICE(TYPE_OSD);
struct osd_uld_device {
int minor;
- struct kref kref;
+ struct device class_dev;
struct cdev cdev;
struct osd_dev od;
+ struct osd_dev_info odi;
struct gendisk *disk;
- struct device *class_member;
};
-static void __uld_get(struct osd_uld_device *oud);
-static void __uld_put(struct osd_uld_device *oud);
+struct osd_dev_handle {
+ struct osd_dev od;
+ struct file *file;
+ struct osd_uld_device *oud;
+} ;
+
+static DEFINE_IDA(osd_minor_ida);
+
+static struct class osd_uld_class = {
+ .owner = THIS_MODULE,
+ .name = "scsi_osd",
+};
/*
* Char Device operations
@@ -101,7 +110,7 @@ static int osd_uld_open(struct inode *inode, struct file *file)
struct osd_uld_device *oud = container_of(inode->i_cdev,
struct osd_uld_device, cdev);
- __uld_get(oud);
+ get_device(&oud->class_dev);
/* cache osd_uld_device on file handle */
file->private_data = oud;
OSD_DEBUG("osd_uld_open %p\n", oud);
@@ -114,7 +123,7 @@ static int osd_uld_release(struct inode *inode, struct file *file)
OSD_DEBUG("osd_uld_release %p\n", file->private_data);
file->private_data = NULL;
- __uld_put(oud);
+ put_device(&oud->class_dev);
return 0;
}
@@ -177,7 +186,7 @@ static const struct file_operations osd_fops = {
struct osd_dev *osduld_path_lookup(const char *name)
{
struct osd_uld_device *oud;
- struct osd_dev *od;
+ struct osd_dev_handle *odh;
struct file *file;
int error;
@@ -186,8 +195,8 @@ struct osd_dev *osduld_path_lookup(const char *name)
return ERR_PTR(-EINVAL);
}
- od = kzalloc(sizeof(*od), GFP_KERNEL);
- if (!od)
+ odh = kzalloc(sizeof(*odh), GFP_KERNEL);
+ if (unlikely(!odh))
return ERR_PTR(-ENOMEM);
file = filp_open(name, O_RDWR, 0);
@@ -203,33 +212,134 @@ struct osd_dev *osduld_path_lookup(const char *name)
oud = file->private_data;
- *od = oud->od;
- od->file = file;
+ odh->od = oud->od;
+ odh->file = file;
+ odh->oud = oud;
- return od;
+ return &odh->od;
close_file:
fput(file);
free_od:
- kfree(od);
+ kfree(odh);
return ERR_PTR(error);
}
EXPORT_SYMBOL(osduld_path_lookup);
-void osduld_put_device(struct osd_dev *od)
+static inline bool _the_same_or_null(const u8 *a1, unsigned a1_len,
+ const u8 *a2, unsigned a2_len)
{
+ if (!a2_len) /* User string is Empty means don't care */
+ return true;
+
+ if (a1_len != a2_len)
+ return false;
+
+ return 0 == memcmp(a1, a2, a1_len);
+}
+
+struct find_oud_t {
+ const struct osd_dev_info *odi;
+ struct device *dev;
+ struct osd_uld_device *oud;
+} ;
+
+int _mach_odi(struct device *dev, void *find_data)
+{
+ struct osd_uld_device *oud = container_of(dev, struct osd_uld_device,
+ class_dev);
+ struct find_oud_t *fot = find_data;
+ const struct osd_dev_info *odi = fot->odi;
+
+ if (_the_same_or_null(oud->odi.systemid, oud->odi.systemid_len,
+ odi->systemid, odi->systemid_len) &&
+ _the_same_or_null(oud->odi.osdname, oud->odi.osdname_len,
+ odi->osdname, odi->osdname_len)) {
+ OSD_DEBUG("found device sysid_len=%d osdname=%d\n",
+ odi->systemid_len, odi->osdname_len);
+ fot->oud = oud;
+ return 1;
+ } else {
+ return 0;
+ }
+}
+
+/* osduld_info_lookup - Loop through all devices, return the requested osd_dev.
+ *
+ * if @odi->systemid_len and/or @odi->osdname_len are zero, they act as a don't
+ * care. .e.g if they're both zero /dev/osd0 is returned.
+ */
+struct osd_dev *osduld_info_lookup(const struct osd_dev_info *odi)
+{
+ struct find_oud_t find = {.odi = odi};
+
+ find.dev = class_find_device(&osd_uld_class, NULL, &find, _mach_odi);
+ if (likely(find.dev)) {
+ struct osd_dev_handle *odh = kzalloc(sizeof(*odh), GFP_KERNEL);
+
+ if (unlikely(!odh)) {
+ put_device(find.dev);
+ return ERR_PTR(-ENOMEM);
+ }
+ odh->od = find.oud->od;
+ odh->oud = find.oud;
+
+ return &odh->od;
+ }
+
+ return ERR_PTR(-ENODEV);
+}
+EXPORT_SYMBOL(osduld_info_lookup);
+
+void osduld_put_device(struct osd_dev *od)
+{
if (od && !IS_ERR(od)) {
- struct osd_uld_device *oud = od->file->private_data;
+ struct osd_dev_handle *odh =
+ container_of(od, struct osd_dev_handle, od);
+ struct osd_uld_device *oud = odh->oud;
BUG_ON(od->scsi_device != oud->od.scsi_device);
- fput(od->file);
- kfree(od);
+ /* If scsi has released the device (logout), and exofs has last
+ * reference on oud it will be freed by above osd_uld_release
+ * within fput below. But this will oops in cdev_release which
+ * is called after the fops->release. A get_/put_ pair makes
+ * sure we have a cdev for the duration of fput
+ */
+ if (odh->file) {
+ get_device(&oud->class_dev);
+ fput(odh->file);
+ }
+ put_device(&oud->class_dev);
+ kfree(odh);
}
}
EXPORT_SYMBOL(osduld_put_device);
+const struct osd_dev_info *osduld_device_info(struct osd_dev *od)
+{
+ struct osd_dev_handle *odh =
+ container_of(od, struct osd_dev_handle, od);
+ return &odh->oud->odi;
+}
+EXPORT_SYMBOL(osduld_device_info);
+
+bool osduld_device_same(struct osd_dev *od, const struct osd_dev_info *odi)
+{
+ struct osd_dev_handle *odh =
+ container_of(od, struct osd_dev_handle, od);
+ struct osd_uld_device *oud = odh->oud;
+
+ return (oud->odi.systemid_len == odi->systemid_len) &&
+ _the_same_or_null(oud->odi.systemid, oud->odi.systemid_len,
+ odi->systemid, odi->systemid_len) &&
+ (oud->odi.osdname_len == odi->osdname_len) &&
+ _the_same_or_null(oud->odi.osdname, oud->odi.osdname_len,
+ odi->osdname, odi->osdname_len);
+}
+EXPORT_SYMBOL(osduld_device_same);
+
/*
* Scsi Device operations
*/
@@ -250,14 +360,35 @@ static int __detect_osd(struct osd_uld_device *oud)
OSD_ERR("warning: scsi_test_unit_ready failed\n");
osd_sec_init_nosec_doall_caps(caps, &osd_root_object, false, true);
- if (osd_auto_detect_ver(&oud->od, caps))
+ if (osd_auto_detect_ver(&oud->od, caps, &oud->odi))
return -ENODEV;
return 0;
}
-static struct class *osd_sysfs_class;
-static DEFINE_IDA(osd_minor_ida);
+static void __remove(struct device *dev)
+{
+ struct osd_uld_device *oud = container_of(dev, struct osd_uld_device,
+ class_dev);
+ struct scsi_device *scsi_device = oud->od.scsi_device;
+
+ kfree(oud->odi.osdname);
+
+ if (oud->cdev.owner)
+ cdev_del(&oud->cdev);
+
+ osd_dev_fini(&oud->od);
+ scsi_device_put(scsi_device);
+
+ OSD_INFO("osd_remove %s\n",
+ oud->disk ? oud->disk->disk_name : NULL);
+
+ if (oud->disk)
+ put_disk(oud->disk);
+ ida_remove(&osd_minor_ida, oud->minor);
+
+ kfree(oud);
+}
static int osd_probe(struct device *dev)
{
@@ -289,7 +420,6 @@ static int osd_probe(struct device *dev)
if (NULL == oud)
goto err_retract_minor;
- kref_init(&oud->kref);
dev_set_drvdata(dev, oud);
oud->minor = minor;
@@ -327,18 +457,25 @@ static int osd_probe(struct device *dev)
OSD_ERR("cdev_add failed\n");
goto err_put_disk;
}
- kobject_get(&oud->cdev.kobj); /* 2nd ref see osd_remove() */
-
- /* class_member */
- oud->class_member = device_create(osd_sysfs_class, dev,
- MKDEV(SCSI_OSD_MAJOR, oud->minor), "%s", disk->disk_name);
- if (IS_ERR(oud->class_member)) {
- OSD_ERR("class_device_create failed\n");
- error = PTR_ERR(oud->class_member);
+
+ /* class device member */
+ oud->class_dev.devt = oud->cdev.dev;
+ oud->class_dev.class = &osd_uld_class;
+ oud->class_dev.parent = dev;
+ oud->class_dev.release = __remove;
+ error = dev_set_name(&oud->class_dev, disk->disk_name);
+ if (error) {
+ OSD_ERR("dev_set_name failed => %d\n", error);
+ goto err_put_cdev;
+ }
+
+ error = device_register(&oud->class_dev);
+ if (error) {
+ OSD_ERR("device_register failed => %d\n", error);
goto err_put_cdev;
}
- dev_set_drvdata(oud->class_member, oud);
+ get_device(&oud->class_dev);
OSD_INFO("osd_probe %s\n", disk->disk_name);
return 0;
@@ -367,54 +504,12 @@ static int osd_remove(struct device *dev)
scsi_device);
}
- if (oud->class_member)
- device_destroy(osd_sysfs_class,
- MKDEV(SCSI_OSD_MAJOR, oud->minor));
+ device_unregister(&oud->class_dev);
- /* We have 2 references to the cdev. One is released here
- * and also takes down the /dev/osdX mapping. The second
- * Will be released in __remove() after all users have released
- * the osd_uld_device.
- */
- if (oud->cdev.owner)
- cdev_del(&oud->cdev);
-
- __uld_put(oud);
+ put_device(&oud->class_dev);
return 0;
}
-static void __remove(struct kref *kref)
-{
- struct osd_uld_device *oud = container_of(kref,
- struct osd_uld_device, kref);
- struct scsi_device *scsi_device = oud->od.scsi_device;
-
- /* now let delete the char_dev */
- kobject_put(&oud->cdev.kobj);
-
- osd_dev_fini(&oud->od);
- scsi_device_put(scsi_device);
-
- OSD_INFO("osd_remove %s\n",
- oud->disk ? oud->disk->disk_name : NULL);
-
- if (oud->disk)
- put_disk(oud->disk);
-
- ida_remove(&osd_minor_ida, oud->minor);
- kfree(oud);
-}
-
-static void __uld_get(struct osd_uld_device *oud)
-{
- kref_get(&oud->kref);
-}
-
-static void __uld_put(struct osd_uld_device *oud)
-{
- kref_put(&oud->kref, __remove);
-}
-
/*
* Global driver and scsi registration
*/
@@ -432,11 +527,10 @@ static int __init osd_uld_init(void)
{
int err;
- osd_sysfs_class = class_create(THIS_MODULE, osd_symlink);
- if (IS_ERR(osd_sysfs_class)) {
- OSD_ERR("Unable to register sysfs class => %ld\n",
- PTR_ERR(osd_sysfs_class));
- return PTR_ERR(osd_sysfs_class);
+ err = class_register(&osd_uld_class);
+ if (err) {
+ OSD_ERR("Unable to register sysfs class => %d\n", err);
+ return err;
}
err = register_chrdev_region(MKDEV(SCSI_OSD_MAJOR, 0),
@@ -459,7 +553,7 @@ static int __init osd_uld_init(void)
err_out_chrdev:
unregister_chrdev_region(MKDEV(SCSI_OSD_MAJOR, 0), SCSI_OSD_MAX_MINOR);
err_out:
- class_destroy(osd_sysfs_class);
+ class_unregister(&osd_uld_class);
return err;
}
@@ -467,7 +561,7 @@ static void __exit osd_uld_exit(void)
{
scsi_unregister_driver(&osd_driver.gendrv);
unregister_chrdev_region(MKDEV(SCSI_OSD_MAJOR, 0), SCSI_OSD_MAX_MINOR);
- class_destroy(osd_sysfs_class);
+ class_unregister(&osd_uld_class);
OSD_INFO("UNLOADED %s\n", osd_version_string);
}
diff --git a/drivers/scsi/pm8001/Makefile b/drivers/scsi/pm8001/Makefile
new file mode 100644
index 00000000000..52f04296171
--- /dev/null
+++ b/drivers/scsi/pm8001/Makefile
@@ -0,0 +1,12 @@
+#
+# Kernel configuration file for the PM8001 SAS/SATA 8x6G based HBA driver
+#
+# Copyright (C) 2008-2009 USI Co., Ltd.
+
+
+obj-$(CONFIG_SCSI_PM8001) += pm8001.o
+pm8001-y += pm8001_init.o \
+ pm8001_sas.o \
+ pm8001_ctl.o \
+ pm8001_hwi.o
+
diff --git a/drivers/scsi/pm8001/pm8001_chips.h b/drivers/scsi/pm8001/pm8001_chips.h
new file mode 100644
index 00000000000..4efa4d0950e
--- /dev/null
+++ b/drivers/scsi/pm8001/pm8001_chips.h
@@ -0,0 +1,89 @@
+/*
+ * PMC-Sierra SPC 8001 SAS/SATA based host adapters driver
+ *
+ * Copyright (c) 2008-2009 USI Co., Ltd.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions, and the following disclaimer,
+ * without modification.
+ * 2. Redistributions in binary form must reproduce at minimum a disclaimer
+ * substantially similar to the "NO WARRANTY" disclaimer below
+ * ("Disclaimer") and any redistribution must be conditioned upon
+ * including a substantially similar Disclaimer requirement for further
+ * binary redistribution.
+ * 3. Neither the names of the above-listed copyright holders nor the names
+ * of any contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2 as published by the Free
+ * Software Foundation.
+ *
+ * NO WARRANTY
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ */
+
+#ifndef _PM8001_CHIPS_H_
+#define _PM8001_CHIPS_H_
+
+static inline u32 pm8001_read_32(void *virt_addr)
+{
+ return *((u32 *)virt_addr);
+}
+
+static inline void pm8001_write_32(void *addr, u32 offset, u32 val)
+{
+ *((u32 *)(addr + offset)) = val;
+}
+
+static inline u32 pm8001_cr32(struct pm8001_hba_info *pm8001_ha, u32 bar,
+ u32 offset)
+{
+ return readl(pm8001_ha->io_mem[bar].memvirtaddr + offset);
+}
+
+static inline void pm8001_cw32(struct pm8001_hba_info *pm8001_ha, u32 bar,
+ u32 addr, u32 val)
+{
+ writel(val, pm8001_ha->io_mem[bar].memvirtaddr + addr);
+}
+static inline u32 pm8001_mr32(void __iomem *addr, u32 offset)
+{
+ return readl(addr + offset);
+}
+static inline void pm8001_mw32(void __iomem *addr, u32 offset, u32 val)
+{
+ writel(val, addr + offset);
+}
+static inline u32 get_pci_bar_index(u32 pcibar)
+{
+ switch (pcibar) {
+ case 0x18:
+ case 0x1C:
+ return 1;
+ case 0x20:
+ return 2;
+ case 0x24:
+ return 3;
+ default:
+ return 0;
+ }
+}
+
+#endif /* _PM8001_CHIPS_H_ */
+
diff --git a/drivers/scsi/pm8001/pm8001_ctl.c b/drivers/scsi/pm8001/pm8001_ctl.c
new file mode 100644
index 00000000000..14b13acae6d
--- /dev/null
+++ b/drivers/scsi/pm8001/pm8001_ctl.c
@@ -0,0 +1,573 @@
+/*
+ * PMC-Sierra SPC 8001 SAS/SATA based host adapters driver
+ *
+ * Copyright (c) 2008-2009 USI Co., Ltd.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions, and the following disclaimer,
+ * without modification.
+ * 2. Redistributions in binary form must reproduce at minimum a disclaimer
+ * substantially similar to the "NO WARRANTY" disclaimer below
+ * ("Disclaimer") and any redistribution must be conditioned upon
+ * including a substantially similar Disclaimer requirement for further
+ * binary redistribution.
+ * 3. Neither the names of the above-listed copyright holders nor the names
+ * of any contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2 as published by the Free
+ * Software Foundation.
+ *
+ * NO WARRANTY
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ */
+#include <linux/firmware.h>
+#include "pm8001_sas.h"
+#include "pm8001_ctl.h"
+
+/* scsi host attributes */
+
+/**
+ * pm8001_ctl_mpi_interface_rev_show - MPI interface revision number
+ * @cdev: pointer to embedded class device
+ * @buf: the buffer returned
+ *
+ * A sysfs 'read-only' shost attribute.
+ */
+static ssize_t pm8001_ctl_mpi_interface_rev_show(struct device *cdev,
+ struct device_attribute *attr, char *buf)
+{
+ struct Scsi_Host *shost = class_to_shost(cdev);
+ struct sas_ha_struct *sha = SHOST_TO_SAS_HA(shost);
+ struct pm8001_hba_info *pm8001_ha = sha->lldd_ha;
+
+ return snprintf(buf, PAGE_SIZE, "%d\n",
+ pm8001_ha->main_cfg_tbl.interface_rev);
+}
+static
+DEVICE_ATTR(interface_rev, S_IRUGO, pm8001_ctl_mpi_interface_rev_show, NULL);
+
+/**
+ * pm8001_ctl_fw_version_show - firmware version
+ * @cdev: pointer to embedded class device
+ * @buf: the buffer returned
+ *
+ * A sysfs 'read-only' shost attribute.
+ */
+static ssize_t pm8001_ctl_fw_version_show(struct device *cdev,
+ struct device_attribute *attr, char *buf)
+{
+ struct Scsi_Host *shost = class_to_shost(cdev);
+ struct sas_ha_struct *sha = SHOST_TO_SAS_HA(shost);
+ struct pm8001_hba_info *pm8001_ha = sha->lldd_ha;
+
+ return snprintf(buf, PAGE_SIZE, "%02x.%02x.%02x.%02x\n",
+ (u8)(pm8001_ha->main_cfg_tbl.firmware_rev >> 24),
+ (u8)(pm8001_ha->main_cfg_tbl.firmware_rev >> 16),
+ (u8)(pm8001_ha->main_cfg_tbl.firmware_rev >> 8),
+ (u8)(pm8001_ha->main_cfg_tbl.firmware_rev));
+}
+static DEVICE_ATTR(fw_version, S_IRUGO, pm8001_ctl_fw_version_show, NULL);
+/**
+ * pm8001_ctl_max_out_io_show - max outstanding io supported
+ * @cdev: pointer to embedded class device
+ * @buf: the buffer returned
+ *
+ * A sysfs 'read-only' shost attribute.
+ */
+static ssize_t pm8001_ctl_max_out_io_show(struct device *cdev,
+ struct device_attribute *attr, char *buf)
+{
+ struct Scsi_Host *shost = class_to_shost(cdev);
+ struct sas_ha_struct *sha = SHOST_TO_SAS_HA(shost);
+ struct pm8001_hba_info *pm8001_ha = sha->lldd_ha;
+
+ return snprintf(buf, PAGE_SIZE, "%d\n",
+ pm8001_ha->main_cfg_tbl.max_out_io);
+}
+static DEVICE_ATTR(max_out_io, S_IRUGO, pm8001_ctl_max_out_io_show, NULL);
+/**
+ * pm8001_ctl_max_devices_show - max devices support
+ * @cdev: pointer to embedded class device
+ * @buf: the buffer returned
+ *
+ * A sysfs 'read-only' shost attribute.
+ */
+static ssize_t pm8001_ctl_max_devices_show(struct device *cdev,
+ struct device_attribute *attr, char *buf)
+{
+ struct Scsi_Host *shost = class_to_shost(cdev);
+ struct sas_ha_struct *sha = SHOST_TO_SAS_HA(shost);
+ struct pm8001_hba_info *pm8001_ha = sha->lldd_ha;
+
+ return snprintf(buf, PAGE_SIZE, "%04d\n",
+ (u16)(pm8001_ha->main_cfg_tbl.max_sgl >> 16));
+}
+static DEVICE_ATTR(max_devices, S_IRUGO, pm8001_ctl_max_devices_show, NULL);
+/**
+ * pm8001_ctl_max_sg_list_show - max sg list supported iff not 0.0 for no
+ * hardware limitation
+ * @cdev: pointer to embedded class device
+ * @buf: the buffer returned
+ *
+ * A sysfs 'read-only' shost attribute.
+ */
+static ssize_t pm8001_ctl_max_sg_list_show(struct device *cdev,
+ struct device_attribute *attr, char *buf)
+{
+ struct Scsi_Host *shost = class_to_shost(cdev);
+ struct sas_ha_struct *sha = SHOST_TO_SAS_HA(shost);
+ struct pm8001_hba_info *pm8001_ha = sha->lldd_ha;
+
+ return snprintf(buf, PAGE_SIZE, "%04d\n",
+ pm8001_ha->main_cfg_tbl.max_sgl & 0x0000FFFF);
+}
+static DEVICE_ATTR(max_sg_list, S_IRUGO, pm8001_ctl_max_sg_list_show, NULL);
+
+#define SAS_1_0 0x1
+#define SAS_1_1 0x2
+#define SAS_2_0 0x4
+
+static ssize_t
+show_sas_spec_support_status(unsigned int mode, char *buf)
+{
+ ssize_t len = 0;
+
+ if (mode & SAS_1_1)
+ len = sprintf(buf, "%s", "SAS1.1");
+ if (mode & SAS_2_0)
+ len += sprintf(buf + len, "%s%s", len ? ", " : "", "SAS2.0");
+ len += sprintf(buf + len, "\n");
+
+ return len;
+}
+
+/**
+ * pm8001_ctl_sas_spec_support_show - sas spec supported
+ * @cdev: pointer to embedded class device
+ * @buf: the buffer returned
+ *
+ * A sysfs 'read-only' shost attribute.
+ */
+static ssize_t pm8001_ctl_sas_spec_support_show(struct device *cdev,
+ struct device_attribute *attr, char *buf)
+{
+ unsigned int mode;
+ struct Scsi_Host *shost = class_to_shost(cdev);
+ struct sas_ha_struct *sha = SHOST_TO_SAS_HA(shost);
+ struct pm8001_hba_info *pm8001_ha = sha->lldd_ha;
+ mode = (pm8001_ha->main_cfg_tbl.ctrl_cap_flag & 0xfe000000)>>25;
+ return show_sas_spec_support_status(mode, buf);
+}
+static DEVICE_ATTR(sas_spec_support, S_IRUGO,
+ pm8001_ctl_sas_spec_support_show, NULL);
+
+/**
+ * pm8001_ctl_sas_address_show - sas address
+ * @cdev: pointer to embedded class device
+ * @buf: the buffer returned
+ *
+ * This is the controller sas address
+ *
+ * A sysfs 'read-only' shost attribute.
+ */
+static ssize_t pm8001_ctl_host_sas_address_show(struct device *cdev,
+ struct device_attribute *attr, char *buf)
+{
+ struct Scsi_Host *shost = class_to_shost(cdev);
+ struct sas_ha_struct *sha = SHOST_TO_SAS_HA(shost);
+ struct pm8001_hba_info *pm8001_ha = sha->lldd_ha;
+ return snprintf(buf, PAGE_SIZE, "0x%016llx\n",
+ be64_to_cpu(*(__be64 *)pm8001_ha->sas_addr));
+}
+static DEVICE_ATTR(host_sas_address, S_IRUGO,
+ pm8001_ctl_host_sas_address_show, NULL);
+
+/**
+ * pm8001_ctl_logging_level_show - logging level
+ * @cdev: pointer to embedded class device
+ * @buf: the buffer returned
+ *
+ * A sysfs 'read/write' shost attribute.
+ */
+static ssize_t pm8001_ctl_logging_level_show(struct device *cdev,
+ struct device_attribute *attr, char *buf)
+{
+ struct Scsi_Host *shost = class_to_shost(cdev);
+ struct sas_ha_struct *sha = SHOST_TO_SAS_HA(shost);
+ struct pm8001_hba_info *pm8001_ha = sha->lldd_ha;
+
+ return snprintf(buf, PAGE_SIZE, "%08xh\n", pm8001_ha->logging_level);
+}
+static ssize_t pm8001_ctl_logging_level_store(struct device *cdev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ struct Scsi_Host *shost = class_to_shost(cdev);
+ struct sas_ha_struct *sha = SHOST_TO_SAS_HA(shost);
+ struct pm8001_hba_info *pm8001_ha = sha->lldd_ha;
+ int val = 0;
+
+ if (sscanf(buf, "%x", &val) != 1)
+ return -EINVAL;
+
+ pm8001_ha->logging_level = val;
+ return strlen(buf);
+}
+
+static DEVICE_ATTR(logging_level, S_IRUGO | S_IWUSR,
+ pm8001_ctl_logging_level_show, pm8001_ctl_logging_level_store);
+/**
+ * pm8001_ctl_aap_log_show - aap1 event log
+ * @cdev: pointer to embedded class device
+ * @buf: the buffer returned
+ *
+ * A sysfs 'read-only' shost attribute.
+ */
+static ssize_t pm8001_ctl_aap_log_show(struct device *cdev,
+ struct device_attribute *attr, char *buf)
+{
+ struct Scsi_Host *shost = class_to_shost(cdev);
+ struct sas_ha_struct *sha = SHOST_TO_SAS_HA(shost);
+ struct pm8001_hba_info *pm8001_ha = sha->lldd_ha;
+ int i;
+#define AAP1_MEMMAP(r, c) \
+ (*(u32 *)((u8*)pm8001_ha->memoryMap.region[AAP1].virt_ptr + (r) * 32 \
+ + (c)))
+
+ char *str = buf;
+ int max = 2;
+ for (i = 0; i < max; i++) {
+ str += sprintf(str, "0x%08x 0x%08x 0x%08x 0x%08x 0x%08x 0x%08x"
+ "0x%08x 0x%08x\n",
+ AAP1_MEMMAP(i, 0),
+ AAP1_MEMMAP(i, 4),
+ AAP1_MEMMAP(i, 8),
+ AAP1_MEMMAP(i, 12),
+ AAP1_MEMMAP(i, 16),
+ AAP1_MEMMAP(i, 20),
+ AAP1_MEMMAP(i, 24),
+ AAP1_MEMMAP(i, 28));
+ }
+
+ return str - buf;
+}
+static DEVICE_ATTR(aap_log, S_IRUGO, pm8001_ctl_aap_log_show, NULL);
+/**
+ * pm8001_ctl_aap_log_show - IOP event log
+ * @cdev: pointer to embedded class device
+ * @buf: the buffer returned
+ *
+ * A sysfs 'read-only' shost attribute.
+ */
+static ssize_t pm8001_ctl_iop_log_show(struct device *cdev,
+ struct device_attribute *attr, char *buf)
+{
+ struct Scsi_Host *shost = class_to_shost(cdev);
+ struct sas_ha_struct *sha = SHOST_TO_SAS_HA(shost);
+ struct pm8001_hba_info *pm8001_ha = sha->lldd_ha;
+#define IOP_MEMMAP(r, c) \
+ (*(u32 *)((u8*)pm8001_ha->memoryMap.region[IOP].virt_ptr + (r) * 32 \
+ + (c)))
+ int i;
+ char *str = buf;
+ int max = 2;
+ for (i = 0; i < max; i++) {
+ str += sprintf(str, "0x%08x 0x%08x 0x%08x 0x%08x 0x%08x 0x%08x"
+ "0x%08x 0x%08x\n",
+ IOP_MEMMAP(i, 0),
+ IOP_MEMMAP(i, 4),
+ IOP_MEMMAP(i, 8),
+ IOP_MEMMAP(i, 12),
+ IOP_MEMMAP(i, 16),
+ IOP_MEMMAP(i, 20),
+ IOP_MEMMAP(i, 24),
+ IOP_MEMMAP(i, 28));
+ }
+
+ return str - buf;
+}
+static DEVICE_ATTR(iop_log, S_IRUGO, pm8001_ctl_iop_log_show, NULL);
+
+#define FLASH_CMD_NONE 0x00
+#define FLASH_CMD_UPDATE 0x01
+#define FLASH_CMD_SET_NVMD 0x02
+
+struct flash_command {
+ u8 command[8];
+ int code;
+};
+
+static struct flash_command flash_command_table[] =
+{
+ {"set_nvmd", FLASH_CMD_SET_NVMD},
+ {"update", FLASH_CMD_UPDATE},
+ {"", FLASH_CMD_NONE} /* Last entry should be NULL. */
+};
+
+struct error_fw {
+ char *reason;
+ int err_code;
+};
+
+static struct error_fw flash_error_table[] =
+{
+ {"Failed to open fw image file", FAIL_OPEN_BIOS_FILE},
+ {"image header mismatch", FLASH_UPDATE_HDR_ERR},
+ {"image offset mismatch", FLASH_UPDATE_OFFSET_ERR},
+ {"image CRC Error", FLASH_UPDATE_CRC_ERR},
+ {"image length Error.", FLASH_UPDATE_LENGTH_ERR},
+ {"Failed to program flash chip", FLASH_UPDATE_HW_ERR},
+ {"Flash chip not supported.", FLASH_UPDATE_DNLD_NOT_SUPPORTED},
+ {"Flash update disabled.", FLASH_UPDATE_DISABLED},
+ {"Flash in progress", FLASH_IN_PROGRESS},
+ {"Image file size Error", FAIL_FILE_SIZE},
+ {"Input parameter error", FAIL_PARAMETERS},
+ {"Out of memory", FAIL_OUT_MEMORY},
+ {"OK", 0} /* Last entry err_code = 0. */
+};
+
+static int pm8001_set_nvmd(struct pm8001_hba_info *pm8001_ha)
+{
+ struct pm8001_ioctl_payload *payload;
+ DECLARE_COMPLETION_ONSTACK(completion);
+ u8 *ioctlbuffer = NULL;
+ u32 length = 0;
+ u32 ret = 0;
+
+ length = 1024 * 5 + sizeof(*payload) - 1;
+ ioctlbuffer = kzalloc(length, GFP_KERNEL);
+ if (!ioctlbuffer)
+ return -ENOMEM;
+ if ((pm8001_ha->fw_image->size <= 0) ||
+ (pm8001_ha->fw_image->size > 4096)) {
+ ret = FAIL_FILE_SIZE;
+ goto out;
+ }
+ payload = (struct pm8001_ioctl_payload *)ioctlbuffer;
+ memcpy((u8 *)payload->func_specific, (u8 *)pm8001_ha->fw_image->data,
+ pm8001_ha->fw_image->size);
+ payload->length = pm8001_ha->fw_image->size;
+ payload->id = 0;
+ pm8001_ha->nvmd_completion = &completion;
+ ret = PM8001_CHIP_DISP->set_nvmd_req(pm8001_ha, payload);
+ wait_for_completion(&completion);
+out:
+ kfree(ioctlbuffer);
+ return ret;
+}
+
+static int pm8001_update_flash(struct pm8001_hba_info *pm8001_ha)
+{
+ struct pm8001_ioctl_payload *payload;
+ DECLARE_COMPLETION_ONSTACK(completion);
+ u8 *ioctlbuffer = NULL;
+ u32 length = 0;
+ struct fw_control_info *fwControl;
+ u32 loopNumber, loopcount = 0;
+ u32 sizeRead = 0;
+ u32 partitionSize, partitionSizeTmp;
+ u32 ret = 0;
+ u32 partitionNumber = 0;
+ struct pm8001_fw_image_header *image_hdr;
+
+ length = 1024 * 16 + sizeof(*payload) - 1;
+ ioctlbuffer = kzalloc(length, GFP_KERNEL);
+ image_hdr = (struct pm8001_fw_image_header *)pm8001_ha->fw_image->data;
+ if (!ioctlbuffer)
+ return -ENOMEM;
+ if (pm8001_ha->fw_image->size < 28) {
+ ret = FAIL_FILE_SIZE;
+ goto out;
+ }
+
+ while (sizeRead < pm8001_ha->fw_image->size) {
+ partitionSizeTmp =
+ *(u32 *)((u8 *)&image_hdr->image_length + sizeRead);
+ partitionSize = be32_to_cpu(partitionSizeTmp);
+ loopcount = (partitionSize + HEADER_LEN)/IOCTL_BUF_SIZE;
+ if (loopcount % IOCTL_BUF_SIZE)
+ loopcount++;
+ if (loopcount == 0)
+ loopcount++;
+ for (loopNumber = 0; loopNumber < loopcount; loopNumber++) {
+ payload = (struct pm8001_ioctl_payload *)ioctlbuffer;
+ payload->length = 1024*16;
+ payload->id = 0;
+ fwControl =
+ (struct fw_control_info *)payload->func_specific;
+ fwControl->len = IOCTL_BUF_SIZE; /* IN */
+ fwControl->size = partitionSize + HEADER_LEN;/* IN */
+ fwControl->retcode = 0;/* OUT */
+ fwControl->offset = loopNumber * IOCTL_BUF_SIZE;/*OUT */
+
+ /* for the last chunk of data in case file size is not even with
+ 4k, load only the rest*/
+ if (((loopcount-loopNumber) == 1) &&
+ ((partitionSize + HEADER_LEN) % IOCTL_BUF_SIZE)) {
+ fwControl->len =
+ (partitionSize + HEADER_LEN) % IOCTL_BUF_SIZE;
+ memcpy((u8 *)fwControl->buffer,
+ (u8 *)pm8001_ha->fw_image->data + sizeRead,
+ (partitionSize + HEADER_LEN) % IOCTL_BUF_SIZE);
+ sizeRead +=
+ (partitionSize + HEADER_LEN) % IOCTL_BUF_SIZE;
+ } else {
+ memcpy((u8 *)fwControl->buffer,
+ (u8 *)pm8001_ha->fw_image->data + sizeRead,
+ IOCTL_BUF_SIZE);
+ sizeRead += IOCTL_BUF_SIZE;
+ }
+
+ pm8001_ha->nvmd_completion = &completion;
+ ret = PM8001_CHIP_DISP->fw_flash_update_req(pm8001_ha, payload);
+ wait_for_completion(&completion);
+ if (ret || (fwControl->retcode > FLASH_UPDATE_IN_PROGRESS)) {
+ ret = fwControl->retcode;
+ kfree(ioctlbuffer);
+ ioctlbuffer = NULL;
+ break;
+ }
+ }
+ if (ret)
+ break;
+ partitionNumber++;
+}
+out:
+ kfree(ioctlbuffer);
+ return ret;
+}
+static ssize_t pm8001_store_update_fw(struct device *cdev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct Scsi_Host *shost = class_to_shost(cdev);
+ struct sas_ha_struct *sha = SHOST_TO_SAS_HA(shost);
+ struct pm8001_hba_info *pm8001_ha = sha->lldd_ha;
+ char *cmd_ptr, *filename_ptr;
+ int res, i;
+ int flash_command = FLASH_CMD_NONE;
+ int err = 0;
+ if (!capable(CAP_SYS_ADMIN))
+ return -EACCES;
+
+ cmd_ptr = kzalloc(count*2, GFP_KERNEL);
+
+ if (!cmd_ptr) {
+ err = FAIL_OUT_MEMORY;
+ goto out;
+ }
+
+ filename_ptr = cmd_ptr + count;
+ res = sscanf(buf, "%s %s", cmd_ptr, filename_ptr);
+ if (res != 2) {
+ err = FAIL_PARAMETERS;
+ goto out1;
+ }
+
+ for (i = 0; flash_command_table[i].code != FLASH_CMD_NONE; i++) {
+ if (!memcmp(flash_command_table[i].command,
+ cmd_ptr, strlen(cmd_ptr))) {
+ flash_command = flash_command_table[i].code;
+ break;
+ }
+ }
+ if (flash_command == FLASH_CMD_NONE) {
+ err = FAIL_PARAMETERS;
+ goto out1;
+ }
+
+ if (pm8001_ha->fw_status == FLASH_IN_PROGRESS) {
+ err = FLASH_IN_PROGRESS;
+ goto out1;
+ }
+ err = request_firmware(&pm8001_ha->fw_image,
+ filename_ptr,
+ pm8001_ha->dev);
+
+ if (err) {
+ PM8001_FAIL_DBG(pm8001_ha,
+ pm8001_printk("Failed to load firmware image file %s,"
+ " error %d\n", filename_ptr, err));
+ err = FAIL_OPEN_BIOS_FILE;
+ goto out1;
+ }
+
+ switch (flash_command) {
+ case FLASH_CMD_UPDATE:
+ pm8001_ha->fw_status = FLASH_IN_PROGRESS;
+ err = pm8001_update_flash(pm8001_ha);
+ break;
+ case FLASH_CMD_SET_NVMD:
+ pm8001_ha->fw_status = FLASH_IN_PROGRESS;
+ err = pm8001_set_nvmd(pm8001_ha);
+ break;
+ default:
+ pm8001_ha->fw_status = FAIL_PARAMETERS;
+ err = FAIL_PARAMETERS;
+ break;
+ }
+ release_firmware(pm8001_ha->fw_image);
+out1:
+ kfree(cmd_ptr);
+out:
+ pm8001_ha->fw_status = err;
+
+ if (!err)
+ return count;
+ else
+ return -err;
+}
+
+static ssize_t pm8001_show_update_fw(struct device *cdev,
+ struct device_attribute *attr, char *buf)
+{
+ int i;
+ struct Scsi_Host *shost = class_to_shost(cdev);
+ struct sas_ha_struct *sha = SHOST_TO_SAS_HA(shost);
+ struct pm8001_hba_info *pm8001_ha = sha->lldd_ha;
+
+ for (i = 0; flash_error_table[i].err_code != 0; i++) {
+ if (flash_error_table[i].err_code == pm8001_ha->fw_status)
+ break;
+ }
+ if (pm8001_ha->fw_status != FLASH_IN_PROGRESS)
+ pm8001_ha->fw_status = FLASH_OK;
+
+ return snprintf(buf, PAGE_SIZE, "status=%x %s\n",
+ flash_error_table[i].err_code,
+ flash_error_table[i].reason);
+}
+
+static DEVICE_ATTR(update_fw, S_IRUGO|S_IWUGO,
+ pm8001_show_update_fw, pm8001_store_update_fw);
+struct device_attribute *pm8001_host_attrs[] = {
+ &dev_attr_interface_rev,
+ &dev_attr_fw_version,
+ &dev_attr_update_fw,
+ &dev_attr_aap_log,
+ &dev_attr_iop_log,
+ &dev_attr_max_out_io,
+ &dev_attr_max_devices,
+ &dev_attr_max_sg_list,
+ &dev_attr_sas_spec_support,
+ &dev_attr_logging_level,
+ &dev_attr_host_sas_address,
+ NULL,
+};
+
diff --git a/drivers/scsi/pm8001/pm8001_ctl.h b/drivers/scsi/pm8001/pm8001_ctl.h
new file mode 100644
index 00000000000..22644de2639
--- /dev/null
+++ b/drivers/scsi/pm8001/pm8001_ctl.h
@@ -0,0 +1,67 @@
+ /*
+ * PMC-Sierra SPC 8001 SAS/SATA based host adapters driver
+ *
+ * Copyright (c) 2008-2009 USI Co., Ltd.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions, and the following disclaimer,
+ * without modification.
+ * 2. Redistributions in binary form must reproduce at minimum a disclaimer
+ * substantially similar to the "NO WARRANTY" disclaimer below
+ * ("Disclaimer") and any redistribution must be conditioned upon
+ * including a substantially similar Disclaimer requirement for further
+ * binary redistribution.
+ * 3. Neither the names of the above-listed copyright holders nor the names
+ * of any contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2 as published by the Free
+ * Software Foundation.
+ *
+ * NO WARRANTY
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ */
+
+#ifndef PM8001_CTL_H_INCLUDED
+#define PM8001_CTL_H_INCLUDED
+
+#define IOCTL_BUF_SIZE 4096
+#define HEADER_LEN 28
+#define SIZE_OFFSET 16
+
+struct pm8001_ioctl_payload {
+ u32 signature;
+ u16 major_function;
+ u16 minor_function;
+ u16 length;
+ u16 status;
+ u16 offset;
+ u16 id;
+ u8 func_specific[1];
+};
+
+#define FLASH_OK 0x000000
+#define FAIL_OPEN_BIOS_FILE 0x000100
+#define FAIL_FILE_SIZE 0x000a00
+#define FAIL_PARAMETERS 0x000b00
+#define FAIL_OUT_MEMORY 0x000c00
+#define FLASH_IN_PROGRESS 0x001000
+
+#endif /* PM8001_CTL_H_INCLUDED */
+
diff --git a/drivers/scsi/pm8001/pm8001_defs.h b/drivers/scsi/pm8001/pm8001_defs.h
new file mode 100644
index 00000000000..944afada61e
--- /dev/null
+++ b/drivers/scsi/pm8001/pm8001_defs.h
@@ -0,0 +1,112 @@
+/*
+ * PMC-Sierra SPC 8001 SAS/SATA based host adapters driver
+ *
+ * Copyright (c) 2008-2009 USI Co., Ltd.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions, and the following disclaimer,
+ * without modification.
+ * 2. Redistributions in binary form must reproduce at minimum a disclaimer
+ * substantially similar to the "NO WARRANTY" disclaimer below
+ * ("Disclaimer") and any redistribution must be conditioned upon
+ * including a substantially similar Disclaimer requirement for further
+ * binary redistribution.
+ * 3. Neither the names of the above-listed copyright holders nor the names
+ * of any contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2 as published by the Free
+ * Software Foundation.
+ *
+ * NO WARRANTY
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ */
+
+#ifndef _PM8001_DEFS_H_
+#define _PM8001_DEFS_H_
+
+enum chip_flavors {
+ chip_8001,
+};
+#define USI_MAX_MEMCNT 9
+#define PM8001_MAX_DMA_SG SG_ALL
+enum phy_speed {
+ PHY_SPEED_15 = 0x01,
+ PHY_SPEED_30 = 0x02,
+ PHY_SPEED_60 = 0x04,
+};
+
+enum data_direction {
+ DATA_DIR_NONE = 0x0, /* NO TRANSFER */
+ DATA_DIR_IN = 0x01, /* INBOUND */
+ DATA_DIR_OUT = 0x02, /* OUTBOUND */
+ DATA_DIR_BYRECIPIENT = 0x04, /* UNSPECIFIED */
+};
+
+enum port_type {
+ PORT_TYPE_SAS = (1L << 1),
+ PORT_TYPE_SATA = (1L << 0),
+};
+
+/* driver compile-time configuration */
+#define PM8001_MAX_CCB 512 /* max ccbs supported */
+#define PM8001_MAX_INB_NUM 1
+#define PM8001_MAX_OUTB_NUM 1
+#define PM8001_CAN_QUEUE 128 /* SCSI Queue depth */
+
+/* unchangeable hardware details */
+#define PM8001_MAX_PHYS 8 /* max. possible phys */
+#define PM8001_MAX_PORTS 8 /* max. possible ports */
+#define PM8001_MAX_DEVICES 1024 /* max supported device */
+
+enum memory_region_num {
+ AAP1 = 0x0, /* application acceleration processor */
+ IOP, /* IO processor */
+ CI, /* consumer index */
+ PI, /* producer index */
+ IB, /* inbound queue */
+ OB, /* outbound queue */
+ NVMD, /* NVM device */
+ DEV_MEM, /* memory for devices */
+ CCB_MEM, /* memory for command control block */
+};
+#define PM8001_EVENT_LOG_SIZE (128 * 1024)
+
+/*error code*/
+enum mpi_err {
+ MPI_IO_STATUS_SUCCESS = 0x0,
+ MPI_IO_STATUS_BUSY = 0x01,
+ MPI_IO_STATUS_FAIL = 0x02,
+};
+
+/**
+ * Phy Control constants
+ */
+enum phy_control_type {
+ PHY_LINK_RESET = 0x01,
+ PHY_HARD_RESET = 0x02,
+ PHY_NOTIFY_ENABLE_SPINUP = 0x10,
+};
+
+enum pm8001_hba_info_flags {
+ PM8001F_INIT_TIME = (1U << 0),
+ PM8001F_RUN_TIME = (1U << 1),
+};
+
+#endif
diff --git a/drivers/scsi/pm8001/pm8001_hwi.c b/drivers/scsi/pm8001/pm8001_hwi.c
new file mode 100644
index 00000000000..a3de306b904
--- /dev/null
+++ b/drivers/scsi/pm8001/pm8001_hwi.c
@@ -0,0 +1,4458 @@
+/*
+ * PMC-Sierra SPC 8001 SAS/SATA based host adapters driver
+ *
+ * Copyright (c) 2008-2009 USI Co., Ltd.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions, and the following disclaimer,
+ * without modification.
+ * 2. Redistributions in binary form must reproduce at minimum a disclaimer
+ * substantially similar to the "NO WARRANTY" disclaimer below
+ * ("Disclaimer") and any redistribution must be conditioned upon
+ * including a substantially similar Disclaimer requirement for further
+ * binary redistribution.
+ * 3. Neither the names of the above-listed copyright holders nor the names
+ * of any contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2 as published by the Free
+ * Software Foundation.
+ *
+ * NO WARRANTY
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ */
+ #include "pm8001_sas.h"
+ #include "pm8001_hwi.h"
+ #include "pm8001_chips.h"
+ #include "pm8001_ctl.h"
+
+/**
+ * read_main_config_table - read the configure table and save it.
+ * @pm8001_ha: our hba card information
+ */
+static void __devinit read_main_config_table(struct pm8001_hba_info *pm8001_ha)
+{
+ void __iomem *address = pm8001_ha->main_cfg_tbl_addr;
+ pm8001_ha->main_cfg_tbl.signature = pm8001_mr32(address, 0x00);
+ pm8001_ha->main_cfg_tbl.interface_rev = pm8001_mr32(address, 0x04);
+ pm8001_ha->main_cfg_tbl.firmware_rev = pm8001_mr32(address, 0x08);
+ pm8001_ha->main_cfg_tbl.max_out_io = pm8001_mr32(address, 0x0C);
+ pm8001_ha->main_cfg_tbl.max_sgl = pm8001_mr32(address, 0x10);
+ pm8001_ha->main_cfg_tbl.ctrl_cap_flag = pm8001_mr32(address, 0x14);
+ pm8001_ha->main_cfg_tbl.gst_offset = pm8001_mr32(address, 0x18);
+ pm8001_ha->main_cfg_tbl.inbound_queue_offset =
+ pm8001_mr32(address, MAIN_IBQ_OFFSET);
+ pm8001_ha->main_cfg_tbl.outbound_queue_offset =
+ pm8001_mr32(address, MAIN_OBQ_OFFSET);
+ pm8001_ha->main_cfg_tbl.hda_mode_flag =
+ pm8001_mr32(address, MAIN_HDA_FLAGS_OFFSET);
+
+ /* read analog Setting offset from the configuration table */
+ pm8001_ha->main_cfg_tbl.anolog_setup_table_offset =
+ pm8001_mr32(address, MAIN_ANALOG_SETUP_OFFSET);
+
+ /* read Error Dump Offset and Length */
+ pm8001_ha->main_cfg_tbl.fatal_err_dump_offset0 =
+ pm8001_mr32(address, MAIN_FATAL_ERROR_RDUMP0_OFFSET);
+ pm8001_ha->main_cfg_tbl.fatal_err_dump_length0 =
+ pm8001_mr32(address, MAIN_FATAL_ERROR_RDUMP0_LENGTH);
+ pm8001_ha->main_cfg_tbl.fatal_err_dump_offset1 =
+ pm8001_mr32(address, MAIN_FATAL_ERROR_RDUMP1_OFFSET);
+ pm8001_ha->main_cfg_tbl.fatal_err_dump_length1 =
+ pm8001_mr32(address, MAIN_FATAL_ERROR_RDUMP1_LENGTH);
+}
+
+/**
+ * read_general_status_table - read the general status table and save it.
+ * @pm8001_ha: our hba card information
+ */
+static void __devinit
+read_general_status_table(struct pm8001_hba_info *pm8001_ha)
+{
+ void __iomem *address = pm8001_ha->general_stat_tbl_addr;
+ pm8001_ha->gs_tbl.gst_len_mpistate = pm8001_mr32(address, 0x00);
+ pm8001_ha->gs_tbl.iq_freeze_state0 = pm8001_mr32(address, 0x04);
+ pm8001_ha->gs_tbl.iq_freeze_state1 = pm8001_mr32(address, 0x08);
+ pm8001_ha->gs_tbl.msgu_tcnt = pm8001_mr32(address, 0x0C);
+ pm8001_ha->gs_tbl.iop_tcnt = pm8001_mr32(address, 0x10);
+ pm8001_ha->gs_tbl.reserved = pm8001_mr32(address, 0x14);
+ pm8001_ha->gs_tbl.phy_state[0] = pm8001_mr32(address, 0x18);
+ pm8001_ha->gs_tbl.phy_state[1] = pm8001_mr32(address, 0x1C);
+ pm8001_ha->gs_tbl.phy_state[2] = pm8001_mr32(address, 0x20);
+ pm8001_ha->gs_tbl.phy_state[3] = pm8001_mr32(address, 0x24);
+ pm8001_ha->gs_tbl.phy_state[4] = pm8001_mr32(address, 0x28);
+ pm8001_ha->gs_tbl.phy_state[5] = pm8001_mr32(address, 0x2C);
+ pm8001_ha->gs_tbl.phy_state[6] = pm8001_mr32(address, 0x30);
+ pm8001_ha->gs_tbl.phy_state[7] = pm8001_mr32(address, 0x34);
+ pm8001_ha->gs_tbl.reserved1 = pm8001_mr32(address, 0x38);
+ pm8001_ha->gs_tbl.reserved2 = pm8001_mr32(address, 0x3C);
+ pm8001_ha->gs_tbl.reserved3 = pm8001_mr32(address, 0x40);
+ pm8001_ha->gs_tbl.recover_err_info[0] = pm8001_mr32(address, 0x44);
+ pm8001_ha->gs_tbl.recover_err_info[1] = pm8001_mr32(address, 0x48);
+ pm8001_ha->gs_tbl.recover_err_info[2] = pm8001_mr32(address, 0x4C);
+ pm8001_ha->gs_tbl.recover_err_info[3] = pm8001_mr32(address, 0x50);
+ pm8001_ha->gs_tbl.recover_err_info[4] = pm8001_mr32(address, 0x54);
+ pm8001_ha->gs_tbl.recover_err_info[5] = pm8001_mr32(address, 0x58);
+ pm8001_ha->gs_tbl.recover_err_info[6] = pm8001_mr32(address, 0x5C);
+ pm8001_ha->gs_tbl.recover_err_info[7] = pm8001_mr32(address, 0x60);
+}
+
+/**
+ * read_inbnd_queue_table - read the inbound queue table and save it.
+ * @pm8001_ha: our hba card information
+ */
+static void __devinit
+read_inbnd_queue_table(struct pm8001_hba_info *pm8001_ha)
+{
+ int inbQ_num = 1;
+ int i;
+ void __iomem *address = pm8001_ha->inbnd_q_tbl_addr;
+ for (i = 0; i < inbQ_num; i++) {
+ u32 offset = i * 0x20;
+ pm8001_ha->inbnd_q_tbl[i].pi_pci_bar =
+ get_pci_bar_index(pm8001_mr32(address, (offset + 0x14)));
+ pm8001_ha->inbnd_q_tbl[i].pi_offset =
+ pm8001_mr32(address, (offset + 0x18));
+ }
+}
+
+/**
+ * read_outbnd_queue_table - read the outbound queue table and save it.
+ * @pm8001_ha: our hba card information
+ */
+static void __devinit
+read_outbnd_queue_table(struct pm8001_hba_info *pm8001_ha)
+{
+ int outbQ_num = 1;
+ int i;
+ void __iomem *address = pm8001_ha->outbnd_q_tbl_addr;
+ for (i = 0; i < outbQ_num; i++) {
+ u32 offset = i * 0x24;
+ pm8001_ha->outbnd_q_tbl[i].ci_pci_bar =
+ get_pci_bar_index(pm8001_mr32(address, (offset + 0x14)));
+ pm8001_ha->outbnd_q_tbl[i].ci_offset =
+ pm8001_mr32(address, (offset + 0x18));
+ }
+}
+
+/**
+ * init_default_table_values - init the default table.
+ * @pm8001_ha: our hba card information
+ */
+static void __devinit
+init_default_table_values(struct pm8001_hba_info *pm8001_ha)
+{
+ int qn = 1;
+ int i;
+ u32 offsetib, offsetob;
+ void __iomem *addressib = pm8001_ha->inbnd_q_tbl_addr;
+ void __iomem *addressob = pm8001_ha->outbnd_q_tbl_addr;
+
+ pm8001_ha->main_cfg_tbl.inbound_q_nppd_hppd = 0;
+ pm8001_ha->main_cfg_tbl.outbound_hw_event_pid0_3 = 0;
+ pm8001_ha->main_cfg_tbl.outbound_hw_event_pid4_7 = 0;
+ pm8001_ha->main_cfg_tbl.outbound_ncq_event_pid0_3 = 0;
+ pm8001_ha->main_cfg_tbl.outbound_ncq_event_pid4_7 = 0;
+ pm8001_ha->main_cfg_tbl.outbound_tgt_ITNexus_event_pid0_3 = 0;
+ pm8001_ha->main_cfg_tbl.outbound_tgt_ITNexus_event_pid4_7 = 0;
+ pm8001_ha->main_cfg_tbl.outbound_tgt_ssp_event_pid0_3 = 0;
+ pm8001_ha->main_cfg_tbl.outbound_tgt_ssp_event_pid4_7 = 0;
+ pm8001_ha->main_cfg_tbl.outbound_tgt_smp_event_pid0_3 = 0;
+ pm8001_ha->main_cfg_tbl.outbound_tgt_smp_event_pid4_7 = 0;
+
+ pm8001_ha->main_cfg_tbl.upper_event_log_addr =
+ pm8001_ha->memoryMap.region[AAP1].phys_addr_hi;
+ pm8001_ha->main_cfg_tbl.lower_event_log_addr =
+ pm8001_ha->memoryMap.region[AAP1].phys_addr_lo;
+ pm8001_ha->main_cfg_tbl.event_log_size = PM8001_EVENT_LOG_SIZE;
+ pm8001_ha->main_cfg_tbl.event_log_option = 0x01;
+ pm8001_ha->main_cfg_tbl.upper_iop_event_log_addr =
+ pm8001_ha->memoryMap.region[IOP].phys_addr_hi;
+ pm8001_ha->main_cfg_tbl.lower_iop_event_log_addr =
+ pm8001_ha->memoryMap.region[IOP].phys_addr_lo;
+ pm8001_ha->main_cfg_tbl.iop_event_log_size = PM8001_EVENT_LOG_SIZE;
+ pm8001_ha->main_cfg_tbl.iop_event_log_option = 0x01;
+ pm8001_ha->main_cfg_tbl.fatal_err_interrupt = 0x01;
+ for (i = 0; i < qn; i++) {
+ pm8001_ha->inbnd_q_tbl[i].element_pri_size_cnt =
+ 0x00000100 | (0x00000040 << 16) | (0x00<<30);
+ pm8001_ha->inbnd_q_tbl[i].upper_base_addr =
+ pm8001_ha->memoryMap.region[IB].phys_addr_hi;
+ pm8001_ha->inbnd_q_tbl[i].lower_base_addr =
+ pm8001_ha->memoryMap.region[IB].phys_addr_lo;
+ pm8001_ha->inbnd_q_tbl[i].base_virt =
+ (u8 *)pm8001_ha->memoryMap.region[IB].virt_ptr;
+ pm8001_ha->inbnd_q_tbl[i].total_length =
+ pm8001_ha->memoryMap.region[IB].total_len;
+ pm8001_ha->inbnd_q_tbl[i].ci_upper_base_addr =
+ pm8001_ha->memoryMap.region[CI].phys_addr_hi;
+ pm8001_ha->inbnd_q_tbl[i].ci_lower_base_addr =
+ pm8001_ha->memoryMap.region[CI].phys_addr_lo;
+ pm8001_ha->inbnd_q_tbl[i].ci_virt =
+ pm8001_ha->memoryMap.region[CI].virt_ptr;
+ offsetib = i * 0x20;
+ pm8001_ha->inbnd_q_tbl[i].pi_pci_bar =
+ get_pci_bar_index(pm8001_mr32(addressib,
+ (offsetib + 0x14)));
+ pm8001_ha->inbnd_q_tbl[i].pi_offset =
+ pm8001_mr32(addressib, (offsetib + 0x18));
+ pm8001_ha->inbnd_q_tbl[i].producer_idx = 0;
+ pm8001_ha->inbnd_q_tbl[i].consumer_index = 0;
+ }
+ for (i = 0; i < qn; i++) {
+ pm8001_ha->outbnd_q_tbl[i].element_size_cnt =
+ 256 | (64 << 16) | (1<<30);
+ pm8001_ha->outbnd_q_tbl[i].upper_base_addr =
+ pm8001_ha->memoryMap.region[OB].phys_addr_hi;
+ pm8001_ha->outbnd_q_tbl[i].lower_base_addr =
+ pm8001_ha->memoryMap.region[OB].phys_addr_lo;
+ pm8001_ha->outbnd_q_tbl[i].base_virt =
+ (u8 *)pm8001_ha->memoryMap.region[OB].virt_ptr;
+ pm8001_ha->outbnd_q_tbl[i].total_length =
+ pm8001_ha->memoryMap.region[OB].total_len;
+ pm8001_ha->outbnd_q_tbl[i].pi_upper_base_addr =
+ pm8001_ha->memoryMap.region[PI].phys_addr_hi;
+ pm8001_ha->outbnd_q_tbl[i].pi_lower_base_addr =
+ pm8001_ha->memoryMap.region[PI].phys_addr_lo;
+ pm8001_ha->outbnd_q_tbl[i].interrup_vec_cnt_delay =
+ 0 | (10 << 16) | (0 << 24);
+ pm8001_ha->outbnd_q_tbl[i].pi_virt =
+ pm8001_ha->memoryMap.region[PI].virt_ptr;
+ offsetob = i * 0x24;
+ pm8001_ha->outbnd_q_tbl[i].ci_pci_bar =
+ get_pci_bar_index(pm8001_mr32(addressob,
+ offsetob + 0x14));
+ pm8001_ha->outbnd_q_tbl[i].ci_offset =
+ pm8001_mr32(addressob, (offsetob + 0x18));
+ pm8001_ha->outbnd_q_tbl[i].consumer_idx = 0;
+ pm8001_ha->outbnd_q_tbl[i].producer_index = 0;
+ }
+}
+
+/**
+ * update_main_config_table - update the main default table to the HBA.
+ * @pm8001_ha: our hba card information
+ */
+static void __devinit
+update_main_config_table(struct pm8001_hba_info *pm8001_ha)
+{
+ void __iomem *address = pm8001_ha->main_cfg_tbl_addr;
+ pm8001_mw32(address, 0x24,
+ pm8001_ha->main_cfg_tbl.inbound_q_nppd_hppd);
+ pm8001_mw32(address, 0x28,
+ pm8001_ha->main_cfg_tbl.outbound_hw_event_pid0_3);
+ pm8001_mw32(address, 0x2C,
+ pm8001_ha->main_cfg_tbl.outbound_hw_event_pid4_7);
+ pm8001_mw32(address, 0x30,
+ pm8001_ha->main_cfg_tbl.outbound_ncq_event_pid0_3);
+ pm8001_mw32(address, 0x34,
+ pm8001_ha->main_cfg_tbl.outbound_ncq_event_pid4_7);
+ pm8001_mw32(address, 0x38,
+ pm8001_ha->main_cfg_tbl.outbound_tgt_ITNexus_event_pid0_3);
+ pm8001_mw32(address, 0x3C,
+ pm8001_ha->main_cfg_tbl.outbound_tgt_ITNexus_event_pid4_7);
+ pm8001_mw32(address, 0x40,
+ pm8001_ha->main_cfg_tbl.outbound_tgt_ssp_event_pid0_3);
+ pm8001_mw32(address, 0x44,
+ pm8001_ha->main_cfg_tbl.outbound_tgt_ssp_event_pid4_7);
+ pm8001_mw32(address, 0x48,
+ pm8001_ha->main_cfg_tbl.outbound_tgt_smp_event_pid0_3);
+ pm8001_mw32(address, 0x4C,
+ pm8001_ha->main_cfg_tbl.outbound_tgt_smp_event_pid4_7);
+ pm8001_mw32(address, 0x50,
+ pm8001_ha->main_cfg_tbl.upper_event_log_addr);
+ pm8001_mw32(address, 0x54,
+ pm8001_ha->main_cfg_tbl.lower_event_log_addr);
+ pm8001_mw32(address, 0x58, pm8001_ha->main_cfg_tbl.event_log_size);
+ pm8001_mw32(address, 0x5C, pm8001_ha->main_cfg_tbl.event_log_option);
+ pm8001_mw32(address, 0x60,
+ pm8001_ha->main_cfg_tbl.upper_iop_event_log_addr);
+ pm8001_mw32(address, 0x64,
+ pm8001_ha->main_cfg_tbl.lower_iop_event_log_addr);
+ pm8001_mw32(address, 0x68, pm8001_ha->main_cfg_tbl.iop_event_log_size);
+ pm8001_mw32(address, 0x6C,
+ pm8001_ha->main_cfg_tbl.iop_event_log_option);
+ pm8001_mw32(address, 0x70,
+ pm8001_ha->main_cfg_tbl.fatal_err_interrupt);
+}
+
+/**
+ * update_inbnd_queue_table - update the inbound queue table to the HBA.
+ * @pm8001_ha: our hba card information
+ */
+static void __devinit
+update_inbnd_queue_table(struct pm8001_hba_info *pm8001_ha, int number)
+{
+ void __iomem *address = pm8001_ha->inbnd_q_tbl_addr;
+ u16 offset = number * 0x20;
+ pm8001_mw32(address, offset + 0x00,
+ pm8001_ha->inbnd_q_tbl[number].element_pri_size_cnt);
+ pm8001_mw32(address, offset + 0x04,
+ pm8001_ha->inbnd_q_tbl[number].upper_base_addr);
+ pm8001_mw32(address, offset + 0x08,
+ pm8001_ha->inbnd_q_tbl[number].lower_base_addr);
+ pm8001_mw32(address, offset + 0x0C,
+ pm8001_ha->inbnd_q_tbl[number].ci_upper_base_addr);
+ pm8001_mw32(address, offset + 0x10,
+ pm8001_ha->inbnd_q_tbl[number].ci_lower_base_addr);
+}
+
+/**
+ * update_outbnd_queue_table - update the outbound queue table to the HBA.
+ * @pm8001_ha: our hba card information
+ */
+static void __devinit
+update_outbnd_queue_table(struct pm8001_hba_info *pm8001_ha, int number)
+{
+ void __iomem *address = pm8001_ha->outbnd_q_tbl_addr;
+ u16 offset = number * 0x24;
+ pm8001_mw32(address, offset + 0x00,
+ pm8001_ha->outbnd_q_tbl[number].element_size_cnt);
+ pm8001_mw32(address, offset + 0x04,
+ pm8001_ha->outbnd_q_tbl[number].upper_base_addr);
+ pm8001_mw32(address, offset + 0x08,
+ pm8001_ha->outbnd_q_tbl[number].lower_base_addr);
+ pm8001_mw32(address, offset + 0x0C,
+ pm8001_ha->outbnd_q_tbl[number].pi_upper_base_addr);
+ pm8001_mw32(address, offset + 0x10,
+ pm8001_ha->outbnd_q_tbl[number].pi_lower_base_addr);
+ pm8001_mw32(address, offset + 0x1C,
+ pm8001_ha->outbnd_q_tbl[number].interrup_vec_cnt_delay);
+}
+
+/**
+ * bar4_shift - function is called to shift BAR base address
+ * @pm8001_ha : our hba card infomation
+ * @shiftValue : shifting value in memory bar.
+ */
+static int bar4_shift(struct pm8001_hba_info *pm8001_ha, u32 shiftValue)
+{
+ u32 regVal;
+ u32 max_wait_count;
+
+ /* program the inbound AXI translation Lower Address */
+ pm8001_cw32(pm8001_ha, 1, SPC_IBW_AXI_TRANSLATION_LOW, shiftValue);
+
+ /* confirm the setting is written */
+ max_wait_count = 1 * 1000 * 1000; /* 1 sec */
+ do {
+ udelay(1);
+ regVal = pm8001_cr32(pm8001_ha, 1, SPC_IBW_AXI_TRANSLATION_LOW);
+ } while ((regVal != shiftValue) && (--max_wait_count));
+
+ if (!max_wait_count) {
+ PM8001_INIT_DBG(pm8001_ha,
+ pm8001_printk("TIMEOUT:SPC_IBW_AXI_TRANSLATION_LOW"
+ " = 0x%x\n", regVal));
+ return -1;
+ }
+ return 0;
+}
+
+/**
+ * mpi_set_phys_g3_with_ssc
+ * @pm8001_ha: our hba card information
+ * @SSCbit: set SSCbit to 0 to disable all phys ssc; 1 to enable all phys ssc.
+ */
+static void __devinit
+mpi_set_phys_g3_with_ssc(struct pm8001_hba_info *pm8001_ha, u32 SSCbit)
+{
+ u32 offset;
+ u32 value;
+ u32 i, j;
+ u32 bit_cnt;
+
+#define SAS2_SETTINGS_LOCAL_PHY_0_3_SHIFT_ADDR 0x00030000
+#define SAS2_SETTINGS_LOCAL_PHY_4_7_SHIFT_ADDR 0x00040000
+#define SAS2_SETTINGS_LOCAL_PHY_0_3_OFFSET 0x1074
+#define SAS2_SETTINGS_LOCAL_PHY_4_7_OFFSET 0x1074
+#define PHY_G3_WITHOUT_SSC_BIT_SHIFT 12
+#define PHY_G3_WITH_SSC_BIT_SHIFT 13
+#define SNW3_PHY_CAPABILITIES_PARITY 31
+
+ /*
+ * Using shifted destination address 0x3_0000:0x1074 + 0x4000*N (N=0:3)
+ * Using shifted destination address 0x4_0000:0x1074 + 0x4000*(N-4) (N=4:7)
+ */
+ if (-1 == bar4_shift(pm8001_ha, SAS2_SETTINGS_LOCAL_PHY_0_3_SHIFT_ADDR))
+ return;
+ /* set SSC bit of PHY 0 - 3 */
+ for (i = 0; i < 4; i++) {
+ offset = SAS2_SETTINGS_LOCAL_PHY_0_3_OFFSET + 0x4000 * i;
+ value = pm8001_cr32(pm8001_ha, 2, offset);
+ if (SSCbit) {
+ value |= 0x00000001 << PHY_G3_WITH_SSC_BIT_SHIFT;
+ value &= ~(0x00000001 << PHY_G3_WITHOUT_SSC_BIT_SHIFT);
+ } else {
+ value |= 0x00000001 << PHY_G3_WITHOUT_SSC_BIT_SHIFT;
+ value &= ~(0x00000001 << PHY_G3_WITH_SSC_BIT_SHIFT);
+ }
+ bit_cnt = 0;
+ for (j = 0; j < 31; j++)
+ if ((value >> j) & 0x00000001)
+ bit_cnt++;
+ if (bit_cnt % 2)
+ value &= ~(0x00000001 << SNW3_PHY_CAPABILITIES_PARITY);
+ else
+ value |= 0x00000001 << SNW3_PHY_CAPABILITIES_PARITY;
+
+ pm8001_cw32(pm8001_ha, 2, offset, value);
+ }
+
+ /* shift membase 3 for SAS2_SETTINGS_LOCAL_PHY 4 - 7 */
+ if (-1 == bar4_shift(pm8001_ha, SAS2_SETTINGS_LOCAL_PHY_4_7_SHIFT_ADDR))
+ return;
+
+ /* set SSC bit of PHY 4 - 7 */
+ for (i = 4; i < 8; i++) {
+ offset = SAS2_SETTINGS_LOCAL_PHY_4_7_OFFSET + 0x4000 * (i-4);
+ value = pm8001_cr32(pm8001_ha, 2, offset);
+ if (SSCbit) {
+ value |= 0x00000001 << PHY_G3_WITH_SSC_BIT_SHIFT;
+ value &= ~(0x00000001 << PHY_G3_WITHOUT_SSC_BIT_SHIFT);
+ } else {
+ value |= 0x00000001 << PHY_G3_WITHOUT_SSC_BIT_SHIFT;
+ value &= ~(0x00000001 << PHY_G3_WITH_SSC_BIT_SHIFT);
+ }
+ bit_cnt = 0;
+ for (j = 0; j < 31; j++)
+ if ((value >> j) & 0x00000001)
+ bit_cnt++;
+ if (bit_cnt % 2)
+ value &= ~(0x00000001 << SNW3_PHY_CAPABILITIES_PARITY);
+ else
+ value |= 0x00000001 << SNW3_PHY_CAPABILITIES_PARITY;
+
+ pm8001_cw32(pm8001_ha, 2, offset, value);
+ }
+
+ /*set the shifted destination address to 0x0 to avoid error operation */
+ bar4_shift(pm8001_ha, 0x0);
+ return;
+}
+
+/**
+ * mpi_set_open_retry_interval_reg
+ * @pm8001_ha: our hba card information
+ * @interval - interval time for each OPEN_REJECT (RETRY). The units are in 1us.
+ */
+static void __devinit
+mpi_set_open_retry_interval_reg(struct pm8001_hba_info *pm8001_ha,
+ u32 interval)
+{
+ u32 offset;
+ u32 value;
+ u32 i;
+
+#define OPEN_RETRY_INTERVAL_PHY_0_3_SHIFT_ADDR 0x00030000
+#define OPEN_RETRY_INTERVAL_PHY_4_7_SHIFT_ADDR 0x00040000
+#define OPEN_RETRY_INTERVAL_PHY_0_3_OFFSET 0x30B4
+#define OPEN_RETRY_INTERVAL_PHY_4_7_OFFSET 0x30B4
+#define OPEN_RETRY_INTERVAL_REG_MASK 0x0000FFFF
+
+ value = interval & OPEN_RETRY_INTERVAL_REG_MASK;
+ /* shift bar and set the OPEN_REJECT(RETRY) interval time of PHY 0 -3.*/
+ if (-1 == bar4_shift(pm8001_ha,
+ OPEN_RETRY_INTERVAL_PHY_0_3_SHIFT_ADDR))
+ return;
+ for (i = 0; i < 4; i++) {
+ offset = OPEN_RETRY_INTERVAL_PHY_0_3_OFFSET + 0x4000 * i;
+ pm8001_cw32(pm8001_ha, 2, offset, value);
+ }
+
+ if (-1 == bar4_shift(pm8001_ha,
+ OPEN_RETRY_INTERVAL_PHY_4_7_SHIFT_ADDR))
+ return;
+ for (i = 4; i < 8; i++) {
+ offset = OPEN_RETRY_INTERVAL_PHY_4_7_OFFSET + 0x4000 * (i-4);
+ pm8001_cw32(pm8001_ha, 2, offset, value);
+ }
+ /*set the shifted destination address to 0x0 to avoid error operation */
+ bar4_shift(pm8001_ha, 0x0);
+ return;
+}
+
+/**
+ * mpi_init_check - check firmware initialization status.
+ * @pm8001_ha: our hba card information
+ */
+static int mpi_init_check(struct pm8001_hba_info *pm8001_ha)
+{
+ u32 max_wait_count;
+ u32 value;
+ u32 gst_len_mpistate;
+ /* Write bit0=1 to Inbound DoorBell Register to tell the SPC FW the
+ table is updated */
+ pm8001_cw32(pm8001_ha, 0, MSGU_IBDB_SET, SPC_MSGU_CFG_TABLE_UPDATE);
+ /* wait until Inbound DoorBell Clear Register toggled */
+ max_wait_count = 1 * 1000 * 1000;/* 1 sec */
+ do {
+ udelay(1);
+ value = pm8001_cr32(pm8001_ha, 0, MSGU_IBDB_SET);
+ value &= SPC_MSGU_CFG_TABLE_UPDATE;
+ } while ((value != 0) && (--max_wait_count));
+
+ if (!max_wait_count)
+ return -1;
+ /* check the MPI-State for initialization */
+ gst_len_mpistate =
+ pm8001_mr32(pm8001_ha->general_stat_tbl_addr,
+ GST_GSTLEN_MPIS_OFFSET);
+ if (GST_MPI_STATE_INIT != (gst_len_mpistate & GST_MPI_STATE_MASK))
+ return -1;
+ /* check MPI Initialization error */
+ gst_len_mpistate = gst_len_mpistate >> 16;
+ if (0x0000 != gst_len_mpistate)
+ return -1;
+ return 0;
+}
+
+/**
+ * check_fw_ready - The LLDD check if the FW is ready, if not, return error.
+ * @pm8001_ha: our hba card information
+ */
+static int check_fw_ready(struct pm8001_hba_info *pm8001_ha)
+{
+ u32 value, value1;
+ u32 max_wait_count;
+ /* check error state */
+ value = pm8001_cr32(pm8001_ha, 0, MSGU_SCRATCH_PAD_1);
+ value1 = pm8001_cr32(pm8001_ha, 0, MSGU_SCRATCH_PAD_2);
+ /* check AAP error */
+ if (SCRATCH_PAD1_ERR == (value & SCRATCH_PAD_STATE_MASK)) {
+ /* error state */
+ value = pm8001_cr32(pm8001_ha, 0, MSGU_SCRATCH_PAD_0);
+ return -1;
+ }
+
+ /* check IOP error */
+ if (SCRATCH_PAD2_ERR == (value1 & SCRATCH_PAD_STATE_MASK)) {
+ /* error state */
+ value1 = pm8001_cr32(pm8001_ha, 0, MSGU_SCRATCH_PAD_3);
+ return -1;
+ }
+
+ /* bit 4-31 of scratch pad1 should be zeros if it is not
+ in error state*/
+ if (value & SCRATCH_PAD1_STATE_MASK) {
+ /* error case */
+ pm8001_cr32(pm8001_ha, 0, MSGU_SCRATCH_PAD_0);
+ return -1;
+ }
+
+ /* bit 2, 4-31 of scratch pad2 should be zeros if it is not
+ in error state */
+ if (value1 & SCRATCH_PAD2_STATE_MASK) {
+ /* error case */
+ return -1;
+ }
+
+ max_wait_count = 1 * 1000 * 1000;/* 1 sec timeout */
+
+ /* wait until scratch pad 1 and 2 registers in ready state */
+ do {
+ udelay(1);
+ value = pm8001_cr32(pm8001_ha, 0, MSGU_SCRATCH_PAD_1)
+ & SCRATCH_PAD1_RDY;
+ value1 = pm8001_cr32(pm8001_ha, 0, MSGU_SCRATCH_PAD_2)
+ & SCRATCH_PAD2_RDY;
+ if ((--max_wait_count) == 0)
+ return -1;
+ } while ((value != SCRATCH_PAD1_RDY) || (value1 != SCRATCH_PAD2_RDY));
+ return 0;
+}
+
+static void init_pci_device_addresses(struct pm8001_hba_info *pm8001_ha)
+{
+ void __iomem *base_addr;
+ u32 value;
+ u32 offset;
+ u32 pcibar;
+ u32 pcilogic;
+
+ value = pm8001_cr32(pm8001_ha, 0, 0x44);
+ offset = value & 0x03FFFFFF;
+ PM8001_INIT_DBG(pm8001_ha,
+ pm8001_printk("Scratchpad 0 Offset: %x \n", offset));
+ pcilogic = (value & 0xFC000000) >> 26;
+ pcibar = get_pci_bar_index(pcilogic);
+ PM8001_INIT_DBG(pm8001_ha,
+ pm8001_printk("Scratchpad 0 PCI BAR: %d \n", pcibar));
+ pm8001_ha->main_cfg_tbl_addr = base_addr =
+ pm8001_ha->io_mem[pcibar].memvirtaddr + offset;
+ pm8001_ha->general_stat_tbl_addr =
+ base_addr + pm8001_cr32(pm8001_ha, pcibar, offset + 0x18);
+ pm8001_ha->inbnd_q_tbl_addr =
+ base_addr + pm8001_cr32(pm8001_ha, pcibar, offset + 0x1C);
+ pm8001_ha->outbnd_q_tbl_addr =
+ base_addr + pm8001_cr32(pm8001_ha, pcibar, offset + 0x20);
+}
+
+/**
+ * pm8001_chip_init - the main init function that initialize whole PM8001 chip.
+ * @pm8001_ha: our hba card information
+ */
+static int __devinit pm8001_chip_init(struct pm8001_hba_info *pm8001_ha)
+{
+ /* check the firmware status */
+ if (-1 == check_fw_ready(pm8001_ha)) {
+ PM8001_FAIL_DBG(pm8001_ha,
+ pm8001_printk("Firmware is not ready!\n"));
+ return -EBUSY;
+ }
+
+ /* Initialize pci space address eg: mpi offset */
+ init_pci_device_addresses(pm8001_ha);
+ init_default_table_values(pm8001_ha);
+ read_main_config_table(pm8001_ha);
+ read_general_status_table(pm8001_ha);
+ read_inbnd_queue_table(pm8001_ha);
+ read_outbnd_queue_table(pm8001_ha);
+ /* update main config table ,inbound table and outbound table */
+ update_main_config_table(pm8001_ha);
+ update_inbnd_queue_table(pm8001_ha, 0);
+ update_outbnd_queue_table(pm8001_ha, 0);
+ mpi_set_phys_g3_with_ssc(pm8001_ha, 0);
+ mpi_set_open_retry_interval_reg(pm8001_ha, 7);
+ /* notify firmware update finished and check initialization status */
+ if (0 == mpi_init_check(pm8001_ha)) {
+ PM8001_INIT_DBG(pm8001_ha,
+ pm8001_printk("MPI initialize successful!\n"));
+ } else
+ return -EBUSY;
+ /*This register is a 16-bit timer with a resolution of 1us. This is the
+ timer used for interrupt delay/coalescing in the PCIe Application Layer.
+ Zero is not a valid value. A value of 1 in the register will cause the
+ interrupts to be normal. A value greater than 1 will cause coalescing
+ delays.*/
+ pm8001_cw32(pm8001_ha, 1, 0x0033c0, 0x1);
+ pm8001_cw32(pm8001_ha, 1, 0x0033c4, 0x0);
+ return 0;
+}
+
+static int mpi_uninit_check(struct pm8001_hba_info *pm8001_ha)
+{
+ u32 max_wait_count;
+ u32 value;
+ u32 gst_len_mpistate;
+ init_pci_device_addresses(pm8001_ha);
+ /* Write bit1=1 to Inbound DoorBell Register to tell the SPC FW the
+ table is stop */
+ pm8001_cw32(pm8001_ha, 0, MSGU_IBDB_SET, SPC_MSGU_CFG_TABLE_RESET);
+
+ /* wait until Inbound DoorBell Clear Register toggled */
+ max_wait_count = 1 * 1000 * 1000;/* 1 sec */
+ do {
+ udelay(1);
+ value = pm8001_cr32(pm8001_ha, 0, MSGU_IBDB_SET);
+ value &= SPC_MSGU_CFG_TABLE_RESET;
+ } while ((value != 0) && (--max_wait_count));
+
+ if (!max_wait_count) {
+ PM8001_FAIL_DBG(pm8001_ha,
+ pm8001_printk("TIMEOUT:IBDB value/=0x%x\n", value));
+ return -1;
+ }
+
+ /* check the MPI-State for termination in progress */
+ /* wait until Inbound DoorBell Clear Register toggled */
+ max_wait_count = 1 * 1000 * 1000; /* 1 sec */
+ do {
+ udelay(1);
+ gst_len_mpistate =
+ pm8001_mr32(pm8001_ha->general_stat_tbl_addr,
+ GST_GSTLEN_MPIS_OFFSET);
+ if (GST_MPI_STATE_UNINIT ==
+ (gst_len_mpistate & GST_MPI_STATE_MASK))
+ break;
+ } while (--max_wait_count);
+ if (!max_wait_count) {
+ PM8001_FAIL_DBG(pm8001_ha,
+ pm8001_printk(" TIME OUT MPI State = 0x%x\n",
+ gst_len_mpistate & GST_MPI_STATE_MASK));
+ return -1;
+ }
+ return 0;
+}
+
+/**
+ * soft_reset_ready_check - Function to check FW is ready for soft reset.
+ * @pm8001_ha: our hba card information
+ */
+static u32 soft_reset_ready_check(struct pm8001_hba_info *pm8001_ha)
+{
+ u32 regVal, regVal1, regVal2;
+ if (mpi_uninit_check(pm8001_ha) != 0) {
+ PM8001_FAIL_DBG(pm8001_ha,
+ pm8001_printk("MPI state is not ready\n"));
+ return -1;
+ }
+ /* read the scratch pad 2 register bit 2 */
+ regVal = pm8001_cr32(pm8001_ha, 0, MSGU_SCRATCH_PAD_2)
+ & SCRATCH_PAD2_FWRDY_RST;
+ if (regVal == SCRATCH_PAD2_FWRDY_RST) {
+ PM8001_INIT_DBG(pm8001_ha,
+ pm8001_printk("Firmware is ready for reset .\n"));
+ } else {
+ /* Trigger NMI twice via RB6 */
+ if (-1 == bar4_shift(pm8001_ha, RB6_ACCESS_REG)) {
+ PM8001_FAIL_DBG(pm8001_ha,
+ pm8001_printk("Shift Bar4 to 0x%x failed\n",
+ RB6_ACCESS_REG));
+ return -1;
+ }
+ pm8001_cw32(pm8001_ha, 2, SPC_RB6_OFFSET,
+ RB6_MAGIC_NUMBER_RST);
+ pm8001_cw32(pm8001_ha, 2, SPC_RB6_OFFSET, RB6_MAGIC_NUMBER_RST);
+ /* wait for 100 ms */
+ mdelay(100);
+ regVal = pm8001_cr32(pm8001_ha, 0, MSGU_SCRATCH_PAD_2) &
+ SCRATCH_PAD2_FWRDY_RST;
+ if (regVal != SCRATCH_PAD2_FWRDY_RST) {
+ regVal1 = pm8001_cr32(pm8001_ha, 0, MSGU_SCRATCH_PAD_1);
+ regVal2 = pm8001_cr32(pm8001_ha, 0, MSGU_SCRATCH_PAD_2);
+ PM8001_FAIL_DBG(pm8001_ha,
+ pm8001_printk("TIMEOUT:MSGU_SCRATCH_PAD1"
+ "=0x%x, MSGU_SCRATCH_PAD2=0x%x\n",
+ regVal1, regVal2));
+ PM8001_FAIL_DBG(pm8001_ha,
+ pm8001_printk("SCRATCH_PAD0 value = 0x%x\n",
+ pm8001_cr32(pm8001_ha, 0, MSGU_SCRATCH_PAD_0)));
+ PM8001_FAIL_DBG(pm8001_ha,
+ pm8001_printk("SCRATCH_PAD3 value = 0x%x\n",
+ pm8001_cr32(pm8001_ha, 0, MSGU_SCRATCH_PAD_3)));
+ return -1;
+ }
+ }
+ return 0;
+}
+
+/**
+ * pm8001_chip_soft_rst - soft reset the PM8001 chip, so that the clear all
+ * the FW register status to the originated status.
+ * @pm8001_ha: our hba card information
+ * @signature: signature in host scratch pad0 register.
+ */
+static int
+pm8001_chip_soft_rst(struct pm8001_hba_info *pm8001_ha, u32 signature)
+{
+ u32 regVal, toggleVal;
+ u32 max_wait_count;
+ u32 regVal1, regVal2, regVal3;
+
+ /* step1: Check FW is ready for soft reset */
+ if (soft_reset_ready_check(pm8001_ha) != 0) {
+ PM8001_FAIL_DBG(pm8001_ha, pm8001_printk("FW is not ready\n"));
+ return -1;
+ }
+
+ /* step 2: clear NMI status register on AAP1 and IOP, write the same
+ value to clear */
+ /* map 0x60000 to BAR4(0x20), BAR2(win) */
+ if (-1 == bar4_shift(pm8001_ha, MBIC_AAP1_ADDR_BASE)) {
+ PM8001_FAIL_DBG(pm8001_ha,
+ pm8001_printk("Shift Bar4 to 0x%x failed\n",
+ MBIC_AAP1_ADDR_BASE));
+ return -1;
+ }
+ regVal = pm8001_cr32(pm8001_ha, 2, MBIC_NMI_ENABLE_VPE0_IOP);
+ PM8001_INIT_DBG(pm8001_ha,
+ pm8001_printk("MBIC - NMI Enable VPE0 (IOP)= 0x%x\n", regVal));
+ pm8001_cw32(pm8001_ha, 2, MBIC_NMI_ENABLE_VPE0_IOP, 0x0);
+ /* map 0x70000 to BAR4(0x20), BAR2(win) */
+ if (-1 == bar4_shift(pm8001_ha, MBIC_IOP_ADDR_BASE)) {
+ PM8001_FAIL_DBG(pm8001_ha,
+ pm8001_printk("Shift Bar4 to 0x%x failed\n",
+ MBIC_IOP_ADDR_BASE));
+ return -1;
+ }
+ regVal = pm8001_cr32(pm8001_ha, 2, MBIC_NMI_ENABLE_VPE0_AAP1);
+ PM8001_INIT_DBG(pm8001_ha,
+ pm8001_printk("MBIC - NMI Enable VPE0 (AAP1)= 0x%x\n", regVal));
+ pm8001_cw32(pm8001_ha, 2, MBIC_NMI_ENABLE_VPE0_AAP1, 0x0);
+
+ regVal = pm8001_cr32(pm8001_ha, 1, PCIE_EVENT_INTERRUPT_ENABLE);
+ PM8001_INIT_DBG(pm8001_ha,
+ pm8001_printk("PCIE -Event Interrupt Enable = 0x%x\n", regVal));
+ pm8001_cw32(pm8001_ha, 1, PCIE_EVENT_INTERRUPT_ENABLE, 0x0);
+
+ regVal = pm8001_cr32(pm8001_ha, 1, PCIE_EVENT_INTERRUPT);
+ PM8001_INIT_DBG(pm8001_ha,
+ pm8001_printk("PCIE - Event Interrupt = 0x%x\n", regVal));
+ pm8001_cw32(pm8001_ha, 1, PCIE_EVENT_INTERRUPT, regVal);
+
+ regVal = pm8001_cr32(pm8001_ha, 1, PCIE_ERROR_INTERRUPT_ENABLE);
+ PM8001_INIT_DBG(pm8001_ha,
+ pm8001_printk("PCIE -Error Interrupt Enable = 0x%x\n", regVal));
+ pm8001_cw32(pm8001_ha, 1, PCIE_ERROR_INTERRUPT_ENABLE, 0x0);
+
+ regVal = pm8001_cr32(pm8001_ha, 1, PCIE_ERROR_INTERRUPT);
+ PM8001_INIT_DBG(pm8001_ha,
+ pm8001_printk("PCIE - Error Interrupt = 0x%x\n", regVal));
+ pm8001_cw32(pm8001_ha, 1, PCIE_ERROR_INTERRUPT, regVal);
+
+ /* read the scratch pad 1 register bit 2 */
+ regVal = pm8001_cr32(pm8001_ha, 0, MSGU_SCRATCH_PAD_1)
+ & SCRATCH_PAD1_RST;
+ toggleVal = regVal ^ SCRATCH_PAD1_RST;
+
+ /* set signature in host scratch pad0 register to tell SPC that the
+ host performs the soft reset */
+ pm8001_cw32(pm8001_ha, 0, MSGU_HOST_SCRATCH_PAD_0, signature);
+
+ /* read required registers for confirmming */
+ /* map 0x0700000 to BAR4(0x20), BAR2(win) */
+ if (-1 == bar4_shift(pm8001_ha, GSM_ADDR_BASE)) {
+ PM8001_FAIL_DBG(pm8001_ha,
+ pm8001_printk("Shift Bar4 to 0x%x failed\n",
+ GSM_ADDR_BASE));
+ return -1;
+ }
+ PM8001_INIT_DBG(pm8001_ha,
+ pm8001_printk("GSM 0x0(0x00007b88)-GSM Configuration and"
+ " Reset = 0x%x\n",
+ pm8001_cr32(pm8001_ha, 2, GSM_CONFIG_RESET)));
+
+ /* step 3: host read GSM Configuration and Reset register */
+ regVal = pm8001_cr32(pm8001_ha, 2, GSM_CONFIG_RESET);
+ /* Put those bits to low */
+ /* GSM XCBI offset = 0x70 0000
+ 0x00 Bit 13 COM_SLV_SW_RSTB 1
+ 0x00 Bit 12 QSSP_SW_RSTB 1
+ 0x00 Bit 11 RAAE_SW_RSTB 1
+ 0x00 Bit 9 RB_1_SW_RSTB 1
+ 0x00 Bit 8 SM_SW_RSTB 1
+ */
+ regVal &= ~(0x00003b00);
+ /* host write GSM Configuration and Reset register */
+ pm8001_cw32(pm8001_ha, 2, GSM_CONFIG_RESET, regVal);
+ PM8001_INIT_DBG(pm8001_ha,
+ pm8001_printk("GSM 0x0 (0x00007b88 ==> 0x00004088) - GSM "
+ "Configuration and Reset is set to = 0x%x\n",
+ pm8001_cr32(pm8001_ha, 2, GSM_CONFIG_RESET)));
+
+ /* step 4: */
+ /* disable GSM - Read Address Parity Check */
+ regVal1 = pm8001_cr32(pm8001_ha, 2, GSM_READ_ADDR_PARITY_CHECK);
+ PM8001_INIT_DBG(pm8001_ha,
+ pm8001_printk("GSM 0x700038 - Read Address Parity Check "
+ "Enable = 0x%x\n", regVal1));
+ pm8001_cw32(pm8001_ha, 2, GSM_READ_ADDR_PARITY_CHECK, 0x0);
+ PM8001_INIT_DBG(pm8001_ha,
+ pm8001_printk("GSM 0x700038 - Read Address Parity Check Enable"
+ "is set to = 0x%x\n",
+ pm8001_cr32(pm8001_ha, 2, GSM_READ_ADDR_PARITY_CHECK)));
+
+ /* disable GSM - Write Address Parity Check */
+ regVal2 = pm8001_cr32(pm8001_ha, 2, GSM_WRITE_ADDR_PARITY_CHECK);
+ PM8001_INIT_DBG(pm8001_ha,
+ pm8001_printk("GSM 0x700040 - Write Address Parity Check"
+ " Enable = 0x%x\n", regVal2));
+ pm8001_cw32(pm8001_ha, 2, GSM_WRITE_ADDR_PARITY_CHECK, 0x0);
+ PM8001_INIT_DBG(pm8001_ha,
+ pm8001_printk("GSM 0x700040 - Write Address Parity Check "
+ "Enable is set to = 0x%x\n",
+ pm8001_cr32(pm8001_ha, 2, GSM_WRITE_ADDR_PARITY_CHECK)));
+
+ /* disable GSM - Write Data Parity Check */
+ regVal3 = pm8001_cr32(pm8001_ha, 2, GSM_WRITE_DATA_PARITY_CHECK);
+ PM8001_INIT_DBG(pm8001_ha,
+ pm8001_printk("GSM 0x300048 - Write Data Parity Check"
+ " Enable = 0x%x\n", regVal3));
+ pm8001_cw32(pm8001_ha, 2, GSM_WRITE_DATA_PARITY_CHECK, 0x0);
+ PM8001_INIT_DBG(pm8001_ha,
+ pm8001_printk("GSM 0x300048 - Write Data Parity Check Enable"
+ "is set to = 0x%x\n",
+ pm8001_cr32(pm8001_ha, 2, GSM_WRITE_DATA_PARITY_CHECK)));
+
+ /* step 5: delay 10 usec */
+ udelay(10);
+ /* step 5-b: set GPIO-0 output control to tristate anyway */
+ if (-1 == bar4_shift(pm8001_ha, GPIO_ADDR_BASE)) {
+ PM8001_INIT_DBG(pm8001_ha,
+ pm8001_printk("Shift Bar4 to 0x%x failed\n",
+ GPIO_ADDR_BASE));
+ return -1;
+ }
+ regVal = pm8001_cr32(pm8001_ha, 2, GPIO_GPIO_0_0UTPUT_CTL_OFFSET);
+ PM8001_INIT_DBG(pm8001_ha,
+ pm8001_printk("GPIO Output Control Register:"
+ " = 0x%x\n", regVal));
+ /* set GPIO-0 output control to tri-state */
+ regVal &= 0xFFFFFFFC;
+ pm8001_cw32(pm8001_ha, 2, GPIO_GPIO_0_0UTPUT_CTL_OFFSET, regVal);
+
+ /* Step 6: Reset the IOP and AAP1 */
+ /* map 0x00000 to BAR4(0x20), BAR2(win) */
+ if (-1 == bar4_shift(pm8001_ha, SPC_TOP_LEVEL_ADDR_BASE)) {
+ PM8001_FAIL_DBG(pm8001_ha,
+ pm8001_printk("SPC Shift Bar4 to 0x%x failed\n",
+ SPC_TOP_LEVEL_ADDR_BASE));
+ return -1;
+ }
+ regVal = pm8001_cr32(pm8001_ha, 2, SPC_REG_RESET);
+ PM8001_INIT_DBG(pm8001_ha,
+ pm8001_printk("Top Register before resetting IOP/AAP1"
+ ":= 0x%x\n", regVal));
+ regVal &= ~(SPC_REG_RESET_PCS_IOP_SS | SPC_REG_RESET_PCS_AAP1_SS);
+ pm8001_cw32(pm8001_ha, 2, SPC_REG_RESET, regVal);
+
+ /* step 7: Reset the BDMA/OSSP */
+ regVal = pm8001_cr32(pm8001_ha, 2, SPC_REG_RESET);
+ PM8001_INIT_DBG(pm8001_ha,
+ pm8001_printk("Top Register before resetting BDMA/OSSP"
+ ": = 0x%x\n", regVal));
+ regVal &= ~(SPC_REG_RESET_BDMA_CORE | SPC_REG_RESET_OSSP);
+ pm8001_cw32(pm8001_ha, 2, SPC_REG_RESET, regVal);
+
+ /* step 8: delay 10 usec */
+ udelay(10);
+
+ /* step 9: bring the BDMA and OSSP out of reset */
+ regVal = pm8001_cr32(pm8001_ha, 2, SPC_REG_RESET);
+ PM8001_INIT_DBG(pm8001_ha,
+ pm8001_printk("Top Register before bringing up BDMA/OSSP"
+ ":= 0x%x\n", regVal));
+ regVal |= (SPC_REG_RESET_BDMA_CORE | SPC_REG_RESET_OSSP);
+ pm8001_cw32(pm8001_ha, 2, SPC_REG_RESET, regVal);
+
+ /* step 10: delay 10 usec */
+ udelay(10);
+
+ /* step 11: reads and sets the GSM Configuration and Reset Register */
+ /* map 0x0700000 to BAR4(0x20), BAR2(win) */
+ if (-1 == bar4_shift(pm8001_ha, GSM_ADDR_BASE)) {
+ PM8001_FAIL_DBG(pm8001_ha,
+ pm8001_printk("SPC Shift Bar4 to 0x%x failed\n",
+ GSM_ADDR_BASE));
+ return -1;
+ }
+ PM8001_INIT_DBG(pm8001_ha,
+ pm8001_printk("GSM 0x0 (0x00007b88)-GSM Configuration and "
+ "Reset = 0x%x\n", pm8001_cr32(pm8001_ha, 2, GSM_CONFIG_RESET)));
+ regVal = pm8001_cr32(pm8001_ha, 2, GSM_CONFIG_RESET);
+ /* Put those bits to high */
+ /* GSM XCBI offset = 0x70 0000
+ 0x00 Bit 13 COM_SLV_SW_RSTB 1
+ 0x00 Bit 12 QSSP_SW_RSTB 1
+ 0x00 Bit 11 RAAE_SW_RSTB 1
+ 0x00 Bit 9 RB_1_SW_RSTB 1
+ 0x00 Bit 8 SM_SW_RSTB 1
+ */
+ regVal |= (GSM_CONFIG_RESET_VALUE);
+ pm8001_cw32(pm8001_ha, 2, GSM_CONFIG_RESET, regVal);
+ PM8001_INIT_DBG(pm8001_ha,
+ pm8001_printk("GSM (0x00004088 ==> 0x00007b88) - GSM"
+ " Configuration and Reset is set to = 0x%x\n",
+ pm8001_cr32(pm8001_ha, 2, GSM_CONFIG_RESET)));
+
+ /* step 12: Restore GSM - Read Address Parity Check */
+ regVal = pm8001_cr32(pm8001_ha, 2, GSM_READ_ADDR_PARITY_CHECK);
+ /* just for debugging */
+ PM8001_INIT_DBG(pm8001_ha,
+ pm8001_printk("GSM 0x700038 - Read Address Parity Check Enable"
+ " = 0x%x\n", regVal));
+ pm8001_cw32(pm8001_ha, 2, GSM_READ_ADDR_PARITY_CHECK, regVal1);
+ PM8001_INIT_DBG(pm8001_ha,
+ pm8001_printk("GSM 0x700038 - Read Address Parity"
+ " Check Enable is set to = 0x%x\n",
+ pm8001_cr32(pm8001_ha, 2, GSM_READ_ADDR_PARITY_CHECK)));
+ /* Restore GSM - Write Address Parity Check */
+ regVal = pm8001_cr32(pm8001_ha, 2, GSM_WRITE_ADDR_PARITY_CHECK);
+ pm8001_cw32(pm8001_ha, 2, GSM_WRITE_ADDR_PARITY_CHECK, regVal2);
+ PM8001_INIT_DBG(pm8001_ha,
+ pm8001_printk("GSM 0x700040 - Write Address Parity Check"
+ " Enable is set to = 0x%x\n",
+ pm8001_cr32(pm8001_ha, 2, GSM_WRITE_ADDR_PARITY_CHECK)));
+ /* Restore GSM - Write Data Parity Check */
+ regVal = pm8001_cr32(pm8001_ha, 2, GSM_WRITE_DATA_PARITY_CHECK);
+ pm8001_cw32(pm8001_ha, 2, GSM_WRITE_DATA_PARITY_CHECK, regVal3);
+ PM8001_INIT_DBG(pm8001_ha,
+ pm8001_printk("GSM 0x700048 - Write Data Parity Check Enable"
+ "is set to = 0x%x\n",
+ pm8001_cr32(pm8001_ha, 2, GSM_WRITE_DATA_PARITY_CHECK)));
+
+ /* step 13: bring the IOP and AAP1 out of reset */
+ /* map 0x00000 to BAR4(0x20), BAR2(win) */
+ if (-1 == bar4_shift(pm8001_ha, SPC_TOP_LEVEL_ADDR_BASE)) {
+ PM8001_FAIL_DBG(pm8001_ha,
+ pm8001_printk("Shift Bar4 to 0x%x failed\n",
+ SPC_TOP_LEVEL_ADDR_BASE));
+ return -1;
+ }
+ regVal = pm8001_cr32(pm8001_ha, 2, SPC_REG_RESET);
+ regVal |= (SPC_REG_RESET_PCS_IOP_SS | SPC_REG_RESET_PCS_AAP1_SS);
+ pm8001_cw32(pm8001_ha, 2, SPC_REG_RESET, regVal);
+
+ /* step 14: delay 10 usec - Normal Mode */
+ udelay(10);
+ /* check Soft Reset Normal mode or Soft Reset HDA mode */
+ if (signature == SPC_SOFT_RESET_SIGNATURE) {
+ /* step 15 (Normal Mode): wait until scratch pad1 register
+ bit 2 toggled */
+ max_wait_count = 2 * 1000 * 1000;/* 2 sec */
+ do {
+ udelay(1);
+ regVal = pm8001_cr32(pm8001_ha, 0, MSGU_SCRATCH_PAD_1) &
+ SCRATCH_PAD1_RST;
+ } while ((regVal != toggleVal) && (--max_wait_count));
+
+ if (!max_wait_count) {
+ regVal = pm8001_cr32(pm8001_ha, 0,
+ MSGU_SCRATCH_PAD_1);
+ PM8001_FAIL_DBG(pm8001_ha,
+ pm8001_printk("TIMEOUT : ToggleVal 0x%x,"
+ "MSGU_SCRATCH_PAD1 = 0x%x\n",
+ toggleVal, regVal));
+ PM8001_FAIL_DBG(pm8001_ha,
+ pm8001_printk("SCRATCH_PAD0 value = 0x%x\n",
+ pm8001_cr32(pm8001_ha, 0,
+ MSGU_SCRATCH_PAD_0)));
+ PM8001_FAIL_DBG(pm8001_ha,
+ pm8001_printk("SCRATCH_PAD2 value = 0x%x\n",
+ pm8001_cr32(pm8001_ha, 0,
+ MSGU_SCRATCH_PAD_2)));
+ PM8001_FAIL_DBG(pm8001_ha,
+ pm8001_printk("SCRATCH_PAD3 value = 0x%x\n",
+ pm8001_cr32(pm8001_ha, 0,
+ MSGU_SCRATCH_PAD_3)));
+ return -1;
+ }
+
+ /* step 16 (Normal) - Clear ODMR and ODCR */
+ pm8001_cw32(pm8001_ha, 0, MSGU_ODCR, ODCR_CLEAR_ALL);
+ pm8001_cw32(pm8001_ha, 0, MSGU_ODMR, ODMR_CLEAR_ALL);
+
+ /* step 17 (Normal Mode): wait for the FW and IOP to get
+ ready - 1 sec timeout */
+ /* Wait for the SPC Configuration Table to be ready */
+ if (check_fw_ready(pm8001_ha) == -1) {
+ regVal = pm8001_cr32(pm8001_ha, 0, MSGU_SCRATCH_PAD_1);
+ /* return error if MPI Configuration Table not ready */
+ PM8001_INIT_DBG(pm8001_ha,
+ pm8001_printk("FW not ready SCRATCH_PAD1"
+ " = 0x%x\n", regVal));
+ regVal = pm8001_cr32(pm8001_ha, 0, MSGU_SCRATCH_PAD_2);
+ /* return error if MPI Configuration Table not ready */
+ PM8001_INIT_DBG(pm8001_ha,
+ pm8001_printk("FW not ready SCRATCH_PAD2"
+ " = 0x%x\n", regVal));
+ PM8001_INIT_DBG(pm8001_ha,
+ pm8001_printk("SCRATCH_PAD0 value = 0x%x\n",
+ pm8001_cr32(pm8001_ha, 0,
+ MSGU_SCRATCH_PAD_0)));
+ PM8001_INIT_DBG(pm8001_ha,
+ pm8001_printk("SCRATCH_PAD3 value = 0x%x\n",
+ pm8001_cr32(pm8001_ha, 0,
+ MSGU_SCRATCH_PAD_3)));
+ return -1;
+ }
+ }
+
+ PM8001_INIT_DBG(pm8001_ha,
+ pm8001_printk("SPC soft reset Complete\n"));
+ return 0;
+}
+
+static void pm8001_hw_chip_rst(struct pm8001_hba_info *pm8001_ha)
+{
+ u32 i;
+ u32 regVal;
+ PM8001_INIT_DBG(pm8001_ha,
+ pm8001_printk("chip reset start\n"));
+
+ /* do SPC chip reset. */
+ regVal = pm8001_cr32(pm8001_ha, 1, SPC_REG_RESET);
+ regVal &= ~(SPC_REG_RESET_DEVICE);
+ pm8001_cw32(pm8001_ha, 1, SPC_REG_RESET, regVal);
+
+ /* delay 10 usec */
+ udelay(10);
+
+ /* bring chip reset out of reset */
+ regVal = pm8001_cr32(pm8001_ha, 1, SPC_REG_RESET);
+ regVal |= SPC_REG_RESET_DEVICE;
+ pm8001_cw32(pm8001_ha, 1, SPC_REG_RESET, regVal);
+
+ /* delay 10 usec */
+ udelay(10);
+
+ /* wait for 20 msec until the firmware gets reloaded */
+ i = 20;
+ do {
+ mdelay(1);
+ } while ((--i) != 0);
+
+ PM8001_INIT_DBG(pm8001_ha,
+ pm8001_printk("chip reset finished\n"));
+}
+
+/**
+ * pm8001_chip_iounmap - which maped when initilized.
+ * @pm8001_ha: our hba card information
+ */
+static void pm8001_chip_iounmap(struct pm8001_hba_info *pm8001_ha)
+{
+ s8 bar, logical = 0;
+ for (bar = 0; bar < 6; bar++) {
+ /*
+ ** logical BARs for SPC:
+ ** bar 0 and 1 - logical BAR0
+ ** bar 2 and 3 - logical BAR1
+ ** bar4 - logical BAR2
+ ** bar5 - logical BAR3
+ ** Skip the appropriate assignments:
+ */
+ if ((bar == 1) || (bar == 3))
+ continue;
+ if (pm8001_ha->io_mem[logical].memvirtaddr) {
+ iounmap(pm8001_ha->io_mem[logical].memvirtaddr);
+ logical++;
+ }
+ }
+}
+
+/**
+ * pm8001_chip_interrupt_enable - enable PM8001 chip interrupt
+ * @pm8001_ha: our hba card information
+ */
+static void
+pm8001_chip_intx_interrupt_enable(struct pm8001_hba_info *pm8001_ha)
+{
+ pm8001_cw32(pm8001_ha, 0, MSGU_ODMR, ODMR_CLEAR_ALL);
+ pm8001_cw32(pm8001_ha, 0, MSGU_ODCR, ODCR_CLEAR_ALL);
+}
+
+ /**
+ * pm8001_chip_intx_interrupt_disable- disable PM8001 chip interrupt
+ * @pm8001_ha: our hba card information
+ */
+static void
+pm8001_chip_intx_interrupt_disable(struct pm8001_hba_info *pm8001_ha)
+{
+ pm8001_cw32(pm8001_ha, 0, MSGU_ODMR, ODMR_MASK_ALL);
+}
+
+/**
+ * pm8001_chip_msix_interrupt_enable - enable PM8001 chip interrupt
+ * @pm8001_ha: our hba card information
+ */
+static void
+pm8001_chip_msix_interrupt_enable(struct pm8001_hba_info *pm8001_ha,
+ u32 int_vec_idx)
+{
+ u32 msi_index;
+ u32 value;
+ msi_index = int_vec_idx * MSIX_TABLE_ELEMENT_SIZE;
+ msi_index += MSIX_TABLE_BASE;
+ pm8001_cw32(pm8001_ha, 0, msi_index, MSIX_INTERRUPT_ENABLE);
+ value = (1 << int_vec_idx);
+ pm8001_cw32(pm8001_ha, 0, MSGU_ODCR, value);
+
+}
+
+/**
+ * pm8001_chip_msix_interrupt_disable - disable PM8001 chip interrupt
+ * @pm8001_ha: our hba card information
+ */
+static void
+pm8001_chip_msix_interrupt_disable(struct pm8001_hba_info *pm8001_ha,
+ u32 int_vec_idx)
+{
+ u32 msi_index;
+ msi_index = int_vec_idx * MSIX_TABLE_ELEMENT_SIZE;
+ msi_index += MSIX_TABLE_BASE;
+ pm8001_cw32(pm8001_ha, 0, msi_index, MSIX_INTERRUPT_DISABLE);
+
+}
+/**
+ * pm8001_chip_interrupt_enable - enable PM8001 chip interrupt
+ * @pm8001_ha: our hba card information
+ */
+static void
+pm8001_chip_interrupt_enable(struct pm8001_hba_info *pm8001_ha)
+{
+#ifdef PM8001_USE_MSIX
+ pm8001_chip_msix_interrupt_enable(pm8001_ha, 0);
+ return;
+#endif
+ pm8001_chip_intx_interrupt_enable(pm8001_ha);
+
+}
+
+/**
+ * pm8001_chip_intx_interrupt_disable- disable PM8001 chip interrupt
+ * @pm8001_ha: our hba card information
+ */
+static void
+pm8001_chip_interrupt_disable(struct pm8001_hba_info *pm8001_ha)
+{
+#ifdef PM8001_USE_MSIX
+ pm8001_chip_msix_interrupt_disable(pm8001_ha, 0);
+ return;
+#endif
+ pm8001_chip_intx_interrupt_disable(pm8001_ha);
+
+}
+
+/**
+ * mpi_msg_free_get- get the free message buffer for transfer inbound queue.
+ * @circularQ: the inbound queue we want to transfer to HBA.
+ * @messageSize: the message size of this transfer, normally it is 64 bytes
+ * @messagePtr: the pointer to message.
+ */
+static int mpi_msg_free_get(struct inbound_queue_table *circularQ,
+ u16 messageSize, void **messagePtr)
+{
+ u32 offset, consumer_index;
+ struct mpi_msg_hdr *msgHeader;
+ u8 bcCount = 1; /* only support single buffer */
+
+ /* Checks is the requested message size can be allocated in this queue*/
+ if (messageSize > 64) {
+ *messagePtr = NULL;
+ return -1;
+ }
+
+ /* Stores the new consumer index */
+ consumer_index = pm8001_read_32(circularQ->ci_virt);
+ circularQ->consumer_index = cpu_to_le32(consumer_index);
+ if (((circularQ->producer_idx + bcCount) % 256) ==
+ circularQ->consumer_index) {
+ *messagePtr = NULL;
+ return -1;
+ }
+ /* get memory IOMB buffer address */
+ offset = circularQ->producer_idx * 64;
+ /* increment to next bcCount element */
+ circularQ->producer_idx = (circularQ->producer_idx + bcCount) % 256;
+ /* Adds that distance to the base of the region virtual address plus
+ the message header size*/
+ msgHeader = (struct mpi_msg_hdr *)(circularQ->base_virt + offset);
+ *messagePtr = ((void *)msgHeader) + sizeof(struct mpi_msg_hdr);
+ return 0;
+}
+
+/**
+ * mpi_build_cmd- build the message queue for transfer, update the PI to FW
+ * to tell the fw to get this message from IOMB.
+ * @pm8001_ha: our hba card information
+ * @circularQ: the inbound queue we want to transfer to HBA.
+ * @opCode: the operation code represents commands which LLDD and fw recognized.
+ * @payload: the command payload of each operation command.
+ */
+static int mpi_build_cmd(struct pm8001_hba_info *pm8001_ha,
+ struct inbound_queue_table *circularQ,
+ u32 opCode, void *payload)
+{
+ u32 Header = 0, hpriority = 0, bc = 1, category = 0x02;
+ u32 responseQueue = 0;
+ void *pMessage;
+
+ if (mpi_msg_free_get(circularQ, 64, &pMessage) < 0) {
+ PM8001_IO_DBG(pm8001_ha,
+ pm8001_printk("No free mpi buffer \n"));
+ return -1;
+ }
+ BUG_ON(!payload);
+ /*Copy to the payload*/
+ memcpy(pMessage, payload, (64 - sizeof(struct mpi_msg_hdr)));
+
+ /*Build the header*/
+ Header = ((1 << 31) | (hpriority << 30) | ((bc & 0x1f) << 24)
+ | ((responseQueue & 0x3F) << 16)
+ | ((category & 0xF) << 12) | (opCode & 0xFFF));
+
+ pm8001_write_32((pMessage - 4), 0, cpu_to_le32(Header));
+ /*Update the PI to the firmware*/
+ pm8001_cw32(pm8001_ha, circularQ->pi_pci_bar,
+ circularQ->pi_offset, circularQ->producer_idx);
+ PM8001_IO_DBG(pm8001_ha,
+ pm8001_printk("after PI= %d CI= %d \n", circularQ->producer_idx,
+ circularQ->consumer_index));
+ return 0;
+}
+
+static u32 mpi_msg_free_set(struct pm8001_hba_info *pm8001_ha, void *pMsg,
+ struct outbound_queue_table *circularQ, u8 bc)
+{
+ u32 producer_index;
+ struct mpi_msg_hdr *msgHeader;
+ struct mpi_msg_hdr *pOutBoundMsgHeader;
+
+ msgHeader = (struct mpi_msg_hdr *)(pMsg - sizeof(struct mpi_msg_hdr));
+ pOutBoundMsgHeader = (struct mpi_msg_hdr *)(circularQ->base_virt +
+ circularQ->consumer_idx * 64);
+ if (pOutBoundMsgHeader != msgHeader) {
+ PM8001_FAIL_DBG(pm8001_ha,
+ pm8001_printk("consumer_idx = %d msgHeader = %p\n",
+ circularQ->consumer_idx, msgHeader));
+
+ /* Update the producer index from SPC */
+ producer_index = pm8001_read_32(circularQ->pi_virt);
+ circularQ->producer_index = cpu_to_le32(producer_index);
+ PM8001_FAIL_DBG(pm8001_ha,
+ pm8001_printk("consumer_idx = %d producer_index = %d"
+ "msgHeader = %p\n", circularQ->consumer_idx,
+ circularQ->producer_index, msgHeader));
+ return 0;
+ }
+ /* free the circular queue buffer elements associated with the message*/
+ circularQ->consumer_idx = (circularQ->consumer_idx + bc) % 256;
+ /* update the CI of outbound queue */
+ pm8001_cw32(pm8001_ha, circularQ->ci_pci_bar, circularQ->ci_offset,
+ circularQ->consumer_idx);
+ /* Update the producer index from SPC*/
+ producer_index = pm8001_read_32(circularQ->pi_virt);
+ circularQ->producer_index = cpu_to_le32(producer_index);
+ PM8001_IO_DBG(pm8001_ha,
+ pm8001_printk(" CI=%d PI=%d\n", circularQ->consumer_idx,
+ circularQ->producer_index));
+ return 0;
+}
+
+/**
+ * mpi_msg_consume- get the MPI message from outbound queue message table.
+ * @pm8001_ha: our hba card information
+ * @circularQ: the outbound queue table.
+ * @messagePtr1: the message contents of this outbound message.
+ * @pBC: the message size.
+ */
+static u32 mpi_msg_consume(struct pm8001_hba_info *pm8001_ha,
+ struct outbound_queue_table *circularQ,
+ void **messagePtr1, u8 *pBC)
+{
+ struct mpi_msg_hdr *msgHeader;
+ __le32 msgHeader_tmp;
+ u32 header_tmp;
+ do {
+ /* If there are not-yet-delivered messages ... */
+ if (circularQ->producer_index != circularQ->consumer_idx) {
+ /*Get the pointer to the circular queue buffer element*/
+ msgHeader = (struct mpi_msg_hdr *)
+ (circularQ->base_virt +
+ circularQ->consumer_idx * 64);
+ /* read header */
+ header_tmp = pm8001_read_32(msgHeader);
+ msgHeader_tmp = cpu_to_le32(header_tmp);
+ if (0 != (msgHeader_tmp & 0x80000000)) {
+ if (OPC_OUB_SKIP_ENTRY !=
+ (msgHeader_tmp & 0xfff)) {
+ *messagePtr1 =
+ ((u8 *)msgHeader) +
+ sizeof(struct mpi_msg_hdr);
+ *pBC = (u8)((msgHeader_tmp >> 24) &
+ 0x1f);
+ PM8001_IO_DBG(pm8001_ha,
+ pm8001_printk(": CI=%d PI=%d "
+ "msgHeader=%x\n",
+ circularQ->consumer_idx,
+ circularQ->producer_index,
+ msgHeader_tmp));
+ return MPI_IO_STATUS_SUCCESS;
+ } else {
+ circularQ->consumer_idx =
+ (circularQ->consumer_idx +
+ ((msgHeader_tmp >> 24) & 0x1f))
+ % 256;
+ msgHeader_tmp = 0;
+ pm8001_write_32(msgHeader, 0, 0);
+ /* update the CI of outbound queue */
+ pm8001_cw32(pm8001_ha,
+ circularQ->ci_pci_bar,
+ circularQ->ci_offset,
+ circularQ->consumer_idx);
+ }
+ } else {
+ circularQ->consumer_idx =
+ (circularQ->consumer_idx +
+ ((msgHeader_tmp >> 24) & 0x1f)) % 256;
+ msgHeader_tmp = 0;
+ pm8001_write_32(msgHeader, 0, 0);
+ /* update the CI of outbound queue */
+ pm8001_cw32(pm8001_ha, circularQ->ci_pci_bar,
+ circularQ->ci_offset,
+ circularQ->consumer_idx);
+ return MPI_IO_STATUS_FAIL;
+ }
+ } else {
+ u32 producer_index;
+ void *pi_virt = circularQ->pi_virt;
+ /* Update the producer index from SPC */
+ producer_index = pm8001_read_32(pi_virt);
+ circularQ->producer_index = cpu_to_le32(producer_index);
+ }
+ } while (circularQ->producer_index != circularQ->consumer_idx);
+ /* while we don't have any more not-yet-delivered message */
+ /* report empty */
+ return MPI_IO_STATUS_BUSY;
+}
+
+static void pm8001_work_queue(struct work_struct *work)
+{
+ struct delayed_work *dw = container_of(work, struct delayed_work, work);
+ struct pm8001_wq *wq = container_of(dw, struct pm8001_wq, work_q);
+ struct pm8001_device *pm8001_dev;
+ struct domain_device *dev;
+
+ switch (wq->handler) {
+ case IO_OPEN_CNX_ERROR_IT_NEXUS_LOSS:
+ pm8001_dev = wq->data;
+ dev = pm8001_dev->sas_device;
+ pm8001_I_T_nexus_reset(dev);
+ break;
+ case IO_OPEN_CNX_ERROR_STP_RESOURCES_BUSY:
+ pm8001_dev = wq->data;
+ dev = pm8001_dev->sas_device;
+ pm8001_I_T_nexus_reset(dev);
+ break;
+ case IO_DS_IN_ERROR:
+ pm8001_dev = wq->data;
+ dev = pm8001_dev->sas_device;
+ pm8001_I_T_nexus_reset(dev);
+ break;
+ case IO_DS_NON_OPERATIONAL:
+ pm8001_dev = wq->data;
+ dev = pm8001_dev->sas_device;
+ pm8001_I_T_nexus_reset(dev);
+ break;
+ }
+ list_del(&wq->entry);
+ kfree(wq);
+}
+
+static int pm8001_handle_event(struct pm8001_hba_info *pm8001_ha, void *data,
+ int handler)
+{
+ struct pm8001_wq *wq;
+ int ret = 0;
+
+ wq = kmalloc(sizeof(struct pm8001_wq), GFP_ATOMIC);
+ if (wq) {
+ wq->pm8001_ha = pm8001_ha;
+ wq->data = data;
+ wq->handler = handler;
+ INIT_DELAYED_WORK(&wq->work_q, pm8001_work_queue);
+ list_add_tail(&wq->entry, &pm8001_ha->wq_list);
+ schedule_delayed_work(&wq->work_q, 0);
+ } else
+ ret = -ENOMEM;
+
+ return ret;
+}
+
+/**
+ * mpi_ssp_completion- process the event that FW response to the SSP request.
+ * @pm8001_ha: our hba card information
+ * @piomb: the message contents of this outbound message.
+ *
+ * When FW has completed a ssp request for example a IO request, after it has
+ * filled the SG data with the data, it will trigger this event represent
+ * that he has finished the job,please check the coresponding buffer.
+ * So we will tell the caller who maybe waiting the result to tell upper layer
+ * that the task has been finished.
+ */
+static void
+mpi_ssp_completion(struct pm8001_hba_info *pm8001_ha , void *piomb)
+{
+ struct sas_task *t;
+ struct pm8001_ccb_info *ccb;
+ unsigned long flags;
+ u32 status;
+ u32 param;
+ u32 tag;
+ struct ssp_completion_resp *psspPayload;
+ struct task_status_struct *ts;
+ struct ssp_response_iu *iu;
+ struct pm8001_device *pm8001_dev;
+ psspPayload = (struct ssp_completion_resp *)(piomb + 4);
+ status = le32_to_cpu(psspPayload->status);
+ tag = le32_to_cpu(psspPayload->tag);
+ ccb = &pm8001_ha->ccb_info[tag];
+ pm8001_dev = ccb->device;
+ param = le32_to_cpu(psspPayload->param);
+
+ t = ccb->task;
+
+ if (status && status != IO_UNDERFLOW)
+ PM8001_FAIL_DBG(pm8001_ha,
+ pm8001_printk("sas IO status 0x%x\n", status));
+ if (unlikely(!t || !t->lldd_task || !t->dev))
+ return;
+ ts = &t->task_status;
+ switch (status) {
+ case IO_SUCCESS:
+ PM8001_IO_DBG(pm8001_ha, pm8001_printk("IO_SUCCESS"
+ ",param = %d \n", param));
+ if (param == 0) {
+ ts->resp = SAS_TASK_COMPLETE;
+ ts->stat = SAM_GOOD;
+ } else {
+ ts->resp = SAS_TASK_COMPLETE;
+ ts->stat = SAS_PROTO_RESPONSE;
+ ts->residual = param;
+ iu = &psspPayload->ssp_resp_iu;
+ sas_ssp_task_response(pm8001_ha->dev, t, iu);
+ }
+ if (pm8001_dev)
+ pm8001_dev->running_req--;
+ break;
+ case IO_ABORTED:
+ PM8001_IO_DBG(pm8001_ha,
+ pm8001_printk("IO_ABORTED IOMB Tag \n"));
+ ts->resp = SAS_TASK_COMPLETE;
+ ts->stat = SAS_ABORTED_TASK;
+ break;
+ case IO_UNDERFLOW:
+ /* SSP Completion with error */
+ PM8001_IO_DBG(pm8001_ha, pm8001_printk("IO_UNDERFLOW"
+ ",param = %d \n", param));
+ ts->resp = SAS_TASK_COMPLETE;
+ ts->stat = SAS_DATA_UNDERRUN;
+ ts->residual = param;
+ if (pm8001_dev)
+ pm8001_dev->running_req--;
+ break;
+ case IO_NO_DEVICE:
+ PM8001_IO_DBG(pm8001_ha,
+ pm8001_printk("IO_NO_DEVICE\n"));
+ ts->resp = SAS_TASK_UNDELIVERED;
+ ts->stat = SAS_PHY_DOWN;
+ break;
+ case IO_XFER_ERROR_BREAK:
+ PM8001_IO_DBG(pm8001_ha,
+ pm8001_printk("IO_XFER_ERROR_BREAK\n"));
+ ts->resp = SAS_TASK_COMPLETE;
+ ts->stat = SAS_OPEN_REJECT;
+ break;
+ case IO_XFER_ERROR_PHY_NOT_READY:
+ PM8001_IO_DBG(pm8001_ha,
+ pm8001_printk("IO_XFER_ERROR_PHY_NOT_READY\n"));
+ ts->resp = SAS_TASK_COMPLETE;
+ ts->stat = SAS_OPEN_REJECT;
+ ts->open_rej_reason = SAS_OREJ_RSVD_RETRY;
+ break;
+ case IO_OPEN_CNX_ERROR_PROTOCOL_NOT_SUPPORTED:
+ PM8001_IO_DBG(pm8001_ha,
+ pm8001_printk("IO_OPEN_CNX_ERROR_PROTOCOL_NOT_SUPPORTED\n"));
+ ts->resp = SAS_TASK_COMPLETE;
+ ts->stat = SAS_OPEN_REJECT;
+ ts->open_rej_reason = SAS_OREJ_EPROTO;
+ break;
+ case IO_OPEN_CNX_ERROR_ZONE_VIOLATION:
+ PM8001_IO_DBG(pm8001_ha,
+ pm8001_printk("IO_OPEN_CNX_ERROR_ZONE_VIOLATION\n"));
+ ts->resp = SAS_TASK_COMPLETE;
+ ts->stat = SAS_OPEN_REJECT;
+ ts->open_rej_reason = SAS_OREJ_UNKNOWN;
+ break;
+ case IO_OPEN_CNX_ERROR_BREAK:
+ PM8001_IO_DBG(pm8001_ha,
+ pm8001_printk("IO_OPEN_CNX_ERROR_BREAK\n"));
+ ts->resp = SAS_TASK_COMPLETE;
+ ts->stat = SAS_OPEN_REJECT;
+ ts->open_rej_reason = SAS_OREJ_RSVD_RETRY;
+ break;
+ case IO_OPEN_CNX_ERROR_IT_NEXUS_LOSS:
+ PM8001_IO_DBG(pm8001_ha,
+ pm8001_printk("IO_OPEN_CNX_ERROR_IT_NEXUS_LOSS\n"));
+ ts->resp = SAS_TASK_COMPLETE;
+ ts->stat = SAS_OPEN_REJECT;
+ ts->open_rej_reason = SAS_OREJ_UNKNOWN;
+ if (!t->uldd_task)
+ pm8001_handle_event(pm8001_ha,
+ pm8001_dev,
+ IO_OPEN_CNX_ERROR_IT_NEXUS_LOSS);
+ break;
+ case IO_OPEN_CNX_ERROR_BAD_DESTINATION:
+ PM8001_IO_DBG(pm8001_ha,
+ pm8001_printk("IO_OPEN_CNX_ERROR_BAD_DESTINATION\n"));
+ ts->resp = SAS_TASK_COMPLETE;
+ ts->stat = SAS_OPEN_REJECT;
+ ts->open_rej_reason = SAS_OREJ_BAD_DEST;
+ break;
+ case IO_OPEN_CNX_ERROR_CONNECTION_RATE_NOT_SUPPORTED:
+ PM8001_IO_DBG(pm8001_ha,
+ pm8001_printk("IO_OPEN_CNX_ERROR_CONNECTION_RATE_"
+ "NOT_SUPPORTED\n"));
+ ts->resp = SAS_TASK_COMPLETE;
+ ts->stat = SAS_OPEN_REJECT;
+ ts->open_rej_reason = SAS_OREJ_CONN_RATE;
+ break;
+ case IO_OPEN_CNX_ERROR_WRONG_DESTINATION:
+ PM8001_IO_DBG(pm8001_ha,
+ pm8001_printk("IO_OPEN_CNX_ERROR_WRONG_DESTINATION\n"));
+ ts->resp = SAS_TASK_UNDELIVERED;
+ ts->stat = SAS_OPEN_REJECT;
+ ts->open_rej_reason = SAS_OREJ_WRONG_DEST;
+ break;
+ case IO_XFER_ERROR_NAK_RECEIVED:
+ PM8001_IO_DBG(pm8001_ha,
+ pm8001_printk("IO_XFER_ERROR_NAK_RECEIVED\n"));
+ ts->resp = SAS_TASK_COMPLETE;
+ ts->stat = SAS_OPEN_REJECT;
+ ts->open_rej_reason = SAS_OREJ_RSVD_RETRY;
+ break;
+ case IO_XFER_ERROR_ACK_NAK_TIMEOUT:
+ PM8001_IO_DBG(pm8001_ha,
+ pm8001_printk("IO_XFER_ERROR_ACK_NAK_TIMEOUT\n"));
+ ts->resp = SAS_TASK_COMPLETE;
+ ts->stat = SAS_NAK_R_ERR;
+ break;
+ case IO_XFER_ERROR_DMA:
+ PM8001_IO_DBG(pm8001_ha,
+ pm8001_printk("IO_XFER_ERROR_DMA\n"));
+ ts->resp = SAS_TASK_COMPLETE;
+ ts->stat = SAS_OPEN_REJECT;
+ break;
+ case IO_XFER_OPEN_RETRY_TIMEOUT:
+ PM8001_IO_DBG(pm8001_ha,
+ pm8001_printk("IO_XFER_OPEN_RETRY_TIMEOUT\n"));
+ ts->resp = SAS_TASK_COMPLETE;
+ ts->stat = SAS_OPEN_REJECT;
+ ts->open_rej_reason = SAS_OREJ_RSVD_RETRY;
+ break;
+ case IO_XFER_ERROR_OFFSET_MISMATCH:
+ PM8001_IO_DBG(pm8001_ha,
+ pm8001_printk("IO_XFER_ERROR_OFFSET_MISMATCH\n"));
+ ts->resp = SAS_TASK_COMPLETE;
+ ts->stat = SAS_OPEN_REJECT;
+ break;
+ case IO_PORT_IN_RESET:
+ PM8001_IO_DBG(pm8001_ha,
+ pm8001_printk("IO_PORT_IN_RESET\n"));
+ ts->resp = SAS_TASK_COMPLETE;
+ ts->stat = SAS_OPEN_REJECT;
+ break;
+ case IO_DS_NON_OPERATIONAL:
+ PM8001_IO_DBG(pm8001_ha,
+ pm8001_printk("IO_DS_NON_OPERATIONAL\n"));
+ ts->resp = SAS_TASK_COMPLETE;
+ ts->stat = SAS_OPEN_REJECT;
+ if (!t->uldd_task)
+ pm8001_handle_event(pm8001_ha,
+ pm8001_dev,
+ IO_DS_NON_OPERATIONAL);
+ break;
+ case IO_DS_IN_RECOVERY:
+ PM8001_IO_DBG(pm8001_ha,
+ pm8001_printk("IO_DS_IN_RECOVERY\n"));
+ ts->resp = SAS_TASK_COMPLETE;
+ ts->stat = SAS_OPEN_REJECT;
+ break;
+ case IO_TM_TAG_NOT_FOUND:
+ PM8001_IO_DBG(pm8001_ha,
+ pm8001_printk("IO_TM_TAG_NOT_FOUND\n"));
+ ts->resp = SAS_TASK_COMPLETE;
+ ts->stat = SAS_OPEN_REJECT;
+ break;
+ case IO_SSP_EXT_IU_ZERO_LEN_ERROR:
+ PM8001_IO_DBG(pm8001_ha,
+ pm8001_printk("IO_SSP_EXT_IU_ZERO_LEN_ERROR\n"));
+ ts->resp = SAS_TASK_COMPLETE;
+ ts->stat = SAS_OPEN_REJECT;
+ break;
+ case IO_OPEN_CNX_ERROR_HW_RESOURCE_BUSY:
+ PM8001_IO_DBG(pm8001_ha,
+ pm8001_printk("IO_OPEN_CNX_ERROR_HW_RESOURCE_BUSY\n"));
+ ts->resp = SAS_TASK_COMPLETE;
+ ts->stat = SAS_OPEN_REJECT;
+ ts->open_rej_reason = SAS_OREJ_RSVD_RETRY;
+ default:
+ PM8001_IO_DBG(pm8001_ha,
+ pm8001_printk("Unknown status 0x%x\n", status));
+ /* not allowed case. Therefore, return failed status */
+ ts->resp = SAS_TASK_COMPLETE;
+ ts->stat = SAS_OPEN_REJECT;
+ break;
+ }
+ PM8001_IO_DBG(pm8001_ha,
+ pm8001_printk("scsi_status = %x \n ",
+ psspPayload->ssp_resp_iu.status));
+ spin_lock_irqsave(&t->task_state_lock, flags);
+ t->task_state_flags &= ~SAS_TASK_STATE_PENDING;
+ t->task_state_flags &= ~SAS_TASK_AT_INITIATOR;
+ t->task_state_flags |= SAS_TASK_STATE_DONE;
+ if (unlikely((t->task_state_flags & SAS_TASK_STATE_ABORTED))) {
+ spin_unlock_irqrestore(&t->task_state_lock, flags);
+ PM8001_FAIL_DBG(pm8001_ha, pm8001_printk("task 0x%p done with"
+ " io_status 0x%x resp 0x%x "
+ "stat 0x%x but aborted by upper layer!\n",
+ t, status, ts->resp, ts->stat));
+ pm8001_ccb_task_free(pm8001_ha, t, ccb, tag);
+ } else {
+ spin_unlock_irqrestore(&t->task_state_lock, flags);
+ pm8001_ccb_task_free(pm8001_ha, t, ccb, tag);
+ mb();/* in order to force CPU ordering */
+ t->task_done(t);
+ }
+}
+
+/*See the comments for mpi_ssp_completion */
+static void mpi_ssp_event(struct pm8001_hba_info *pm8001_ha , void *piomb)
+{
+ struct sas_task *t;
+ unsigned long flags;
+ struct task_status_struct *ts;
+ struct pm8001_ccb_info *ccb;
+ struct pm8001_device *pm8001_dev;
+ struct ssp_event_resp *psspPayload =
+ (struct ssp_event_resp *)(piomb + 4);
+ u32 event = le32_to_cpu(psspPayload->event);
+ u32 tag = le32_to_cpu(psspPayload->tag);
+ u32 port_id = le32_to_cpu(psspPayload->port_id);
+ u32 dev_id = le32_to_cpu(psspPayload->device_id);
+
+ ccb = &pm8001_ha->ccb_info[tag];
+ t = ccb->task;
+ pm8001_dev = ccb->device;
+ if (event)
+ PM8001_FAIL_DBG(pm8001_ha,
+ pm8001_printk("sas IO status 0x%x\n", event));
+ if (unlikely(!t || !t->lldd_task || !t->dev))
+ return;
+ ts = &t->task_status;
+ PM8001_IO_DBG(pm8001_ha,
+ pm8001_printk("port_id = %x,device_id = %x\n",
+ port_id, dev_id));
+ switch (event) {
+ case IO_OVERFLOW:
+ PM8001_IO_DBG(pm8001_ha, pm8001_printk("IO_UNDERFLOW\n");)
+ ts->resp = SAS_TASK_COMPLETE;
+ ts->stat = SAS_DATA_OVERRUN;
+ ts->residual = 0;
+ if (pm8001_dev)
+ pm8001_dev->running_req--;
+ break;
+ case IO_XFER_ERROR_BREAK:
+ PM8001_IO_DBG(pm8001_ha,
+ pm8001_printk("IO_XFER_ERROR_BREAK\n"));
+ ts->resp = SAS_TASK_COMPLETE;
+ ts->stat = SAS_INTERRUPTED;
+ break;
+ case IO_XFER_ERROR_PHY_NOT_READY:
+ PM8001_IO_DBG(pm8001_ha,
+ pm8001_printk("IO_XFER_ERROR_PHY_NOT_READY\n"));
+ ts->resp = SAS_TASK_COMPLETE;
+ ts->stat = SAS_OPEN_REJECT;
+ ts->open_rej_reason = SAS_OREJ_RSVD_RETRY;
+ break;
+ case IO_OPEN_CNX_ERROR_PROTOCOL_NOT_SUPPORTED:
+ PM8001_IO_DBG(pm8001_ha,
+ pm8001_printk("IO_OPEN_CNX_ERROR_PROTOCOL_NOT"
+ "_SUPPORTED\n"));
+ ts->resp = SAS_TASK_COMPLETE;
+ ts->stat = SAS_OPEN_REJECT;
+ ts->open_rej_reason = SAS_OREJ_EPROTO;
+ break;
+ case IO_OPEN_CNX_ERROR_ZONE_VIOLATION:
+ PM8001_IO_DBG(pm8001_ha,
+ pm8001_printk("IO_OPEN_CNX_ERROR_ZONE_VIOLATION\n"));
+ ts->resp = SAS_TASK_COMPLETE;
+ ts->stat = SAS_OPEN_REJECT;
+ ts->open_rej_reason = SAS_OREJ_UNKNOWN;
+ break;
+ case IO_OPEN_CNX_ERROR_BREAK:
+ PM8001_IO_DBG(pm8001_ha,
+ pm8001_printk("IO_OPEN_CNX_ERROR_BREAK\n"));
+ ts->resp = SAS_TASK_COMPLETE;
+ ts->stat = SAS_OPEN_REJECT;
+ ts->open_rej_reason = SAS_OREJ_RSVD_RETRY;
+ break;
+ case IO_OPEN_CNX_ERROR_IT_NEXUS_LOSS:
+ PM8001_IO_DBG(pm8001_ha,
+ pm8001_printk("IO_OPEN_CNX_ERROR_IT_NEXUS_LOSS\n"));
+ ts->resp = SAS_TASK_COMPLETE;
+ ts->stat = SAS_OPEN_REJECT;
+ ts->open_rej_reason = SAS_OREJ_UNKNOWN;
+ if (!t->uldd_task)
+ pm8001_handle_event(pm8001_ha,
+ pm8001_dev,
+ IO_OPEN_CNX_ERROR_IT_NEXUS_LOSS);
+ break;
+ case IO_OPEN_CNX_ERROR_BAD_DESTINATION:
+ PM8001_IO_DBG(pm8001_ha,
+ pm8001_printk("IO_OPEN_CNX_ERROR_BAD_DESTINATION\n"));
+ ts->resp = SAS_TASK_COMPLETE;
+ ts->stat = SAS_OPEN_REJECT;
+ ts->open_rej_reason = SAS_OREJ_BAD_DEST;
+ break;
+ case IO_OPEN_CNX_ERROR_CONNECTION_RATE_NOT_SUPPORTED:
+ PM8001_IO_DBG(pm8001_ha,
+ pm8001_printk("IO_OPEN_CNX_ERROR_CONNECTION_RATE_"
+ "NOT_SUPPORTED\n"));
+ ts->resp = SAS_TASK_COMPLETE;
+ ts->stat = SAS_OPEN_REJECT;
+ ts->open_rej_reason = SAS_OREJ_CONN_RATE;
+ break;
+ case IO_OPEN_CNX_ERROR_WRONG_DESTINATION:
+ PM8001_IO_DBG(pm8001_ha,
+ pm8001_printk("IO_OPEN_CNX_ERROR_WRONG_DESTINATION\n"));
+ ts->resp = SAS_TASK_COMPLETE;
+ ts->stat = SAS_OPEN_REJECT;
+ ts->open_rej_reason = SAS_OREJ_WRONG_DEST;
+ break;
+ case IO_XFER_ERROR_NAK_RECEIVED:
+ PM8001_IO_DBG(pm8001_ha,
+ pm8001_printk("IO_XFER_ERROR_NAK_RECEIVED\n"));
+ ts->resp = SAS_TASK_COMPLETE;
+ ts->stat = SAS_OPEN_REJECT;
+ ts->open_rej_reason = SAS_OREJ_RSVD_RETRY;
+ break;
+ case IO_XFER_ERROR_ACK_NAK_TIMEOUT:
+ PM8001_IO_DBG(pm8001_ha,
+ pm8001_printk("IO_XFER_ERROR_ACK_NAK_TIMEOUT\n"));
+ ts->resp = SAS_TASK_COMPLETE;
+ ts->stat = SAS_NAK_R_ERR;
+ break;
+ case IO_XFER_OPEN_RETRY_TIMEOUT:
+ PM8001_IO_DBG(pm8001_ha,
+ pm8001_printk("IO_XFER_OPEN_RETRY_TIMEOUT\n"));
+ ts->resp = SAS_TASK_COMPLETE;
+ ts->stat = SAS_OPEN_REJECT;
+ ts->open_rej_reason = SAS_OREJ_RSVD_RETRY;
+ break;
+ case IO_XFER_ERROR_UNEXPECTED_PHASE:
+ PM8001_IO_DBG(pm8001_ha,
+ pm8001_printk("IO_XFER_ERROR_UNEXPECTED_PHASE\n"));
+ ts->resp = SAS_TASK_COMPLETE;
+ ts->stat = SAS_DATA_OVERRUN;
+ break;
+ case IO_XFER_ERROR_XFER_RDY_OVERRUN:
+ PM8001_IO_DBG(pm8001_ha,
+ pm8001_printk("IO_XFER_ERROR_XFER_RDY_OVERRUN\n"));
+ ts->resp = SAS_TASK_COMPLETE;
+ ts->stat = SAS_DATA_OVERRUN;
+ break;
+ case IO_XFER_ERROR_XFER_RDY_NOT_EXPECTED:
+ PM8001_IO_DBG(pm8001_ha,
+ pm8001_printk("IO_XFER_ERROR_XFER_RDY_NOT_EXPECTED\n"));
+ ts->resp = SAS_TASK_COMPLETE;
+ ts->stat = SAS_DATA_OVERRUN;
+ break;
+ case IO_XFER_ERROR_CMD_ISSUE_ACK_NAK_TIMEOUT:
+ PM8001_IO_DBG(pm8001_ha,
+ pm8001_printk("IO_XFER_ERROR_CMD_ISSUE_ACK_NAK_TIMEOUT\n"));
+ ts->resp = SAS_TASK_COMPLETE;
+ ts->stat = SAS_DATA_OVERRUN;
+ break;
+ case IO_XFER_ERROR_OFFSET_MISMATCH:
+ PM8001_IO_DBG(pm8001_ha,
+ pm8001_printk("IO_XFER_ERROR_OFFSET_MISMATCH\n"));
+ ts->resp = SAS_TASK_COMPLETE;
+ ts->stat = SAS_DATA_OVERRUN;
+ break;
+ case IO_XFER_ERROR_XFER_ZERO_DATA_LEN:
+ PM8001_IO_DBG(pm8001_ha,
+ pm8001_printk("IO_XFER_ERROR_XFER_ZERO_DATA_LEN\n"));
+ ts->resp = SAS_TASK_COMPLETE;
+ ts->stat = SAS_DATA_OVERRUN;
+ break;
+ case IO_XFER_CMD_FRAME_ISSUED:
+ PM8001_IO_DBG(pm8001_ha,
+ pm8001_printk(" IO_XFER_CMD_FRAME_ISSUED\n"));
+ return;
+ default:
+ PM8001_IO_DBG(pm8001_ha,
+ pm8001_printk("Unknown status 0x%x\n", event));
+ /* not allowed case. Therefore, return failed status */
+ ts->resp = SAS_TASK_COMPLETE;
+ ts->stat = SAS_DATA_OVERRUN;
+ break;
+ }
+ spin_lock_irqsave(&t->task_state_lock, flags);
+ t->task_state_flags &= ~SAS_TASK_STATE_PENDING;
+ t->task_state_flags &= ~SAS_TASK_AT_INITIATOR;
+ t->task_state_flags |= SAS_TASK_STATE_DONE;
+ if (unlikely((t->task_state_flags & SAS_TASK_STATE_ABORTED))) {
+ spin_unlock_irqrestore(&t->task_state_lock, flags);
+ PM8001_FAIL_DBG(pm8001_ha, pm8001_printk("task 0x%p done with"
+ " event 0x%x resp 0x%x "
+ "stat 0x%x but aborted by upper layer!\n",
+ t, event, ts->resp, ts->stat));
+ pm8001_ccb_task_free(pm8001_ha, t, ccb, tag);
+ } else {
+ spin_unlock_irqrestore(&t->task_state_lock, flags);
+ pm8001_ccb_task_free(pm8001_ha, t, ccb, tag);
+ mb();/* in order to force CPU ordering */
+ t->task_done(t);
+ }
+}
+
+/*See the comments for mpi_ssp_completion */
+static void
+mpi_sata_completion(struct pm8001_hba_info *pm8001_ha, void *piomb)
+{
+ struct sas_task *t;
+ struct pm8001_ccb_info *ccb;
+ unsigned long flags;
+ u32 param;
+ u32 status;
+ u32 tag;
+ struct sata_completion_resp *psataPayload;
+ struct task_status_struct *ts;
+ struct ata_task_resp *resp ;
+ u32 *sata_resp;
+ struct pm8001_device *pm8001_dev;
+
+ psataPayload = (struct sata_completion_resp *)(piomb + 4);
+ status = le32_to_cpu(psataPayload->status);
+ tag = le32_to_cpu(psataPayload->tag);
+
+ ccb = &pm8001_ha->ccb_info[tag];
+ param = le32_to_cpu(psataPayload->param);
+ t = ccb->task;
+ ts = &t->task_status;
+ pm8001_dev = ccb->device;
+ if (status)
+ PM8001_FAIL_DBG(pm8001_ha,
+ pm8001_printk("sata IO status 0x%x\n", status));
+ if (unlikely(!t || !t->lldd_task || !t->dev))
+ return;
+
+ switch (status) {
+ case IO_SUCCESS:
+ PM8001_IO_DBG(pm8001_ha, pm8001_printk("IO_SUCCESS\n"));
+ if (param == 0) {
+ ts->resp = SAS_TASK_COMPLETE;
+ ts->stat = SAM_GOOD;
+ } else {
+ u8 len;
+ ts->resp = SAS_TASK_COMPLETE;
+ ts->stat = SAS_PROTO_RESPONSE;
+ ts->residual = param;
+ PM8001_IO_DBG(pm8001_ha,
+ pm8001_printk("SAS_PROTO_RESPONSE len = %d\n",
+ param));
+ sata_resp = &psataPayload->sata_resp[0];
+ resp = (struct ata_task_resp *)ts->buf;
+ if (t->ata_task.dma_xfer == 0 &&
+ t->data_dir == PCI_DMA_FROMDEVICE) {
+ len = sizeof(struct pio_setup_fis);
+ PM8001_IO_DBG(pm8001_ha,
+ pm8001_printk("PIO read len = %d\n", len));
+ } else if (t->ata_task.use_ncq) {
+ len = sizeof(struct set_dev_bits_fis);
+ PM8001_IO_DBG(pm8001_ha,
+ pm8001_printk("FPDMA len = %d\n", len));
+ } else {
+ len = sizeof(struct dev_to_host_fis);
+ PM8001_IO_DBG(pm8001_ha,
+ pm8001_printk("other len = %d\n", len));
+ }
+ if (SAS_STATUS_BUF_SIZE >= sizeof(*resp)) {
+ resp->frame_len = len;
+ memcpy(&resp->ending_fis[0], sata_resp, len);
+ ts->buf_valid_size = sizeof(*resp);
+ } else
+ PM8001_IO_DBG(pm8001_ha,
+ pm8001_printk("response to large \n"));
+ }
+ if (pm8001_dev)
+ pm8001_dev->running_req--;
+ break;
+ case IO_ABORTED:
+ PM8001_IO_DBG(pm8001_ha,
+ pm8001_printk("IO_ABORTED IOMB Tag \n"));
+ ts->resp = SAS_TASK_COMPLETE;
+ ts->stat = SAS_ABORTED_TASK;
+ if (pm8001_dev)
+ pm8001_dev->running_req--;
+ break;
+ /* following cases are to do cases */
+ case IO_UNDERFLOW:
+ /* SATA Completion with error */
+ PM8001_IO_DBG(pm8001_ha,
+ pm8001_printk("IO_UNDERFLOW param = %d\n", param));
+ ts->resp = SAS_TASK_COMPLETE;
+ ts->stat = SAS_DATA_UNDERRUN;
+ ts->residual = param;
+ if (pm8001_dev)
+ pm8001_dev->running_req--;
+ break;
+ case IO_NO_DEVICE:
+ PM8001_IO_DBG(pm8001_ha,
+ pm8001_printk("IO_NO_DEVICE\n"));
+ ts->resp = SAS_TASK_UNDELIVERED;
+ ts->stat = SAS_PHY_DOWN;
+ break;
+ case IO_XFER_ERROR_BREAK:
+ PM8001_IO_DBG(pm8001_ha,
+ pm8001_printk("IO_XFER_ERROR_BREAK\n"));
+ ts->resp = SAS_TASK_COMPLETE;
+ ts->stat = SAS_INTERRUPTED;
+ break;
+ case IO_XFER_ERROR_PHY_NOT_READY:
+ PM8001_IO_DBG(pm8001_ha,
+ pm8001_printk("IO_XFER_ERROR_PHY_NOT_READY\n"));
+ ts->resp = SAS_TASK_COMPLETE;
+ ts->stat = SAS_OPEN_REJECT;
+ ts->open_rej_reason = SAS_OREJ_RSVD_RETRY;
+ break;
+ case IO_OPEN_CNX_ERROR_PROTOCOL_NOT_SUPPORTED:
+ PM8001_IO_DBG(pm8001_ha,
+ pm8001_printk("IO_OPEN_CNX_ERROR_PROTOCOL_NOT"
+ "_SUPPORTED\n"));
+ ts->resp = SAS_TASK_COMPLETE;
+ ts->stat = SAS_OPEN_REJECT;
+ ts->open_rej_reason = SAS_OREJ_EPROTO;
+ break;
+ case IO_OPEN_CNX_ERROR_ZONE_VIOLATION:
+ PM8001_IO_DBG(pm8001_ha,
+ pm8001_printk("IO_OPEN_CNX_ERROR_ZONE_VIOLATION\n"));
+ ts->resp = SAS_TASK_COMPLETE;
+ ts->stat = SAS_OPEN_REJECT;
+ ts->open_rej_reason = SAS_OREJ_UNKNOWN;
+ break;
+ case IO_OPEN_CNX_ERROR_BREAK:
+ PM8001_IO_DBG(pm8001_ha,
+ pm8001_printk("IO_OPEN_CNX_ERROR_BREAK\n"));
+ ts->resp = SAS_TASK_COMPLETE;
+ ts->stat = SAS_OPEN_REJECT;
+ ts->open_rej_reason = SAS_OREJ_RSVD_CONT0;
+ break;
+ case IO_OPEN_CNX_ERROR_IT_NEXUS_LOSS:
+ PM8001_IO_DBG(pm8001_ha,
+ pm8001_printk("IO_OPEN_CNX_ERROR_IT_NEXUS_LOSS\n"));
+ ts->resp = SAS_TASK_COMPLETE;
+ ts->stat = SAS_DEV_NO_RESPONSE;
+ if (!t->uldd_task) {
+ pm8001_handle_event(pm8001_ha,
+ pm8001_dev,
+ IO_OPEN_CNX_ERROR_IT_NEXUS_LOSS);
+ ts->resp = SAS_TASK_UNDELIVERED;
+ ts->stat = SAS_QUEUE_FULL;
+ pm8001_ccb_task_free(pm8001_ha, t, ccb, tag);
+ mb();/*in order to force CPU ordering*/
+ t->task_done(t);
+ return;
+ }
+ break;
+ case IO_OPEN_CNX_ERROR_BAD_DESTINATION:
+ PM8001_IO_DBG(pm8001_ha,
+ pm8001_printk("IO_OPEN_CNX_ERROR_BAD_DESTINATION\n"));
+ ts->resp = SAS_TASK_UNDELIVERED;
+ ts->stat = SAS_OPEN_REJECT;
+ ts->open_rej_reason = SAS_OREJ_BAD_DEST;
+ if (!t->uldd_task) {
+ pm8001_handle_event(pm8001_ha,
+ pm8001_dev,
+ IO_OPEN_CNX_ERROR_IT_NEXUS_LOSS);
+ ts->resp = SAS_TASK_UNDELIVERED;
+ ts->stat = SAS_QUEUE_FULL;
+ pm8001_ccb_task_free(pm8001_ha, t, ccb, tag);
+ mb();/*ditto*/
+ t->task_done(t);
+ return;
+ }
+ break;
+ case IO_OPEN_CNX_ERROR_CONNECTION_RATE_NOT_SUPPORTED:
+ PM8001_IO_DBG(pm8001_ha,
+ pm8001_printk("IO_OPEN_CNX_ERROR_CONNECTION_RATE_"
+ "NOT_SUPPORTED\n"));
+ ts->resp = SAS_TASK_COMPLETE;
+ ts->stat = SAS_OPEN_REJECT;
+ ts->open_rej_reason = SAS_OREJ_CONN_RATE;
+ break;
+ case IO_OPEN_CNX_ERROR_STP_RESOURCES_BUSY:
+ PM8001_IO_DBG(pm8001_ha,
+ pm8001_printk("IO_OPEN_CNX_ERROR_STP_RESOURCES"
+ "_BUSY\n"));
+ ts->resp = SAS_TASK_COMPLETE;
+ ts->stat = SAS_DEV_NO_RESPONSE;
+ if (!t->uldd_task) {
+ pm8001_handle_event(pm8001_ha,
+ pm8001_dev,
+ IO_OPEN_CNX_ERROR_STP_RESOURCES_BUSY);
+ ts->resp = SAS_TASK_UNDELIVERED;
+ ts->stat = SAS_QUEUE_FULL;
+ pm8001_ccb_task_free(pm8001_ha, t, ccb, tag);
+ mb();/* ditto*/
+ t->task_done(t);
+ return;
+ }
+ break;
+ case IO_OPEN_CNX_ERROR_WRONG_DESTINATION:
+ PM8001_IO_DBG(pm8001_ha,
+ pm8001_printk("IO_OPEN_CNX_ERROR_WRONG_DESTINATION\n"));
+ ts->resp = SAS_TASK_COMPLETE;
+ ts->stat = SAS_OPEN_REJECT;
+ ts->open_rej_reason = SAS_OREJ_WRONG_DEST;
+ break;
+ case IO_XFER_ERROR_NAK_RECEIVED:
+ PM8001_IO_DBG(pm8001_ha,
+ pm8001_printk("IO_XFER_ERROR_NAK_RECEIVED\n"));
+ ts->resp = SAS_TASK_COMPLETE;
+ ts->stat = SAS_NAK_R_ERR;
+ break;
+ case IO_XFER_ERROR_ACK_NAK_TIMEOUT:
+ PM8001_IO_DBG(pm8001_ha,
+ pm8001_printk("IO_XFER_ERROR_ACK_NAK_TIMEOUT\n"));
+ ts->resp = SAS_TASK_COMPLETE;
+ ts->stat = SAS_NAK_R_ERR;
+ break;
+ case IO_XFER_ERROR_DMA:
+ PM8001_IO_DBG(pm8001_ha,
+ pm8001_printk("IO_XFER_ERROR_DMA\n"));
+ ts->resp = SAS_TASK_COMPLETE;
+ ts->stat = SAS_ABORTED_TASK;
+ break;
+ case IO_XFER_ERROR_SATA_LINK_TIMEOUT:
+ PM8001_IO_DBG(pm8001_ha,
+ pm8001_printk("IO_XFER_ERROR_SATA_LINK_TIMEOUT\n"));
+ ts->resp = SAS_TASK_UNDELIVERED;
+ ts->stat = SAS_DEV_NO_RESPONSE;
+ break;
+ case IO_XFER_ERROR_REJECTED_NCQ_MODE:
+ PM8001_IO_DBG(pm8001_ha,
+ pm8001_printk("IO_XFER_ERROR_REJECTED_NCQ_MODE\n"));
+ ts->resp = SAS_TASK_COMPLETE;
+ ts->stat = SAS_DATA_UNDERRUN;
+ break;
+ case IO_XFER_OPEN_RETRY_TIMEOUT:
+ PM8001_IO_DBG(pm8001_ha,
+ pm8001_printk("IO_XFER_OPEN_RETRY_TIMEOUT\n"));
+ ts->resp = SAS_TASK_COMPLETE;
+ ts->stat = SAS_OPEN_TO;
+ break;
+ case IO_PORT_IN_RESET:
+ PM8001_IO_DBG(pm8001_ha,
+ pm8001_printk("IO_PORT_IN_RESET\n"));
+ ts->resp = SAS_TASK_COMPLETE;
+ ts->stat = SAS_DEV_NO_RESPONSE;
+ break;
+ case IO_DS_NON_OPERATIONAL:
+ PM8001_IO_DBG(pm8001_ha,
+ pm8001_printk("IO_DS_NON_OPERATIONAL\n"));
+ ts->resp = SAS_TASK_COMPLETE;
+ ts->stat = SAS_DEV_NO_RESPONSE;
+ if (!t->uldd_task) {
+ pm8001_handle_event(pm8001_ha, pm8001_dev,
+ IO_DS_NON_OPERATIONAL);
+ ts->resp = SAS_TASK_UNDELIVERED;
+ ts->stat = SAS_QUEUE_FULL;
+ pm8001_ccb_task_free(pm8001_ha, t, ccb, tag);
+ mb();/*ditto*/
+ t->task_done(t);
+ return;
+ }
+ break;
+ case IO_DS_IN_RECOVERY:
+ PM8001_IO_DBG(pm8001_ha,
+ pm8001_printk(" IO_DS_IN_RECOVERY\n"));
+ ts->resp = SAS_TASK_COMPLETE;
+ ts->stat = SAS_DEV_NO_RESPONSE;
+ break;
+ case IO_DS_IN_ERROR:
+ PM8001_IO_DBG(pm8001_ha,
+ pm8001_printk("IO_DS_IN_ERROR\n"));
+ ts->resp = SAS_TASK_COMPLETE;
+ ts->stat = SAS_DEV_NO_RESPONSE;
+ if (!t->uldd_task) {
+ pm8001_handle_event(pm8001_ha, pm8001_dev,
+ IO_DS_IN_ERROR);
+ ts->resp = SAS_TASK_UNDELIVERED;
+ ts->stat = SAS_QUEUE_FULL;
+ pm8001_ccb_task_free(pm8001_ha, t, ccb, tag);
+ mb();/*ditto*/
+ t->task_done(t);
+ return;
+ }
+ break;
+ case IO_OPEN_CNX_ERROR_HW_RESOURCE_BUSY:
+ PM8001_IO_DBG(pm8001_ha,
+ pm8001_printk("IO_OPEN_CNX_ERROR_HW_RESOURCE_BUSY\n"));
+ ts->resp = SAS_TASK_COMPLETE;
+ ts->stat = SAS_OPEN_REJECT;
+ ts->open_rej_reason = SAS_OREJ_RSVD_RETRY;
+ default:
+ PM8001_IO_DBG(pm8001_ha,
+ pm8001_printk("Unknown status 0x%x\n", status));
+ /* not allowed case. Therefore, return failed status */
+ ts->resp = SAS_TASK_COMPLETE;
+ ts->stat = SAS_DEV_NO_RESPONSE;
+ break;
+ }
+ spin_lock_irqsave(&t->task_state_lock, flags);
+ t->task_state_flags &= ~SAS_TASK_STATE_PENDING;
+ t->task_state_flags &= ~SAS_TASK_AT_INITIATOR;
+ t->task_state_flags |= SAS_TASK_STATE_DONE;
+ if (unlikely((t->task_state_flags & SAS_TASK_STATE_ABORTED))) {
+ spin_unlock_irqrestore(&t->task_state_lock, flags);
+ PM8001_FAIL_DBG(pm8001_ha,
+ pm8001_printk("task 0x%p done with io_status 0x%x"
+ " resp 0x%x stat 0x%x but aborted by upper layer!\n",
+ t, status, ts->resp, ts->stat));
+ pm8001_ccb_task_free(pm8001_ha, t, ccb, tag);
+ } else {
+ spin_unlock_irqrestore(&t->task_state_lock, flags);
+ pm8001_ccb_task_free(pm8001_ha, t, ccb, tag);
+ mb();/* ditto */
+ t->task_done(t);
+ }
+}
+
+/*See the comments for mpi_ssp_completion */
+static void mpi_sata_event(struct pm8001_hba_info *pm8001_ha , void *piomb)
+{
+ struct sas_task *t;
+ unsigned long flags;
+ struct task_status_struct *ts;
+ struct pm8001_ccb_info *ccb;
+ struct pm8001_device *pm8001_dev;
+ struct sata_event_resp *psataPayload =
+ (struct sata_event_resp *)(piomb + 4);
+ u32 event = le32_to_cpu(psataPayload->event);
+ u32 tag = le32_to_cpu(psataPayload->tag);
+ u32 port_id = le32_to_cpu(psataPayload->port_id);
+ u32 dev_id = le32_to_cpu(psataPayload->device_id);
+
+ ccb = &pm8001_ha->ccb_info[tag];
+ t = ccb->task;
+ pm8001_dev = ccb->device;
+ if (event)
+ PM8001_FAIL_DBG(pm8001_ha,
+ pm8001_printk("sata IO status 0x%x\n", event));
+ if (unlikely(!t || !t->lldd_task || !t->dev))
+ return;
+ ts = &t->task_status;
+ PM8001_IO_DBG(pm8001_ha,
+ pm8001_printk("port_id = %x,device_id = %x\n",
+ port_id, dev_id));
+ switch (event) {
+ case IO_OVERFLOW:
+ PM8001_IO_DBG(pm8001_ha, pm8001_printk("IO_UNDERFLOW\n"));
+ ts->resp = SAS_TASK_COMPLETE;
+ ts->stat = SAS_DATA_OVERRUN;
+ ts->residual = 0;
+ if (pm8001_dev)
+ pm8001_dev->running_req--;
+ break;
+ case IO_XFER_ERROR_BREAK:
+ PM8001_IO_DBG(pm8001_ha,
+ pm8001_printk("IO_XFER_ERROR_BREAK\n"));
+ ts->resp = SAS_TASK_COMPLETE;
+ ts->stat = SAS_INTERRUPTED;
+ break;
+ case IO_XFER_ERROR_PHY_NOT_READY:
+ PM8001_IO_DBG(pm8001_ha,
+ pm8001_printk("IO_XFER_ERROR_PHY_NOT_READY\n"));
+ ts->resp = SAS_TASK_COMPLETE;
+ ts->stat = SAS_OPEN_REJECT;
+ ts->open_rej_reason = SAS_OREJ_RSVD_RETRY;
+ break;
+ case IO_OPEN_CNX_ERROR_PROTOCOL_NOT_SUPPORTED:
+ PM8001_IO_DBG(pm8001_ha,
+ pm8001_printk("IO_OPEN_CNX_ERROR_PROTOCOL_NOT"
+ "_SUPPORTED\n"));
+ ts->resp = SAS_TASK_COMPLETE;
+ ts->stat = SAS_OPEN_REJECT;
+ ts->open_rej_reason = SAS_OREJ_EPROTO;
+ break;
+ case IO_OPEN_CNX_ERROR_ZONE_VIOLATION:
+ PM8001_IO_DBG(pm8001_ha,
+ pm8001_printk("IO_OPEN_CNX_ERROR_ZONE_VIOLATION\n"));
+ ts->resp = SAS_TASK_COMPLETE;
+ ts->stat = SAS_OPEN_REJECT;
+ ts->open_rej_reason = SAS_OREJ_UNKNOWN;
+ break;
+ case IO_OPEN_CNX_ERROR_BREAK:
+ PM8001_IO_DBG(pm8001_ha,
+ pm8001_printk("IO_OPEN_CNX_ERROR_BREAK\n"));
+ ts->resp = SAS_TASK_COMPLETE;
+ ts->stat = SAS_OPEN_REJECT;
+ ts->open_rej_reason = SAS_OREJ_RSVD_CONT0;
+ break;
+ case IO_OPEN_CNX_ERROR_IT_NEXUS_LOSS:
+ PM8001_IO_DBG(pm8001_ha,
+ pm8001_printk("IO_OPEN_CNX_ERROR_IT_NEXUS_LOSS\n"));
+ ts->resp = SAS_TASK_UNDELIVERED;
+ ts->stat = SAS_DEV_NO_RESPONSE;
+ if (!t->uldd_task) {
+ pm8001_handle_event(pm8001_ha,
+ pm8001_dev,
+ IO_OPEN_CNX_ERROR_IT_NEXUS_LOSS);
+ ts->resp = SAS_TASK_COMPLETE;
+ ts->stat = SAS_QUEUE_FULL;
+ pm8001_ccb_task_free(pm8001_ha, t, ccb, tag);
+ mb();/*ditto*/
+ t->task_done(t);
+ return;
+ }
+ break;
+ case IO_OPEN_CNX_ERROR_BAD_DESTINATION:
+ PM8001_IO_DBG(pm8001_ha,
+ pm8001_printk("IO_OPEN_CNX_ERROR_BAD_DESTINATION\n"));
+ ts->resp = SAS_TASK_UNDELIVERED;
+ ts->stat = SAS_OPEN_REJECT;
+ ts->open_rej_reason = SAS_OREJ_BAD_DEST;
+ break;
+ case IO_OPEN_CNX_ERROR_CONNECTION_RATE_NOT_SUPPORTED:
+ PM8001_IO_DBG(pm8001_ha,
+ pm8001_printk("IO_OPEN_CNX_ERROR_CONNECTION_RATE_"
+ "NOT_SUPPORTED\n"));
+ ts->resp = SAS_TASK_COMPLETE;
+ ts->stat = SAS_OPEN_REJECT;
+ ts->open_rej_reason = SAS_OREJ_CONN_RATE;
+ break;
+ case IO_OPEN_CNX_ERROR_WRONG_DESTINATION:
+ PM8001_IO_DBG(pm8001_ha,
+ pm8001_printk("IO_OPEN_CNX_ERROR_WRONG_DESTINATION\n"));
+ ts->resp = SAS_TASK_COMPLETE;
+ ts->stat = SAS_OPEN_REJECT;
+ ts->open_rej_reason = SAS_OREJ_WRONG_DEST;
+ break;
+ case IO_XFER_ERROR_NAK_RECEIVED:
+ PM8001_IO_DBG(pm8001_ha,
+ pm8001_printk("IO_XFER_ERROR_NAK_RECEIVED\n"));
+ ts->resp = SAS_TASK_COMPLETE;
+ ts->stat = SAS_NAK_R_ERR;
+ break;
+ case IO_XFER_ERROR_PEER_ABORTED:
+ PM8001_IO_DBG(pm8001_ha,
+ pm8001_printk("IO_XFER_ERROR_PEER_ABORTED\n"));
+ ts->resp = SAS_TASK_COMPLETE;
+ ts->stat = SAS_NAK_R_ERR;
+ break;
+ case IO_XFER_ERROR_REJECTED_NCQ_MODE:
+ PM8001_IO_DBG(pm8001_ha,
+ pm8001_printk("IO_XFER_ERROR_REJECTED_NCQ_MODE\n"));
+ ts->resp = SAS_TASK_COMPLETE;
+ ts->stat = SAS_DATA_UNDERRUN;
+ break;
+ case IO_XFER_OPEN_RETRY_TIMEOUT:
+ PM8001_IO_DBG(pm8001_ha,
+ pm8001_printk("IO_XFER_OPEN_RETRY_TIMEOUT\n"));
+ ts->resp = SAS_TASK_COMPLETE;
+ ts->stat = SAS_OPEN_TO;
+ break;
+ case IO_XFER_ERROR_UNEXPECTED_PHASE:
+ PM8001_IO_DBG(pm8001_ha,
+ pm8001_printk("IO_XFER_ERROR_UNEXPECTED_PHASE\n"));
+ ts->resp = SAS_TASK_COMPLETE;
+ ts->stat = SAS_OPEN_TO;
+ break;
+ case IO_XFER_ERROR_XFER_RDY_OVERRUN:
+ PM8001_IO_DBG(pm8001_ha,
+ pm8001_printk("IO_XFER_ERROR_XFER_RDY_OVERRUN\n"));
+ ts->resp = SAS_TASK_COMPLETE;
+ ts->stat = SAS_OPEN_TO;
+ break;
+ case IO_XFER_ERROR_XFER_RDY_NOT_EXPECTED:
+ PM8001_IO_DBG(pm8001_ha,
+ pm8001_printk("IO_XFER_ERROR_XFER_RDY_NOT_EXPECTED\n"));
+ ts->resp = SAS_TASK_COMPLETE;
+ ts->stat = SAS_OPEN_TO;
+ break;
+ case IO_XFER_ERROR_OFFSET_MISMATCH:
+ PM8001_IO_DBG(pm8001_ha,
+ pm8001_printk("IO_XFER_ERROR_OFFSET_MISMATCH\n"));
+ ts->resp = SAS_TASK_COMPLETE;
+ ts->stat = SAS_OPEN_TO;
+ break;
+ case IO_XFER_ERROR_XFER_ZERO_DATA_LEN:
+ PM8001_IO_DBG(pm8001_ha,
+ pm8001_printk("IO_XFER_ERROR_XFER_ZERO_DATA_LEN\n"));
+ ts->resp = SAS_TASK_COMPLETE;
+ ts->stat = SAS_OPEN_TO;
+ break;
+ case IO_XFER_CMD_FRAME_ISSUED:
+ PM8001_IO_DBG(pm8001_ha,
+ pm8001_printk("IO_XFER_CMD_FRAME_ISSUED\n"));
+ break;
+ case IO_XFER_PIO_SETUP_ERROR:
+ PM8001_IO_DBG(pm8001_ha,
+ pm8001_printk("IO_XFER_PIO_SETUP_ERROR\n"));
+ ts->resp = SAS_TASK_COMPLETE;
+ ts->stat = SAS_OPEN_TO;
+ break;
+ default:
+ PM8001_IO_DBG(pm8001_ha,
+ pm8001_printk("Unknown status 0x%x\n", event));
+ /* not allowed case. Therefore, return failed status */
+ ts->resp = SAS_TASK_COMPLETE;
+ ts->stat = SAS_OPEN_TO;
+ break;
+ }
+ spin_lock_irqsave(&t->task_state_lock, flags);
+ t->task_state_flags &= ~SAS_TASK_STATE_PENDING;
+ t->task_state_flags &= ~SAS_TASK_AT_INITIATOR;
+ t->task_state_flags |= SAS_TASK_STATE_DONE;
+ if (unlikely((t->task_state_flags & SAS_TASK_STATE_ABORTED))) {
+ spin_unlock_irqrestore(&t->task_state_lock, flags);
+ PM8001_FAIL_DBG(pm8001_ha,
+ pm8001_printk("task 0x%p done with io_status 0x%x"
+ " resp 0x%x stat 0x%x but aborted by upper layer!\n",
+ t, event, ts->resp, ts->stat));
+ pm8001_ccb_task_free(pm8001_ha, t, ccb, tag);
+ } else {
+ spin_unlock_irqrestore(&t->task_state_lock, flags);
+ pm8001_ccb_task_free(pm8001_ha, t, ccb, tag);
+ mb();/* in order to force CPU ordering */
+ t->task_done(t);
+ }
+}
+
+/*See the comments for mpi_ssp_completion */
+static void
+mpi_smp_completion(struct pm8001_hba_info *pm8001_ha, void *piomb)
+{
+ u32 param;
+ struct sas_task *t;
+ struct pm8001_ccb_info *ccb;
+ unsigned long flags;
+ u32 status;
+ u32 tag;
+ struct smp_completion_resp *psmpPayload;
+ struct task_status_struct *ts;
+ struct pm8001_device *pm8001_dev;
+
+ psmpPayload = (struct smp_completion_resp *)(piomb + 4);
+ status = le32_to_cpu(psmpPayload->status);
+ tag = le32_to_cpu(psmpPayload->tag);
+
+ ccb = &pm8001_ha->ccb_info[tag];
+ param = le32_to_cpu(psmpPayload->param);
+ t = ccb->task;
+ ts = &t->task_status;
+ pm8001_dev = ccb->device;
+ if (status)
+ PM8001_FAIL_DBG(pm8001_ha,
+ pm8001_printk("smp IO status 0x%x\n", status));
+ if (unlikely(!t || !t->lldd_task || !t->dev))
+ return;
+
+ switch (status) {
+ case IO_SUCCESS:
+ PM8001_IO_DBG(pm8001_ha, pm8001_printk("IO_SUCCESS\n"));
+ ts->resp = SAS_TASK_COMPLETE;
+ ts->stat = SAM_GOOD;
+ if (pm8001_dev)
+ pm8001_dev->running_req--;
+ break;
+ case IO_ABORTED:
+ PM8001_IO_DBG(pm8001_ha,
+ pm8001_printk("IO_ABORTED IOMB\n"));
+ ts->resp = SAS_TASK_COMPLETE;
+ ts->stat = SAS_ABORTED_TASK;
+ if (pm8001_dev)
+ pm8001_dev->running_req--;
+ break;
+ case IO_OVERFLOW:
+ PM8001_IO_DBG(pm8001_ha, pm8001_printk("IO_UNDERFLOW\n"));
+ ts->resp = SAS_TASK_COMPLETE;
+ ts->stat = SAS_DATA_OVERRUN;
+ ts->residual = 0;
+ if (pm8001_dev)
+ pm8001_dev->running_req--;
+ break;
+ case IO_NO_DEVICE:
+ PM8001_IO_DBG(pm8001_ha, pm8001_printk("IO_NO_DEVICE\n"));
+ ts->resp = SAS_TASK_COMPLETE;
+ ts->stat = SAS_PHY_DOWN;
+ break;
+ case IO_ERROR_HW_TIMEOUT:
+ PM8001_IO_DBG(pm8001_ha,
+ pm8001_printk("IO_ERROR_HW_TIMEOUT\n"));
+ ts->resp = SAS_TASK_COMPLETE;
+ ts->stat = SAM_BUSY;
+ break;
+ case IO_XFER_ERROR_BREAK:
+ PM8001_IO_DBG(pm8001_ha,
+ pm8001_printk("IO_XFER_ERROR_BREAK\n"));
+ ts->resp = SAS_TASK_COMPLETE;
+ ts->stat = SAM_BUSY;
+ break;
+ case IO_XFER_ERROR_PHY_NOT_READY:
+ PM8001_IO_DBG(pm8001_ha,
+ pm8001_printk("IO_XFER_ERROR_PHY_NOT_READY\n"));
+ ts->resp = SAS_TASK_COMPLETE;
+ ts->stat = SAM_BUSY;
+ break;
+ case IO_OPEN_CNX_ERROR_PROTOCOL_NOT_SUPPORTED:
+ PM8001_IO_DBG(pm8001_ha,
+ pm8001_printk("IO_OPEN_CNX_ERROR_PROTOCOL_NOT_SUPPORTED\n"));
+ ts->resp = SAS_TASK_COMPLETE;
+ ts->stat = SAS_OPEN_REJECT;
+ ts->open_rej_reason = SAS_OREJ_UNKNOWN;
+ break;
+ case IO_OPEN_CNX_ERROR_ZONE_VIOLATION:
+ PM8001_IO_DBG(pm8001_ha,
+ pm8001_printk("IO_OPEN_CNX_ERROR_ZONE_VIOLATION\n"));
+ ts->resp = SAS_TASK_COMPLETE;
+ ts->stat = SAS_OPEN_REJECT;
+ ts->open_rej_reason = SAS_OREJ_UNKNOWN;
+ break;
+ case IO_OPEN_CNX_ERROR_BREAK:
+ PM8001_IO_DBG(pm8001_ha,
+ pm8001_printk("IO_OPEN_CNX_ERROR_BREAK\n"));
+ ts->resp = SAS_TASK_COMPLETE;
+ ts->stat = SAS_OPEN_REJECT;
+ ts->open_rej_reason = SAS_OREJ_RSVD_CONT0;
+ break;
+ case IO_OPEN_CNX_ERROR_IT_NEXUS_LOSS:
+ PM8001_IO_DBG(pm8001_ha,
+ pm8001_printk("IO_OPEN_CNX_ERROR_IT_NEXUS_LOSS\n"));
+ ts->resp = SAS_TASK_COMPLETE;
+ ts->stat = SAS_OPEN_REJECT;
+ ts->open_rej_reason = SAS_OREJ_UNKNOWN;
+ pm8001_handle_event(pm8001_ha,
+ pm8001_dev,
+ IO_OPEN_CNX_ERROR_IT_NEXUS_LOSS);
+ break;
+ case IO_OPEN_CNX_ERROR_BAD_DESTINATION:
+ PM8001_IO_DBG(pm8001_ha,
+ pm8001_printk("IO_OPEN_CNX_ERROR_BAD_DESTINATION\n"));
+ ts->resp = SAS_TASK_COMPLETE;
+ ts->stat = SAS_OPEN_REJECT;
+ ts->open_rej_reason = SAS_OREJ_BAD_DEST;
+ break;
+ case IO_OPEN_CNX_ERROR_CONNECTION_RATE_NOT_SUPPORTED:
+ PM8001_IO_DBG(pm8001_ha,
+ pm8001_printk("IO_OPEN_CNX_ERROR_CONNECTION_RATE_"
+ "NOT_SUPPORTED\n"));
+ ts->resp = SAS_TASK_COMPLETE;
+ ts->stat = SAS_OPEN_REJECT;
+ ts->open_rej_reason = SAS_OREJ_CONN_RATE;
+ break;
+ case IO_OPEN_CNX_ERROR_WRONG_DESTINATION:
+ PM8001_IO_DBG(pm8001_ha,
+ pm8001_printk("IO_OPEN_CNX_ERROR_WRONG_DESTINATION\n"));
+ ts->resp = SAS_TASK_COMPLETE;
+ ts->stat = SAS_OPEN_REJECT;
+ ts->open_rej_reason = SAS_OREJ_WRONG_DEST;
+ break;
+ case IO_XFER_ERROR_RX_FRAME:
+ PM8001_IO_DBG(pm8001_ha,
+ pm8001_printk("IO_XFER_ERROR_RX_FRAME\n"));
+ ts->resp = SAS_TASK_COMPLETE;
+ ts->stat = SAS_DEV_NO_RESPONSE;
+ break;
+ case IO_XFER_OPEN_RETRY_TIMEOUT:
+ PM8001_IO_DBG(pm8001_ha,
+ pm8001_printk("IO_XFER_OPEN_RETRY_TIMEOUT\n"));
+ ts->resp = SAS_TASK_COMPLETE;
+ ts->stat = SAS_OPEN_REJECT;
+ ts->open_rej_reason = SAS_OREJ_RSVD_RETRY;
+ break;
+ case IO_ERROR_INTERNAL_SMP_RESOURCE:
+ PM8001_IO_DBG(pm8001_ha,
+ pm8001_printk("IO_ERROR_INTERNAL_SMP_RESOURCE\n"));
+ ts->resp = SAS_TASK_COMPLETE;
+ ts->stat = SAS_QUEUE_FULL;
+ break;
+ case IO_PORT_IN_RESET:
+ PM8001_IO_DBG(pm8001_ha,
+ pm8001_printk("IO_PORT_IN_RESET\n"));
+ ts->resp = SAS_TASK_COMPLETE;
+ ts->stat = SAS_OPEN_REJECT;
+ ts->open_rej_reason = SAS_OREJ_RSVD_RETRY;
+ break;
+ case IO_DS_NON_OPERATIONAL:
+ PM8001_IO_DBG(pm8001_ha,
+ pm8001_printk("IO_DS_NON_OPERATIONAL\n"));
+ ts->resp = SAS_TASK_COMPLETE;
+ ts->stat = SAS_DEV_NO_RESPONSE;
+ break;
+ case IO_DS_IN_RECOVERY:
+ PM8001_IO_DBG(pm8001_ha,
+ pm8001_printk("IO_DS_IN_RECOVERY\n"));
+ ts->resp = SAS_TASK_COMPLETE;
+ ts->stat = SAS_OPEN_REJECT;
+ ts->open_rej_reason = SAS_OREJ_RSVD_RETRY;
+ break;
+ case IO_OPEN_CNX_ERROR_HW_RESOURCE_BUSY:
+ PM8001_IO_DBG(pm8001_ha,
+ pm8001_printk("IO_OPEN_CNX_ERROR_HW_RESOURCE_BUSY\n"));
+ ts->resp = SAS_TASK_COMPLETE;
+ ts->stat = SAS_OPEN_REJECT;
+ ts->open_rej_reason = SAS_OREJ_RSVD_RETRY;
+ break;
+ default:
+ PM8001_IO_DBG(pm8001_ha,
+ pm8001_printk("Unknown status 0x%x\n", status));
+ ts->resp = SAS_TASK_COMPLETE;
+ ts->stat = SAS_DEV_NO_RESPONSE;
+ /* not allowed case. Therefore, return failed status */
+ break;
+ }
+ spin_lock_irqsave(&t->task_state_lock, flags);
+ t->task_state_flags &= ~SAS_TASK_STATE_PENDING;
+ t->task_state_flags &= ~SAS_TASK_AT_INITIATOR;
+ t->task_state_flags |= SAS_TASK_STATE_DONE;
+ if (unlikely((t->task_state_flags & SAS_TASK_STATE_ABORTED))) {
+ spin_unlock_irqrestore(&t->task_state_lock, flags);
+ PM8001_FAIL_DBG(pm8001_ha, pm8001_printk("task 0x%p done with"
+ " io_status 0x%x resp 0x%x "
+ "stat 0x%x but aborted by upper layer!\n",
+ t, status, ts->resp, ts->stat));
+ pm8001_ccb_task_free(pm8001_ha, t, ccb, tag);
+ } else {
+ spin_unlock_irqrestore(&t->task_state_lock, flags);
+ pm8001_ccb_task_free(pm8001_ha, t, ccb, tag);
+ mb();/* in order to force CPU ordering */
+ t->task_done(t);
+ }
+}
+
+static void
+mpi_set_dev_state_resp(struct pm8001_hba_info *pm8001_ha, void *piomb)
+{
+ struct set_dev_state_resp *pPayload =
+ (struct set_dev_state_resp *)(piomb + 4);
+ u32 tag = le32_to_cpu(pPayload->tag);
+ struct pm8001_ccb_info *ccb = &pm8001_ha->ccb_info[tag];
+ struct pm8001_device *pm8001_dev = ccb->device;
+ u32 status = le32_to_cpu(pPayload->status);
+ u32 device_id = le32_to_cpu(pPayload->device_id);
+ u8 pds = le32_to_cpu(pPayload->pds_nds) | PDS_BITS;
+ u8 nds = le32_to_cpu(pPayload->pds_nds) | NDS_BITS;
+ PM8001_MSG_DBG(pm8001_ha, pm8001_printk("Set device id = 0x%x state "
+ "from 0x%x to 0x%x status = 0x%x!\n",
+ device_id, pds, nds, status));
+ complete(pm8001_dev->setds_completion);
+ ccb->task = NULL;
+ ccb->ccb_tag = 0xFFFFFFFF;
+ pm8001_ccb_free(pm8001_ha, tag);
+}
+
+static void
+mpi_set_nvmd_resp(struct pm8001_hba_info *pm8001_ha, void *piomb)
+{
+ struct get_nvm_data_resp *pPayload =
+ (struct get_nvm_data_resp *)(piomb + 4);
+ u32 tag = le32_to_cpu(pPayload->tag);
+ struct pm8001_ccb_info *ccb = &pm8001_ha->ccb_info[tag];
+ u32 dlen_status = le32_to_cpu(pPayload->dlen_status);
+ complete(pm8001_ha->nvmd_completion);
+ PM8001_MSG_DBG(pm8001_ha, pm8001_printk("Set nvm data complete!\n"));
+ if ((dlen_status & NVMD_STAT) != 0) {
+ PM8001_FAIL_DBG(pm8001_ha,
+ pm8001_printk("Set nvm data error!\n"));
+ return;
+ }
+ ccb->task = NULL;
+ ccb->ccb_tag = 0xFFFFFFFF;
+ pm8001_ccb_free(pm8001_ha, tag);
+}
+
+static void
+mpi_get_nvmd_resp(struct pm8001_hba_info *pm8001_ha, void *piomb)
+{
+ struct fw_control_ex *fw_control_context;
+ struct get_nvm_data_resp *pPayload =
+ (struct get_nvm_data_resp *)(piomb + 4);
+ u32 tag = le32_to_cpu(pPayload->tag);
+ struct pm8001_ccb_info *ccb = &pm8001_ha->ccb_info[tag];
+ u32 dlen_status = le32_to_cpu(pPayload->dlen_status);
+ u32 ir_tds_bn_dps_das_nvm =
+ le32_to_cpu(pPayload->ir_tda_bn_dps_das_nvm);
+ void *virt_addr = pm8001_ha->memoryMap.region[NVMD].virt_ptr;
+ fw_control_context = ccb->fw_control_context;
+
+ PM8001_MSG_DBG(pm8001_ha, pm8001_printk("Get nvm data complete!\n"));
+ if ((dlen_status & NVMD_STAT) != 0) {
+ PM8001_FAIL_DBG(pm8001_ha,
+ pm8001_printk("Get nvm data error!\n"));
+ complete(pm8001_ha->nvmd_completion);
+ return;
+ }
+
+ if (ir_tds_bn_dps_das_nvm & IPMode) {
+ /* indirect mode - IR bit set */
+ PM8001_MSG_DBG(pm8001_ha,
+ pm8001_printk("Get NVMD success, IR=1\n"));
+ if ((ir_tds_bn_dps_das_nvm & NVMD_TYPE) == TWI_DEVICE) {
+ if (ir_tds_bn_dps_das_nvm == 0x80a80200) {
+ memcpy(pm8001_ha->sas_addr,
+ ((u8 *)virt_addr + 4),
+ SAS_ADDR_SIZE);
+ PM8001_MSG_DBG(pm8001_ha,
+ pm8001_printk("Get SAS address"
+ " from VPD successfully!\n"));
+ }
+ } else if (((ir_tds_bn_dps_das_nvm & NVMD_TYPE) == C_SEEPROM)
+ || ((ir_tds_bn_dps_das_nvm & NVMD_TYPE) == VPD_FLASH) ||
+ ((ir_tds_bn_dps_das_nvm & NVMD_TYPE) == EXPAN_ROM)) {
+ ;
+ } else if (((ir_tds_bn_dps_das_nvm & NVMD_TYPE) == AAP1_RDUMP)
+ || ((ir_tds_bn_dps_das_nvm & NVMD_TYPE) == IOP_RDUMP)) {
+ ;
+ } else {
+ /* Should not be happened*/
+ PM8001_MSG_DBG(pm8001_ha,
+ pm8001_printk("(IR=1)Wrong Device type 0x%x\n",
+ ir_tds_bn_dps_das_nvm));
+ }
+ } else /* direct mode */{
+ PM8001_MSG_DBG(pm8001_ha,
+ pm8001_printk("Get NVMD success, IR=0, dataLen=%d\n",
+ (dlen_status & NVMD_LEN) >> 24));
+ }
+ memcpy(fw_control_context->usrAddr,
+ pm8001_ha->memoryMap.region[NVMD].virt_ptr,
+ fw_control_context->len);
+ complete(pm8001_ha->nvmd_completion);
+ ccb->task = NULL;
+ ccb->ccb_tag = 0xFFFFFFFF;
+ pm8001_ccb_free(pm8001_ha, tag);
+}
+
+static int mpi_local_phy_ctl(struct pm8001_hba_info *pm8001_ha, void *piomb)
+{
+ struct local_phy_ctl_resp *pPayload =
+ (struct local_phy_ctl_resp *)(piomb + 4);
+ u32 status = le32_to_cpu(pPayload->status);
+ u32 phy_id = le32_to_cpu(pPayload->phyop_phyid) & ID_BITS;
+ u32 phy_op = le32_to_cpu(pPayload->phyop_phyid) & OP_BITS;
+ if (status != 0) {
+ PM8001_MSG_DBG(pm8001_ha,
+ pm8001_printk("%x phy execute %x phy op failed! \n",
+ phy_id, phy_op));
+ } else
+ PM8001_MSG_DBG(pm8001_ha,
+ pm8001_printk("%x phy execute %x phy op success! \n",
+ phy_id, phy_op));
+ return 0;
+}
+
+/**
+ * pm8001_bytes_dmaed - one of the interface function communication with libsas
+ * @pm8001_ha: our hba card information
+ * @i: which phy that received the event.
+ *
+ * when HBA driver received the identify done event or initiate FIS received
+ * event(for SATA), it will invoke this function to notify the sas layer that
+ * the sas toplogy has formed, please discover the the whole sas domain,
+ * while receive a broadcast(change) primitive just tell the sas
+ * layer to discover the changed domain rather than the whole domain.
+ */
+static void pm8001_bytes_dmaed(struct pm8001_hba_info *pm8001_ha, int i)
+{
+ struct pm8001_phy *phy = &pm8001_ha->phy[i];
+ struct asd_sas_phy *sas_phy = &phy->sas_phy;
+ struct sas_ha_struct *sas_ha;
+ if (!phy->phy_attached)
+ return;
+
+ sas_ha = pm8001_ha->sas;
+ if (sas_phy->phy) {
+ struct sas_phy *sphy = sas_phy->phy;
+ sphy->negotiated_linkrate = sas_phy->linkrate;
+ sphy->minimum_linkrate = phy->minimum_linkrate;
+ sphy->minimum_linkrate_hw = SAS_LINK_RATE_1_5_GBPS;
+ sphy->maximum_linkrate = phy->maximum_linkrate;
+ sphy->maximum_linkrate_hw = phy->maximum_linkrate;
+ }
+
+ if (phy->phy_type & PORT_TYPE_SAS) {
+ struct sas_identify_frame *id;
+ id = (struct sas_identify_frame *)phy->frame_rcvd;
+ id->dev_type = phy->identify.device_type;
+ id->initiator_bits = SAS_PROTOCOL_ALL;
+ id->target_bits = phy->identify.target_port_protocols;
+ } else if (phy->phy_type & PORT_TYPE_SATA) {
+ /*Nothing*/
+ }
+ PM8001_MSG_DBG(pm8001_ha, pm8001_printk("phy %d byte dmaded.\n", i));
+
+ sas_phy->frame_rcvd_size = phy->frame_rcvd_size;
+ pm8001_ha->sas->notify_port_event(sas_phy, PORTE_BYTES_DMAED);
+}
+
+/* Get the link rate speed */
+static void get_lrate_mode(struct pm8001_phy *phy, u8 link_rate)
+{
+ struct sas_phy *sas_phy = phy->sas_phy.phy;
+
+ switch (link_rate) {
+ case PHY_SPEED_60:
+ phy->sas_phy.linkrate = SAS_LINK_RATE_6_0_GBPS;
+ phy->sas_phy.phy->negotiated_linkrate = SAS_LINK_RATE_6_0_GBPS;
+ break;
+ case PHY_SPEED_30:
+ phy->sas_phy.linkrate = SAS_LINK_RATE_3_0_GBPS;
+ phy->sas_phy.phy->negotiated_linkrate = SAS_LINK_RATE_3_0_GBPS;
+ break;
+ case PHY_SPEED_15:
+ phy->sas_phy.linkrate = SAS_LINK_RATE_1_5_GBPS;
+ phy->sas_phy.phy->negotiated_linkrate = SAS_LINK_RATE_1_5_GBPS;
+ break;
+ }
+ sas_phy->negotiated_linkrate = phy->sas_phy.linkrate;
+ sas_phy->maximum_linkrate_hw = SAS_LINK_RATE_6_0_GBPS;
+ sas_phy->minimum_linkrate_hw = SAS_LINK_RATE_1_5_GBPS;
+ sas_phy->maximum_linkrate = SAS_LINK_RATE_6_0_GBPS;
+ sas_phy->minimum_linkrate = SAS_LINK_RATE_1_5_GBPS;
+}
+
+/**
+ * asd_get_attached_sas_addr -- extract/generate attached SAS address
+ * @phy: pointer to asd_phy
+ * @sas_addr: pointer to buffer where the SAS address is to be written
+ *
+ * This function extracts the SAS address from an IDENTIFY frame
+ * received. If OOB is SATA, then a SAS address is generated from the
+ * HA tables.
+ *
+ * LOCKING: the frame_rcvd_lock needs to be held since this parses the frame
+ * buffer.
+ */
+static void pm8001_get_attached_sas_addr(struct pm8001_phy *phy,
+ u8 *sas_addr)
+{
+ if (phy->sas_phy.frame_rcvd[0] == 0x34
+ && phy->sas_phy.oob_mode == SATA_OOB_MODE) {
+ struct pm8001_hba_info *pm8001_ha = phy->sas_phy.ha->lldd_ha;
+ /* FIS device-to-host */
+ u64 addr = be64_to_cpu(*(__be64 *)pm8001_ha->sas_addr);
+ addr += phy->sas_phy.id;
+ *(__be64 *)sas_addr = cpu_to_be64(addr);
+ } else {
+ struct sas_identify_frame *idframe =
+ (void *) phy->sas_phy.frame_rcvd;
+ memcpy(sas_addr, idframe->sas_addr, SAS_ADDR_SIZE);
+ }
+}
+
+/**
+ * pm8001_hw_event_ack_req- For PM8001,some events need to acknowage to FW.
+ * @pm8001_ha: our hba card information
+ * @Qnum: the outbound queue message number.
+ * @SEA: source of event to ack
+ * @port_id: port id.
+ * @phyId: phy id.
+ * @param0: parameter 0.
+ * @param1: parameter 1.
+ */
+static void pm8001_hw_event_ack_req(struct pm8001_hba_info *pm8001_ha,
+ u32 Qnum, u32 SEA, u32 port_id, u32 phyId, u32 param0, u32 param1)
+{
+ struct hw_event_ack_req payload;
+ u32 opc = OPC_INB_SAS_HW_EVENT_ACK;
+
+ struct inbound_queue_table *circularQ;
+
+ memset((u8 *)&payload, 0, sizeof(payload));
+ circularQ = &pm8001_ha->inbnd_q_tbl[Qnum];
+ payload.tag = 1;
+ payload.sea_phyid_portid = cpu_to_le32(((SEA & 0xFFFF) << 8) |
+ ((phyId & 0x0F) << 4) | (port_id & 0x0F));
+ payload.param0 = cpu_to_le32(param0);
+ payload.param1 = cpu_to_le32(param1);
+ mpi_build_cmd(pm8001_ha, circularQ, opc, &payload);
+}
+
+static int pm8001_chip_phy_ctl_req(struct pm8001_hba_info *pm8001_ha,
+ u32 phyId, u32 phy_op);
+
+/**
+ * hw_event_sas_phy_up -FW tells me a SAS phy up event.
+ * @pm8001_ha: our hba card information
+ * @piomb: IO message buffer
+ */
+static void
+hw_event_sas_phy_up(struct pm8001_hba_info *pm8001_ha, void *piomb)
+{
+ struct hw_event_resp *pPayload =
+ (struct hw_event_resp *)(piomb + 4);
+ u32 lr_evt_status_phyid_portid =
+ le32_to_cpu(pPayload->lr_evt_status_phyid_portid);
+ u8 link_rate =
+ (u8)((lr_evt_status_phyid_portid & 0xF0000000) >> 28);
+ u8 phy_id =
+ (u8)((lr_evt_status_phyid_portid & 0x000000F0) >> 4);
+ struct sas_ha_struct *sas_ha = pm8001_ha->sas;
+ struct pm8001_phy *phy = &pm8001_ha->phy[phy_id];
+ unsigned long flags;
+ u8 deviceType = pPayload->sas_identify.dev_type;
+
+ PM8001_MSG_DBG(pm8001_ha,
+ pm8001_printk("HW_EVENT_SAS_PHY_UP \n"));
+
+ switch (deviceType) {
+ case SAS_PHY_UNUSED:
+ PM8001_MSG_DBG(pm8001_ha,
+ pm8001_printk("device type no device.\n"));
+ break;
+ case SAS_END_DEVICE:
+ PM8001_MSG_DBG(pm8001_ha, pm8001_printk("end device.\n"));
+ pm8001_chip_phy_ctl_req(pm8001_ha, phy_id,
+ PHY_NOTIFY_ENABLE_SPINUP);
+ get_lrate_mode(phy, link_rate);
+ break;
+ case SAS_EDGE_EXPANDER_DEVICE:
+ PM8001_MSG_DBG(pm8001_ha,
+ pm8001_printk("expander device.\n"));
+ get_lrate_mode(phy, link_rate);
+ break;
+ case SAS_FANOUT_EXPANDER_DEVICE:
+ PM8001_MSG_DBG(pm8001_ha,
+ pm8001_printk("fanout expander device.\n"));
+ get_lrate_mode(phy, link_rate);
+ break;
+ default:
+ PM8001_MSG_DBG(pm8001_ha,
+ pm8001_printk("unkown device type(%x)\n", deviceType));
+ break;
+ }
+ phy->phy_type |= PORT_TYPE_SAS;
+ phy->identify.device_type = deviceType;
+ phy->phy_attached = 1;
+ if (phy->identify.device_type == SAS_END_DEV)
+ phy->identify.target_port_protocols = SAS_PROTOCOL_SSP;
+ else if (phy->identify.device_type != NO_DEVICE)
+ phy->identify.target_port_protocols = SAS_PROTOCOL_SMP;
+ phy->sas_phy.oob_mode = SAS_OOB_MODE;
+ sas_ha->notify_phy_event(&phy->sas_phy, PHYE_OOB_DONE);
+ spin_lock_irqsave(&phy->sas_phy.frame_rcvd_lock, flags);
+ memcpy(phy->frame_rcvd, &pPayload->sas_identify,
+ sizeof(struct sas_identify_frame)-4);
+ phy->frame_rcvd_size = sizeof(struct sas_identify_frame) - 4;
+ pm8001_get_attached_sas_addr(phy, phy->sas_phy.attached_sas_addr);
+ spin_unlock_irqrestore(&phy->sas_phy.frame_rcvd_lock, flags);
+ if (pm8001_ha->flags == PM8001F_RUN_TIME)
+ mdelay(200);/*delay a moment to wait disk to spinup*/
+ pm8001_bytes_dmaed(pm8001_ha, phy_id);
+}
+
+/**
+ * hw_event_sata_phy_up -FW tells me a SATA phy up event.
+ * @pm8001_ha: our hba card information
+ * @piomb: IO message buffer
+ */
+static void
+hw_event_sata_phy_up(struct pm8001_hba_info *pm8001_ha, void *piomb)
+{
+ struct hw_event_resp *pPayload =
+ (struct hw_event_resp *)(piomb + 4);
+ u32 lr_evt_status_phyid_portid =
+ le32_to_cpu(pPayload->lr_evt_status_phyid_portid);
+ u8 link_rate =
+ (u8)((lr_evt_status_phyid_portid & 0xF0000000) >> 28);
+ u8 phy_id =
+ (u8)((lr_evt_status_phyid_portid & 0x000000F0) >> 4);
+ struct sas_ha_struct *sas_ha = pm8001_ha->sas;
+ struct pm8001_phy *phy = &pm8001_ha->phy[phy_id];
+ unsigned long flags;
+ get_lrate_mode(phy, link_rate);
+ phy->phy_type |= PORT_TYPE_SATA;
+ phy->phy_attached = 1;
+ phy->sas_phy.oob_mode = SATA_OOB_MODE;
+ sas_ha->notify_phy_event(&phy->sas_phy, PHYE_OOB_DONE);
+ spin_lock_irqsave(&phy->sas_phy.frame_rcvd_lock, flags);
+ memcpy(phy->frame_rcvd, ((u8 *)&pPayload->sata_fis - 4),
+ sizeof(struct dev_to_host_fis));
+ phy->frame_rcvd_size = sizeof(struct dev_to_host_fis);
+ phy->identify.target_port_protocols = SAS_PROTOCOL_SATA;
+ phy->identify.device_type = SATA_DEV;
+ pm8001_get_attached_sas_addr(phy, phy->sas_phy.attached_sas_addr);
+ spin_unlock_irqrestore(&phy->sas_phy.frame_rcvd_lock, flags);
+ pm8001_bytes_dmaed(pm8001_ha, phy_id);
+}
+
+/**
+ * hw_event_phy_down -we should notify the libsas the phy is down.
+ * @pm8001_ha: our hba card information
+ * @piomb: IO message buffer
+ */
+static void
+hw_event_phy_down(struct pm8001_hba_info *pm8001_ha, void *piomb)
+{
+ struct hw_event_resp *pPayload =
+ (struct hw_event_resp *)(piomb + 4);
+ u32 lr_evt_status_phyid_portid =
+ le32_to_cpu(pPayload->lr_evt_status_phyid_portid);
+ u8 port_id = (u8)(lr_evt_status_phyid_portid & 0x0000000F);
+ u8 phy_id =
+ (u8)((lr_evt_status_phyid_portid & 0x000000F0) >> 4);
+ u32 npip_portstate = le32_to_cpu(pPayload->npip_portstate);
+ u8 portstate = (u8)(npip_portstate & 0x0000000F);
+
+ switch (portstate) {
+ case PORT_VALID:
+ break;
+ case PORT_INVALID:
+ PM8001_MSG_DBG(pm8001_ha,
+ pm8001_printk(" PortInvalid portID %d \n", port_id));
+ PM8001_MSG_DBG(pm8001_ha,
+ pm8001_printk(" Last phy Down and port invalid\n"));
+ pm8001_hw_event_ack_req(pm8001_ha, 0, HW_EVENT_PHY_DOWN,
+ port_id, phy_id, 0, 0);
+ break;
+ case PORT_IN_RESET:
+ PM8001_MSG_DBG(pm8001_ha,
+ pm8001_printk(" PortInReset portID %d \n", port_id));
+ break;
+ case PORT_NOT_ESTABLISHED:
+ PM8001_MSG_DBG(pm8001_ha,
+ pm8001_printk(" phy Down and PORT_NOT_ESTABLISHED\n"));
+ break;
+ case PORT_LOSTCOMM:
+ PM8001_MSG_DBG(pm8001_ha,
+ pm8001_printk(" phy Down and PORT_LOSTCOMM\n"));
+ PM8001_MSG_DBG(pm8001_ha,
+ pm8001_printk(" Last phy Down and port invalid\n"));
+ pm8001_hw_event_ack_req(pm8001_ha, 0, HW_EVENT_PHY_DOWN,
+ port_id, phy_id, 0, 0);
+ break;
+ default:
+ PM8001_MSG_DBG(pm8001_ha,
+ pm8001_printk(" phy Down and(default) = %x\n",
+ portstate));
+ break;
+
+ }
+}
+
+/**
+ * mpi_reg_resp -process register device ID response.
+ * @pm8001_ha: our hba card information
+ * @piomb: IO message buffer
+ *
+ * when sas layer find a device it will notify LLDD, then the driver register
+ * the domain device to FW, this event is the return device ID which the FW
+ * has assigned, from now,inter-communication with FW is no longer using the
+ * SAS address, use device ID which FW assigned.
+ */
+static int mpi_reg_resp(struct pm8001_hba_info *pm8001_ha, void *piomb)
+{
+ u32 status;
+ u32 device_id;
+ u32 htag;
+ struct pm8001_ccb_info *ccb;
+ struct pm8001_device *pm8001_dev;
+ struct dev_reg_resp *registerRespPayload =
+ (struct dev_reg_resp *)(piomb + 4);
+
+ htag = le32_to_cpu(registerRespPayload->tag);
+ ccb = &pm8001_ha->ccb_info[registerRespPayload->tag];
+ pm8001_dev = ccb->device;
+ status = le32_to_cpu(registerRespPayload->status);
+ device_id = le32_to_cpu(registerRespPayload->device_id);
+ PM8001_MSG_DBG(pm8001_ha,
+ pm8001_printk(" register device is status = %d\n", status));
+ switch (status) {
+ case DEVREG_SUCCESS:
+ PM8001_MSG_DBG(pm8001_ha, pm8001_printk("DEVREG_SUCCESS\n"));
+ pm8001_dev->device_id = device_id;
+ break;
+ case DEVREG_FAILURE_OUT_OF_RESOURCE:
+ PM8001_MSG_DBG(pm8001_ha,
+ pm8001_printk("DEVREG_FAILURE_OUT_OF_RESOURCE\n"));
+ break;
+ case DEVREG_FAILURE_DEVICE_ALREADY_REGISTERED:
+ PM8001_MSG_DBG(pm8001_ha,
+ pm8001_printk("DEVREG_FAILURE_DEVICE_ALREADY_REGISTERED\n"));
+ break;
+ case DEVREG_FAILURE_INVALID_PHY_ID:
+ PM8001_MSG_DBG(pm8001_ha,
+ pm8001_printk("DEVREG_FAILURE_INVALID_PHY_ID\n"));
+ break;
+ case DEVREG_FAILURE_PHY_ID_ALREADY_REGISTERED:
+ PM8001_MSG_DBG(pm8001_ha,
+ pm8001_printk("DEVREG_FAILURE_PHY_ID_ALREADY_REGISTERED\n"));
+ break;
+ case DEVREG_FAILURE_PORT_ID_OUT_OF_RANGE:
+ PM8001_MSG_DBG(pm8001_ha,
+ pm8001_printk("DEVREG_FAILURE_PORT_ID_OUT_OF_RANGE\n"));
+ break;
+ case DEVREG_FAILURE_PORT_NOT_VALID_STATE:
+ PM8001_MSG_DBG(pm8001_ha,
+ pm8001_printk("DEVREG_FAILURE_PORT_NOT_VALID_STATE\n"));
+ break;
+ case DEVREG_FAILURE_DEVICE_TYPE_NOT_VALID:
+ PM8001_MSG_DBG(pm8001_ha,
+ pm8001_printk("DEVREG_FAILURE_DEVICE_TYPE_NOT_VALID\n"));
+ break;
+ default:
+ PM8001_MSG_DBG(pm8001_ha,
+ pm8001_printk("DEVREG_FAILURE_DEVICE_TYPE_NOT_UNSORPORTED\n"));
+ break;
+ }
+ complete(pm8001_dev->dcompletion);
+ ccb->task = NULL;
+ ccb->ccb_tag = 0xFFFFFFFF;
+ pm8001_ccb_free(pm8001_ha, htag);
+ return 0;
+}
+
+static int mpi_dereg_resp(struct pm8001_hba_info *pm8001_ha, void *piomb)
+{
+ u32 status;
+ u32 device_id;
+ struct dev_reg_resp *registerRespPayload =
+ (struct dev_reg_resp *)(piomb + 4);
+
+ status = le32_to_cpu(registerRespPayload->status);
+ device_id = le32_to_cpu(registerRespPayload->device_id);
+ if (status != 0)
+ PM8001_MSG_DBG(pm8001_ha,
+ pm8001_printk(" deregister device failed ,status = %x"
+ ", device_id = %x\n", status, device_id));
+ return 0;
+}
+
+static int
+mpi_fw_flash_update_resp(struct pm8001_hba_info *pm8001_ha, void *piomb)
+{
+ u32 status;
+ struct fw_control_ex fw_control_context;
+ struct fw_flash_Update_resp *ppayload =
+ (struct fw_flash_Update_resp *)(piomb + 4);
+ u32 tag = le32_to_cpu(ppayload->tag);
+ struct pm8001_ccb_info *ccb = &pm8001_ha->ccb_info[tag];
+ status = le32_to_cpu(ppayload->status);
+ memcpy(&fw_control_context,
+ ccb->fw_control_context,
+ sizeof(fw_control_context));
+ switch (status) {
+ case FLASH_UPDATE_COMPLETE_PENDING_REBOOT:
+ PM8001_MSG_DBG(pm8001_ha,
+ pm8001_printk(": FLASH_UPDATE_COMPLETE_PENDING_REBOOT\n"));
+ break;
+ case FLASH_UPDATE_IN_PROGRESS:
+ PM8001_MSG_DBG(pm8001_ha,
+ pm8001_printk(": FLASH_UPDATE_IN_PROGRESS\n"));
+ break;
+ case FLASH_UPDATE_HDR_ERR:
+ PM8001_MSG_DBG(pm8001_ha,
+ pm8001_printk(": FLASH_UPDATE_HDR_ERR\n"));
+ break;
+ case FLASH_UPDATE_OFFSET_ERR:
+ PM8001_MSG_DBG(pm8001_ha,
+ pm8001_printk(": FLASH_UPDATE_OFFSET_ERR\n"));
+ break;
+ case FLASH_UPDATE_CRC_ERR:
+ PM8001_MSG_DBG(pm8001_ha,
+ pm8001_printk(": FLASH_UPDATE_CRC_ERR\n"));
+ break;
+ case FLASH_UPDATE_LENGTH_ERR:
+ PM8001_MSG_DBG(pm8001_ha,
+ pm8001_printk(": FLASH_UPDATE_LENGTH_ERR\n"));
+ break;
+ case FLASH_UPDATE_HW_ERR:
+ PM8001_MSG_DBG(pm8001_ha,
+ pm8001_printk(": FLASH_UPDATE_HW_ERR\n"));
+ break;
+ case FLASH_UPDATE_DNLD_NOT_SUPPORTED:
+ PM8001_MSG_DBG(pm8001_ha,
+ pm8001_printk(": FLASH_UPDATE_DNLD_NOT_SUPPORTED\n"));
+ break;
+ case FLASH_UPDATE_DISABLED:
+ PM8001_MSG_DBG(pm8001_ha,
+ pm8001_printk(": FLASH_UPDATE_DISABLED\n"));
+ break;
+ default:
+ PM8001_MSG_DBG(pm8001_ha,
+ pm8001_printk("No matched status = %d\n", status));
+ break;
+ }
+ ccb->fw_control_context->fw_control->retcode = status;
+ pci_free_consistent(pm8001_ha->pdev,
+ fw_control_context.len,
+ fw_control_context.virtAddr,
+ fw_control_context.phys_addr);
+ complete(pm8001_ha->nvmd_completion);
+ ccb->task = NULL;
+ ccb->ccb_tag = 0xFFFFFFFF;
+ pm8001_ccb_free(pm8001_ha, tag);
+ return 0;
+}
+
+static int
+mpi_general_event(struct pm8001_hba_info *pm8001_ha , void *piomb)
+{
+ u32 status;
+ int i;
+ struct general_event_resp *pPayload =
+ (struct general_event_resp *)(piomb + 4);
+ status = le32_to_cpu(pPayload->status);
+ PM8001_MSG_DBG(pm8001_ha,
+ pm8001_printk(" status = 0x%x\n", status));
+ for (i = 0; i < GENERAL_EVENT_PAYLOAD; i++)
+ PM8001_MSG_DBG(pm8001_ha,
+ pm8001_printk("inb_IOMB_payload[0x%x] 0x%x, \n", i,
+ pPayload->inb_IOMB_payload[i]));
+ return 0;
+}
+
+static int
+mpi_task_abort_resp(struct pm8001_hba_info *pm8001_ha, void *piomb)
+{
+ struct sas_task *t;
+ struct pm8001_ccb_info *ccb;
+ unsigned long flags;
+ u32 status ;
+ u32 tag, scp;
+ struct task_status_struct *ts;
+
+ struct task_abort_resp *pPayload =
+ (struct task_abort_resp *)(piomb + 4);
+ ccb = &pm8001_ha->ccb_info[pPayload->tag];
+ t = ccb->task;
+
+
+ status = le32_to_cpu(pPayload->status);
+ tag = le32_to_cpu(pPayload->tag);
+ scp = le32_to_cpu(pPayload->scp);
+ PM8001_IO_DBG(pm8001_ha,
+ pm8001_printk(" status = 0x%x\n", status));
+ if (t == NULL)
+ return -1;
+ ts = &t->task_status;
+ if (status != 0)
+ PM8001_FAIL_DBG(pm8001_ha,
+ pm8001_printk("task abort failed status 0x%x ,"
+ "tag = 0x%x, scp= 0x%x\n", status, tag, scp));
+ switch (status) {
+ case IO_SUCCESS:
+ PM8001_EH_DBG(pm8001_ha, pm8001_printk("IO_SUCCESS\n"));
+ ts->resp = SAS_TASK_COMPLETE;
+ ts->stat = SAM_GOOD;
+ break;
+ case IO_NOT_VALID:
+ PM8001_EH_DBG(pm8001_ha, pm8001_printk("IO_NOT_VALID\n"));
+ ts->resp = TMF_RESP_FUNC_FAILED;
+ break;
+ }
+ spin_lock_irqsave(&t->task_state_lock, flags);
+ t->task_state_flags &= ~SAS_TASK_STATE_PENDING;
+ t->task_state_flags &= ~SAS_TASK_AT_INITIATOR;
+ t->task_state_flags |= SAS_TASK_STATE_DONE;
+ spin_unlock_irqrestore(&t->task_state_lock, flags);
+ pm8001_ccb_task_free(pm8001_ha, t, ccb, pPayload->tag);
+ mb();
+ t->task_done(t);
+ return 0;
+}
+
+/**
+ * mpi_hw_event -The hw event has come.
+ * @pm8001_ha: our hba card information
+ * @piomb: IO message buffer
+ */
+static int mpi_hw_event(struct pm8001_hba_info *pm8001_ha, void* piomb)
+{
+ unsigned long flags;
+ struct hw_event_resp *pPayload =
+ (struct hw_event_resp *)(piomb + 4);
+ u32 lr_evt_status_phyid_portid =
+ le32_to_cpu(pPayload->lr_evt_status_phyid_portid);
+ u8 port_id = (u8)(lr_evt_status_phyid_portid & 0x0000000F);
+ u8 phy_id =
+ (u8)((lr_evt_status_phyid_portid & 0x000000F0) >> 4);
+ u16 eventType =
+ (u16)((lr_evt_status_phyid_portid & 0x00FFFF00) >> 8);
+ u8 status =
+ (u8)((lr_evt_status_phyid_portid & 0x0F000000) >> 24);
+ struct sas_ha_struct *sas_ha = pm8001_ha->sas;
+ struct pm8001_phy *phy = &pm8001_ha->phy[phy_id];
+ struct asd_sas_phy *sas_phy = sas_ha->sas_phy[phy_id];
+ PM8001_MSG_DBG(pm8001_ha,
+ pm8001_printk("outbound queue HW event & event type : "));
+ switch (eventType) {
+ case HW_EVENT_PHY_START_STATUS:
+ PM8001_MSG_DBG(pm8001_ha,
+ pm8001_printk("HW_EVENT_PHY_START_STATUS"
+ " status = %x\n", status));
+ if (status == 0) {
+ phy->phy_state = 1;
+ if (pm8001_ha->flags == PM8001F_RUN_TIME)
+ complete(phy->enable_completion);
+ }
+ break;
+ case HW_EVENT_SAS_PHY_UP:
+ PM8001_MSG_DBG(pm8001_ha,
+ pm8001_printk("HW_EVENT_PHY_START_STATUS \n"));
+ hw_event_sas_phy_up(pm8001_ha, piomb);
+ break;
+ case HW_EVENT_SATA_PHY_UP:
+ PM8001_MSG_DBG(pm8001_ha,
+ pm8001_printk("HW_EVENT_SATA_PHY_UP \n"));
+ hw_event_sata_phy_up(pm8001_ha, piomb);
+ break;
+ case HW_EVENT_PHY_STOP_STATUS:
+ PM8001_MSG_DBG(pm8001_ha,
+ pm8001_printk("HW_EVENT_PHY_STOP_STATUS "
+ "status = %x\n", status));
+ if (status == 0)
+ phy->phy_state = 0;
+ break;
+ case HW_EVENT_SATA_SPINUP_HOLD:
+ PM8001_MSG_DBG(pm8001_ha,
+ pm8001_printk("HW_EVENT_SATA_SPINUP_HOLD \n"));
+ sas_ha->notify_phy_event(&phy->sas_phy, PHYE_SPINUP_HOLD);
+ break;
+ case HW_EVENT_PHY_DOWN:
+ PM8001_MSG_DBG(pm8001_ha,
+ pm8001_printk("HW_EVENT_PHY_DOWN \n"));
+ sas_ha->notify_phy_event(&phy->sas_phy, PHYE_LOSS_OF_SIGNAL);
+ phy->phy_attached = 0;
+ phy->phy_state = 0;
+ hw_event_phy_down(pm8001_ha, piomb);
+ break;
+ case HW_EVENT_PORT_INVALID:
+ PM8001_MSG_DBG(pm8001_ha,
+ pm8001_printk("HW_EVENT_PORT_INVALID\n"));
+ sas_phy_disconnected(sas_phy);
+ phy->phy_attached = 0;
+ sas_ha->notify_port_event(sas_phy, PORTE_LINK_RESET_ERR);
+ break;
+ /* the broadcast change primitive received, tell the LIBSAS this event
+ to revalidate the sas domain*/
+ case HW_EVENT_BROADCAST_CHANGE:
+ PM8001_MSG_DBG(pm8001_ha,
+ pm8001_printk("HW_EVENT_BROADCAST_CHANGE\n"));
+ pm8001_hw_event_ack_req(pm8001_ha, 0, HW_EVENT_BROADCAST_CHANGE,
+ port_id, phy_id, 1, 0);
+ spin_lock_irqsave(&sas_phy->sas_prim_lock, flags);
+ sas_phy->sas_prim = HW_EVENT_BROADCAST_CHANGE;
+ spin_unlock_irqrestore(&sas_phy->sas_prim_lock, flags);
+ sas_ha->notify_port_event(sas_phy, PORTE_BROADCAST_RCVD);
+ break;
+ case HW_EVENT_PHY_ERROR:
+ PM8001_MSG_DBG(pm8001_ha,
+ pm8001_printk("HW_EVENT_PHY_ERROR\n"));
+ sas_phy_disconnected(&phy->sas_phy);
+ phy->phy_attached = 0;
+ sas_ha->notify_phy_event(&phy->sas_phy, PHYE_OOB_ERROR);
+ break;
+ case HW_EVENT_BROADCAST_EXP:
+ PM8001_MSG_DBG(pm8001_ha,
+ pm8001_printk("HW_EVENT_BROADCAST_EXP\n"));
+ spin_lock_irqsave(&sas_phy->sas_prim_lock, flags);
+ sas_phy->sas_prim = HW_EVENT_BROADCAST_EXP;
+ spin_unlock_irqrestore(&sas_phy->sas_prim_lock, flags);
+ sas_ha->notify_port_event(sas_phy, PORTE_BROADCAST_RCVD);
+ break;
+ case HW_EVENT_LINK_ERR_INVALID_DWORD:
+ PM8001_MSG_DBG(pm8001_ha,
+ pm8001_printk("HW_EVENT_LINK_ERR_INVALID_DWORD\n"));
+ pm8001_hw_event_ack_req(pm8001_ha, 0,
+ HW_EVENT_LINK_ERR_INVALID_DWORD, port_id, phy_id, 0, 0);
+ sas_phy_disconnected(sas_phy);
+ phy->phy_attached = 0;
+ sas_ha->notify_port_event(sas_phy, PORTE_LINK_RESET_ERR);
+ break;
+ case HW_EVENT_LINK_ERR_DISPARITY_ERROR:
+ PM8001_MSG_DBG(pm8001_ha,
+ pm8001_printk("HW_EVENT_LINK_ERR_DISPARITY_ERROR\n"));
+ pm8001_hw_event_ack_req(pm8001_ha, 0,
+ HW_EVENT_LINK_ERR_DISPARITY_ERROR,
+ port_id, phy_id, 0, 0);
+ sas_phy_disconnected(sas_phy);
+ phy->phy_attached = 0;
+ sas_ha->notify_port_event(sas_phy, PORTE_LINK_RESET_ERR);
+ break;
+ case HW_EVENT_LINK_ERR_CODE_VIOLATION:
+ PM8001_MSG_DBG(pm8001_ha,
+ pm8001_printk("HW_EVENT_LINK_ERR_CODE_VIOLATION\n"));
+ pm8001_hw_event_ack_req(pm8001_ha, 0,
+ HW_EVENT_LINK_ERR_CODE_VIOLATION,
+ port_id, phy_id, 0, 0);
+ sas_phy_disconnected(sas_phy);
+ phy->phy_attached = 0;
+ sas_ha->notify_port_event(sas_phy, PORTE_LINK_RESET_ERR);
+ break;
+ case HW_EVENT_LINK_ERR_LOSS_OF_DWORD_SYNCH:
+ PM8001_MSG_DBG(pm8001_ha,
+ pm8001_printk("HW_EVENT_LINK_ERR_LOSS_OF_DWORD_SYNCH\n"));
+ pm8001_hw_event_ack_req(pm8001_ha, 0,
+ HW_EVENT_LINK_ERR_LOSS_OF_DWORD_SYNCH,
+ port_id, phy_id, 0, 0);
+ sas_phy_disconnected(sas_phy);
+ phy->phy_attached = 0;
+ sas_ha->notify_port_event(sas_phy, PORTE_LINK_RESET_ERR);
+ break;
+ case HW_EVENT_MALFUNCTION:
+ PM8001_MSG_DBG(pm8001_ha,
+ pm8001_printk("HW_EVENT_MALFUNCTION\n"));
+ break;
+ case HW_EVENT_BROADCAST_SES:
+ PM8001_MSG_DBG(pm8001_ha,
+ pm8001_printk("HW_EVENT_BROADCAST_SES\n"));
+ spin_lock_irqsave(&sas_phy->sas_prim_lock, flags);
+ sas_phy->sas_prim = HW_EVENT_BROADCAST_SES;
+ spin_unlock_irqrestore(&sas_phy->sas_prim_lock, flags);
+ sas_ha->notify_port_event(sas_phy, PORTE_BROADCAST_RCVD);
+ break;
+ case HW_EVENT_INBOUND_CRC_ERROR:
+ PM8001_MSG_DBG(pm8001_ha,
+ pm8001_printk("HW_EVENT_INBOUND_CRC_ERROR\n"));
+ pm8001_hw_event_ack_req(pm8001_ha, 0,
+ HW_EVENT_INBOUND_CRC_ERROR,
+ port_id, phy_id, 0, 0);
+ break;
+ case HW_EVENT_HARD_RESET_RECEIVED:
+ PM8001_MSG_DBG(pm8001_ha,
+ pm8001_printk("HW_EVENT_HARD_RESET_RECEIVED\n"));
+ sas_ha->notify_port_event(sas_phy, PORTE_HARD_RESET);
+ break;
+ case HW_EVENT_ID_FRAME_TIMEOUT:
+ PM8001_MSG_DBG(pm8001_ha,
+ pm8001_printk("HW_EVENT_ID_FRAME_TIMEOUT\n"));
+ sas_phy_disconnected(sas_phy);
+ phy->phy_attached = 0;
+ sas_ha->notify_port_event(sas_phy, PORTE_LINK_RESET_ERR);
+ break;
+ case HW_EVENT_LINK_ERR_PHY_RESET_FAILED:
+ PM8001_MSG_DBG(pm8001_ha,
+ pm8001_printk("HW_EVENT_LINK_ERR_PHY_RESET_FAILED \n"));
+ pm8001_hw_event_ack_req(pm8001_ha, 0,
+ HW_EVENT_LINK_ERR_PHY_RESET_FAILED,
+ port_id, phy_id, 0, 0);
+ sas_phy_disconnected(sas_phy);
+ phy->phy_attached = 0;
+ sas_ha->notify_port_event(sas_phy, PORTE_LINK_RESET_ERR);
+ break;
+ case HW_EVENT_PORT_RESET_TIMER_TMO:
+ PM8001_MSG_DBG(pm8001_ha,
+ pm8001_printk("HW_EVENT_PORT_RESET_TIMER_TMO \n"));
+ sas_phy_disconnected(sas_phy);
+ phy->phy_attached = 0;
+ sas_ha->notify_port_event(sas_phy, PORTE_LINK_RESET_ERR);
+ break;
+ case HW_EVENT_PORT_RECOVERY_TIMER_TMO:
+ PM8001_MSG_DBG(pm8001_ha,
+ pm8001_printk("HW_EVENT_PORT_RECOVERY_TIMER_TMO \n"));
+ sas_phy_disconnected(sas_phy);
+ phy->phy_attached = 0;
+ sas_ha->notify_port_event(sas_phy, PORTE_LINK_RESET_ERR);
+ break;
+ case HW_EVENT_PORT_RECOVER:
+ PM8001_MSG_DBG(pm8001_ha,
+ pm8001_printk("HW_EVENT_PORT_RECOVER \n"));
+ break;
+ case HW_EVENT_PORT_RESET_COMPLETE:
+ PM8001_MSG_DBG(pm8001_ha,
+ pm8001_printk("HW_EVENT_PORT_RESET_COMPLETE \n"));
+ break;
+ case EVENT_BROADCAST_ASYNCH_EVENT:
+ PM8001_MSG_DBG(pm8001_ha,
+ pm8001_printk("EVENT_BROADCAST_ASYNCH_EVENT\n"));
+ break;
+ default:
+ PM8001_MSG_DBG(pm8001_ha,
+ pm8001_printk("Unknown event type = %x\n", eventType));
+ break;
+ }
+ return 0;
+}
+
+/**
+ * process_one_iomb - process one outbound Queue memory block
+ * @pm8001_ha: our hba card information
+ * @piomb: IO message buffer
+ */
+static void process_one_iomb(struct pm8001_hba_info *pm8001_ha, void *piomb)
+{
+ u32 pHeader = (u32)*(u32 *)piomb;
+ u8 opc = (u8)((le32_to_cpu(pHeader)) & 0xFFF);
+
+ PM8001_MSG_DBG(pm8001_ha, pm8001_printk("process_one_iomb:"));
+
+ switch (opc) {
+ case OPC_OUB_ECHO:
+ PM8001_MSG_DBG(pm8001_ha, pm8001_printk("OPC_OUB_ECHO \n"));
+ break;
+ case OPC_OUB_HW_EVENT:
+ PM8001_MSG_DBG(pm8001_ha,
+ pm8001_printk("OPC_OUB_HW_EVENT \n"));
+ mpi_hw_event(pm8001_ha, piomb);
+ break;
+ case OPC_OUB_SSP_COMP:
+ PM8001_MSG_DBG(pm8001_ha,
+ pm8001_printk("OPC_OUB_SSP_COMP \n"));
+ mpi_ssp_completion(pm8001_ha, piomb);
+ break;
+ case OPC_OUB_SMP_COMP:
+ PM8001_MSG_DBG(pm8001_ha,
+ pm8001_printk("OPC_OUB_SMP_COMP \n"));
+ mpi_smp_completion(pm8001_ha, piomb);
+ break;
+ case OPC_OUB_LOCAL_PHY_CNTRL:
+ PM8001_MSG_DBG(pm8001_ha,
+ pm8001_printk("OPC_OUB_LOCAL_PHY_CNTRL\n"));
+ mpi_local_phy_ctl(pm8001_ha, piomb);
+ break;
+ case OPC_OUB_DEV_REGIST:
+ PM8001_MSG_DBG(pm8001_ha,
+ pm8001_printk("OPC_OUB_DEV_REGIST \n"));
+ mpi_reg_resp(pm8001_ha, piomb);
+ break;
+ case OPC_OUB_DEREG_DEV:
+ PM8001_MSG_DBG(pm8001_ha,
+ pm8001_printk("unresgister the deviece \n"));
+ mpi_dereg_resp(pm8001_ha, piomb);
+ break;
+ case OPC_OUB_GET_DEV_HANDLE:
+ PM8001_MSG_DBG(pm8001_ha,
+ pm8001_printk("OPC_OUB_GET_DEV_HANDLE \n"));
+ break;
+ case OPC_OUB_SATA_COMP:
+ PM8001_MSG_DBG(pm8001_ha,
+ pm8001_printk("OPC_OUB_SATA_COMP \n"));
+ mpi_sata_completion(pm8001_ha, piomb);
+ break;
+ case OPC_OUB_SATA_EVENT:
+ PM8001_MSG_DBG(pm8001_ha,
+ pm8001_printk("OPC_OUB_SATA_EVENT \n"));
+ mpi_sata_event(pm8001_ha, piomb);
+ break;
+ case OPC_OUB_SSP_EVENT:
+ PM8001_MSG_DBG(pm8001_ha,
+ pm8001_printk("OPC_OUB_SSP_EVENT\n"));
+ mpi_ssp_event(pm8001_ha, piomb);
+ break;
+ case OPC_OUB_DEV_HANDLE_ARRIV:
+ PM8001_MSG_DBG(pm8001_ha,
+ pm8001_printk("OPC_OUB_DEV_HANDLE_ARRIV\n"));
+ /*This is for target*/
+ break;
+ case OPC_OUB_SSP_RECV_EVENT:
+ PM8001_MSG_DBG(pm8001_ha,
+ pm8001_printk("OPC_OUB_SSP_RECV_EVENT\n"));
+ /*This is for target*/
+ break;
+ case OPC_OUB_DEV_INFO:
+ PM8001_MSG_DBG(pm8001_ha,
+ pm8001_printk("OPC_OUB_DEV_INFO\n"));
+ break;
+ case OPC_OUB_FW_FLASH_UPDATE:
+ PM8001_MSG_DBG(pm8001_ha,
+ pm8001_printk("OPC_OUB_FW_FLASH_UPDATE\n"));
+ mpi_fw_flash_update_resp(pm8001_ha, piomb);
+ break;
+ case OPC_OUB_GPIO_RESPONSE:
+ PM8001_MSG_DBG(pm8001_ha,
+ pm8001_printk("OPC_OUB_GPIO_RESPONSE\n"));
+ break;
+ case OPC_OUB_GPIO_EVENT:
+ PM8001_MSG_DBG(pm8001_ha,
+ pm8001_printk("OPC_OUB_GPIO_EVENT\n"));
+ break;
+ case OPC_OUB_GENERAL_EVENT:
+ PM8001_MSG_DBG(pm8001_ha,
+ pm8001_printk("OPC_OUB_GENERAL_EVENT\n"));
+ mpi_general_event(pm8001_ha, piomb);
+ break;
+ case OPC_OUB_SSP_ABORT_RSP:
+ PM8001_MSG_DBG(pm8001_ha,
+ pm8001_printk("OPC_OUB_SSP_ABORT_RSP\n"));
+ mpi_task_abort_resp(pm8001_ha, piomb);
+ break;
+ case OPC_OUB_SATA_ABORT_RSP:
+ PM8001_MSG_DBG(pm8001_ha,
+ pm8001_printk("OPC_OUB_SATA_ABORT_RSP\n"));
+ mpi_task_abort_resp(pm8001_ha, piomb);
+ break;
+ case OPC_OUB_SAS_DIAG_MODE_START_END:
+ PM8001_MSG_DBG(pm8001_ha,
+ pm8001_printk("OPC_OUB_SAS_DIAG_MODE_START_END\n"));
+ break;
+ case OPC_OUB_SAS_DIAG_EXECUTE:
+ PM8001_MSG_DBG(pm8001_ha,
+ pm8001_printk("OPC_OUB_SAS_DIAG_EXECUTE\n"));
+ break;
+ case OPC_OUB_GET_TIME_STAMP:
+ PM8001_MSG_DBG(pm8001_ha,
+ pm8001_printk("OPC_OUB_GET_TIME_STAMP\n"));
+ break;
+ case OPC_OUB_SAS_HW_EVENT_ACK:
+ PM8001_MSG_DBG(pm8001_ha,
+ pm8001_printk("OPC_OUB_SAS_HW_EVENT_ACK\n"));
+ break;
+ case OPC_OUB_PORT_CONTROL:
+ PM8001_MSG_DBG(pm8001_ha,
+ pm8001_printk("OPC_OUB_PORT_CONTROL\n"));
+ break;
+ case OPC_OUB_SMP_ABORT_RSP:
+ PM8001_MSG_DBG(pm8001_ha,
+ pm8001_printk("OPC_OUB_SMP_ABORT_RSP\n"));
+ mpi_task_abort_resp(pm8001_ha, piomb);
+ break;
+ case OPC_OUB_GET_NVMD_DATA:
+ PM8001_MSG_DBG(pm8001_ha,
+ pm8001_printk("OPC_OUB_GET_NVMD_DATA\n"));
+ mpi_get_nvmd_resp(pm8001_ha, piomb);
+ break;
+ case OPC_OUB_SET_NVMD_DATA:
+ PM8001_MSG_DBG(pm8001_ha,
+ pm8001_printk("OPC_OUB_SET_NVMD_DATA\n"));
+ mpi_set_nvmd_resp(pm8001_ha, piomb);
+ break;
+ case OPC_OUB_DEVICE_HANDLE_REMOVAL:
+ PM8001_MSG_DBG(pm8001_ha,
+ pm8001_printk("OPC_OUB_DEVICE_HANDLE_REMOVAL\n"));
+ break;
+ case OPC_OUB_SET_DEVICE_STATE:
+ PM8001_MSG_DBG(pm8001_ha,
+ pm8001_printk("OPC_OUB_SET_DEVICE_STATE\n"));
+ mpi_set_dev_state_resp(pm8001_ha, piomb);
+ break;
+ case OPC_OUB_GET_DEVICE_STATE:
+ PM8001_MSG_DBG(pm8001_ha,
+ pm8001_printk("OPC_OUB_GET_DEVICE_STATE\n"));
+ break;
+ case OPC_OUB_SET_DEV_INFO:
+ PM8001_MSG_DBG(pm8001_ha,
+ pm8001_printk("OPC_OUB_SET_DEV_INFO\n"));
+ break;
+ case OPC_OUB_SAS_RE_INITIALIZE:
+ PM8001_MSG_DBG(pm8001_ha,
+ pm8001_printk("OPC_OUB_SAS_RE_INITIALIZE\n"));
+ break;
+ default:
+ PM8001_MSG_DBG(pm8001_ha,
+ pm8001_printk("Unknown outbound Queue IOMB OPC = %x\n",
+ opc));
+ break;
+ }
+}
+
+static int process_oq(struct pm8001_hba_info *pm8001_ha)
+{
+ struct outbound_queue_table *circularQ;
+ void *pMsg1 = NULL;
+ u8 bc = 0;
+ u32 ret = MPI_IO_STATUS_FAIL;
+
+ circularQ = &pm8001_ha->outbnd_q_tbl[0];
+ do {
+ ret = mpi_msg_consume(pm8001_ha, circularQ, &pMsg1, &bc);
+ if (MPI_IO_STATUS_SUCCESS == ret) {
+ /* process the outbound message */
+ process_one_iomb(pm8001_ha, (void *)(pMsg1 - 4));
+ /* free the message from the outbound circular buffer */
+ mpi_msg_free_set(pm8001_ha, pMsg1, circularQ, bc);
+ }
+ if (MPI_IO_STATUS_BUSY == ret) {
+ u32 producer_idx;
+ /* Update the producer index from SPC */
+ producer_idx = pm8001_read_32(circularQ->pi_virt);
+ circularQ->producer_index = cpu_to_le32(producer_idx);
+ if (circularQ->producer_index ==
+ circularQ->consumer_idx)
+ /* OQ is empty */
+ break;
+ }
+ } while (1);
+ return ret;
+}
+
+/* PCI_DMA_... to our direction translation. */
+static const u8 data_dir_flags[] = {
+ [PCI_DMA_BIDIRECTIONAL] = DATA_DIR_BYRECIPIENT,/* UNSPECIFIED */
+ [PCI_DMA_TODEVICE] = DATA_DIR_OUT,/* OUTBOUND */
+ [PCI_DMA_FROMDEVICE] = DATA_DIR_IN,/* INBOUND */
+ [PCI_DMA_NONE] = DATA_DIR_NONE,/* NO TRANSFER */
+};
+static void
+pm8001_chip_make_sg(struct scatterlist *scatter, int nr, void *prd)
+{
+ int i;
+ struct scatterlist *sg;
+ struct pm8001_prd *buf_prd = prd;
+
+ for_each_sg(scatter, sg, nr, i) {
+ buf_prd->addr = cpu_to_le64(sg_dma_address(sg));
+ buf_prd->im_len.len = cpu_to_le32(sg_dma_len(sg));
+ buf_prd->im_len.e = 0;
+ buf_prd++;
+ }
+}
+
+static void build_smp_cmd(u32 deviceID, u32 hTag, struct smp_req *psmp_cmd)
+{
+ psmp_cmd->tag = cpu_to_le32(hTag);
+ psmp_cmd->device_id = cpu_to_le32(deviceID);
+ psmp_cmd->len_ip_ir = cpu_to_le32(1|(1 << 1));
+}
+
+/**
+ * pm8001_chip_smp_req - send a SMP task to FW
+ * @pm8001_ha: our hba card information.
+ * @ccb: the ccb information this request used.
+ */
+static int pm8001_chip_smp_req(struct pm8001_hba_info *pm8001_ha,
+ struct pm8001_ccb_info *ccb)
+{
+ int elem, rc;
+ struct sas_task *task = ccb->task;
+ struct domain_device *dev = task->dev;
+ struct pm8001_device *pm8001_dev = dev->lldd_dev;
+ struct scatterlist *sg_req, *sg_resp;
+ u32 req_len, resp_len;
+ struct smp_req smp_cmd;
+ u32 opc;
+ struct inbound_queue_table *circularQ;
+
+ memset(&smp_cmd, 0, sizeof(smp_cmd));
+ /*
+ * DMA-map SMP request, response buffers
+ */
+ sg_req = &task->smp_task.smp_req;
+ elem = dma_map_sg(pm8001_ha->dev, sg_req, 1, PCI_DMA_TODEVICE);
+ if (!elem)
+ return -ENOMEM;
+ req_len = sg_dma_len(sg_req);
+
+ sg_resp = &task->smp_task.smp_resp;
+ elem = dma_map_sg(pm8001_ha->dev, sg_resp, 1, PCI_DMA_FROMDEVICE);
+ if (!elem) {
+ rc = -ENOMEM;
+ goto err_out;
+ }
+ resp_len = sg_dma_len(sg_resp);
+ /* must be in dwords */
+ if ((req_len & 0x3) || (resp_len & 0x3)) {
+ rc = -EINVAL;
+ goto err_out_2;
+ }
+
+ opc = OPC_INB_SMP_REQUEST;
+ circularQ = &pm8001_ha->inbnd_q_tbl[0];
+ smp_cmd.tag = cpu_to_le32(ccb->ccb_tag);
+ smp_cmd.long_smp_req.long_req_addr =
+ cpu_to_le64((u64)sg_dma_address(&task->smp_task.smp_req));
+ smp_cmd.long_smp_req.long_req_size =
+ cpu_to_le32((u32)sg_dma_len(&task->smp_task.smp_req)-4);
+ smp_cmd.long_smp_req.long_resp_addr =
+ cpu_to_le64((u64)sg_dma_address(&task->smp_task.smp_resp));
+ smp_cmd.long_smp_req.long_resp_size =
+ cpu_to_le32((u32)sg_dma_len(&task->smp_task.smp_resp)-4);
+ build_smp_cmd(pm8001_dev->device_id, smp_cmd.tag, &smp_cmd);
+ mpi_build_cmd(pm8001_ha, circularQ, opc, (u32 *)&smp_cmd);
+ return 0;
+
+err_out_2:
+ dma_unmap_sg(pm8001_ha->dev, &ccb->task->smp_task.smp_resp, 1,
+ PCI_DMA_FROMDEVICE);
+err_out:
+ dma_unmap_sg(pm8001_ha->dev, &ccb->task->smp_task.smp_req, 1,
+ PCI_DMA_TODEVICE);
+ return rc;
+}
+
+/**
+ * pm8001_chip_ssp_io_req - send a SSP task to FW
+ * @pm8001_ha: our hba card information.
+ * @ccb: the ccb information this request used.
+ */
+static int pm8001_chip_ssp_io_req(struct pm8001_hba_info *pm8001_ha,
+ struct pm8001_ccb_info *ccb)
+{
+ struct sas_task *task = ccb->task;
+ struct domain_device *dev = task->dev;
+ struct pm8001_device *pm8001_dev = dev->lldd_dev;
+ struct ssp_ini_io_start_req ssp_cmd;
+ u32 tag = ccb->ccb_tag;
+ int ret;
+ __le64 phys_addr;
+ struct inbound_queue_table *circularQ;
+ u32 opc = OPC_INB_SSPINIIOSTART;
+ memset(&ssp_cmd, 0, sizeof(ssp_cmd));
+ memcpy(ssp_cmd.ssp_iu.lun, task->ssp_task.LUN, 8);
+ ssp_cmd.dir_m_tlr = data_dir_flags[task->data_dir] << 8 | 0x0;/*0 for
+ SAS 1.1 compatible TLR*/
+ ssp_cmd.data_len = cpu_to_le32(task->total_xfer_len);
+ ssp_cmd.device_id = cpu_to_le32(pm8001_dev->device_id);
+ ssp_cmd.tag = cpu_to_le32(tag);
+ if (task->ssp_task.enable_first_burst)
+ ssp_cmd.ssp_iu.efb_prio_attr |= 0x80;
+ ssp_cmd.ssp_iu.efb_prio_attr |= (task->ssp_task.task_prio << 3);
+ ssp_cmd.ssp_iu.efb_prio_attr |= (task->ssp_task.task_attr & 7);
+ memcpy(ssp_cmd.ssp_iu.cdb, task->ssp_task.cdb, 16);
+ circularQ = &pm8001_ha->inbnd_q_tbl[0];
+
+ /* fill in PRD (scatter/gather) table, if any */
+ if (task->num_scatter > 1) {
+ pm8001_chip_make_sg(task->scatter, ccb->n_elem, ccb->buf_prd);
+ phys_addr = cpu_to_le64(ccb->ccb_dma_handle +
+ offsetof(struct pm8001_ccb_info, buf_prd[0]));
+ ssp_cmd.addr_low = lower_32_bits(phys_addr);
+ ssp_cmd.addr_high = upper_32_bits(phys_addr);
+ ssp_cmd.esgl = cpu_to_le32(1<<31);
+ } else if (task->num_scatter == 1) {
+ __le64 dma_addr = cpu_to_le64(sg_dma_address(task->scatter));
+ ssp_cmd.addr_low = lower_32_bits(dma_addr);
+ ssp_cmd.addr_high = upper_32_bits(dma_addr);
+ ssp_cmd.len = cpu_to_le32(task->total_xfer_len);
+ ssp_cmd.esgl = 0;
+ } else if (task->num_scatter == 0) {
+ ssp_cmd.addr_low = 0;
+ ssp_cmd.addr_high = 0;
+ ssp_cmd.len = cpu_to_le32(task->total_xfer_len);
+ ssp_cmd.esgl = 0;
+ }
+ ret = mpi_build_cmd(pm8001_ha, circularQ, opc, &ssp_cmd);
+ return ret;
+}
+
+static int pm8001_chip_sata_req(struct pm8001_hba_info *pm8001_ha,
+ struct pm8001_ccb_info *ccb)
+{
+ struct sas_task *task = ccb->task;
+ struct domain_device *dev = task->dev;
+ struct pm8001_device *pm8001_ha_dev = dev->lldd_dev;
+ u32 tag = ccb->ccb_tag;
+ int ret;
+ struct sata_start_req sata_cmd;
+ u32 hdr_tag, ncg_tag = 0;
+ __le64 phys_addr;
+ u32 ATAP = 0x0;
+ u32 dir;
+ struct inbound_queue_table *circularQ;
+ u32 opc = OPC_INB_SATA_HOST_OPSTART;
+ memset(&sata_cmd, 0, sizeof(sata_cmd));
+ circularQ = &pm8001_ha->inbnd_q_tbl[0];
+ if (task->data_dir == PCI_DMA_NONE) {
+ ATAP = 0x04; /* no data*/
+ PM8001_IO_DBG(pm8001_ha, pm8001_printk("no data \n"));
+ } else if (likely(!task->ata_task.device_control_reg_update)) {
+ if (task->ata_task.dma_xfer) {
+ ATAP = 0x06; /* DMA */
+ PM8001_IO_DBG(pm8001_ha, pm8001_printk("DMA \n"));
+ } else {
+ ATAP = 0x05; /* PIO*/
+ PM8001_IO_DBG(pm8001_ha, pm8001_printk("PIO \n"));
+ }
+ if (task->ata_task.use_ncq &&
+ dev->sata_dev.command_set != ATAPI_COMMAND_SET) {
+ ATAP = 0x07; /* FPDMA */
+ PM8001_IO_DBG(pm8001_ha, pm8001_printk("FPDMA \n"));
+ }
+ }
+ if (task->ata_task.use_ncq && pm8001_get_ncq_tag(task, &hdr_tag))
+ ncg_tag = cpu_to_le32(hdr_tag);
+ dir = data_dir_flags[task->data_dir] << 8;
+ sata_cmd.tag = cpu_to_le32(tag);
+ sata_cmd.device_id = cpu_to_le32(pm8001_ha_dev->device_id);
+ sata_cmd.data_len = cpu_to_le32(task->total_xfer_len);
+ sata_cmd.ncqtag_atap_dir_m =
+ cpu_to_le32(((ncg_tag & 0xff)<<16)|((ATAP & 0x3f) << 10) | dir);
+ sata_cmd.sata_fis = task->ata_task.fis;
+ if (likely(!task->ata_task.device_control_reg_update))
+ sata_cmd.sata_fis.flags |= 0x80;/* C=1: update ATA cmd reg */
+ sata_cmd.sata_fis.flags &= 0xF0;/* PM_PORT field shall be 0 */
+ /* fill in PRD (scatter/gather) table, if any */
+ if (task->num_scatter > 1) {
+ pm8001_chip_make_sg(task->scatter, ccb->n_elem, ccb->buf_prd);
+ phys_addr = cpu_to_le64(ccb->ccb_dma_handle +
+ offsetof(struct pm8001_ccb_info, buf_prd[0]));
+ sata_cmd.addr_low = lower_32_bits(phys_addr);
+ sata_cmd.addr_high = upper_32_bits(phys_addr);
+ sata_cmd.esgl = cpu_to_le32(1 << 31);
+ } else if (task->num_scatter == 1) {
+ __le64 dma_addr = cpu_to_le64(sg_dma_address(task->scatter));
+ sata_cmd.addr_low = lower_32_bits(dma_addr);
+ sata_cmd.addr_high = upper_32_bits(dma_addr);
+ sata_cmd.len = cpu_to_le32(task->total_xfer_len);
+ sata_cmd.esgl = 0;
+ } else if (task->num_scatter == 0) {
+ sata_cmd.addr_low = 0;
+ sata_cmd.addr_high = 0;
+ sata_cmd.len = cpu_to_le32(task->total_xfer_len);
+ sata_cmd.esgl = 0;
+ }
+ ret = mpi_build_cmd(pm8001_ha, circularQ, opc, &sata_cmd);
+ return ret;
+}
+
+/**
+ * pm8001_chip_phy_start_req - start phy via PHY_START COMMAND
+ * @pm8001_ha: our hba card information.
+ * @num: the inbound queue number
+ * @phy_id: the phy id which we wanted to start up.
+ */
+static int
+pm8001_chip_phy_start_req(struct pm8001_hba_info *pm8001_ha, u8 phy_id)
+{
+ struct phy_start_req payload;
+ struct inbound_queue_table *circularQ;
+ int ret;
+ u32 tag = 0x01;
+ u32 opcode = OPC_INB_PHYSTART;
+ circularQ = &pm8001_ha->inbnd_q_tbl[0];
+ memset(&payload, 0, sizeof(payload));
+ payload.tag = cpu_to_le32(tag);
+ /*
+ ** [0:7] PHY Identifier
+ ** [8:11] link rate 1.5G, 3G, 6G
+ ** [12:13] link mode 01b SAS mode; 10b SATA mode; 11b both
+ ** [14] 0b disable spin up hold; 1b enable spin up hold
+ */
+ payload.ase_sh_lm_slr_phyid = cpu_to_le32(SPINHOLD_DISABLE |
+ LINKMODE_AUTO | LINKRATE_15 |
+ LINKRATE_30 | LINKRATE_60 | phy_id);
+ payload.sas_identify.dev_type = SAS_END_DEV;
+ payload.sas_identify.initiator_bits = SAS_PROTOCOL_ALL;
+ memcpy(payload.sas_identify.sas_addr,
+ pm8001_ha->sas_addr, SAS_ADDR_SIZE);
+ payload.sas_identify.phy_id = phy_id;
+ ret = mpi_build_cmd(pm8001_ha, circularQ, opcode, &payload);
+ return ret;
+}
+
+/**
+ * pm8001_chip_phy_stop_req - start phy via PHY_STOP COMMAND
+ * @pm8001_ha: our hba card information.
+ * @num: the inbound queue number
+ * @phy_id: the phy id which we wanted to start up.
+ */
+static int pm8001_chip_phy_stop_req(struct pm8001_hba_info *pm8001_ha,
+ u8 phy_id)
+{
+ struct phy_stop_req payload;
+ struct inbound_queue_table *circularQ;
+ int ret;
+ u32 tag = 0x01;
+ u32 opcode = OPC_INB_PHYSTOP;
+ circularQ = &pm8001_ha->inbnd_q_tbl[0];
+ memset(&payload, 0, sizeof(payload));
+ payload.tag = cpu_to_le32(tag);
+ payload.phy_id = cpu_to_le32(phy_id);
+ ret = mpi_build_cmd(pm8001_ha, circularQ, opcode, &payload);
+ return ret;
+}
+
+/**
+ * see comments on mpi_reg_resp.
+ */
+static int pm8001_chip_reg_dev_req(struct pm8001_hba_info *pm8001_ha,
+ struct pm8001_device *pm8001_dev, u32 flag)
+{
+ struct reg_dev_req payload;
+ u32 opc;
+ u32 stp_sspsmp_sata = 0x4;
+ struct inbound_queue_table *circularQ;
+ u32 linkrate, phy_id;
+ int rc, tag = 0xdeadbeef;
+ struct pm8001_ccb_info *ccb;
+ u8 retryFlag = 0x1;
+ u16 firstBurstSize = 0;
+ u16 ITNT = 2000;
+ struct domain_device *dev = pm8001_dev->sas_device;
+ struct domain_device *parent_dev = dev->parent;
+ circularQ = &pm8001_ha->inbnd_q_tbl[0];
+
+ memset(&payload, 0, sizeof(payload));
+ rc = pm8001_tag_alloc(pm8001_ha, &tag);
+ if (rc)
+ return rc;
+ ccb = &pm8001_ha->ccb_info[tag];
+ ccb->device = pm8001_dev;
+ ccb->ccb_tag = tag;
+ payload.tag = cpu_to_le32(tag);
+ if (flag == 1)
+ stp_sspsmp_sata = 0x02; /*direct attached sata */
+ else {
+ if (pm8001_dev->dev_type == SATA_DEV)
+ stp_sspsmp_sata = 0x00; /* stp*/
+ else if (pm8001_dev->dev_type == SAS_END_DEV ||
+ pm8001_dev->dev_type == EDGE_DEV ||
+ pm8001_dev->dev_type == FANOUT_DEV)
+ stp_sspsmp_sata = 0x01; /*ssp or smp*/
+ }
+ if (parent_dev && DEV_IS_EXPANDER(parent_dev->dev_type))
+ phy_id = parent_dev->ex_dev.ex_phy->phy_id;
+ else
+ phy_id = pm8001_dev->attached_phy;
+ opc = OPC_INB_REG_DEV;
+ linkrate = (pm8001_dev->sas_device->linkrate < dev->port->linkrate) ?
+ pm8001_dev->sas_device->linkrate : dev->port->linkrate;
+ payload.phyid_portid =
+ cpu_to_le32(((pm8001_dev->sas_device->port->id) & 0x0F) |
+ ((phy_id & 0x0F) << 4));
+ payload.dtype_dlr_retry = cpu_to_le32((retryFlag & 0x01) |
+ ((linkrate & 0x0F) * 0x1000000) |
+ ((stp_sspsmp_sata & 0x03) * 0x10000000));
+ payload.firstburstsize_ITNexustimeout =
+ cpu_to_le32(ITNT | (firstBurstSize * 0x10000));
+ memcpy(&payload.sas_addr_hi, pm8001_dev->sas_device->sas_addr,
+ SAS_ADDR_SIZE);
+ rc = mpi_build_cmd(pm8001_ha, circularQ, opc, &payload);
+ return rc;
+}
+
+/**
+ * see comments on mpi_reg_resp.
+ */
+static int pm8001_chip_dereg_dev_req(struct pm8001_hba_info *pm8001_ha,
+ u32 device_id)
+{
+ struct dereg_dev_req payload;
+ u32 opc = OPC_INB_DEREG_DEV_HANDLE;
+ int ret;
+ struct inbound_queue_table *circularQ;
+
+ circularQ = &pm8001_ha->inbnd_q_tbl[0];
+ memset(&payload, 0, sizeof(payload));
+ payload.tag = 1;
+ payload.device_id = cpu_to_le32(device_id);
+ PM8001_MSG_DBG(pm8001_ha,
+ pm8001_printk("unregister device device_id = %d\n", device_id));
+ ret = mpi_build_cmd(pm8001_ha, circularQ, opc, &payload);
+ return ret;
+}
+
+/**
+ * pm8001_chip_phy_ctl_req - support the local phy operation
+ * @pm8001_ha: our hba card information.
+ * @num: the inbound queue number
+ * @phy_id: the phy id which we wanted to operate
+ * @phy_op:
+ */
+static int pm8001_chip_phy_ctl_req(struct pm8001_hba_info *pm8001_ha,
+ u32 phyId, u32 phy_op)
+{
+ struct local_phy_ctl_req payload;
+ struct inbound_queue_table *circularQ;
+ int ret;
+ u32 opc = OPC_INB_LOCAL_PHY_CONTROL;
+ memset((u8 *)&payload, 0, sizeof(payload));
+ circularQ = &pm8001_ha->inbnd_q_tbl[0];
+ payload.tag = 1;
+ payload.phyop_phyid =
+ cpu_to_le32(((phy_op & 0xff) << 8) | (phyId & 0x0F));
+ ret = mpi_build_cmd(pm8001_ha, circularQ, opc, &payload);
+ return ret;
+}
+
+static u32 pm8001_chip_is_our_interupt(struct pm8001_hba_info *pm8001_ha)
+{
+ u32 value;
+#ifdef PM8001_USE_MSIX
+ return 1;
+#endif
+ value = pm8001_cr32(pm8001_ha, 0, MSGU_ODR);
+ if (value)
+ return 1;
+ return 0;
+
+}
+
+/**
+ * pm8001_chip_isr - PM8001 isr handler.
+ * @pm8001_ha: our hba card information.
+ * @irq: irq number.
+ * @stat: stat.
+ */
+static irqreturn_t
+pm8001_chip_isr(struct pm8001_hba_info *pm8001_ha)
+{
+ unsigned long flags;
+ spin_lock_irqsave(&pm8001_ha->lock, flags);
+ pm8001_chip_interrupt_disable(pm8001_ha);
+ process_oq(pm8001_ha);
+ pm8001_chip_interrupt_enable(pm8001_ha);
+ spin_unlock_irqrestore(&pm8001_ha->lock, flags);
+ return IRQ_HANDLED;
+}
+
+static int send_task_abort(struct pm8001_hba_info *pm8001_ha, u32 opc,
+ u32 dev_id, u8 flag, u32 task_tag, u32 cmd_tag)
+{
+ struct task_abort_req task_abort;
+ struct inbound_queue_table *circularQ;
+ int ret;
+ circularQ = &pm8001_ha->inbnd_q_tbl[0];
+ memset(&task_abort, 0, sizeof(task_abort));
+ if (ABORT_SINGLE == (flag & ABORT_MASK)) {
+ task_abort.abort_all = 0;
+ task_abort.device_id = cpu_to_le32(dev_id);
+ task_abort.tag_to_abort = cpu_to_le32(task_tag);
+ task_abort.tag = cpu_to_le32(cmd_tag);
+ } else if (ABORT_ALL == (flag & ABORT_MASK)) {
+ task_abort.abort_all = cpu_to_le32(1);
+ task_abort.device_id = cpu_to_le32(dev_id);
+ task_abort.tag = cpu_to_le32(cmd_tag);
+ }
+ ret = mpi_build_cmd(pm8001_ha, circularQ, opc, &task_abort);
+ return ret;
+}
+
+/**
+ * pm8001_chip_abort_task - SAS abort task when error or exception happened.
+ * @task: the task we wanted to aborted.
+ * @flag: the abort flag.
+ */
+static int pm8001_chip_abort_task(struct pm8001_hba_info *pm8001_ha,
+ struct pm8001_device *pm8001_dev, u8 flag, u32 task_tag, u32 cmd_tag)
+{
+ u32 opc, device_id;
+ int rc = TMF_RESP_FUNC_FAILED;
+ PM8001_EH_DBG(pm8001_ha, pm8001_printk("cmd_tag = %x, abort task tag"
+ " = %x", cmd_tag, task_tag));
+ if (pm8001_dev->dev_type == SAS_END_DEV)
+ opc = OPC_INB_SSP_ABORT;
+ else if (pm8001_dev->dev_type == SATA_DEV)
+ opc = OPC_INB_SATA_ABORT;
+ else
+ opc = OPC_INB_SMP_ABORT;/* SMP */
+ device_id = pm8001_dev->device_id;
+ rc = send_task_abort(pm8001_ha, opc, device_id, flag,
+ task_tag, cmd_tag);
+ if (rc != TMF_RESP_FUNC_COMPLETE)
+ PM8001_EH_DBG(pm8001_ha, pm8001_printk("rc= %d\n", rc));
+ return rc;
+}
+
+/**
+ * pm8001_chip_ssp_tm_req - built the task managment command.
+ * @pm8001_ha: our hba card information.
+ * @ccb: the ccb information.
+ * @tmf: task management function.
+ */
+static int pm8001_chip_ssp_tm_req(struct pm8001_hba_info *pm8001_ha,
+ struct pm8001_ccb_info *ccb, struct pm8001_tmf_task *tmf)
+{
+ struct sas_task *task = ccb->task;
+ struct domain_device *dev = task->dev;
+ struct pm8001_device *pm8001_dev = dev->lldd_dev;
+ u32 opc = OPC_INB_SSPINITMSTART;
+ struct inbound_queue_table *circularQ;
+ struct ssp_ini_tm_start_req sspTMCmd;
+ int ret;
+
+ memset(&sspTMCmd, 0, sizeof(sspTMCmd));
+ sspTMCmd.device_id = cpu_to_le32(pm8001_dev->device_id);
+ sspTMCmd.relate_tag = cpu_to_le32(tmf->tag_of_task_to_be_managed);
+ sspTMCmd.tmf = cpu_to_le32(tmf->tmf);
+ memcpy(sspTMCmd.lun, task->ssp_task.LUN, 8);
+ sspTMCmd.tag = cpu_to_le32(ccb->ccb_tag);
+ circularQ = &pm8001_ha->inbnd_q_tbl[0];
+ ret = mpi_build_cmd(pm8001_ha, circularQ, opc, &sspTMCmd);
+ return ret;
+}
+
+static int pm8001_chip_get_nvmd_req(struct pm8001_hba_info *pm8001_ha,
+ void *payload)
+{
+ u32 opc = OPC_INB_GET_NVMD_DATA;
+ u32 nvmd_type;
+ int rc;
+ u32 tag;
+ struct pm8001_ccb_info *ccb;
+ struct inbound_queue_table *circularQ;
+ struct get_nvm_data_req nvmd_req;
+ struct fw_control_ex *fw_control_context;
+ struct pm8001_ioctl_payload *ioctl_payload = payload;
+
+ nvmd_type = ioctl_payload->minor_function;
+ fw_control_context = kzalloc(sizeof(struct fw_control_ex), GFP_KERNEL);
+ fw_control_context->usrAddr = (u8 *)&ioctl_payload->func_specific[0];
+ fw_control_context->len = ioctl_payload->length;
+ circularQ = &pm8001_ha->inbnd_q_tbl[0];
+ memset(&nvmd_req, 0, sizeof(nvmd_req));
+ rc = pm8001_tag_alloc(pm8001_ha, &tag);
+ if (rc)
+ return rc;
+ ccb = &pm8001_ha->ccb_info[tag];
+ ccb->ccb_tag = tag;
+ ccb->fw_control_context = fw_control_context;
+ nvmd_req.tag = cpu_to_le32(tag);
+
+ switch (nvmd_type) {
+ case TWI_DEVICE: {
+ u32 twi_addr, twi_page_size;
+ twi_addr = 0xa8;
+ twi_page_size = 2;
+
+ nvmd_req.len_ir_vpdd = cpu_to_le32(IPMode | twi_addr << 16 |
+ twi_page_size << 8 | TWI_DEVICE);
+ nvmd_req.resp_len = cpu_to_le32(ioctl_payload->length);
+ nvmd_req.resp_addr_hi =
+ cpu_to_le32(pm8001_ha->memoryMap.region[NVMD].phys_addr_hi);
+ nvmd_req.resp_addr_lo =
+ cpu_to_le32(pm8001_ha->memoryMap.region[NVMD].phys_addr_lo);
+ break;
+ }
+ case C_SEEPROM: {
+ nvmd_req.len_ir_vpdd = cpu_to_le32(IPMode | C_SEEPROM);
+ nvmd_req.resp_len = cpu_to_le32(ioctl_payload->length);
+ nvmd_req.resp_addr_hi =
+ cpu_to_le32(pm8001_ha->memoryMap.region[NVMD].phys_addr_hi);
+ nvmd_req.resp_addr_lo =
+ cpu_to_le32(pm8001_ha->memoryMap.region[NVMD].phys_addr_lo);
+ break;
+ }
+ case VPD_FLASH: {
+ nvmd_req.len_ir_vpdd = cpu_to_le32(IPMode | VPD_FLASH);
+ nvmd_req.resp_len = cpu_to_le32(ioctl_payload->length);
+ nvmd_req.resp_addr_hi =
+ cpu_to_le32(pm8001_ha->memoryMap.region[NVMD].phys_addr_hi);
+ nvmd_req.resp_addr_lo =
+ cpu_to_le32(pm8001_ha->memoryMap.region[NVMD].phys_addr_lo);
+ break;
+ }
+ case EXPAN_ROM: {
+ nvmd_req.len_ir_vpdd = cpu_to_le32(IPMode | EXPAN_ROM);
+ nvmd_req.resp_len = cpu_to_le32(ioctl_payload->length);
+ nvmd_req.resp_addr_hi =
+ cpu_to_le32(pm8001_ha->memoryMap.region[NVMD].phys_addr_hi);
+ nvmd_req.resp_addr_lo =
+ cpu_to_le32(pm8001_ha->memoryMap.region[NVMD].phys_addr_lo);
+ break;
+ }
+ default:
+ break;
+ }
+ rc = mpi_build_cmd(pm8001_ha, circularQ, opc, &nvmd_req);
+ return rc;
+}
+
+static int pm8001_chip_set_nvmd_req(struct pm8001_hba_info *pm8001_ha,
+ void *payload)
+{
+ u32 opc = OPC_INB_SET_NVMD_DATA;
+ u32 nvmd_type;
+ int rc;
+ u32 tag;
+ struct pm8001_ccb_info *ccb;
+ struct inbound_queue_table *circularQ;
+ struct set_nvm_data_req nvmd_req;
+ struct fw_control_ex *fw_control_context;
+ struct pm8001_ioctl_payload *ioctl_payload = payload;
+
+ nvmd_type = ioctl_payload->minor_function;
+ fw_control_context = kzalloc(sizeof(struct fw_control_ex), GFP_KERNEL);
+ circularQ = &pm8001_ha->inbnd_q_tbl[0];
+ memcpy(pm8001_ha->memoryMap.region[NVMD].virt_ptr,
+ ioctl_payload->func_specific,
+ ioctl_payload->length);
+ memset(&nvmd_req, 0, sizeof(nvmd_req));
+ rc = pm8001_tag_alloc(pm8001_ha, &tag);
+ if (rc)
+ return rc;
+ ccb = &pm8001_ha->ccb_info[tag];
+ ccb->fw_control_context = fw_control_context;
+ ccb->ccb_tag = tag;
+ nvmd_req.tag = cpu_to_le32(tag);
+ switch (nvmd_type) {
+ case TWI_DEVICE: {
+ u32 twi_addr, twi_page_size;
+ twi_addr = 0xa8;
+ twi_page_size = 2;
+ nvmd_req.reserved[0] = cpu_to_le32(0xFEDCBA98);
+ nvmd_req.len_ir_vpdd = cpu_to_le32(IPMode | twi_addr << 16 |
+ twi_page_size << 8 | TWI_DEVICE);
+ nvmd_req.resp_len = cpu_to_le32(ioctl_payload->length);
+ nvmd_req.resp_addr_hi =
+ cpu_to_le32(pm8001_ha->memoryMap.region[NVMD].phys_addr_hi);
+ nvmd_req.resp_addr_lo =
+ cpu_to_le32(pm8001_ha->memoryMap.region[NVMD].phys_addr_lo);
+ break;
+ }
+ case C_SEEPROM:
+ nvmd_req.len_ir_vpdd = cpu_to_le32(IPMode | C_SEEPROM);
+ nvmd_req.resp_len = cpu_to_le32(ioctl_payload->length);
+ nvmd_req.reserved[0] = cpu_to_le32(0xFEDCBA98);
+ nvmd_req.resp_addr_hi =
+ cpu_to_le32(pm8001_ha->memoryMap.region[NVMD].phys_addr_hi);
+ nvmd_req.resp_addr_lo =
+ cpu_to_le32(pm8001_ha->memoryMap.region[NVMD].phys_addr_lo);
+ break;
+ case VPD_FLASH:
+ nvmd_req.len_ir_vpdd = cpu_to_le32(IPMode | VPD_FLASH);
+ nvmd_req.resp_len = cpu_to_le32(ioctl_payload->length);
+ nvmd_req.reserved[0] = cpu_to_le32(0xFEDCBA98);
+ nvmd_req.resp_addr_hi =
+ cpu_to_le32(pm8001_ha->memoryMap.region[NVMD].phys_addr_hi);
+ nvmd_req.resp_addr_lo =
+ cpu_to_le32(pm8001_ha->memoryMap.region[NVMD].phys_addr_lo);
+ break;
+ case EXPAN_ROM:
+ nvmd_req.len_ir_vpdd = cpu_to_le32(IPMode | EXPAN_ROM);
+ nvmd_req.resp_len = cpu_to_le32(ioctl_payload->length);
+ nvmd_req.reserved[0] = cpu_to_le32(0xFEDCBA98);
+ nvmd_req.resp_addr_hi =
+ cpu_to_le32(pm8001_ha->memoryMap.region[NVMD].phys_addr_hi);
+ nvmd_req.resp_addr_lo =
+ cpu_to_le32(pm8001_ha->memoryMap.region[NVMD].phys_addr_lo);
+ break;
+ default:
+ break;
+ }
+ rc = mpi_build_cmd(pm8001_ha, circularQ, opc, &nvmd_req);
+ return rc;
+}
+
+/**
+ * pm8001_chip_fw_flash_update_build - support the firmware update operation
+ * @pm8001_ha: our hba card information.
+ * @fw_flash_updata_info: firmware flash update param
+ */
+static int
+pm8001_chip_fw_flash_update_build(struct pm8001_hba_info *pm8001_ha,
+ void *fw_flash_updata_info, u32 tag)
+{
+ struct fw_flash_Update_req payload;
+ struct fw_flash_updata_info *info;
+ struct inbound_queue_table *circularQ;
+ int ret;
+ u32 opc = OPC_INB_FW_FLASH_UPDATE;
+
+ memset(&payload, 0, sizeof(struct fw_flash_Update_req));
+ circularQ = &pm8001_ha->inbnd_q_tbl[0];
+ info = fw_flash_updata_info;
+ payload.tag = cpu_to_le32(tag);
+ payload.cur_image_len = cpu_to_le32(info->cur_image_len);
+ payload.cur_image_offset = cpu_to_le32(info->cur_image_offset);
+ payload.total_image_len = cpu_to_le32(info->total_image_len);
+ payload.len = info->sgl.im_len.len ;
+ payload.sgl_addr_lo = lower_32_bits(info->sgl.addr);
+ payload.sgl_addr_hi = upper_32_bits(info->sgl.addr);
+ ret = mpi_build_cmd(pm8001_ha, circularQ, opc, &payload);
+ return ret;
+}
+
+static int
+pm8001_chip_fw_flash_update_req(struct pm8001_hba_info *pm8001_ha,
+ void *payload)
+{
+ struct fw_flash_updata_info flash_update_info;
+ struct fw_control_info *fw_control;
+ struct fw_control_ex *fw_control_context;
+ int rc;
+ u32 tag;
+ struct pm8001_ccb_info *ccb;
+ void *buffer = NULL;
+ dma_addr_t phys_addr;
+ u32 phys_addr_hi;
+ u32 phys_addr_lo;
+ struct pm8001_ioctl_payload *ioctl_payload = payload;
+
+ fw_control_context = kzalloc(sizeof(struct fw_control_ex), GFP_KERNEL);
+ fw_control = (struct fw_control_info *)&ioctl_payload->func_specific[0];
+ if (fw_control->len != 0) {
+ if (pm8001_mem_alloc(pm8001_ha->pdev,
+ (void **)&buffer,
+ &phys_addr,
+ &phys_addr_hi,
+ &phys_addr_lo,
+ fw_control->len, 0) != 0) {
+ PM8001_FAIL_DBG(pm8001_ha,
+ pm8001_printk("Mem alloc failure\n"));
+ return -ENOMEM;
+ }
+ }
+ memset(buffer, 0, fw_control->len);
+ memcpy(buffer, fw_control->buffer, fw_control->len);
+ flash_update_info.sgl.addr = cpu_to_le64(phys_addr);
+ flash_update_info.sgl.im_len.len = cpu_to_le32(fw_control->len);
+ flash_update_info.sgl.im_len.e = 0;
+ flash_update_info.cur_image_offset = fw_control->offset;
+ flash_update_info.cur_image_len = fw_control->len;
+ flash_update_info.total_image_len = fw_control->size;
+ fw_control_context->fw_control = fw_control;
+ fw_control_context->virtAddr = buffer;
+ fw_control_context->len = fw_control->len;
+ rc = pm8001_tag_alloc(pm8001_ha, &tag);
+ if (rc)
+ return rc;
+ ccb = &pm8001_ha->ccb_info[tag];
+ ccb->fw_control_context = fw_control_context;
+ ccb->ccb_tag = tag;
+ rc = pm8001_chip_fw_flash_update_build(pm8001_ha, &flash_update_info,
+ tag);
+ return rc;
+}
+
+static int
+pm8001_chip_set_dev_state_req(struct pm8001_hba_info *pm8001_ha,
+ struct pm8001_device *pm8001_dev, u32 state)
+{
+ struct set_dev_state_req payload;
+ struct inbound_queue_table *circularQ;
+ struct pm8001_ccb_info *ccb;
+ int rc;
+ u32 tag;
+ u32 opc = OPC_INB_SET_DEVICE_STATE;
+ memset(&payload, 0, sizeof(payload));
+ rc = pm8001_tag_alloc(pm8001_ha, &tag);
+ if (rc)
+ return -1;
+ ccb = &pm8001_ha->ccb_info[tag];
+ ccb->ccb_tag = tag;
+ ccb->device = pm8001_dev;
+ circularQ = &pm8001_ha->inbnd_q_tbl[0];
+ payload.tag = cpu_to_le32(tag);
+ payload.device_id = cpu_to_le32(pm8001_dev->device_id);
+ payload.nds = cpu_to_le32(state);
+ rc = mpi_build_cmd(pm8001_ha, circularQ, opc, &payload);
+ return rc;
+
+}
+
+static int
+pm8001_chip_sas_re_initialization(struct pm8001_hba_info *pm8001_ha)
+{
+ struct sas_re_initialization_req payload;
+ struct inbound_queue_table *circularQ;
+ struct pm8001_ccb_info *ccb;
+ int rc;
+ u32 tag;
+ u32 opc = OPC_INB_SAS_RE_INITIALIZE;
+ memset(&payload, 0, sizeof(payload));
+ rc = pm8001_tag_alloc(pm8001_ha, &tag);
+ if (rc)
+ return -1;
+ ccb = &pm8001_ha->ccb_info[tag];
+ ccb->ccb_tag = tag;
+ circularQ = &pm8001_ha->inbnd_q_tbl[0];
+ payload.tag = cpu_to_le32(tag);
+ payload.SSAHOLT = cpu_to_le32(0xd << 25);
+ payload.sata_hol_tmo = cpu_to_le32(80);
+ payload.open_reject_cmdretries_data_retries = cpu_to_le32(0xff00ff);
+ rc = mpi_build_cmd(pm8001_ha, circularQ, opc, &payload);
+ return rc;
+
+}
+
+const struct pm8001_dispatch pm8001_8001_dispatch = {
+ .name = "pmc8001",
+ .chip_init = pm8001_chip_init,
+ .chip_soft_rst = pm8001_chip_soft_rst,
+ .chip_rst = pm8001_hw_chip_rst,
+ .chip_iounmap = pm8001_chip_iounmap,
+ .isr = pm8001_chip_isr,
+ .is_our_interupt = pm8001_chip_is_our_interupt,
+ .isr_process_oq = process_oq,
+ .interrupt_enable = pm8001_chip_interrupt_enable,
+ .interrupt_disable = pm8001_chip_interrupt_disable,
+ .make_prd = pm8001_chip_make_sg,
+ .smp_req = pm8001_chip_smp_req,
+ .ssp_io_req = pm8001_chip_ssp_io_req,
+ .sata_req = pm8001_chip_sata_req,
+ .phy_start_req = pm8001_chip_phy_start_req,
+ .phy_stop_req = pm8001_chip_phy_stop_req,
+ .reg_dev_req = pm8001_chip_reg_dev_req,
+ .dereg_dev_req = pm8001_chip_dereg_dev_req,
+ .phy_ctl_req = pm8001_chip_phy_ctl_req,
+ .task_abort = pm8001_chip_abort_task,
+ .ssp_tm_req = pm8001_chip_ssp_tm_req,
+ .get_nvmd_req = pm8001_chip_get_nvmd_req,
+ .set_nvmd_req = pm8001_chip_set_nvmd_req,
+ .fw_flash_update_req = pm8001_chip_fw_flash_update_req,
+ .set_dev_state_req = pm8001_chip_set_dev_state_req,
+ .sas_re_init_req = pm8001_chip_sas_re_initialization,
+};
+
diff --git a/drivers/scsi/pm8001/pm8001_hwi.h b/drivers/scsi/pm8001/pm8001_hwi.h
new file mode 100644
index 00000000000..96e4daa68b8
--- /dev/null
+++ b/drivers/scsi/pm8001/pm8001_hwi.h
@@ -0,0 +1,1030 @@
+/*
+ * PMC-Sierra SPC 8001 SAS/SATA based host adapters driver
+ *
+ * Copyright (c) 2008-2009 USI Co., Ltd.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions, and the following disclaimer,
+ * without modification.
+ * 2. Redistributions in binary form must reproduce at minimum a disclaimer
+ * substantially similar to the "NO WARRANTY" disclaimer below
+ * ("Disclaimer") and any redistribution must be conditioned upon
+ * including a substantially similar Disclaimer requirement for further
+ * binary redistribution.
+ * 3. Neither the names of the above-listed copyright holders nor the names
+ * of any contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2 as published by the Free
+ * Software Foundation.
+ *
+ * NO WARRANTY
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ */
+#ifndef _PMC8001_REG_H_
+#define _PMC8001_REG_H_
+
+#include <linux/types.h>
+#include <scsi/libsas.h>
+
+
+/* for Request Opcode of IOMB */
+#define OPC_INB_ECHO 1 /* 0x000 */
+#define OPC_INB_PHYSTART 4 /* 0x004 */
+#define OPC_INB_PHYSTOP 5 /* 0x005 */
+#define OPC_INB_SSPINIIOSTART 6 /* 0x006 */
+#define OPC_INB_SSPINITMSTART 7 /* 0x007 */
+#define OPC_INB_SSPINIEXTIOSTART 8 /* 0x008 */
+#define OPC_INB_DEV_HANDLE_ACCEPT 9 /* 0x009 */
+#define OPC_INB_SSPTGTIOSTART 10 /* 0x00A */
+#define OPC_INB_SSPTGTRSPSTART 11 /* 0x00B */
+#define OPC_INB_SSPINIEDCIOSTART 12 /* 0x00C */
+#define OPC_INB_SSPINIEXTEDCIOSTART 13 /* 0x00D */
+#define OPC_INB_SSPTGTEDCIOSTART 14 /* 0x00E */
+#define OPC_INB_SSP_ABORT 15 /* 0x00F */
+#define OPC_INB_DEREG_DEV_HANDLE 16 /* 0x010 */
+#define OPC_INB_GET_DEV_HANDLE 17 /* 0x011 */
+#define OPC_INB_SMP_REQUEST 18 /* 0x012 */
+/* SMP_RESPONSE is removed */
+#define OPC_INB_SMP_RESPONSE 19 /* 0x013 */
+#define OPC_INB_SMP_ABORT 20 /* 0x014 */
+#define OPC_INB_REG_DEV 22 /* 0x016 */
+#define OPC_INB_SATA_HOST_OPSTART 23 /* 0x017 */
+#define OPC_INB_SATA_ABORT 24 /* 0x018 */
+#define OPC_INB_LOCAL_PHY_CONTROL 25 /* 0x019 */
+#define OPC_INB_GET_DEV_INFO 26 /* 0x01A */
+#define OPC_INB_FW_FLASH_UPDATE 32 /* 0x020 */
+#define OPC_INB_GPIO 34 /* 0x022 */
+#define OPC_INB_SAS_DIAG_MODE_START_END 35 /* 0x023 */
+#define OPC_INB_SAS_DIAG_EXECUTE 36 /* 0x024 */
+#define OPC_INB_SAS_HW_EVENT_ACK 37 /* 0x025 */
+#define OPC_INB_GET_TIME_STAMP 38 /* 0x026 */
+#define OPC_INB_PORT_CONTROL 39 /* 0x027 */
+#define OPC_INB_GET_NVMD_DATA 40 /* 0x028 */
+#define OPC_INB_SET_NVMD_DATA 41 /* 0x029 */
+#define OPC_INB_SET_DEVICE_STATE 42 /* 0x02A */
+#define OPC_INB_GET_DEVICE_STATE 43 /* 0x02B */
+#define OPC_INB_SET_DEV_INFO 44 /* 0x02C */
+#define OPC_INB_SAS_RE_INITIALIZE 45 /* 0x02D */
+
+/* for Response Opcode of IOMB */
+#define OPC_OUB_ECHO 1 /* 0x001 */
+#define OPC_OUB_HW_EVENT 4 /* 0x004 */
+#define OPC_OUB_SSP_COMP 5 /* 0x005 */
+#define OPC_OUB_SMP_COMP 6 /* 0x006 */
+#define OPC_OUB_LOCAL_PHY_CNTRL 7 /* 0x007 */
+#define OPC_OUB_DEV_REGIST 10 /* 0x00A */
+#define OPC_OUB_DEREG_DEV 11 /* 0x00B */
+#define OPC_OUB_GET_DEV_HANDLE 12 /* 0x00C */
+#define OPC_OUB_SATA_COMP 13 /* 0x00D */
+#define OPC_OUB_SATA_EVENT 14 /* 0x00E */
+#define OPC_OUB_SSP_EVENT 15 /* 0x00F */
+#define OPC_OUB_DEV_HANDLE_ARRIV 16 /* 0x010 */
+/* SMP_RECEIVED Notification is removed */
+#define OPC_OUB_SMP_RECV_EVENT 17 /* 0x011 */
+#define OPC_OUB_SSP_RECV_EVENT 18 /* 0x012 */
+#define OPC_OUB_DEV_INFO 19 /* 0x013 */
+#define OPC_OUB_FW_FLASH_UPDATE 20 /* 0x014 */
+#define OPC_OUB_GPIO_RESPONSE 22 /* 0x016 */
+#define OPC_OUB_GPIO_EVENT 23 /* 0x017 */
+#define OPC_OUB_GENERAL_EVENT 24 /* 0x018 */
+#define OPC_OUB_SSP_ABORT_RSP 26 /* 0x01A */
+#define OPC_OUB_SATA_ABORT_RSP 27 /* 0x01B */
+#define OPC_OUB_SAS_DIAG_MODE_START_END 28 /* 0x01C */
+#define OPC_OUB_SAS_DIAG_EXECUTE 29 /* 0x01D */
+#define OPC_OUB_GET_TIME_STAMP 30 /* 0x01E */
+#define OPC_OUB_SAS_HW_EVENT_ACK 31 /* 0x01F */
+#define OPC_OUB_PORT_CONTROL 32 /* 0x020 */
+#define OPC_OUB_SKIP_ENTRY 33 /* 0x021 */
+#define OPC_OUB_SMP_ABORT_RSP 34 /* 0x022 */
+#define OPC_OUB_GET_NVMD_DATA 35 /* 0x023 */
+#define OPC_OUB_SET_NVMD_DATA 36 /* 0x024 */
+#define OPC_OUB_DEVICE_HANDLE_REMOVAL 37 /* 0x025 */
+#define OPC_OUB_SET_DEVICE_STATE 38 /* 0x026 */
+#define OPC_OUB_GET_DEVICE_STATE 39 /* 0x027 */
+#define OPC_OUB_SET_DEV_INFO 40 /* 0x028 */
+#define OPC_OUB_SAS_RE_INITIALIZE 41 /* 0x029 */
+
+/* for phy start*/
+#define SPINHOLD_DISABLE (0x00 << 14)
+#define SPINHOLD_ENABLE (0x01 << 14)
+#define LINKMODE_SAS (0x01 << 12)
+#define LINKMODE_DSATA (0x02 << 12)
+#define LINKMODE_AUTO (0x03 << 12)
+#define LINKRATE_15 (0x01 << 8)
+#define LINKRATE_30 (0x02 << 8)
+#define LINKRATE_60 (0x04 << 8)
+
+struct mpi_msg_hdr{
+ __le32 header; /* Bits [11:0] - Message operation code */
+ /* Bits [15:12] - Message Category */
+ /* Bits [21:16] - Outboundqueue ID for the
+ operation completion message */
+ /* Bits [23:22] - Reserved */
+ /* Bits [28:24] - Buffer Count, indicates how
+ many buffer are allocated for the massage */
+ /* Bits [30:29] - Reserved */
+ /* Bits [31] - Message Valid bit */
+} __attribute__((packed, aligned(4)));
+
+
+/*
+ * brief the data structure of PHY Start Command
+ * use to describe enable the phy (64 bytes)
+ */
+struct phy_start_req {
+ __le32 tag;
+ __le32 ase_sh_lm_slr_phyid;
+ struct sas_identify_frame sas_identify;
+ u32 reserved[5];
+} __attribute__((packed, aligned(4)));
+
+
+/*
+ * brief the data structure of PHY Start Command
+ * use to disable the phy (64 bytes)
+ */
+struct phy_stop_req {
+ __le32 tag;
+ __le32 phy_id;
+ u32 reserved[13];
+} __attribute__((packed, aligned(4)));
+
+
+/* set device bits fis - device to host */
+struct set_dev_bits_fis {
+ u8 fis_type; /* 0xA1*/
+ u8 n_i_pmport;
+ /* b7 : n Bit. Notification bit. If set device needs attention. */
+ /* b6 : i Bit. Interrupt Bit */
+ /* b5-b4: reserved2 */
+ /* b3-b0: PM Port */
+ u8 status;
+ u8 error;
+ u32 _r_a;
+} __attribute__ ((packed));
+/* PIO setup FIS - device to host */
+struct pio_setup_fis {
+ u8 fis_type; /* 0x5f */
+ u8 i_d_pmPort;
+ /* b7 : reserved */
+ /* b6 : i bit. Interrupt bit */
+ /* b5 : d bit. data transfer direction. set to 1 for device to host
+ xfer */
+ /* b4 : reserved */
+ /* b3-b0: PM Port */
+ u8 status;
+ u8 error;
+ u8 lbal;
+ u8 lbam;
+ u8 lbah;
+ u8 device;
+ u8 lbal_exp;
+ u8 lbam_exp;
+ u8 lbah_exp;
+ u8 _r_a;
+ u8 sector_count;
+ u8 sector_count_exp;
+ u8 _r_b;
+ u8 e_status;
+ u8 _r_c[2];
+ u8 transfer_count;
+} __attribute__ ((packed));
+
+/*
+ * brief the data structure of SATA Completion Response
+ * use to discribe the sata task response (64 bytes)
+ */
+struct sata_completion_resp {
+ __le32 tag;
+ __le32 status;
+ __le32 param;
+ u32 sata_resp[12];
+} __attribute__((packed, aligned(4)));
+
+
+/*
+ * brief the data structure of SAS HW Event Notification
+ * use to alert the host about the hardware event(64 bytes)
+ */
+struct hw_event_resp {
+ __le32 lr_evt_status_phyid_portid;
+ __le32 evt_param;
+ __le32 npip_portstate;
+ struct sas_identify_frame sas_identify;
+ struct dev_to_host_fis sata_fis;
+} __attribute__((packed, aligned(4)));
+
+
+/*
+ * brief the data structure of REGISTER DEVICE Command
+ * use to describe MPI REGISTER DEVICE Command (64 bytes)
+ */
+
+struct reg_dev_req {
+ __le32 tag;
+ __le32 phyid_portid;
+ __le32 dtype_dlr_retry;
+ __le32 firstburstsize_ITNexustimeout;
+ u32 sas_addr_hi;
+ u32 sas_addr_low;
+ __le32 upper_device_id;
+ u32 reserved[8];
+} __attribute__((packed, aligned(4)));
+
+
+/*
+ * brief the data structure of DEREGISTER DEVICE Command
+ * use to request spc to remove all internal resources associated
+ * with the device id (64 bytes)
+ */
+
+struct dereg_dev_req {
+ __le32 tag;
+ __le32 device_id;
+ u32 reserved[13];
+} __attribute__((packed, aligned(4)));
+
+
+/*
+ * brief the data structure of DEVICE_REGISTRATION Response
+ * use to notify the completion of the device registration (64 bytes)
+ */
+
+struct dev_reg_resp {
+ __le32 tag;
+ __le32 status;
+ __le32 device_id;
+ u32 reserved[12];
+} __attribute__((packed, aligned(4)));
+
+
+/*
+ * brief the data structure of Local PHY Control Command
+ * use to issue PHY CONTROL to local phy (64 bytes)
+ */
+struct local_phy_ctl_req {
+ __le32 tag;
+ __le32 phyop_phyid;
+ u32 reserved1[13];
+} __attribute__((packed, aligned(4)));
+
+
+/**
+ * brief the data structure of Local Phy Control Response
+ * use to describe MPI Local Phy Control Response (64 bytes)
+ */
+struct local_phy_ctl_resp {
+ __le32 tag;
+ __le32 phyop_phyid;
+ __le32 status;
+ u32 reserved[12];
+} __attribute__((packed, aligned(4)));
+
+
+#define OP_BITS 0x0000FF00
+#define ID_BITS 0x0000000F
+
+/*
+ * brief the data structure of PORT Control Command
+ * use to control port properties (64 bytes)
+ */
+
+struct port_ctl_req {
+ __le32 tag;
+ __le32 portop_portid;
+ __le32 param0;
+ __le32 param1;
+ u32 reserved1[11];
+} __attribute__((packed, aligned(4)));
+
+
+/*
+ * brief the data structure of HW Event Ack Command
+ * use to acknowledge receive HW event (64 bytes)
+ */
+
+struct hw_event_ack_req {
+ __le32 tag;
+ __le32 sea_phyid_portid;
+ __le32 param0;
+ __le32 param1;
+ u32 reserved1[11];
+} __attribute__((packed, aligned(4)));
+
+
+/*
+ * brief the data structure of SSP Completion Response
+ * use to indicate a SSP Completion (n bytes)
+ */
+struct ssp_completion_resp {
+ __le32 tag;
+ __le32 status;
+ __le32 param;
+ __le32 ssptag_rescv_rescpad;
+ struct ssp_response_iu ssp_resp_iu;
+ __le32 residual_count;
+} __attribute__((packed, aligned(4)));
+
+
+#define SSP_RESCV_BIT 0x00010000
+
+/*
+ * brief the data structure of SATA EVNET esponse
+ * use to indicate a SATA Completion (64 bytes)
+ */
+
+struct sata_event_resp {
+ __le32 tag;
+ __le32 event;
+ __le32 port_id;
+ __le32 device_id;
+ u32 reserved[11];
+} __attribute__((packed, aligned(4)));
+
+/*
+ * brief the data structure of SSP EVNET esponse
+ * use to indicate a SSP Completion (64 bytes)
+ */
+
+struct ssp_event_resp {
+ __le32 tag;
+ __le32 event;
+ __le32 port_id;
+ __le32 device_id;
+ u32 reserved[11];
+} __attribute__((packed, aligned(4)));
+
+/**
+ * brief the data structure of General Event Notification Response
+ * use to describe MPI General Event Notification Response (64 bytes)
+ */
+struct general_event_resp {
+ __le32 status;
+ __le32 inb_IOMB_payload[14];
+} __attribute__((packed, aligned(4)));
+
+
+#define GENERAL_EVENT_PAYLOAD 14
+#define OPCODE_BITS 0x00000fff
+
+/*
+ * brief the data structure of SMP Request Command
+ * use to describe MPI SMP REQUEST Command (64 bytes)
+ */
+struct smp_req {
+ __le32 tag;
+ __le32 device_id;
+ __le32 len_ip_ir;
+ /* Bits [0] - Indirect response */
+ /* Bits [1] - Indirect Payload */
+ /* Bits [15:2] - Reserved */
+ /* Bits [23:16] - direct payload Len */
+ /* Bits [31:24] - Reserved */
+ u8 smp_req16[16];
+ union {
+ u8 smp_req[32];
+ struct {
+ __le64 long_req_addr;/* sg dma address, LE */
+ __le32 long_req_size;/* LE */
+ u32 _r_a;
+ __le64 long_resp_addr;/* sg dma address, LE */
+ __le32 long_resp_size;/* LE */
+ u32 _r_b;
+ } long_smp_req;/* sequencer extension */
+ };
+} __attribute__((packed, aligned(4)));
+/*
+ * brief the data structure of SMP Completion Response
+ * use to describe MPI SMP Completion Response (64 bytes)
+ */
+struct smp_completion_resp {
+ __le32 tag;
+ __le32 status;
+ __le32 param;
+ __le32 _r_a[12];
+} __attribute__((packed, aligned(4)));
+
+/*
+ *brief the data structure of SSP SMP SATA Abort Command
+ * use to describe MPI SSP SMP & SATA Abort Command (64 bytes)
+ */
+struct task_abort_req {
+ __le32 tag;
+ __le32 device_id;
+ __le32 tag_to_abort;
+ __le32 abort_all;
+ u32 reserved[11];
+} __attribute__((packed, aligned(4)));
+
+/* These flags used for SSP SMP & SATA Abort */
+#define ABORT_MASK 0x3
+#define ABORT_SINGLE 0x0
+#define ABORT_ALL 0x1
+
+/**
+ * brief the data structure of SSP SATA SMP Abort Response
+ * use to describe SSP SMP & SATA Abort Response ( 64 bytes)
+ */
+struct task_abort_resp {
+ __le32 tag;
+ __le32 status;
+ __le32 scp;
+ u32 reserved[12];
+} __attribute__((packed, aligned(4)));
+
+
+/**
+ * brief the data structure of SAS Diagnostic Start/End Command
+ * use to describe MPI SAS Diagnostic Start/End Command (64 bytes)
+ */
+struct sas_diag_start_end_req {
+ __le32 tag;
+ __le32 operation_phyid;
+ u32 reserved[13];
+} __attribute__((packed, aligned(4)));
+
+
+/**
+ * brief the data structure of SAS Diagnostic Execute Command
+ * use to describe MPI SAS Diagnostic Execute Command (64 bytes)
+ */
+struct sas_diag_execute_req{
+ __le32 tag;
+ __le32 cmdtype_cmddesc_phyid;
+ __le32 pat1_pat2;
+ __le32 threshold;
+ __le32 codepat_errmsk;
+ __le32 pmon;
+ __le32 pERF1CTL;
+ u32 reserved[8];
+} __attribute__((packed, aligned(4)));
+
+
+#define SAS_DIAG_PARAM_BYTES 24
+
+/*
+ * brief the data structure of Set Device State Command
+ * use to describe MPI Set Device State Command (64 bytes)
+ */
+struct set_dev_state_req {
+ __le32 tag;
+ __le32 device_id;
+ __le32 nds;
+ u32 reserved[12];
+} __attribute__((packed, aligned(4)));
+
+/*
+ * brief the data structure of sas_re_initialization
+ */
+struct sas_re_initialization_req {
+
+ __le32 tag;
+ __le32 SSAHOLT;/* bit29-set max port;
+ ** bit28-set open reject cmd retries.
+ ** bit27-set open reject data retries.
+ ** bit26-set open reject option, remap:1 or not:0.
+ ** bit25-set sata head of line time out.
+ */
+ __le32 reserved_maxPorts;
+ __le32 open_reject_cmdretries_data_retries;/* cmd retries: 31-bit16;
+ * data retries: bit15-bit0.
+ */
+ __le32 sata_hol_tmo;
+ u32 reserved1[10];
+} __attribute__((packed, aligned(4)));
+
+/*
+ * brief the data structure of SATA Start Command
+ * use to describe MPI SATA IO Start Command (64 bytes)
+ */
+
+struct sata_start_req {
+ __le32 tag;
+ __le32 device_id;
+ __le32 data_len;
+ __le32 ncqtag_atap_dir_m;
+ struct host_to_dev_fis sata_fis;
+ u32 reserved1;
+ u32 reserved2;
+ u32 addr_low;
+ u32 addr_high;
+ __le32 len;
+ __le32 esgl;
+} __attribute__((packed, aligned(4)));
+
+/**
+ * brief the data structure of SSP INI TM Start Command
+ * use to describe MPI SSP INI TM Start Command (64 bytes)
+ */
+struct ssp_ini_tm_start_req {
+ __le32 tag;
+ __le32 device_id;
+ __le32 relate_tag;
+ __le32 tmf;
+ u8 lun[8];
+ __le32 ds_ads_m;
+ u32 reserved[8];
+} __attribute__((packed, aligned(4)));
+
+
+struct ssp_info_unit {
+ u8 lun[8];/* SCSI Logical Unit Number */
+ u8 reserved1;/* reserved */
+ u8 efb_prio_attr;
+ /* B7 : enabledFirstBurst */
+ /* B6-3 : taskPriority */
+ /* B2-0 : taskAttribute */
+ u8 reserved2; /* reserved */
+ u8 additional_cdb_len;
+ /* B7-2 : additional_cdb_len */
+ /* B1-0 : reserved */
+ u8 cdb[16];/* The SCSI CDB up to 16 bytes length */
+} __attribute__((packed, aligned(4)));
+
+
+/**
+ * brief the data structure of SSP INI IO Start Command
+ * use to describe MPI SSP INI IO Start Command (64 bytes)
+ */
+struct ssp_ini_io_start_req {
+ __le32 tag;
+ __le32 device_id;
+ __le32 data_len;
+ __le32 dir_m_tlr;
+ struct ssp_info_unit ssp_iu;
+ __le32 addr_low;
+ __le32 addr_high;
+ __le32 len;
+ __le32 esgl;
+} __attribute__((packed, aligned(4)));
+
+
+/**
+ * brief the data structure of Firmware download
+ * use to describe MPI FW DOWNLOAD Command (64 bytes)
+ */
+struct fw_flash_Update_req {
+ __le32 tag;
+ __le32 cur_image_offset;
+ __le32 cur_image_len;
+ __le32 total_image_len;
+ u32 reserved0[7];
+ __le32 sgl_addr_lo;
+ __le32 sgl_addr_hi;
+ __le32 len;
+ __le32 ext_reserved;
+} __attribute__((packed, aligned(4)));
+
+
+#define FWFLASH_IOMB_RESERVED_LEN 0x07
+/**
+ * brief the data structure of FW_FLASH_UPDATE Response
+ * use to describe MPI FW_FLASH_UPDATE Response (64 bytes)
+ *
+ */
+struct fw_flash_Update_resp {
+ dma_addr_t tag;
+ __le32 status;
+ u32 reserved[13];
+} __attribute__((packed, aligned(4)));
+
+
+/**
+ * brief the data structure of Get NVM Data Command
+ * use to get data from NVM in HBA(64 bytes)
+ */
+struct get_nvm_data_req {
+ __le32 tag;
+ __le32 len_ir_vpdd;
+ __le32 vpd_offset;
+ u32 reserved[8];
+ __le32 resp_addr_lo;
+ __le32 resp_addr_hi;
+ __le32 resp_len;
+ u32 reserved1;
+} __attribute__((packed, aligned(4)));
+
+
+struct set_nvm_data_req {
+ __le32 tag;
+ __le32 len_ir_vpdd;
+ __le32 vpd_offset;
+ u32 reserved[8];
+ __le32 resp_addr_lo;
+ __le32 resp_addr_hi;
+ __le32 resp_len;
+ u32 reserved1;
+} __attribute__((packed, aligned(4)));
+
+
+#define TWI_DEVICE 0x0
+#define C_SEEPROM 0x1
+#define VPD_FLASH 0x4
+#define AAP1_RDUMP 0x5
+#define IOP_RDUMP 0x6
+#define EXPAN_ROM 0x7
+
+#define IPMode 0x80000000
+#define NVMD_TYPE 0x0000000F
+#define NVMD_STAT 0x0000FFFF
+#define NVMD_LEN 0xFF000000
+/**
+ * brief the data structure of Get NVMD Data Response
+ * use to describe MPI Get NVMD Data Response (64 bytes)
+ */
+struct get_nvm_data_resp {
+ __le32 tag;
+ __le32 ir_tda_bn_dps_das_nvm;
+ __le32 dlen_status;
+ __le32 nvm_data[12];
+} __attribute__((packed, aligned(4)));
+
+
+/**
+ * brief the data structure of SAS Diagnostic Start/End Response
+ * use to describe MPI SAS Diagnostic Start/End Response (64 bytes)
+ *
+ */
+struct sas_diag_start_end_resp {
+ __le32 tag;
+ __le32 status;
+ u32 reserved[13];
+} __attribute__((packed, aligned(4)));
+
+
+/**
+ * brief the data structure of SAS Diagnostic Execute Response
+ * use to describe MPI SAS Diagnostic Execute Response (64 bytes)
+ *
+ */
+struct sas_diag_execute_resp {
+ __le32 tag;
+ __le32 cmdtype_cmddesc_phyid;
+ __le32 Status;
+ __le32 ReportData;
+ u32 reserved[11];
+} __attribute__((packed, aligned(4)));
+
+
+/**
+ * brief the data structure of Set Device State Response
+ * use to describe MPI Set Device State Response (64 bytes)
+ *
+ */
+struct set_dev_state_resp {
+ __le32 tag;
+ __le32 status;
+ __le32 device_id;
+ __le32 pds_nds;
+ u32 reserved[11];
+} __attribute__((packed, aligned(4)));
+
+
+#define NDS_BITS 0x0F
+#define PDS_BITS 0xF0
+
+/*
+ * HW Events type
+ */
+
+#define HW_EVENT_RESET_START 0x01
+#define HW_EVENT_CHIP_RESET_COMPLETE 0x02
+#define HW_EVENT_PHY_STOP_STATUS 0x03
+#define HW_EVENT_SAS_PHY_UP 0x04
+#define HW_EVENT_SATA_PHY_UP 0x05
+#define HW_EVENT_SATA_SPINUP_HOLD 0x06
+#define HW_EVENT_PHY_DOWN 0x07
+#define HW_EVENT_PORT_INVALID 0x08
+#define HW_EVENT_BROADCAST_CHANGE 0x09
+#define HW_EVENT_PHY_ERROR 0x0A
+#define HW_EVENT_BROADCAST_SES 0x0B
+#define HW_EVENT_INBOUND_CRC_ERROR 0x0C
+#define HW_EVENT_HARD_RESET_RECEIVED 0x0D
+#define HW_EVENT_MALFUNCTION 0x0E
+#define HW_EVENT_ID_FRAME_TIMEOUT 0x0F
+#define HW_EVENT_BROADCAST_EXP 0x10
+#define HW_EVENT_PHY_START_STATUS 0x11
+#define HW_EVENT_LINK_ERR_INVALID_DWORD 0x12
+#define HW_EVENT_LINK_ERR_DISPARITY_ERROR 0x13
+#define HW_EVENT_LINK_ERR_CODE_VIOLATION 0x14
+#define HW_EVENT_LINK_ERR_LOSS_OF_DWORD_SYNCH 0x15
+#define HW_EVENT_LINK_ERR_PHY_RESET_FAILED 0x16
+#define HW_EVENT_PORT_RECOVERY_TIMER_TMO 0x17
+#define HW_EVENT_PORT_RECOVER 0x18
+#define HW_EVENT_PORT_RESET_TIMER_TMO 0x19
+#define HW_EVENT_PORT_RESET_COMPLETE 0x20
+#define EVENT_BROADCAST_ASYNCH_EVENT 0x21
+
+/* port state */
+#define PORT_NOT_ESTABLISHED 0x00
+#define PORT_VALID 0x01
+#define PORT_LOSTCOMM 0x02
+#define PORT_IN_RESET 0x04
+#define PORT_INVALID 0x08
+
+/*
+ * SSP/SMP/SATA IO Completion Status values
+ */
+
+#define IO_SUCCESS 0x00
+#define IO_ABORTED 0x01
+#define IO_OVERFLOW 0x02
+#define IO_UNDERFLOW 0x03
+#define IO_FAILED 0x04
+#define IO_ABORT_RESET 0x05
+#define IO_NOT_VALID 0x06
+#define IO_NO_DEVICE 0x07
+#define IO_ILLEGAL_PARAMETER 0x08
+#define IO_LINK_FAILURE 0x09
+#define IO_PROG_ERROR 0x0A
+#define IO_EDC_IN_ERROR 0x0B
+#define IO_EDC_OUT_ERROR 0x0C
+#define IO_ERROR_HW_TIMEOUT 0x0D
+#define IO_XFER_ERROR_BREAK 0x0E
+#define IO_XFER_ERROR_PHY_NOT_READY 0x0F
+#define IO_OPEN_CNX_ERROR_PROTOCOL_NOT_SUPPORTED 0x10
+#define IO_OPEN_CNX_ERROR_ZONE_VIOLATION 0x11
+#define IO_OPEN_CNX_ERROR_BREAK 0x12
+#define IO_OPEN_CNX_ERROR_IT_NEXUS_LOSS 0x13
+#define IO_OPEN_CNX_ERROR_BAD_DESTINATION 0x14
+#define IO_OPEN_CNX_ERROR_CONNECTION_RATE_NOT_SUPPORTED 0x15
+#define IO_OPEN_CNX_ERROR_STP_RESOURCES_BUSY 0x16
+#define IO_OPEN_CNX_ERROR_WRONG_DESTINATION 0x17
+#define IO_OPEN_CNX_ERROR_UNKNOWN_ERROR 0x18
+#define IO_XFER_ERROR_NAK_RECEIVED 0x19
+#define IO_XFER_ERROR_ACK_NAK_TIMEOUT 0x1A
+#define IO_XFER_ERROR_PEER_ABORTED 0x1B
+#define IO_XFER_ERROR_RX_FRAME 0x1C
+#define IO_XFER_ERROR_DMA 0x1D
+#define IO_XFER_ERROR_CREDIT_TIMEOUT 0x1E
+#define IO_XFER_ERROR_SATA_LINK_TIMEOUT 0x1F
+#define IO_XFER_ERROR_SATA 0x20
+#define IO_XFER_ERROR_ABORTED_DUE_TO_SRST 0x22
+#define IO_XFER_ERROR_REJECTED_NCQ_MODE 0x21
+#define IO_XFER_ERROR_ABORTED_NCQ_MODE 0x23
+#define IO_XFER_OPEN_RETRY_TIMEOUT 0x24
+#define IO_XFER_SMP_RESP_CONNECTION_ERROR 0x25
+#define IO_XFER_ERROR_UNEXPECTED_PHASE 0x26
+#define IO_XFER_ERROR_XFER_RDY_OVERRUN 0x27
+#define IO_XFER_ERROR_XFER_RDY_NOT_EXPECTED 0x28
+
+#define IO_XFER_ERROR_CMD_ISSUE_ACK_NAK_TIMEOUT 0x30
+#define IO_XFER_ERROR_CMD_ISSUE_BREAK_BEFORE_ACK_NAK 0x31
+#define IO_XFER_ERROR_CMD_ISSUE_PHY_DOWN_BEFORE_ACK_NAK 0x32
+
+#define IO_XFER_ERROR_OFFSET_MISMATCH 0x34
+#define IO_XFER_ERROR_XFER_ZERO_DATA_LEN 0x35
+#define IO_XFER_CMD_FRAME_ISSUED 0x36
+#define IO_ERROR_INTERNAL_SMP_RESOURCE 0x37
+#define IO_PORT_IN_RESET 0x38
+#define IO_DS_NON_OPERATIONAL 0x39
+#define IO_DS_IN_RECOVERY 0x3A
+#define IO_TM_TAG_NOT_FOUND 0x3B
+#define IO_XFER_PIO_SETUP_ERROR 0x3C
+#define IO_SSP_EXT_IU_ZERO_LEN_ERROR 0x3D
+#define IO_DS_IN_ERROR 0x3E
+#define IO_OPEN_CNX_ERROR_HW_RESOURCE_BUSY 0x3F
+#define IO_ABORT_IN_PROGRESS 0x40
+#define IO_ABORT_DELAYED 0x41
+#define IO_INVALID_LENGTH 0x42
+
+/* WARNING: This error code must always be the last number.
+ * If you add error code, modify this code also
+ * It is used as an index
+ */
+#define IO_ERROR_UNKNOWN_GENERIC 0x43
+
+/* MSGU CONFIGURATION TABLE*/
+
+#define SPC_MSGU_CFG_TABLE_UPDATE 0x01/* Inbound doorbell bit0 */
+#define SPC_MSGU_CFG_TABLE_RESET 0x02/* Inbound doorbell bit1 */
+#define SPC_MSGU_CFG_TABLE_FREEZE 0x04/* Inbound doorbell bit2 */
+#define SPC_MSGU_CFG_TABLE_UNFREEZE 0x08/* Inbound doorbell bit4 */
+#define MSGU_IBDB_SET 0x04
+#define MSGU_HOST_INT_STATUS 0x08
+#define MSGU_HOST_INT_MASK 0x0C
+#define MSGU_IOPIB_INT_STATUS 0x18
+#define MSGU_IOPIB_INT_MASK 0x1C
+#define MSGU_IBDB_CLEAR 0x20/* RevB - Host not use */
+#define MSGU_MSGU_CONTROL 0x24
+#define MSGU_ODR 0x3C/* RevB */
+#define MSGU_ODCR 0x40/* RevB */
+#define MSGU_SCRATCH_PAD_0 0x44
+#define MSGU_SCRATCH_PAD_1 0x48
+#define MSGU_SCRATCH_PAD_2 0x4C
+#define MSGU_SCRATCH_PAD_3 0x50
+#define MSGU_HOST_SCRATCH_PAD_0 0x54
+#define MSGU_HOST_SCRATCH_PAD_1 0x58
+#define MSGU_HOST_SCRATCH_PAD_2 0x5C
+#define MSGU_HOST_SCRATCH_PAD_3 0x60
+#define MSGU_HOST_SCRATCH_PAD_4 0x64
+#define MSGU_HOST_SCRATCH_PAD_5 0x68
+#define MSGU_HOST_SCRATCH_PAD_6 0x6C
+#define MSGU_HOST_SCRATCH_PAD_7 0x70
+#define MSGU_ODMR 0x74/* RevB */
+
+/* bit definition for ODMR register */
+#define ODMR_MASK_ALL 0xFFFFFFFF/* mask all
+ interrupt vector */
+#define ODMR_CLEAR_ALL 0/* clear all
+ interrupt vector */
+/* bit definition for ODCR register */
+#define ODCR_CLEAR_ALL 0xFFFFFFFF /* mask all
+ interrupt vector*/
+/* MSIX Interupts */
+#define MSIX_TABLE_OFFSET 0x2000
+#define MSIX_TABLE_ELEMENT_SIZE 0x10
+#define MSIX_INTERRUPT_CONTROL_OFFSET 0xC
+#define MSIX_TABLE_BASE (MSIX_TABLE_OFFSET + MSIX_INTERRUPT_CONTROL_OFFSET)
+#define MSIX_INTERRUPT_DISABLE 0x1
+#define MSIX_INTERRUPT_ENABLE 0x0
+
+
+/* state definition for Scratch Pad1 register */
+#define SCRATCH_PAD1_POR 0x00 /* power on reset state */
+#define SCRATCH_PAD1_SFR 0x01 /* soft reset state */
+#define SCRATCH_PAD1_ERR 0x02 /* error state */
+#define SCRATCH_PAD1_RDY 0x03 /* ready state */
+#define SCRATCH_PAD1_RST 0x04 /* soft reset toggle flag */
+#define SCRATCH_PAD1_AAP1RDY_RST 0x08 /* AAP1 ready for soft reset */
+#define SCRATCH_PAD1_STATE_MASK 0xFFFFFFF0 /* ScratchPad1
+ Mask, bit1-0 State, bit2 Soft Reset, bit3 FW RDY for Soft Reset */
+#define SCRATCH_PAD1_RESERVED 0x000003F8 /* Scratch Pad1
+ Reserved bit 3 to 9 */
+
+ /* state definition for Scratch Pad2 register */
+#define SCRATCH_PAD2_POR 0x00 /* power on state */
+#define SCRATCH_PAD2_SFR 0x01 /* soft reset state */
+#define SCRATCH_PAD2_ERR 0x02 /* error state */
+#define SCRATCH_PAD2_RDY 0x03 /* ready state */
+#define SCRATCH_PAD2_FWRDY_RST 0x04 /* FW ready for soft reset flag*/
+#define SCRATCH_PAD2_IOPRDY_RST 0x08 /* IOP ready for soft reset */
+#define SCRATCH_PAD2_STATE_MASK 0xFFFFFFF4 /* ScratchPad 2
+ Mask, bit1-0 State */
+#define SCRATCH_PAD2_RESERVED 0x000003FC /* Scratch Pad1
+ Reserved bit 2 to 9 */
+
+#define SCRATCH_PAD_ERROR_MASK 0xFFFFFC00 /* Error mask bits */
+#define SCRATCH_PAD_STATE_MASK 0x00000003 /* State Mask bits */
+
+/* main configuration offset - byte offset */
+#define MAIN_SIGNATURE_OFFSET 0x00/* DWORD 0x00 */
+#define MAIN_INTERFACE_REVISION 0x04/* DWORD 0x01 */
+#define MAIN_FW_REVISION 0x08/* DWORD 0x02 */
+#define MAIN_MAX_OUTSTANDING_IO_OFFSET 0x0C/* DWORD 0x03 */
+#define MAIN_MAX_SGL_OFFSET 0x10/* DWORD 0x04 */
+#define MAIN_CNTRL_CAP_OFFSET 0x14/* DWORD 0x05 */
+#define MAIN_GST_OFFSET 0x18/* DWORD 0x06 */
+#define MAIN_IBQ_OFFSET 0x1C/* DWORD 0x07 */
+#define MAIN_OBQ_OFFSET 0x20/* DWORD 0x08 */
+#define MAIN_IQNPPD_HPPD_OFFSET 0x24/* DWORD 0x09 */
+#define MAIN_OB_HW_EVENT_PID03_OFFSET 0x28/* DWORD 0x0A */
+#define MAIN_OB_HW_EVENT_PID47_OFFSET 0x2C/* DWORD 0x0B */
+#define MAIN_OB_NCQ_EVENT_PID03_OFFSET 0x30/* DWORD 0x0C */
+#define MAIN_OB_NCQ_EVENT_PID47_OFFSET 0x34/* DWORD 0x0D */
+#define MAIN_TITNX_EVENT_PID03_OFFSET 0x38/* DWORD 0x0E */
+#define MAIN_TITNX_EVENT_PID47_OFFSET 0x3C/* DWORD 0x0F */
+#define MAIN_OB_SSP_EVENT_PID03_OFFSET 0x40/* DWORD 0x10 */
+#define MAIN_OB_SSP_EVENT_PID47_OFFSET 0x44/* DWORD 0x11 */
+#define MAIN_OB_SMP_EVENT_PID03_OFFSET 0x48/* DWORD 0x12 */
+#define MAIN_OB_SMP_EVENT_PID47_OFFSET 0x4C/* DWORD 0x13 */
+#define MAIN_EVENT_LOG_ADDR_HI 0x50/* DWORD 0x14 */
+#define MAIN_EVENT_LOG_ADDR_LO 0x54/* DWORD 0x15 */
+#define MAIN_EVENT_LOG_BUFF_SIZE 0x58/* DWORD 0x16 */
+#define MAIN_EVENT_LOG_OPTION 0x5C/* DWORD 0x17 */
+#define MAIN_IOP_EVENT_LOG_ADDR_HI 0x60/* DWORD 0x18 */
+#define MAIN_IOP_EVENT_LOG_ADDR_LO 0x64/* DWORD 0x19 */
+#define MAIN_IOP_EVENT_LOG_BUFF_SIZE 0x68/* DWORD 0x1A */
+#define MAIN_IOP_EVENT_LOG_OPTION 0x6C/* DWORD 0x1B */
+#define MAIN_FATAL_ERROR_INTERRUPT 0x70/* DWORD 0x1C */
+#define MAIN_FATAL_ERROR_RDUMP0_OFFSET 0x74/* DWORD 0x1D */
+#define MAIN_FATAL_ERROR_RDUMP0_LENGTH 0x78/* DWORD 0x1E */
+#define MAIN_FATAL_ERROR_RDUMP1_OFFSET 0x7C/* DWORD 0x1F */
+#define MAIN_FATAL_ERROR_RDUMP1_LENGTH 0x80/* DWORD 0x20 */
+#define MAIN_HDA_FLAGS_OFFSET 0x84/* DWORD 0x21 */
+#define MAIN_ANALOG_SETUP_OFFSET 0x88/* DWORD 0x22 */
+
+/* Gereral Status Table offset - byte offset */
+#define GST_GSTLEN_MPIS_OFFSET 0x00
+#define GST_IQ_FREEZE_STATE0_OFFSET 0x04
+#define GST_IQ_FREEZE_STATE1_OFFSET 0x08
+#define GST_MSGUTCNT_OFFSET 0x0C
+#define GST_IOPTCNT_OFFSET 0x10
+#define GST_PHYSTATE_OFFSET 0x18
+#define GST_PHYSTATE0_OFFSET 0x18
+#define GST_PHYSTATE1_OFFSET 0x1C
+#define GST_PHYSTATE2_OFFSET 0x20
+#define GST_PHYSTATE3_OFFSET 0x24
+#define GST_PHYSTATE4_OFFSET 0x28
+#define GST_PHYSTATE5_OFFSET 0x2C
+#define GST_PHYSTATE6_OFFSET 0x30
+#define GST_PHYSTATE7_OFFSET 0x34
+#define GST_RERRINFO_OFFSET 0x44
+
+/* General Status Table - MPI state */
+#define GST_MPI_STATE_UNINIT 0x00
+#define GST_MPI_STATE_INIT 0x01
+#define GST_MPI_STATE_TERMINATION 0x02
+#define GST_MPI_STATE_ERROR 0x03
+#define GST_MPI_STATE_MASK 0x07
+
+#define MBIC_NMI_ENABLE_VPE0_IOP 0x000418
+#define MBIC_NMI_ENABLE_VPE0_AAP1 0x000418
+/* PCIE registers - BAR2(0x18), BAR1(win) 0x010000 */
+#define PCIE_EVENT_INTERRUPT_ENABLE 0x003040
+#define PCIE_EVENT_INTERRUPT 0x003044
+#define PCIE_ERROR_INTERRUPT_ENABLE 0x003048
+#define PCIE_ERROR_INTERRUPT 0x00304C
+/* signature defintion for host scratch pad0 register */
+#define SPC_SOFT_RESET_SIGNATURE 0x252acbcd
+/* Signature for Soft Reset */
+
+/* SPC Reset register - BAR4(0x20), BAR2(win) (need dynamic mapping) */
+#define SPC_REG_RESET 0x000000/* reset register */
+
+/* bit difination for SPC_RESET register */
+#define SPC_REG_RESET_OSSP 0x00000001
+#define SPC_REG_RESET_RAAE 0x00000002
+#define SPC_REG_RESET_PCS_SPBC 0x00000004
+#define SPC_REG_RESET_PCS_IOP_SS 0x00000008
+#define SPC_REG_RESET_PCS_AAP1_SS 0x00000010
+#define SPC_REG_RESET_PCS_AAP2_SS 0x00000020
+#define SPC_REG_RESET_PCS_LM 0x00000040
+#define SPC_REG_RESET_PCS 0x00000080
+#define SPC_REG_RESET_GSM 0x00000100
+#define SPC_REG_RESET_DDR2 0x00010000
+#define SPC_REG_RESET_BDMA_CORE 0x00020000
+#define SPC_REG_RESET_BDMA_SXCBI 0x00040000
+#define SPC_REG_RESET_PCIE_AL_SXCBI 0x00080000
+#define SPC_REG_RESET_PCIE_PWR 0x00100000
+#define SPC_REG_RESET_PCIE_SFT 0x00200000
+#define SPC_REG_RESET_PCS_SXCBI 0x00400000
+#define SPC_REG_RESET_LMS_SXCBI 0x00800000
+#define SPC_REG_RESET_PMIC_SXCBI 0x01000000
+#define SPC_REG_RESET_PMIC_CORE 0x02000000
+#define SPC_REG_RESET_PCIE_PC_SXCBI 0x04000000
+#define SPC_REG_RESET_DEVICE 0x80000000
+
+/* registers for BAR Shifting - BAR2(0x18), BAR1(win) */
+#define SPC_IBW_AXI_TRANSLATION_LOW 0x003258
+
+#define MBIC_AAP1_ADDR_BASE 0x060000
+#define MBIC_IOP_ADDR_BASE 0x070000
+#define GSM_ADDR_BASE 0x0700000
+/* Dynamic map through Bar4 - 0x00700000 */
+#define GSM_CONFIG_RESET 0x00000000
+#define RAM_ECC_DB_ERR 0x00000018
+#define GSM_READ_ADDR_PARITY_INDIC 0x00000058
+#define GSM_WRITE_ADDR_PARITY_INDIC 0x00000060
+#define GSM_WRITE_DATA_PARITY_INDIC 0x00000068
+#define GSM_READ_ADDR_PARITY_CHECK 0x00000038
+#define GSM_WRITE_ADDR_PARITY_CHECK 0x00000040
+#define GSM_WRITE_DATA_PARITY_CHECK 0x00000048
+
+#define RB6_ACCESS_REG 0x6A0000
+#define HDAC_EXEC_CMD 0x0002
+#define HDA_C_PA 0xcb
+#define HDA_SEQ_ID_BITS 0x00ff0000
+#define HDA_GSM_OFFSET_BITS 0x00FFFFFF
+#define MBIC_AAP1_ADDR_BASE 0x060000
+#define MBIC_IOP_ADDR_BASE 0x070000
+#define GSM_ADDR_BASE 0x0700000
+#define SPC_TOP_LEVEL_ADDR_BASE 0x000000
+#define GSM_CONFIG_RESET_VALUE 0x00003b00
+#define GPIO_ADDR_BASE 0x00090000
+#define GPIO_GPIO_0_0UTPUT_CTL_OFFSET 0x0000010c
+
+/* RB6 offset */
+#define SPC_RB6_OFFSET 0x80C0
+/* Magic number of soft reset for RB6 */
+#define RB6_MAGIC_NUMBER_RST 0x1234
+
+/* Device Register status */
+#define DEVREG_SUCCESS 0x00
+#define DEVREG_FAILURE_OUT_OF_RESOURCE 0x01
+#define DEVREG_FAILURE_DEVICE_ALREADY_REGISTERED 0x02
+#define DEVREG_FAILURE_INVALID_PHY_ID 0x03
+#define DEVREG_FAILURE_PHY_ID_ALREADY_REGISTERED 0x04
+#define DEVREG_FAILURE_PORT_ID_OUT_OF_RANGE 0x05
+#define DEVREG_FAILURE_PORT_NOT_VALID_STATE 0x06
+#define DEVREG_FAILURE_DEVICE_TYPE_NOT_VALID 0x07
+
+#endif
+
diff --git a/drivers/scsi/pm8001/pm8001_init.c b/drivers/scsi/pm8001/pm8001_init.c
new file mode 100644
index 00000000000..42ebe725d5a
--- /dev/null
+++ b/drivers/scsi/pm8001/pm8001_init.c
@@ -0,0 +1,891 @@
+/*
+ * PMC-Sierra SPC 8001 SAS/SATA based host adapters driver
+ *
+ * Copyright (c) 2008-2009 USI Co., Ltd.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions, and the following disclaimer,
+ * without modification.
+ * 2. Redistributions in binary form must reproduce at minimum a disclaimer
+ * substantially similar to the "NO WARRANTY" disclaimer below
+ * ("Disclaimer") and any redistribution must be conditioned upon
+ * including a substantially similar Disclaimer requirement for further
+ * binary redistribution.
+ * 3. Neither the names of the above-listed copyright holders nor the names
+ * of any contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2 as published by the Free
+ * Software Foundation.
+ *
+ * NO WARRANTY
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ */
+
+#include "pm8001_sas.h"
+#include "pm8001_chips.h"
+
+static struct scsi_transport_template *pm8001_stt;
+
+static const struct pm8001_chip_info pm8001_chips[] = {
+ [chip_8001] = { 8, &pm8001_8001_dispatch,},
+};
+static int pm8001_id;
+
+LIST_HEAD(hba_list);
+
+/**
+ * The main structure which LLDD must register for scsi core.
+ */
+static struct scsi_host_template pm8001_sht = {
+ .module = THIS_MODULE,
+ .name = DRV_NAME,
+ .queuecommand = sas_queuecommand,
+ .target_alloc = sas_target_alloc,
+ .slave_configure = pm8001_slave_configure,
+ .slave_destroy = sas_slave_destroy,
+ .scan_finished = pm8001_scan_finished,
+ .scan_start = pm8001_scan_start,
+ .change_queue_depth = sas_change_queue_depth,
+ .change_queue_type = sas_change_queue_type,
+ .bios_param = sas_bios_param,
+ .can_queue = 1,
+ .cmd_per_lun = 1,
+ .this_id = -1,
+ .sg_tablesize = SG_ALL,
+ .max_sectors = SCSI_DEFAULT_MAX_SECTORS,
+ .use_clustering = ENABLE_CLUSTERING,
+ .eh_device_reset_handler = sas_eh_device_reset_handler,
+ .eh_bus_reset_handler = sas_eh_bus_reset_handler,
+ .slave_alloc = pm8001_slave_alloc,
+ .target_destroy = sas_target_destroy,
+ .ioctl = sas_ioctl,
+ .shost_attrs = pm8001_host_attrs,
+};
+
+/**
+ * Sas layer call this function to execute specific task.
+ */
+static struct sas_domain_function_template pm8001_transport_ops = {
+ .lldd_dev_found = pm8001_dev_found,
+ .lldd_dev_gone = pm8001_dev_gone,
+
+ .lldd_execute_task = pm8001_queue_command,
+ .lldd_control_phy = pm8001_phy_control,
+
+ .lldd_abort_task = pm8001_abort_task,
+ .lldd_abort_task_set = pm8001_abort_task_set,
+ .lldd_clear_aca = pm8001_clear_aca,
+ .lldd_clear_task_set = pm8001_clear_task_set,
+ .lldd_I_T_nexus_reset = pm8001_I_T_nexus_reset,
+ .lldd_lu_reset = pm8001_lu_reset,
+ .lldd_query_task = pm8001_query_task,
+};
+
+/**
+ *pm8001_phy_init - initiate our adapter phys
+ *@pm8001_ha: our hba structure.
+ *@phy_id: phy id.
+ */
+static void __devinit pm8001_phy_init(struct pm8001_hba_info *pm8001_ha,
+ int phy_id)
+{
+ struct pm8001_phy *phy = &pm8001_ha->phy[phy_id];
+ struct asd_sas_phy *sas_phy = &phy->sas_phy;
+ phy->phy_state = 0;
+ phy->pm8001_ha = pm8001_ha;
+ sas_phy->enabled = (phy_id < pm8001_ha->chip->n_phy) ? 1 : 0;
+ sas_phy->class = SAS;
+ sas_phy->iproto = SAS_PROTOCOL_ALL;
+ sas_phy->tproto = 0;
+ sas_phy->type = PHY_TYPE_PHYSICAL;
+ sas_phy->role = PHY_ROLE_INITIATOR;
+ sas_phy->oob_mode = OOB_NOT_CONNECTED;
+ sas_phy->linkrate = SAS_LINK_RATE_UNKNOWN;
+ sas_phy->id = phy_id;
+ sas_phy->sas_addr = &pm8001_ha->sas_addr[0];
+ sas_phy->frame_rcvd = &phy->frame_rcvd[0];
+ sas_phy->ha = (struct sas_ha_struct *)pm8001_ha->shost->hostdata;
+ sas_phy->lldd_phy = phy;
+}
+
+/**
+ *pm8001_free - free hba
+ *@pm8001_ha: our hba structure.
+ *
+ */
+static void pm8001_free(struct pm8001_hba_info *pm8001_ha)
+{
+ int i;
+ struct pm8001_wq *wq;
+
+ if (!pm8001_ha)
+ return;
+
+ for (i = 0; i < USI_MAX_MEMCNT; i++) {
+ if (pm8001_ha->memoryMap.region[i].virt_ptr != NULL) {
+ pci_free_consistent(pm8001_ha->pdev,
+ pm8001_ha->memoryMap.region[i].element_size,
+ pm8001_ha->memoryMap.region[i].virt_ptr,
+ pm8001_ha->memoryMap.region[i].phys_addr);
+ }
+ }
+ PM8001_CHIP_DISP->chip_iounmap(pm8001_ha);
+ if (pm8001_ha->shost)
+ scsi_host_put(pm8001_ha->shost);
+ list_for_each_entry(wq, &pm8001_ha->wq_list, entry)
+ cancel_delayed_work(&wq->work_q);
+ kfree(pm8001_ha->tags);
+ kfree(pm8001_ha);
+}
+
+#ifdef PM8001_USE_TASKLET
+static void pm8001_tasklet(unsigned long opaque)
+{
+ struct pm8001_hba_info *pm8001_ha;
+ pm8001_ha = (struct pm8001_hba_info *)opaque;;
+ if (unlikely(!pm8001_ha))
+ BUG_ON(1);
+ PM8001_CHIP_DISP->isr(pm8001_ha);
+}
+#endif
+
+
+ /**
+ * pm8001_interrupt - when HBA originate a interrupt,we should invoke this
+ * dispatcher to handle each case.
+ * @irq: irq number.
+ * @opaque: the passed general host adapter struct
+ */
+static irqreturn_t pm8001_interrupt(int irq, void *opaque)
+{
+ struct pm8001_hba_info *pm8001_ha;
+ irqreturn_t ret = IRQ_HANDLED;
+ struct sas_ha_struct *sha = opaque;
+ pm8001_ha = sha->lldd_ha;
+ if (unlikely(!pm8001_ha))
+ return IRQ_NONE;
+ if (!PM8001_CHIP_DISP->is_our_interupt(pm8001_ha))
+ return IRQ_NONE;
+#ifdef PM8001_USE_TASKLET
+ tasklet_schedule(&pm8001_ha->tasklet);
+#else
+ ret = PM8001_CHIP_DISP->isr(pm8001_ha);
+#endif
+ return ret;
+}
+
+/**
+ * pm8001_alloc - initiate our hba structure and 6 DMAs area.
+ * @pm8001_ha:our hba structure.
+ *
+ */
+static int __devinit pm8001_alloc(struct pm8001_hba_info *pm8001_ha)
+{
+ int i;
+ spin_lock_init(&pm8001_ha->lock);
+ for (i = 0; i < pm8001_ha->chip->n_phy; i++)
+ pm8001_phy_init(pm8001_ha, i);
+
+ pm8001_ha->tags = kzalloc(PM8001_MAX_CCB, GFP_KERNEL);
+ if (!pm8001_ha->tags)
+ goto err_out;
+ /* MPI Memory region 1 for AAP Event Log for fw */
+ pm8001_ha->memoryMap.region[AAP1].num_elements = 1;
+ pm8001_ha->memoryMap.region[AAP1].element_size = PM8001_EVENT_LOG_SIZE;
+ pm8001_ha->memoryMap.region[AAP1].total_len = PM8001_EVENT_LOG_SIZE;
+ pm8001_ha->memoryMap.region[AAP1].alignment = 32;
+
+ /* MPI Memory region 2 for IOP Event Log for fw */
+ pm8001_ha->memoryMap.region[IOP].num_elements = 1;
+ pm8001_ha->memoryMap.region[IOP].element_size = PM8001_EVENT_LOG_SIZE;
+ pm8001_ha->memoryMap.region[IOP].total_len = PM8001_EVENT_LOG_SIZE;
+ pm8001_ha->memoryMap.region[IOP].alignment = 32;
+
+ /* MPI Memory region 3 for consumer Index of inbound queues */
+ pm8001_ha->memoryMap.region[CI].num_elements = 1;
+ pm8001_ha->memoryMap.region[CI].element_size = 4;
+ pm8001_ha->memoryMap.region[CI].total_len = 4;
+ pm8001_ha->memoryMap.region[CI].alignment = 4;
+
+ /* MPI Memory region 4 for producer Index of outbound queues */
+ pm8001_ha->memoryMap.region[PI].num_elements = 1;
+ pm8001_ha->memoryMap.region[PI].element_size = 4;
+ pm8001_ha->memoryMap.region[PI].total_len = 4;
+ pm8001_ha->memoryMap.region[PI].alignment = 4;
+
+ /* MPI Memory region 5 inbound queues */
+ pm8001_ha->memoryMap.region[IB].num_elements = 256;
+ pm8001_ha->memoryMap.region[IB].element_size = 64;
+ pm8001_ha->memoryMap.region[IB].total_len = 256 * 64;
+ pm8001_ha->memoryMap.region[IB].alignment = 64;
+
+ /* MPI Memory region 6 inbound queues */
+ pm8001_ha->memoryMap.region[OB].num_elements = 256;
+ pm8001_ha->memoryMap.region[OB].element_size = 64;
+ pm8001_ha->memoryMap.region[OB].total_len = 256 * 64;
+ pm8001_ha->memoryMap.region[OB].alignment = 64;
+
+ /* Memory region write DMA*/
+ pm8001_ha->memoryMap.region[NVMD].num_elements = 1;
+ pm8001_ha->memoryMap.region[NVMD].element_size = 4096;
+ pm8001_ha->memoryMap.region[NVMD].total_len = 4096;
+ /* Memory region for devices*/
+ pm8001_ha->memoryMap.region[DEV_MEM].num_elements = 1;
+ pm8001_ha->memoryMap.region[DEV_MEM].element_size = PM8001_MAX_DEVICES *
+ sizeof(struct pm8001_device);
+ pm8001_ha->memoryMap.region[DEV_MEM].total_len = PM8001_MAX_DEVICES *
+ sizeof(struct pm8001_device);
+
+ /* Memory region for ccb_info*/
+ pm8001_ha->memoryMap.region[CCB_MEM].num_elements = 1;
+ pm8001_ha->memoryMap.region[CCB_MEM].element_size = PM8001_MAX_CCB *
+ sizeof(struct pm8001_ccb_info);
+ pm8001_ha->memoryMap.region[CCB_MEM].total_len = PM8001_MAX_CCB *
+ sizeof(struct pm8001_ccb_info);
+
+ for (i = 0; i < USI_MAX_MEMCNT; i++) {
+ if (pm8001_mem_alloc(pm8001_ha->pdev,
+ &pm8001_ha->memoryMap.region[i].virt_ptr,
+ &pm8001_ha->memoryMap.region[i].phys_addr,
+ &pm8001_ha->memoryMap.region[i].phys_addr_hi,
+ &pm8001_ha->memoryMap.region[i].phys_addr_lo,
+ pm8001_ha->memoryMap.region[i].total_len,
+ pm8001_ha->memoryMap.region[i].alignment) != 0) {
+ PM8001_FAIL_DBG(pm8001_ha,
+ pm8001_printk("Mem%d alloc failed\n",
+ i));
+ goto err_out;
+ }
+ }
+
+ pm8001_ha->devices = pm8001_ha->memoryMap.region[DEV_MEM].virt_ptr;
+ for (i = 0; i < PM8001_MAX_DEVICES; i++) {
+ pm8001_ha->devices[i].dev_type = NO_DEVICE;
+ pm8001_ha->devices[i].id = i;
+ pm8001_ha->devices[i].device_id = PM8001_MAX_DEVICES;
+ pm8001_ha->devices[i].running_req = 0;
+ }
+ pm8001_ha->ccb_info = pm8001_ha->memoryMap.region[CCB_MEM].virt_ptr;
+ for (i = 0; i < PM8001_MAX_CCB; i++) {
+ pm8001_ha->ccb_info[i].ccb_dma_handle =
+ pm8001_ha->memoryMap.region[CCB_MEM].phys_addr +
+ i * sizeof(struct pm8001_ccb_info);
+ pm8001_ha->ccb_info[i].task = NULL;
+ pm8001_ha->ccb_info[i].ccb_tag = 0xffffffff;
+ pm8001_ha->ccb_info[i].device = NULL;
+ ++pm8001_ha->tags_num;
+ }
+ pm8001_ha->flags = PM8001F_INIT_TIME;
+ /* Initialize tags */
+ pm8001_tag_init(pm8001_ha);
+ return 0;
+err_out:
+ return 1;
+}
+
+/**
+ * pm8001_ioremap - remap the pci high physical address to kernal virtual
+ * address so that we can access them.
+ * @pm8001_ha:our hba structure.
+ */
+static int pm8001_ioremap(struct pm8001_hba_info *pm8001_ha)
+{
+ u32 bar;
+ u32 logicalBar = 0;
+ struct pci_dev *pdev;
+
+ pdev = pm8001_ha->pdev;
+ /* map pci mem (PMC pci base 0-3)*/
+ for (bar = 0; bar < 6; bar++) {
+ /*
+ ** logical BARs for SPC:
+ ** bar 0 and 1 - logical BAR0
+ ** bar 2 and 3 - logical BAR1
+ ** bar4 - logical BAR2
+ ** bar5 - logical BAR3
+ ** Skip the appropriate assignments:
+ */
+ if ((bar == 1) || (bar == 3))
+ continue;
+ if (pci_resource_flags(pdev, bar) & IORESOURCE_MEM) {
+ pm8001_ha->io_mem[logicalBar].membase =
+ pci_resource_start(pdev, bar);
+ pm8001_ha->io_mem[logicalBar].membase &=
+ (u32)PCI_BASE_ADDRESS_MEM_MASK;
+ pm8001_ha->io_mem[logicalBar].memsize =
+ pci_resource_len(pdev, bar);
+ pm8001_ha->io_mem[logicalBar].memvirtaddr =
+ ioremap(pm8001_ha->io_mem[logicalBar].membase,
+ pm8001_ha->io_mem[logicalBar].memsize);
+ PM8001_INIT_DBG(pm8001_ha,
+ pm8001_printk("PCI: bar %d, logicalBar %d "
+ "virt_addr=%lx,len=%d\n", bar, logicalBar,
+ (unsigned long)
+ pm8001_ha->io_mem[logicalBar].memvirtaddr,
+ pm8001_ha->io_mem[logicalBar].memsize));
+ } else {
+ pm8001_ha->io_mem[logicalBar].membase = 0;
+ pm8001_ha->io_mem[logicalBar].memsize = 0;
+ pm8001_ha->io_mem[logicalBar].memvirtaddr = 0;
+ }
+ logicalBar++;
+ }
+ return 0;
+}
+
+/**
+ * pm8001_pci_alloc - initialize our ha card structure
+ * @pdev: pci device.
+ * @ent: ent
+ * @shost: scsi host struct which has been initialized before.
+ */
+static struct pm8001_hba_info *__devinit
+pm8001_pci_alloc(struct pci_dev *pdev, u32 chip_id, struct Scsi_Host *shost)
+{
+ struct pm8001_hba_info *pm8001_ha;
+ struct sas_ha_struct *sha = SHOST_TO_SAS_HA(shost);
+
+
+ pm8001_ha = sha->lldd_ha;
+ if (!pm8001_ha)
+ return NULL;
+
+ pm8001_ha->pdev = pdev;
+ pm8001_ha->dev = &pdev->dev;
+ pm8001_ha->chip_id = chip_id;
+ pm8001_ha->chip = &pm8001_chips[pm8001_ha->chip_id];
+ pm8001_ha->irq = pdev->irq;
+ pm8001_ha->sas = sha;
+ pm8001_ha->shost = shost;
+ pm8001_ha->id = pm8001_id++;
+ INIT_LIST_HEAD(&pm8001_ha->wq_list);
+ pm8001_ha->logging_level = 0x01;
+ sprintf(pm8001_ha->name, "%s%d", DRV_NAME, pm8001_ha->id);
+#ifdef PM8001_USE_TASKLET
+ tasklet_init(&pm8001_ha->tasklet, pm8001_tasklet,
+ (unsigned long)pm8001_ha);
+#endif
+ pm8001_ioremap(pm8001_ha);
+ if (!pm8001_alloc(pm8001_ha))
+ return pm8001_ha;
+ pm8001_free(pm8001_ha);
+ return NULL;
+}
+
+/**
+ * pci_go_44 - pm8001 specified, its DMA is 44 bit rather than 64 bit
+ * @pdev: pci device.
+ */
+static int pci_go_44(struct pci_dev *pdev)
+{
+ int rc;
+
+ if (!pci_set_dma_mask(pdev, DMA_BIT_MASK(44))) {
+ rc = pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(44));
+ if (rc) {
+ rc = pci_set_consistent_dma_mask(pdev,
+ DMA_BIT_MASK(32));
+ if (rc) {
+ dev_printk(KERN_ERR, &pdev->dev,
+ "44-bit DMA enable failed\n");
+ return rc;
+ }
+ }
+ } else {
+ rc = pci_set_dma_mask(pdev, DMA_BIT_MASK(32));
+ if (rc) {
+ dev_printk(KERN_ERR, &pdev->dev,
+ "32-bit DMA enable failed\n");
+ return rc;
+ }
+ rc = pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(32));
+ if (rc) {
+ dev_printk(KERN_ERR, &pdev->dev,
+ "32-bit consistent DMA enable failed\n");
+ return rc;
+ }
+ }
+ return rc;
+}
+
+/**
+ * pm8001_prep_sas_ha_init - allocate memory in general hba struct && init them.
+ * @shost: scsi host which has been allocated outside.
+ * @chip_info: our ha struct.
+ */
+static int __devinit pm8001_prep_sas_ha_init(struct Scsi_Host * shost,
+ const struct pm8001_chip_info *chip_info)
+{
+ int phy_nr, port_nr;
+ struct asd_sas_phy **arr_phy;
+ struct asd_sas_port **arr_port;
+ struct sas_ha_struct *sha = SHOST_TO_SAS_HA(shost);
+
+ phy_nr = chip_info->n_phy;
+ port_nr = phy_nr;
+ memset(sha, 0x00, sizeof(*sha));
+ arr_phy = kcalloc(phy_nr, sizeof(void *), GFP_KERNEL);
+ if (!arr_phy)
+ goto exit;
+ arr_port = kcalloc(port_nr, sizeof(void *), GFP_KERNEL);
+ if (!arr_port)
+ goto exit_free2;
+
+ sha->sas_phy = arr_phy;
+ sha->sas_port = arr_port;
+ sha->lldd_ha = kzalloc(sizeof(struct pm8001_hba_info), GFP_KERNEL);
+ if (!sha->lldd_ha)
+ goto exit_free1;
+
+ shost->transportt = pm8001_stt;
+ shost->max_id = PM8001_MAX_DEVICES;
+ shost->max_lun = 8;
+ shost->max_channel = 0;
+ shost->unique_id = pm8001_id;
+ shost->max_cmd_len = 16;
+ shost->can_queue = PM8001_CAN_QUEUE;
+ shost->cmd_per_lun = 32;
+ return 0;
+exit_free1:
+ kfree(arr_port);
+exit_free2:
+ kfree(arr_phy);
+exit:
+ return -1;
+}
+
+/**
+ * pm8001_post_sas_ha_init - initialize general hba struct defined in libsas
+ * @shost: scsi host which has been allocated outside
+ * @chip_info: our ha struct.
+ */
+static void __devinit pm8001_post_sas_ha_init(struct Scsi_Host *shost,
+ const struct pm8001_chip_info *chip_info)
+{
+ int i = 0;
+ struct pm8001_hba_info *pm8001_ha;
+ struct sas_ha_struct *sha = SHOST_TO_SAS_HA(shost);
+
+ pm8001_ha = sha->lldd_ha;
+ for (i = 0; i < chip_info->n_phy; i++) {
+ sha->sas_phy[i] = &pm8001_ha->phy[i].sas_phy;
+ sha->sas_port[i] = &pm8001_ha->port[i].sas_port;
+ }
+ sha->sas_ha_name = DRV_NAME;
+ sha->dev = pm8001_ha->dev;
+
+ sha->lldd_module = THIS_MODULE;
+ sha->sas_addr = &pm8001_ha->sas_addr[0];
+ sha->num_phys = chip_info->n_phy;
+ sha->lldd_max_execute_num = 1;
+ sha->lldd_queue_size = PM8001_CAN_QUEUE;
+ sha->core.shost = shost;
+}
+
+/**
+ * pm8001_init_sas_add - initialize sas address
+ * @chip_info: our ha struct.
+ *
+ * Currently we just set the fixed SAS address to our HBA,for manufacture,
+ * it should read from the EEPROM
+ */
+static void pm8001_init_sas_add(struct pm8001_hba_info *pm8001_ha)
+{
+ u8 i;
+#ifdef PM8001_READ_VPD
+ DECLARE_COMPLETION_ONSTACK(completion);
+ pm8001_ha->nvmd_completion = &completion;
+ PM8001_CHIP_DISP->get_nvmd_req(pm8001_ha, 0, 0);
+ wait_for_completion(&completion);
+ for (i = 0; i < pm8001_ha->chip->n_phy; i++) {
+ memcpy(&pm8001_ha->phy[i].dev_sas_addr, pm8001_ha->sas_addr,
+ SAS_ADDR_SIZE);
+ PM8001_INIT_DBG(pm8001_ha,
+ pm8001_printk("phy %d sas_addr = %x \n", i,
+ (u64)pm8001_ha->phy[i].dev_sas_addr));
+ }
+#else
+ for (i = 0; i < pm8001_ha->chip->n_phy; i++) {
+ pm8001_ha->phy[i].dev_sas_addr = 0x500e004010000004ULL;
+ pm8001_ha->phy[i].dev_sas_addr =
+ cpu_to_be64((u64)
+ (*(u64 *)&pm8001_ha->phy[i].dev_sas_addr));
+ }
+ memcpy(pm8001_ha->sas_addr, &pm8001_ha->phy[0].dev_sas_addr,
+ SAS_ADDR_SIZE);
+#endif
+}
+
+#ifdef PM8001_USE_MSIX
+/**
+ * pm8001_setup_msix - enable MSI-X interrupt
+ * @chip_info: our ha struct.
+ * @irq_handler: irq_handler
+ */
+static u32 pm8001_setup_msix(struct pm8001_hba_info *pm8001_ha,
+ irq_handler_t irq_handler)
+{
+ u32 i = 0, j = 0;
+ u32 number_of_intr = 1;
+ int flag = 0;
+ u32 max_entry;
+ int rc;
+ max_entry = sizeof(pm8001_ha->msix_entries) /
+ sizeof(pm8001_ha->msix_entries[0]);
+ flag |= IRQF_DISABLED;
+ for (i = 0; i < max_entry ; i++)
+ pm8001_ha->msix_entries[i].entry = i;
+ rc = pci_enable_msix(pm8001_ha->pdev, pm8001_ha->msix_entries,
+ number_of_intr);
+ pm8001_ha->number_of_intr = number_of_intr;
+ if (!rc) {
+ for (i = 0; i < number_of_intr; i++) {
+ if (request_irq(pm8001_ha->msix_entries[i].vector,
+ irq_handler, flag, DRV_NAME,
+ SHOST_TO_SAS_HA(pm8001_ha->shost))) {
+ for (j = 0; j < i; j++)
+ free_irq(
+ pm8001_ha->msix_entries[j].vector,
+ SHOST_TO_SAS_HA(pm8001_ha->shost));
+ pci_disable_msix(pm8001_ha->pdev);
+ break;
+ }
+ }
+ }
+ return rc;
+}
+#endif
+
+/**
+ * pm8001_request_irq - register interrupt
+ * @chip_info: our ha struct.
+ */
+static u32 pm8001_request_irq(struct pm8001_hba_info *pm8001_ha)
+{
+ struct pci_dev *pdev;
+ irq_handler_t irq_handler = pm8001_interrupt;
+ int rc;
+
+ pdev = pm8001_ha->pdev;
+
+#ifdef PM8001_USE_MSIX
+ if (pci_find_capability(pdev, PCI_CAP_ID_MSIX))
+ return pm8001_setup_msix(pm8001_ha, irq_handler);
+ else
+ goto intx;
+#endif
+
+intx:
+ /* intialize the INT-X interrupt */
+ rc = request_irq(pdev->irq, irq_handler, IRQF_SHARED, DRV_NAME,
+ SHOST_TO_SAS_HA(pm8001_ha->shost));
+ return rc;
+}
+
+/**
+ * pm8001_pci_probe - probe supported device
+ * @pdev: pci device which kernel has been prepared for.
+ * @ent: pci device id
+ *
+ * This function is the main initialization function, when register a new
+ * pci driver it is invoked, all struct an hardware initilization should be done
+ * here, also, register interrupt
+ */
+static int __devinit pm8001_pci_probe(struct pci_dev *pdev,
+ const struct pci_device_id *ent)
+{
+ unsigned int rc;
+ u32 pci_reg;
+ struct pm8001_hba_info *pm8001_ha;
+ struct Scsi_Host *shost = NULL;
+ const struct pm8001_chip_info *chip;
+
+ dev_printk(KERN_INFO, &pdev->dev,
+ "pm8001: driver version %s\n", DRV_VERSION);
+ rc = pci_enable_device(pdev);
+ if (rc)
+ goto err_out_enable;
+ pci_set_master(pdev);
+ /*
+ * Enable pci slot busmaster by setting pci command register.
+ * This is required by FW for Cyclone card.
+ */
+
+ pci_read_config_dword(pdev, PCI_COMMAND, &pci_reg);
+ pci_reg |= 0x157;
+ pci_write_config_dword(pdev, PCI_COMMAND, pci_reg);
+ rc = pci_request_regions(pdev, DRV_NAME);
+ if (rc)
+ goto err_out_disable;
+ rc = pci_go_44(pdev);
+ if (rc)
+ goto err_out_regions;
+
+ shost = scsi_host_alloc(&pm8001_sht, sizeof(void *));
+ if (!shost) {
+ rc = -ENOMEM;
+ goto err_out_regions;
+ }
+ chip = &pm8001_chips[ent->driver_data];
+ SHOST_TO_SAS_HA(shost) =
+ kcalloc(1, sizeof(struct sas_ha_struct), GFP_KERNEL);
+ if (!SHOST_TO_SAS_HA(shost)) {
+ rc = -ENOMEM;
+ goto err_out_free_host;
+ }
+
+ rc = pm8001_prep_sas_ha_init(shost, chip);
+ if (rc) {
+ rc = -ENOMEM;
+ goto err_out_free;
+ }
+ pci_set_drvdata(pdev, SHOST_TO_SAS_HA(shost));
+ pm8001_ha = pm8001_pci_alloc(pdev, chip_8001, shost);
+ if (!pm8001_ha) {
+ rc = -ENOMEM;
+ goto err_out_free;
+ }
+ list_add_tail(&pm8001_ha->list, &hba_list);
+ PM8001_CHIP_DISP->chip_soft_rst(pm8001_ha, 0x252acbcd);
+ rc = PM8001_CHIP_DISP->chip_init(pm8001_ha);
+ if (rc)
+ goto err_out_ha_free;
+
+ rc = scsi_add_host(shost, &pdev->dev);
+ if (rc)
+ goto err_out_ha_free;
+ rc = pm8001_request_irq(pm8001_ha);
+ if (rc)
+ goto err_out_shost;
+
+ PM8001_CHIP_DISP->interrupt_enable(pm8001_ha);
+ pm8001_init_sas_add(pm8001_ha);
+ pm8001_post_sas_ha_init(shost, chip);
+ rc = sas_register_ha(SHOST_TO_SAS_HA(shost));
+ if (rc)
+ goto err_out_shost;
+ scsi_scan_host(pm8001_ha->shost);
+ return 0;
+
+err_out_shost:
+ scsi_remove_host(pm8001_ha->shost);
+err_out_ha_free:
+ pm8001_free(pm8001_ha);
+err_out_free:
+ kfree(SHOST_TO_SAS_HA(shost));
+err_out_free_host:
+ kfree(shost);
+err_out_regions:
+ pci_release_regions(pdev);
+err_out_disable:
+ pci_disable_device(pdev);
+err_out_enable:
+ return rc;
+}
+
+static void __devexit pm8001_pci_remove(struct pci_dev *pdev)
+{
+ struct sas_ha_struct *sha = pci_get_drvdata(pdev);
+ struct pm8001_hba_info *pm8001_ha;
+ int i;
+ pm8001_ha = sha->lldd_ha;
+ pci_set_drvdata(pdev, NULL);
+ sas_unregister_ha(sha);
+ sas_remove_host(pm8001_ha->shost);
+ list_del(&pm8001_ha->list);
+ scsi_remove_host(pm8001_ha->shost);
+ PM8001_CHIP_DISP->interrupt_disable(pm8001_ha);
+ PM8001_CHIP_DISP->chip_soft_rst(pm8001_ha, 0x252acbcd);
+
+#ifdef PM8001_USE_MSIX
+ for (i = 0; i < pm8001_ha->number_of_intr; i++)
+ synchronize_irq(pm8001_ha->msix_entries[i].vector);
+ for (i = 0; i < pm8001_ha->number_of_intr; i++)
+ free_irq(pm8001_ha->msix_entries[i].vector, sha);
+ pci_disable_msix(pdev);
+#else
+ free_irq(pm8001_ha->irq, sha);
+#endif
+#ifdef PM8001_USE_TASKLET
+ tasklet_kill(&pm8001_ha->tasklet);
+#endif
+ pm8001_free(pm8001_ha);
+ kfree(sha->sas_phy);
+ kfree(sha->sas_port);
+ kfree(sha);
+ pci_release_regions(pdev);
+ pci_disable_device(pdev);
+}
+
+/**
+ * pm8001_pci_suspend - power management suspend main entry point
+ * @pdev: PCI device struct
+ * @state: PM state change to (usually PCI_D3)
+ *
+ * Returns 0 success, anything else error.
+ */
+static int pm8001_pci_suspend(struct pci_dev *pdev, pm_message_t state)
+{
+ struct sas_ha_struct *sha = pci_get_drvdata(pdev);
+ struct pm8001_hba_info *pm8001_ha;
+ int i , pos;
+ u32 device_state;
+ pm8001_ha = sha->lldd_ha;
+ flush_scheduled_work();
+ scsi_block_requests(pm8001_ha->shost);
+ pos = pci_find_capability(pdev, PCI_CAP_ID_PM);
+ if (pos == 0) {
+ printk(KERN_ERR " PCI PM not supported\n");
+ return -ENODEV;
+ }
+ PM8001_CHIP_DISP->interrupt_disable(pm8001_ha);
+ PM8001_CHIP_DISP->chip_soft_rst(pm8001_ha, 0x252acbcd);
+#ifdef PM8001_USE_MSIX
+ for (i = 0; i < pm8001_ha->number_of_intr; i++)
+ synchronize_irq(pm8001_ha->msix_entries[i].vector);
+ for (i = 0; i < pm8001_ha->number_of_intr; i++)
+ free_irq(pm8001_ha->msix_entries[i].vector, sha);
+ pci_disable_msix(pdev);
+#else
+ free_irq(pm8001_ha->irq, sha);
+#endif
+#ifdef PM8001_USE_TASKLET
+ tasklet_kill(&pm8001_ha->tasklet);
+#endif
+ device_state = pci_choose_state(pdev, state);
+ pm8001_printk("pdev=0x%p, slot=%s, entering "
+ "operating state [D%d]\n", pdev,
+ pm8001_ha->name, device_state);
+ pci_save_state(pdev);
+ pci_disable_device(pdev);
+ pci_set_power_state(pdev, device_state);
+ return 0;
+}
+
+/**
+ * pm8001_pci_resume - power management resume main entry point
+ * @pdev: PCI device struct
+ *
+ * Returns 0 success, anything else error.
+ */
+static int pm8001_pci_resume(struct pci_dev *pdev)
+{
+ struct sas_ha_struct *sha = pci_get_drvdata(pdev);
+ struct pm8001_hba_info *pm8001_ha;
+ int rc;
+ u32 device_state;
+ pm8001_ha = sha->lldd_ha;
+ device_state = pdev->current_state;
+
+ pm8001_printk("pdev=0x%p, slot=%s, resuming from previous "
+ "operating state [D%d]\n", pdev, pm8001_ha->name, device_state);
+
+ pci_set_power_state(pdev, PCI_D0);
+ pci_enable_wake(pdev, PCI_D0, 0);
+ pci_restore_state(pdev);
+ rc = pci_enable_device(pdev);
+ if (rc) {
+ pm8001_printk("slot=%s Enable device failed during resume\n",
+ pm8001_ha->name);
+ goto err_out_enable;
+ }
+
+ pci_set_master(pdev);
+ rc = pci_go_44(pdev);
+ if (rc)
+ goto err_out_disable;
+
+ PM8001_CHIP_DISP->chip_soft_rst(pm8001_ha, 0x252acbcd);
+ rc = PM8001_CHIP_DISP->chip_init(pm8001_ha);
+ if (rc)
+ goto err_out_disable;
+ PM8001_CHIP_DISP->interrupt_disable(pm8001_ha);
+ rc = pm8001_request_irq(pm8001_ha);
+ if (rc)
+ goto err_out_disable;
+ #ifdef PM8001_USE_TASKLET
+ tasklet_init(&pm8001_ha->tasklet, pm8001_tasklet,
+ (unsigned long)pm8001_ha);
+ #endif
+ PM8001_CHIP_DISP->interrupt_enable(pm8001_ha);
+ scsi_unblock_requests(pm8001_ha->shost);
+ return 0;
+
+err_out_disable:
+ scsi_remove_host(pm8001_ha->shost);
+ pci_disable_device(pdev);
+err_out_enable:
+ return rc;
+}
+
+static struct pci_device_id __devinitdata pm8001_pci_table[] = {
+ {
+ PCI_VDEVICE(PMC_Sierra, 0x8001), chip_8001
+ },
+ {
+ PCI_DEVICE(0x117c, 0x0042),
+ .driver_data = chip_8001
+ },
+ {} /* terminate list */
+};
+
+static struct pci_driver pm8001_pci_driver = {
+ .name = DRV_NAME,
+ .id_table = pm8001_pci_table,
+ .probe = pm8001_pci_probe,
+ .remove = __devexit_p(pm8001_pci_remove),
+ .suspend = pm8001_pci_suspend,
+ .resume = pm8001_pci_resume,
+};
+
+/**
+ * pm8001_init - initialize scsi transport template
+ */
+static int __init pm8001_init(void)
+{
+ int rc;
+ pm8001_id = 0;
+ pm8001_stt = sas_domain_attach_transport(&pm8001_transport_ops);
+ if (!pm8001_stt)
+ return -ENOMEM;
+ rc = pci_register_driver(&pm8001_pci_driver);
+ if (rc)
+ goto err_out;
+ return 0;
+err_out:
+ sas_release_transport(pm8001_stt);
+ return rc;
+}
+
+static void __exit pm8001_exit(void)
+{
+ pci_unregister_driver(&pm8001_pci_driver);
+ sas_release_transport(pm8001_stt);
+}
+
+module_init(pm8001_init);
+module_exit(pm8001_exit);
+
+MODULE_AUTHOR("Jack Wang <jack_wang@usish.com>");
+MODULE_DESCRIPTION("PMC-Sierra PM8001 SAS/SATA controller driver");
+MODULE_VERSION(DRV_VERSION);
+MODULE_LICENSE("GPL");
+MODULE_DEVICE_TABLE(pci, pm8001_pci_table);
+
diff --git a/drivers/scsi/pm8001/pm8001_sas.c b/drivers/scsi/pm8001/pm8001_sas.c
new file mode 100644
index 00000000000..1f767a0e727
--- /dev/null
+++ b/drivers/scsi/pm8001/pm8001_sas.c
@@ -0,0 +1,1103 @@
+/*
+ * PMC-Sierra SPC 8001 SAS/SATA based host adapters driver
+ *
+ * Copyright (c) 2008-2009 USI Co., Ltd.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions, and the following disclaimer,
+ * without modification.
+ * 2. Redistributions in binary form must reproduce at minimum a disclaimer
+ * substantially similar to the "NO WARRANTY" disclaimer below
+ * ("Disclaimer") and any redistribution must be conditioned upon
+ * including a substantially similar Disclaimer requirement for further
+ * binary redistribution.
+ * 3. Neither the names of the above-listed copyright holders nor the names
+ * of any contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2 as published by the Free
+ * Software Foundation.
+ *
+ * NO WARRANTY
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ */
+
+#include "pm8001_sas.h"
+
+/**
+ * pm8001_find_tag - from sas task to find out tag that belongs to this task
+ * @task: the task sent to the LLDD
+ * @tag: the found tag associated with the task
+ */
+static int pm8001_find_tag(struct sas_task *task, u32 *tag)
+{
+ if (task->lldd_task) {
+ struct pm8001_ccb_info *ccb;
+ ccb = task->lldd_task;
+ *tag = ccb->ccb_tag;
+ return 1;
+ }
+ return 0;
+}
+
+/**
+ * pm8001_tag_clear - clear the tags bitmap
+ * @pm8001_ha: our hba struct
+ * @tag: the found tag associated with the task
+ */
+static void pm8001_tag_clear(struct pm8001_hba_info *pm8001_ha, u32 tag)
+{
+ void *bitmap = pm8001_ha->tags;
+ clear_bit(tag, bitmap);
+}
+
+static void pm8001_tag_free(struct pm8001_hba_info *pm8001_ha, u32 tag)
+{
+ pm8001_tag_clear(pm8001_ha, tag);
+}
+
+static void pm8001_tag_set(struct pm8001_hba_info *pm8001_ha, u32 tag)
+{
+ void *bitmap = pm8001_ha->tags;
+ set_bit(tag, bitmap);
+}
+
+/**
+ * pm8001_tag_alloc - allocate a empty tag for task used.
+ * @pm8001_ha: our hba struct
+ * @tag_out: the found empty tag .
+ */
+inline int pm8001_tag_alloc(struct pm8001_hba_info *pm8001_ha, u32 *tag_out)
+{
+ unsigned int index, tag;
+ void *bitmap = pm8001_ha->tags;
+
+ index = find_first_zero_bit(bitmap, pm8001_ha->tags_num);
+ tag = index;
+ if (tag >= pm8001_ha->tags_num)
+ return -SAS_QUEUE_FULL;
+ pm8001_tag_set(pm8001_ha, tag);
+ *tag_out = tag;
+ return 0;
+}
+
+void pm8001_tag_init(struct pm8001_hba_info *pm8001_ha)
+{
+ int i;
+ for (i = 0; i < pm8001_ha->tags_num; ++i)
+ pm8001_tag_clear(pm8001_ha, i);
+}
+
+ /**
+ * pm8001_mem_alloc - allocate memory for pm8001.
+ * @pdev: pci device.
+ * @virt_addr: the allocated virtual address
+ * @pphys_addr_hi: the physical address high byte address.
+ * @pphys_addr_lo: the physical address low byte address.
+ * @mem_size: memory size.
+ */
+int pm8001_mem_alloc(struct pci_dev *pdev, void **virt_addr,
+ dma_addr_t *pphys_addr, u32 *pphys_addr_hi,
+ u32 *pphys_addr_lo, u32 mem_size, u32 align)
+{
+ caddr_t mem_virt_alloc;
+ dma_addr_t mem_dma_handle;
+ u64 phys_align;
+ u64 align_offset = 0;
+ if (align)
+ align_offset = (dma_addr_t)align - 1;
+ mem_virt_alloc =
+ pci_alloc_consistent(pdev, mem_size + align, &mem_dma_handle);
+ if (!mem_virt_alloc) {
+ pm8001_printk("memory allocation error\n");
+ return -1;
+ }
+ memset((void *)mem_virt_alloc, 0, mem_size+align);
+ *pphys_addr = mem_dma_handle;
+ phys_align = (*pphys_addr + align_offset) & ~align_offset;
+ *virt_addr = (void *)mem_virt_alloc + phys_align - *pphys_addr;
+ *pphys_addr_hi = upper_32_bits(phys_align);
+ *pphys_addr_lo = lower_32_bits(phys_align);
+ return 0;
+}
+/**
+ * pm8001_find_ha_by_dev - from domain device which come from sas layer to
+ * find out our hba struct.
+ * @dev: the domain device which from sas layer.
+ */
+static
+struct pm8001_hba_info *pm8001_find_ha_by_dev(struct domain_device *dev)
+{
+ struct sas_ha_struct *sha = dev->port->ha;
+ struct pm8001_hba_info *pm8001_ha = sha->lldd_ha;
+ return pm8001_ha;
+}
+
+/**
+ * pm8001_phy_control - this function should be registered to
+ * sas_domain_function_template to provide libsas used, note: this is just
+ * control the HBA phy rather than other expander phy if you want control
+ * other phy, you should use SMP command.
+ * @sas_phy: which phy in HBA phys.
+ * @func: the operation.
+ * @funcdata: always NULL.
+ */
+int pm8001_phy_control(struct asd_sas_phy *sas_phy, enum phy_func func,
+ void *funcdata)
+{
+ int rc = 0, phy_id = sas_phy->id;
+ struct pm8001_hba_info *pm8001_ha = NULL;
+ struct sas_phy_linkrates *rates;
+ DECLARE_COMPLETION_ONSTACK(completion);
+ pm8001_ha = sas_phy->ha->lldd_ha;
+ pm8001_ha->phy[phy_id].enable_completion = &completion;
+ switch (func) {
+ case PHY_FUNC_SET_LINK_RATE:
+ rates = funcdata;
+ if (rates->minimum_linkrate) {
+ pm8001_ha->phy[phy_id].minimum_linkrate =
+ rates->minimum_linkrate;
+ }
+ if (rates->maximum_linkrate) {
+ pm8001_ha->phy[phy_id].maximum_linkrate =
+ rates->maximum_linkrate;
+ }
+ if (pm8001_ha->phy[phy_id].phy_state == 0) {
+ PM8001_CHIP_DISP->phy_start_req(pm8001_ha, phy_id);
+ wait_for_completion(&completion);
+ }
+ PM8001_CHIP_DISP->phy_ctl_req(pm8001_ha, phy_id,
+ PHY_LINK_RESET);
+ break;
+ case PHY_FUNC_HARD_RESET:
+ if (pm8001_ha->phy[phy_id].phy_state == 0) {
+ PM8001_CHIP_DISP->phy_start_req(pm8001_ha, phy_id);
+ wait_for_completion(&completion);
+ }
+ PM8001_CHIP_DISP->phy_ctl_req(pm8001_ha, phy_id,
+ PHY_HARD_RESET);
+ break;
+ case PHY_FUNC_LINK_RESET:
+ if (pm8001_ha->phy[phy_id].phy_state == 0) {
+ PM8001_CHIP_DISP->phy_start_req(pm8001_ha, phy_id);
+ wait_for_completion(&completion);
+ }
+ PM8001_CHIP_DISP->phy_ctl_req(pm8001_ha, phy_id,
+ PHY_LINK_RESET);
+ break;
+ case PHY_FUNC_RELEASE_SPINUP_HOLD:
+ PM8001_CHIP_DISP->phy_ctl_req(pm8001_ha, phy_id,
+ PHY_LINK_RESET);
+ break;
+ case PHY_FUNC_DISABLE:
+ PM8001_CHIP_DISP->phy_stop_req(pm8001_ha, phy_id);
+ break;
+ default:
+ rc = -EOPNOTSUPP;
+ }
+ msleep(300);
+ return rc;
+}
+
+int pm8001_slave_alloc(struct scsi_device *scsi_dev)
+{
+ struct domain_device *dev = sdev_to_domain_dev(scsi_dev);
+ if (dev_is_sata(dev)) {
+ /* We don't need to rescan targets
+ * if REPORT_LUNS request is failed
+ */
+ if (scsi_dev->lun > 0)
+ return -ENXIO;
+ scsi_dev->tagged_supported = 1;
+ }
+ return sas_slave_alloc(scsi_dev);
+}
+
+/**
+ * pm8001_scan_start - we should enable all HBA phys by sending the phy_start
+ * command to HBA.
+ * @shost: the scsi host data.
+ */
+void pm8001_scan_start(struct Scsi_Host *shost)
+{
+ int i;
+ struct pm8001_hba_info *pm8001_ha;
+ struct sas_ha_struct *sha = SHOST_TO_SAS_HA(shost);
+ pm8001_ha = sha->lldd_ha;
+ PM8001_CHIP_DISP->sas_re_init_req(pm8001_ha);
+ for (i = 0; i < pm8001_ha->chip->n_phy; ++i)
+ PM8001_CHIP_DISP->phy_start_req(pm8001_ha, i);
+}
+
+int pm8001_scan_finished(struct Scsi_Host *shost, unsigned long time)
+{
+ /* give the phy enabling interrupt event time to come in (1s
+ * is empirically about all it takes) */
+ if (time < HZ)
+ return 0;
+ /* Wait for discovery to finish */
+ scsi_flush_work(shost);
+ return 1;
+}
+
+/**
+ * pm8001_task_prep_smp - the dispatcher function, prepare data for smp task
+ * @pm8001_ha: our hba card information
+ * @ccb: the ccb which attached to smp task
+ */
+static int pm8001_task_prep_smp(struct pm8001_hba_info *pm8001_ha,
+ struct pm8001_ccb_info *ccb)
+{
+ return PM8001_CHIP_DISP->smp_req(pm8001_ha, ccb);
+}
+
+u32 pm8001_get_ncq_tag(struct sas_task *task, u32 *tag)
+{
+ struct ata_queued_cmd *qc = task->uldd_task;
+ if (qc) {
+ if (qc->tf.command == ATA_CMD_FPDMA_WRITE ||
+ qc->tf.command == ATA_CMD_FPDMA_READ) {
+ *tag = qc->tag;
+ return 1;
+ }
+ }
+ return 0;
+}
+
+/**
+ * pm8001_task_prep_ata - the dispatcher function, prepare data for sata task
+ * @pm8001_ha: our hba card information
+ * @ccb: the ccb which attached to sata task
+ */
+static int pm8001_task_prep_ata(struct pm8001_hba_info *pm8001_ha,
+ struct pm8001_ccb_info *ccb)
+{
+ return PM8001_CHIP_DISP->sata_req(pm8001_ha, ccb);
+}
+
+/**
+ * pm8001_task_prep_ssp_tm - the dispatcher function, prepare task management data
+ * @pm8001_ha: our hba card information
+ * @ccb: the ccb which attached to TM
+ * @tmf: the task management IU
+ */
+static int pm8001_task_prep_ssp_tm(struct pm8001_hba_info *pm8001_ha,
+ struct pm8001_ccb_info *ccb, struct pm8001_tmf_task *tmf)
+{
+ return PM8001_CHIP_DISP->ssp_tm_req(pm8001_ha, ccb, tmf);
+}
+
+/**
+ * pm8001_task_prep_ssp - the dispatcher function,prepare ssp data for ssp task
+ * @pm8001_ha: our hba card information
+ * @ccb: the ccb which attached to ssp task
+ */
+static int pm8001_task_prep_ssp(struct pm8001_hba_info *pm8001_ha,
+ struct pm8001_ccb_info *ccb)
+{
+ return PM8001_CHIP_DISP->ssp_io_req(pm8001_ha, ccb);
+}
+int pm8001_slave_configure(struct scsi_device *sdev)
+{
+ struct domain_device *dev = sdev_to_domain_dev(sdev);
+ int ret = sas_slave_configure(sdev);
+ if (ret)
+ return ret;
+ if (dev_is_sata(dev)) {
+ #ifdef PM8001_DISABLE_NCQ
+ struct ata_port *ap = dev->sata_dev.ap;
+ struct ata_device *adev = ap->link.device;
+ adev->flags |= ATA_DFLAG_NCQ_OFF;
+ scsi_adjust_queue_depth(sdev, MSG_SIMPLE_TAG, 1);
+ #endif
+ }
+ return 0;
+}
+/**
+ * pm8001_task_exec - queue the task(ssp, smp && ata) to the hardware.
+ * @task: the task to be execute.
+ * @num: if can_queue great than 1, the task can be queued up. for SMP task,
+ * we always execute one one time.
+ * @gfp_flags: gfp_flags.
+ * @is_tmf: if it is task management task.
+ * @tmf: the task management IU
+ */
+#define DEV_IS_GONE(pm8001_dev) \
+ ((!pm8001_dev || (pm8001_dev->dev_type == NO_DEVICE)))
+static int pm8001_task_exec(struct sas_task *task, const int num,
+ gfp_t gfp_flags, int is_tmf, struct pm8001_tmf_task *tmf)
+{
+ struct domain_device *dev = task->dev;
+ struct pm8001_hba_info *pm8001_ha;
+ struct pm8001_device *pm8001_dev;
+ struct sas_task *t = task;
+ struct pm8001_ccb_info *ccb;
+ u32 tag = 0xdeadbeef, rc, n_elem = 0;
+ u32 n = num;
+ unsigned long flags = 0;
+
+ if (!dev->port) {
+ struct task_status_struct *tsm = &t->task_status;
+ tsm->resp = SAS_TASK_UNDELIVERED;
+ tsm->stat = SAS_PHY_DOWN;
+ if (dev->dev_type != SATA_DEV)
+ t->task_done(t);
+ return 0;
+ }
+ pm8001_ha = pm8001_find_ha_by_dev(task->dev);
+ PM8001_IO_DBG(pm8001_ha, pm8001_printk("pm8001_task_exec device \n "));
+ spin_lock_irqsave(&pm8001_ha->lock, flags);
+ do {
+ dev = t->dev;
+ pm8001_dev = dev->lldd_dev;
+ if (DEV_IS_GONE(pm8001_dev)) {
+ if (pm8001_dev) {
+ PM8001_IO_DBG(pm8001_ha,
+ pm8001_printk("device %d not ready.\n",
+ pm8001_dev->device_id));
+ } else {
+ PM8001_IO_DBG(pm8001_ha,
+ pm8001_printk("device %016llx not "
+ "ready.\n", SAS_ADDR(dev->sas_addr)));
+ }
+ rc = SAS_PHY_DOWN;
+ goto out_done;
+ }
+ rc = pm8001_tag_alloc(pm8001_ha, &tag);
+ if (rc)
+ goto err_out;
+ ccb = &pm8001_ha->ccb_info[tag];
+
+ if (!sas_protocol_ata(t->task_proto)) {
+ if (t->num_scatter) {
+ n_elem = dma_map_sg(pm8001_ha->dev,
+ t->scatter,
+ t->num_scatter,
+ t->data_dir);
+ if (!n_elem) {
+ rc = -ENOMEM;
+ goto err_out_tag;
+ }
+ }
+ } else {
+ n_elem = t->num_scatter;
+ }
+
+ t->lldd_task = ccb;
+ ccb->n_elem = n_elem;
+ ccb->ccb_tag = tag;
+ ccb->task = t;
+ switch (t->task_proto) {
+ case SAS_PROTOCOL_SMP:
+ rc = pm8001_task_prep_smp(pm8001_ha, ccb);
+ break;
+ case SAS_PROTOCOL_SSP:
+ if (is_tmf)
+ rc = pm8001_task_prep_ssp_tm(pm8001_ha,
+ ccb, tmf);
+ else
+ rc = pm8001_task_prep_ssp(pm8001_ha, ccb);
+ break;
+ case SAS_PROTOCOL_SATA:
+ case SAS_PROTOCOL_STP:
+ case SAS_PROTOCOL_SATA | SAS_PROTOCOL_STP:
+ rc = pm8001_task_prep_ata(pm8001_ha, ccb);
+ break;
+ default:
+ dev_printk(KERN_ERR, pm8001_ha->dev,
+ "unknown sas_task proto: 0x%x\n",
+ t->task_proto);
+ rc = -EINVAL;
+ break;
+ }
+
+ if (rc) {
+ PM8001_IO_DBG(pm8001_ha,
+ pm8001_printk("rc is %x\n", rc));
+ goto err_out_tag;
+ }
+ /* TODO: select normal or high priority */
+ spin_lock(&t->task_state_lock);
+ t->task_state_flags |= SAS_TASK_AT_INITIATOR;
+ spin_unlock(&t->task_state_lock);
+ pm8001_dev->running_req++;
+ if (n > 1)
+ t = list_entry(t->list.next, struct sas_task, list);
+ } while (--n);
+ rc = 0;
+ goto out_done;
+
+err_out_tag:
+ pm8001_tag_free(pm8001_ha, tag);
+err_out:
+ dev_printk(KERN_ERR, pm8001_ha->dev, "pm8001 exec failed[%d]!\n", rc);
+ if (!sas_protocol_ata(t->task_proto))
+ if (n_elem)
+ dma_unmap_sg(pm8001_ha->dev, t->scatter, n_elem,
+ t->data_dir);
+out_done:
+ spin_unlock_irqrestore(&pm8001_ha->lock, flags);
+ return rc;
+}
+
+/**
+ * pm8001_queue_command - register for upper layer used, all IO commands sent
+ * to HBA are from this interface.
+ * @task: the task to be execute.
+ * @num: if can_queue great than 1, the task can be queued up. for SMP task,
+ * we always execute one one time
+ * @gfp_flags: gfp_flags
+ */
+int pm8001_queue_command(struct sas_task *task, const int num,
+ gfp_t gfp_flags)
+{
+ return pm8001_task_exec(task, num, gfp_flags, 0, NULL);
+}
+
+void pm8001_ccb_free(struct pm8001_hba_info *pm8001_ha, u32 ccb_idx)
+{
+ pm8001_tag_clear(pm8001_ha, ccb_idx);
+}
+
+/**
+ * pm8001_ccb_task_free - free the sg for ssp and smp command, free the ccb.
+ * @pm8001_ha: our hba card information
+ * @ccb: the ccb which attached to ssp task
+ * @task: the task to be free.
+ * @ccb_idx: ccb index.
+ */
+void pm8001_ccb_task_free(struct pm8001_hba_info *pm8001_ha,
+ struct sas_task *task, struct pm8001_ccb_info *ccb, u32 ccb_idx)
+{
+ if (!ccb->task)
+ return;
+ if (!sas_protocol_ata(task->task_proto))
+ if (ccb->n_elem)
+ dma_unmap_sg(pm8001_ha->dev, task->scatter,
+ task->num_scatter, task->data_dir);
+
+ switch (task->task_proto) {
+ case SAS_PROTOCOL_SMP:
+ dma_unmap_sg(pm8001_ha->dev, &task->smp_task.smp_resp, 1,
+ PCI_DMA_FROMDEVICE);
+ dma_unmap_sg(pm8001_ha->dev, &task->smp_task.smp_req, 1,
+ PCI_DMA_TODEVICE);
+ break;
+
+ case SAS_PROTOCOL_SATA:
+ case SAS_PROTOCOL_STP:
+ case SAS_PROTOCOL_SSP:
+ default:
+ /* do nothing */
+ break;
+ }
+ task->lldd_task = NULL;
+ ccb->task = NULL;
+ ccb->ccb_tag = 0xFFFFFFFF;
+ pm8001_ccb_free(pm8001_ha, ccb_idx);
+}
+
+ /**
+ * pm8001_alloc_dev - find a empty pm8001_device
+ * @pm8001_ha: our hba card information
+ */
+struct pm8001_device *pm8001_alloc_dev(struct pm8001_hba_info *pm8001_ha)
+{
+ u32 dev;
+ for (dev = 0; dev < PM8001_MAX_DEVICES; dev++) {
+ if (pm8001_ha->devices[dev].dev_type == NO_DEVICE) {
+ pm8001_ha->devices[dev].id = dev;
+ return &pm8001_ha->devices[dev];
+ }
+ }
+ if (dev == PM8001_MAX_DEVICES) {
+ PM8001_FAIL_DBG(pm8001_ha,
+ pm8001_printk("max support %d devices, ignore ..\n",
+ PM8001_MAX_DEVICES));
+ }
+ return NULL;
+}
+
+static void pm8001_free_dev(struct pm8001_device *pm8001_dev)
+{
+ u32 id = pm8001_dev->id;
+ memset(pm8001_dev, 0, sizeof(*pm8001_dev));
+ pm8001_dev->id = id;
+ pm8001_dev->dev_type = NO_DEVICE;
+ pm8001_dev->device_id = PM8001_MAX_DEVICES;
+ pm8001_dev->sas_device = NULL;
+}
+
+/**
+ * pm8001_dev_found_notify - libsas notify a device is found.
+ * @dev: the device structure which sas layer used.
+ *
+ * when libsas find a sas domain device, it should tell the LLDD that
+ * device is found, and then LLDD register this device to HBA firmware
+ * by the command "OPC_INB_REG_DEV", after that the HBA will assign a
+ * device ID(according to device's sas address) and returned it to LLDD. From
+ * now on, we communicate with HBA FW with the device ID which HBA assigned
+ * rather than sas address. it is the neccessary step for our HBA but it is
+ * the optional for other HBA driver.
+ */
+static int pm8001_dev_found_notify(struct domain_device *dev)
+{
+ unsigned long flags = 0;
+ int res = 0;
+ struct pm8001_hba_info *pm8001_ha = NULL;
+ struct domain_device *parent_dev = dev->parent;
+ struct pm8001_device *pm8001_device;
+ DECLARE_COMPLETION_ONSTACK(completion);
+ u32 flag = 0;
+ pm8001_ha = pm8001_find_ha_by_dev(dev);
+ spin_lock_irqsave(&pm8001_ha->lock, flags);
+
+ pm8001_device = pm8001_alloc_dev(pm8001_ha);
+ pm8001_device->sas_device = dev;
+ if (!pm8001_device) {
+ res = -1;
+ goto found_out;
+ }
+ dev->lldd_dev = pm8001_device;
+ pm8001_device->dev_type = dev->dev_type;
+ pm8001_device->dcompletion = &completion;
+ if (parent_dev && DEV_IS_EXPANDER(parent_dev->dev_type)) {
+ int phy_id;
+ struct ex_phy *phy;
+ for (phy_id = 0; phy_id < parent_dev->ex_dev.num_phys;
+ phy_id++) {
+ phy = &parent_dev->ex_dev.ex_phy[phy_id];
+ if (SAS_ADDR(phy->attached_sas_addr)
+ == SAS_ADDR(dev->sas_addr)) {
+ pm8001_device->attached_phy = phy_id;
+ break;
+ }
+ }
+ if (phy_id == parent_dev->ex_dev.num_phys) {
+ PM8001_FAIL_DBG(pm8001_ha,
+ pm8001_printk("Error: no attached dev:%016llx"
+ " at ex:%016llx.\n", SAS_ADDR(dev->sas_addr),
+ SAS_ADDR(parent_dev->sas_addr)));
+ res = -1;
+ }
+ } else {
+ if (dev->dev_type == SATA_DEV) {
+ pm8001_device->attached_phy =
+ dev->rphy->identify.phy_identifier;
+ flag = 1; /* directly sata*/
+ }
+ } /*register this device to HBA*/
+ PM8001_DISC_DBG(pm8001_ha, pm8001_printk("Found device \n"));
+ PM8001_CHIP_DISP->reg_dev_req(pm8001_ha, pm8001_device, flag);
+ spin_unlock_irqrestore(&pm8001_ha->lock, flags);
+ wait_for_completion(&completion);
+ if (dev->dev_type == SAS_END_DEV)
+ msleep(50);
+ pm8001_ha->flags = PM8001F_RUN_TIME ;
+ return 0;
+found_out:
+ spin_unlock_irqrestore(&pm8001_ha->lock, flags);
+ return res;
+}
+
+int pm8001_dev_found(struct domain_device *dev)
+{
+ return pm8001_dev_found_notify(dev);
+}
+
+/**
+ * pm8001_alloc_task - allocate a task structure for TMF
+ */
+static struct sas_task *pm8001_alloc_task(void)
+{
+ struct sas_task *task = kzalloc(sizeof(*task), GFP_KERNEL);
+ if (task) {
+ INIT_LIST_HEAD(&task->list);
+ spin_lock_init(&task->task_state_lock);
+ task->task_state_flags = SAS_TASK_STATE_PENDING;
+ init_timer(&task->timer);
+ init_completion(&task->completion);
+ }
+ return task;
+}
+
+static void pm8001_free_task(struct sas_task *task)
+{
+ if (task) {
+ BUG_ON(!list_empty(&task->list));
+ kfree(task);
+ }
+}
+
+static void pm8001_task_done(struct sas_task *task)
+{
+ if (!del_timer(&task->timer))
+ return;
+ complete(&task->completion);
+}
+
+static void pm8001_tmf_timedout(unsigned long data)
+{
+ struct sas_task *task = (struct sas_task *)data;
+
+ task->task_state_flags |= SAS_TASK_STATE_ABORTED;
+ complete(&task->completion);
+}
+
+#define PM8001_TASK_TIMEOUT 20
+/**
+ * pm8001_exec_internal_tmf_task - execute some task management commands.
+ * @dev: the wanted device.
+ * @tmf: which task management wanted to be take.
+ * @para_len: para_len.
+ * @parameter: ssp task parameter.
+ *
+ * when errors or exception happened, we may want to do something, for example
+ * abort the issued task which result in this execption, it is done by calling
+ * this function, note it is also with the task execute interface.
+ */
+static int pm8001_exec_internal_tmf_task(struct domain_device *dev,
+ void *parameter, u32 para_len, struct pm8001_tmf_task *tmf)
+{
+ int res, retry;
+ struct sas_task *task = NULL;
+ struct pm8001_hba_info *pm8001_ha = pm8001_find_ha_by_dev(dev);
+
+ for (retry = 0; retry < 3; retry++) {
+ task = pm8001_alloc_task();
+ if (!task)
+ return -ENOMEM;
+
+ task->dev = dev;
+ task->task_proto = dev->tproto;
+ memcpy(&task->ssp_task, parameter, para_len);
+ task->task_done = pm8001_task_done;
+ task->timer.data = (unsigned long)task;
+ task->timer.function = pm8001_tmf_timedout;
+ task->timer.expires = jiffies + PM8001_TASK_TIMEOUT*HZ;
+ add_timer(&task->timer);
+
+ res = pm8001_task_exec(task, 1, GFP_KERNEL, 1, tmf);
+
+ if (res) {
+ del_timer(&task->timer);
+ PM8001_FAIL_DBG(pm8001_ha,
+ pm8001_printk("Executing internal task "
+ "failed\n"));
+ goto ex_err;
+ }
+ wait_for_completion(&task->completion);
+ res = -TMF_RESP_FUNC_FAILED;
+ /* Even TMF timed out, return direct. */
+ if ((task->task_state_flags & SAS_TASK_STATE_ABORTED)) {
+ if (!(task->task_state_flags & SAS_TASK_STATE_DONE)) {
+ PM8001_FAIL_DBG(pm8001_ha,
+ pm8001_printk("TMF task[%x]timeout.\n",
+ tmf->tmf));
+ goto ex_err;
+ }
+ }
+
+ if (task->task_status.resp == SAS_TASK_COMPLETE &&
+ task->task_status.stat == SAM_GOOD) {
+ res = TMF_RESP_FUNC_COMPLETE;
+ break;
+ }
+
+ if (task->task_status.resp == SAS_TASK_COMPLETE &&
+ task->task_status.stat == SAS_DATA_UNDERRUN) {
+ /* no error, but return the number of bytes of
+ * underrun */
+ res = task->task_status.residual;
+ break;
+ }
+
+ if (task->task_status.resp == SAS_TASK_COMPLETE &&
+ task->task_status.stat == SAS_DATA_OVERRUN) {
+ PM8001_FAIL_DBG(pm8001_ha,
+ pm8001_printk("Blocked task error.\n"));
+ res = -EMSGSIZE;
+ break;
+ } else {
+ PM8001_EH_DBG(pm8001_ha,
+ pm8001_printk(" Task to dev %016llx response:"
+ "0x%x status 0x%x\n",
+ SAS_ADDR(dev->sas_addr),
+ task->task_status.resp,
+ task->task_status.stat));
+ pm8001_free_task(task);
+ task = NULL;
+ }
+ }
+ex_err:
+ BUG_ON(retry == 3 && task != NULL);
+ if (task != NULL)
+ pm8001_free_task(task);
+ return res;
+}
+
+static int
+pm8001_exec_internal_task_abort(struct pm8001_hba_info *pm8001_ha,
+ struct pm8001_device *pm8001_dev, struct domain_device *dev, u32 flag,
+ u32 task_tag)
+{
+ int res, retry;
+ u32 ccb_tag;
+ struct pm8001_ccb_info *ccb;
+ struct sas_task *task = NULL;
+
+ for (retry = 0; retry < 3; retry++) {
+ task = pm8001_alloc_task();
+ if (!task)
+ return -ENOMEM;
+
+ task->dev = dev;
+ task->task_proto = dev->tproto;
+ task->task_done = pm8001_task_done;
+ task->timer.data = (unsigned long)task;
+ task->timer.function = pm8001_tmf_timedout;
+ task->timer.expires = jiffies + PM8001_TASK_TIMEOUT*HZ;
+ add_timer(&task->timer);
+
+ res = pm8001_tag_alloc(pm8001_ha, &ccb_tag);
+ if (res)
+ return res;
+ ccb = &pm8001_ha->ccb_info[ccb_tag];
+ ccb->device = pm8001_dev;
+ ccb->ccb_tag = ccb_tag;
+ ccb->task = task;
+
+ res = PM8001_CHIP_DISP->task_abort(pm8001_ha,
+ pm8001_dev, flag, task_tag, ccb_tag);
+
+ if (res) {
+ del_timer(&task->timer);
+ PM8001_FAIL_DBG(pm8001_ha,
+ pm8001_printk("Executing internal task "
+ "failed\n"));
+ goto ex_err;
+ }
+ wait_for_completion(&task->completion);
+ res = TMF_RESP_FUNC_FAILED;
+ /* Even TMF timed out, return direct. */
+ if ((task->task_state_flags & SAS_TASK_STATE_ABORTED)) {
+ if (!(task->task_state_flags & SAS_TASK_STATE_DONE)) {
+ PM8001_FAIL_DBG(pm8001_ha,
+ pm8001_printk("TMF task timeout.\n"));
+ goto ex_err;
+ }
+ }
+
+ if (task->task_status.resp == SAS_TASK_COMPLETE &&
+ task->task_status.stat == SAM_GOOD) {
+ res = TMF_RESP_FUNC_COMPLETE;
+ break;
+
+ } else {
+ PM8001_EH_DBG(pm8001_ha,
+ pm8001_printk(" Task to dev %016llx response: "
+ "0x%x status 0x%x\n",
+ SAS_ADDR(dev->sas_addr),
+ task->task_status.resp,
+ task->task_status.stat));
+ pm8001_free_task(task);
+ task = NULL;
+ }
+ }
+ex_err:
+ BUG_ON(retry == 3 && task != NULL);
+ if (task != NULL)
+ pm8001_free_task(task);
+ return res;
+}
+
+/**
+ * pm8001_dev_gone_notify - see the comments for "pm8001_dev_found_notify"
+ * @dev: the device structure which sas layer used.
+ */
+static void pm8001_dev_gone_notify(struct domain_device *dev)
+{
+ unsigned long flags = 0;
+ u32 tag;
+ struct pm8001_hba_info *pm8001_ha;
+ struct pm8001_device *pm8001_dev = dev->lldd_dev;
+ u32 device_id = pm8001_dev->device_id;
+ pm8001_ha = pm8001_find_ha_by_dev(dev);
+ spin_lock_irqsave(&pm8001_ha->lock, flags);
+ pm8001_tag_alloc(pm8001_ha, &tag);
+ if (pm8001_dev) {
+ PM8001_DISC_DBG(pm8001_ha,
+ pm8001_printk("found dev[%d:%x] is gone.\n",
+ pm8001_dev->device_id, pm8001_dev->dev_type));
+ if (pm8001_dev->running_req) {
+ spin_unlock_irqrestore(&pm8001_ha->lock, flags);
+ pm8001_exec_internal_task_abort(pm8001_ha, pm8001_dev ,
+ dev, 1, 0);
+ spin_lock_irqsave(&pm8001_ha->lock, flags);
+ }
+ PM8001_CHIP_DISP->dereg_dev_req(pm8001_ha, device_id);
+ pm8001_free_dev(pm8001_dev);
+ } else {
+ PM8001_DISC_DBG(pm8001_ha,
+ pm8001_printk("Found dev has gone.\n"));
+ }
+ dev->lldd_dev = NULL;
+ spin_unlock_irqrestore(&pm8001_ha->lock, flags);
+}
+
+void pm8001_dev_gone(struct domain_device *dev)
+{
+ pm8001_dev_gone_notify(dev);
+}
+
+static int pm8001_issue_ssp_tmf(struct domain_device *dev,
+ u8 *lun, struct pm8001_tmf_task *tmf)
+{
+ struct sas_ssp_task ssp_task;
+ if (!(dev->tproto & SAS_PROTOCOL_SSP))
+ return TMF_RESP_FUNC_ESUPP;
+
+ strncpy((u8 *)&ssp_task.LUN, lun, 8);
+ return pm8001_exec_internal_tmf_task(dev, &ssp_task, sizeof(ssp_task),
+ tmf);
+}
+
+/**
+ * Standard mandates link reset for ATA (type 0) and hard reset for
+ * SSP (type 1) , only for RECOVERY
+ */
+int pm8001_I_T_nexus_reset(struct domain_device *dev)
+{
+ int rc = TMF_RESP_FUNC_FAILED;
+ struct pm8001_device *pm8001_dev;
+ struct pm8001_hba_info *pm8001_ha;
+ struct sas_phy *phy;
+ if (!dev || !dev->lldd_dev)
+ return -1;
+
+ pm8001_dev = dev->lldd_dev;
+ pm8001_ha = pm8001_find_ha_by_dev(dev);
+ phy = sas_find_local_phy(dev);
+
+ if (dev_is_sata(dev)) {
+ DECLARE_COMPLETION_ONSTACK(completion_setstate);
+ rc = sas_phy_reset(phy, 1);
+ msleep(2000);
+ rc = pm8001_exec_internal_task_abort(pm8001_ha, pm8001_dev ,
+ dev, 1, 0);
+ pm8001_dev->setds_completion = &completion_setstate;
+ rc = PM8001_CHIP_DISP->set_dev_state_req(pm8001_ha,
+ pm8001_dev, 0x01);
+ wait_for_completion(&completion_setstate);
+ } else{
+ rc = sas_phy_reset(phy, 1);
+ msleep(2000);
+ }
+ PM8001_EH_DBG(pm8001_ha, pm8001_printk(" for device[%x]:rc=%d\n",
+ pm8001_dev->device_id, rc));
+ return rc;
+}
+
+/* mandatory SAM-3, the task reset the specified LUN*/
+int pm8001_lu_reset(struct domain_device *dev, u8 *lun)
+{
+ int rc = TMF_RESP_FUNC_FAILED;
+ struct pm8001_tmf_task tmf_task;
+ struct pm8001_device *pm8001_dev = dev->lldd_dev;
+ struct pm8001_hba_info *pm8001_ha = pm8001_find_ha_by_dev(dev);
+ if (dev_is_sata(dev)) {
+ struct sas_phy *phy = sas_find_local_phy(dev);
+ rc = pm8001_exec_internal_task_abort(pm8001_ha, pm8001_dev ,
+ dev, 1, 0);
+ rc = sas_phy_reset(phy, 1);
+ rc = PM8001_CHIP_DISP->set_dev_state_req(pm8001_ha,
+ pm8001_dev, 0x01);
+ msleep(2000);
+ } else {
+ tmf_task.tmf = TMF_LU_RESET;
+ rc = pm8001_issue_ssp_tmf(dev, lun, &tmf_task);
+ }
+ /* If failed, fall-through I_T_Nexus reset */
+ PM8001_EH_DBG(pm8001_ha, pm8001_printk("for device[%x]:rc=%d\n",
+ pm8001_dev->device_id, rc));
+ return rc;
+}
+
+/* optional SAM-3 */
+int pm8001_query_task(struct sas_task *task)
+{
+ u32 tag = 0xdeadbeef;
+ int i = 0;
+ struct scsi_lun lun;
+ struct pm8001_tmf_task tmf_task;
+ int rc = TMF_RESP_FUNC_FAILED;
+ if (unlikely(!task || !task->lldd_task || !task->dev))
+ return rc;
+
+ if (task->task_proto & SAS_PROTOCOL_SSP) {
+ struct scsi_cmnd *cmnd = task->uldd_task;
+ struct domain_device *dev = task->dev;
+ struct pm8001_hba_info *pm8001_ha =
+ pm8001_find_ha_by_dev(dev);
+
+ int_to_scsilun(cmnd->device->lun, &lun);
+ rc = pm8001_find_tag(task, &tag);
+ if (rc == 0) {
+ rc = TMF_RESP_FUNC_FAILED;
+ return rc;
+ }
+ PM8001_EH_DBG(pm8001_ha, pm8001_printk("Query:["));
+ for (i = 0; i < 16; i++)
+ printk(KERN_INFO "%02x ", cmnd->cmnd[i]);
+ printk(KERN_INFO "]\n");
+ tmf_task.tmf = TMF_QUERY_TASK;
+ tmf_task.tag_of_task_to_be_managed = tag;
+
+ rc = pm8001_issue_ssp_tmf(dev, lun.scsi_lun, &tmf_task);
+ switch (rc) {
+ /* The task is still in Lun, release it then */
+ case TMF_RESP_FUNC_SUCC:
+ PM8001_EH_DBG(pm8001_ha,
+ pm8001_printk("The task is still in Lun \n"));
+ /* The task is not in Lun or failed, reset the phy */
+ case TMF_RESP_FUNC_FAILED:
+ case TMF_RESP_FUNC_COMPLETE:
+ PM8001_EH_DBG(pm8001_ha,
+ pm8001_printk("The task is not in Lun or failed,"
+ " reset the phy \n"));
+ break;
+ }
+ }
+ pm8001_printk(":rc= %d\n", rc);
+ return rc;
+}
+
+/* mandatory SAM-3, still need free task/ccb info, abord the specified task */
+int pm8001_abort_task(struct sas_task *task)
+{
+ unsigned long flags;
+ u32 tag = 0xdeadbeef;
+ u32 device_id;
+ struct domain_device *dev ;
+ struct pm8001_hba_info *pm8001_ha = NULL;
+ struct pm8001_ccb_info *ccb;
+ struct scsi_lun lun;
+ struct pm8001_device *pm8001_dev;
+ struct pm8001_tmf_task tmf_task;
+ int rc = TMF_RESP_FUNC_FAILED;
+ if (unlikely(!task || !task->lldd_task || !task->dev))
+ return rc;
+ spin_lock_irqsave(&task->task_state_lock, flags);
+ if (task->task_state_flags & SAS_TASK_STATE_DONE) {
+ spin_unlock_irqrestore(&task->task_state_lock, flags);
+ rc = TMF_RESP_FUNC_COMPLETE;
+ goto out;
+ }
+ spin_unlock_irqrestore(&task->task_state_lock, flags);
+ if (task->task_proto & SAS_PROTOCOL_SSP) {
+ struct scsi_cmnd *cmnd = task->uldd_task;
+ dev = task->dev;
+ ccb = task->lldd_task;
+ pm8001_dev = dev->lldd_dev;
+ pm8001_ha = pm8001_find_ha_by_dev(dev);
+ int_to_scsilun(cmnd->device->lun, &lun);
+ rc = pm8001_find_tag(task, &tag);
+ if (rc == 0) {
+ printk(KERN_INFO "No such tag in %s\n", __func__);
+ rc = TMF_RESP_FUNC_FAILED;
+ return rc;
+ }
+ device_id = pm8001_dev->device_id;
+ PM8001_EH_DBG(pm8001_ha,
+ pm8001_printk("abort io to deviceid= %d\n", device_id));
+ tmf_task.tmf = TMF_ABORT_TASK;
+ tmf_task.tag_of_task_to_be_managed = tag;
+ rc = pm8001_issue_ssp_tmf(dev, lun.scsi_lun, &tmf_task);
+ pm8001_exec_internal_task_abort(pm8001_ha, pm8001_dev,
+ pm8001_dev->sas_device, 0, tag);
+ } else if (task->task_proto & SAS_PROTOCOL_SATA ||
+ task->task_proto & SAS_PROTOCOL_STP) {
+ dev = task->dev;
+ pm8001_dev = dev->lldd_dev;
+ pm8001_ha = pm8001_find_ha_by_dev(dev);
+ rc = pm8001_find_tag(task, &tag);
+ if (rc == 0) {
+ printk(KERN_INFO "No such tag in %s\n", __func__);
+ rc = TMF_RESP_FUNC_FAILED;
+ return rc;
+ }
+ rc = pm8001_exec_internal_task_abort(pm8001_ha, pm8001_dev,
+ pm8001_dev->sas_device, 0, tag);
+ } else if (task->task_proto & SAS_PROTOCOL_SMP) {
+ /* SMP */
+ dev = task->dev;
+ pm8001_dev = dev->lldd_dev;
+ pm8001_ha = pm8001_find_ha_by_dev(dev);
+ rc = pm8001_find_tag(task, &tag);
+ if (rc == 0) {
+ printk(KERN_INFO "No such tag in %s\n", __func__);
+ rc = TMF_RESP_FUNC_FAILED;
+ return rc;
+ }
+ rc = pm8001_exec_internal_task_abort(pm8001_ha, pm8001_dev,
+ pm8001_dev->sas_device, 0, tag);
+
+ }
+out:
+ if (rc != TMF_RESP_FUNC_COMPLETE)
+ pm8001_printk("rc= %d\n", rc);
+ return rc;
+}
+
+int pm8001_abort_task_set(struct domain_device *dev, u8 *lun)
+{
+ int rc = TMF_RESP_FUNC_FAILED;
+ struct pm8001_tmf_task tmf_task;
+
+ tmf_task.tmf = TMF_ABORT_TASK_SET;
+ rc = pm8001_issue_ssp_tmf(dev, lun, &tmf_task);
+ return rc;
+}
+
+int pm8001_clear_aca(struct domain_device *dev, u8 *lun)
+{
+ int rc = TMF_RESP_FUNC_FAILED;
+ struct pm8001_tmf_task tmf_task;
+
+ tmf_task.tmf = TMF_CLEAR_ACA;
+ rc = pm8001_issue_ssp_tmf(dev, lun, &tmf_task);
+
+ return rc;
+}
+
+int pm8001_clear_task_set(struct domain_device *dev, u8 *lun)
+{
+ int rc = TMF_RESP_FUNC_FAILED;
+ struct pm8001_tmf_task tmf_task;
+ struct pm8001_device *pm8001_dev = dev->lldd_dev;
+ struct pm8001_hba_info *pm8001_ha = pm8001_find_ha_by_dev(dev);
+
+ PM8001_EH_DBG(pm8001_ha,
+ pm8001_printk("I_T_L_Q clear task set[%x]\n",
+ pm8001_dev->device_id));
+ tmf_task.tmf = TMF_CLEAR_TASK_SET;
+ rc = pm8001_issue_ssp_tmf(dev, lun, &tmf_task);
+ return rc;
+}
+
diff --git a/drivers/scsi/pm8001/pm8001_sas.h b/drivers/scsi/pm8001/pm8001_sas.h
new file mode 100644
index 00000000000..30f2ede55a7
--- /dev/null
+++ b/drivers/scsi/pm8001/pm8001_sas.h
@@ -0,0 +1,481 @@
+/*
+ * PMC-Sierra SPC 8001 SAS/SATA based host adapters driver
+ *
+ * Copyright (c) 2008-2009 USI Co., Ltd.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions, and the following disclaimer,
+ * without modification.
+ * 2. Redistributions in binary form must reproduce at minimum a disclaimer
+ * substantially similar to the "NO WARRANTY" disclaimer below
+ * ("Disclaimer") and any redistribution must be conditioned upon
+ * including a substantially similar Disclaimer requirement for further
+ * binary redistribution.
+ * 3. Neither the names of the above-listed copyright holders nor the names
+ * of any contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2 as published by the Free
+ * Software Foundation.
+ *
+ * NO WARRANTY
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ */
+
+#ifndef _PM8001_SAS_H_
+#define _PM8001_SAS_H_
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/spinlock.h>
+#include <linux/delay.h>
+#include <linux/types.h>
+#include <linux/ctype.h>
+#include <linux/dma-mapping.h>
+#include <linux/pci.h>
+#include <linux/interrupt.h>
+#include <linux/smp_lock.h>
+#include <scsi/libsas.h>
+#include <scsi/scsi_tcq.h>
+#include <scsi/sas_ata.h>
+#include <asm/atomic.h>
+#include "pm8001_defs.h"
+
+#define DRV_NAME "pm8001"
+#define DRV_VERSION "0.1.36"
+#define PM8001_FAIL_LOGGING 0x01 /* libsas EH function logging */
+#define PM8001_INIT_LOGGING 0x02 /* driver init logging */
+#define PM8001_DISC_LOGGING 0x04 /* discovery layer logging */
+#define PM8001_IO_LOGGING 0x08 /* I/O path logging */
+#define PM8001_EH_LOGGING 0x10 /* Error message logging */
+#define PM8001_IOCTL_LOGGING 0x20 /* IOCTL message logging */
+#define PM8001_MSG_LOGGING 0x40 /* misc message logging */
+#define pm8001_printk(format, arg...) printk(KERN_INFO "%s %d:" format,\
+ __func__, __LINE__, ## arg)
+#define PM8001_CHECK_LOGGING(HBA, LEVEL, CMD) \
+do { \
+ if (unlikely(HBA->logging_level & LEVEL)) \
+ do { \
+ CMD; \
+ } while (0); \
+} while (0);
+
+#define PM8001_EH_DBG(HBA, CMD) \
+ PM8001_CHECK_LOGGING(HBA, PM8001_EH_LOGGING, CMD)
+
+#define PM8001_INIT_DBG(HBA, CMD) \
+ PM8001_CHECK_LOGGING(HBA, PM8001_INIT_LOGGING, CMD)
+
+#define PM8001_DISC_DBG(HBA, CMD) \
+ PM8001_CHECK_LOGGING(HBA, PM8001_DISC_LOGGING, CMD)
+
+#define PM8001_IO_DBG(HBA, CMD) \
+ PM8001_CHECK_LOGGING(HBA, PM8001_IO_LOGGING, CMD)
+
+#define PM8001_FAIL_DBG(HBA, CMD) \
+ PM8001_CHECK_LOGGING(HBA, PM8001_FAIL_LOGGING, CMD)
+
+#define PM8001_IOCTL_DBG(HBA, CMD) \
+ PM8001_CHECK_LOGGING(HBA, PM8001_IOCTL_LOGGING, CMD)
+
+#define PM8001_MSG_DBG(HBA, CMD) \
+ PM8001_CHECK_LOGGING(HBA, PM8001_MSG_LOGGING, CMD)
+
+
+#define PM8001_USE_TASKLET
+#define PM8001_USE_MSIX
+
+
+#define DEV_IS_EXPANDER(type) ((type == EDGE_DEV) || (type == FANOUT_DEV))
+
+#define PM8001_NAME_LENGTH 32/* generic length of strings */
+extern struct list_head hba_list;
+extern const struct pm8001_dispatch pm8001_8001_dispatch;
+
+struct pm8001_hba_info;
+struct pm8001_ccb_info;
+struct pm8001_device;
+struct pm8001_tmf_task;
+struct pm8001_dispatch {
+ char *name;
+ int (*chip_init)(struct pm8001_hba_info *pm8001_ha);
+ int (*chip_soft_rst)(struct pm8001_hba_info *pm8001_ha, u32 signature);
+ void (*chip_rst)(struct pm8001_hba_info *pm8001_ha);
+ int (*chip_ioremap)(struct pm8001_hba_info *pm8001_ha);
+ void (*chip_iounmap)(struct pm8001_hba_info *pm8001_ha);
+ irqreturn_t (*isr)(struct pm8001_hba_info *pm8001_ha);
+ u32 (*is_our_interupt)(struct pm8001_hba_info *pm8001_ha);
+ int (*isr_process_oq)(struct pm8001_hba_info *pm8001_ha);
+ void (*interrupt_enable)(struct pm8001_hba_info *pm8001_ha);
+ void (*interrupt_disable)(struct pm8001_hba_info *pm8001_ha);
+ void (*make_prd)(struct scatterlist *scatter, int nr, void *prd);
+ int (*smp_req)(struct pm8001_hba_info *pm8001_ha,
+ struct pm8001_ccb_info *ccb);
+ int (*ssp_io_req)(struct pm8001_hba_info *pm8001_ha,
+ struct pm8001_ccb_info *ccb);
+ int (*sata_req)(struct pm8001_hba_info *pm8001_ha,
+ struct pm8001_ccb_info *ccb);
+ int (*phy_start_req)(struct pm8001_hba_info *pm8001_ha, u8 phy_id);
+ int (*phy_stop_req)(struct pm8001_hba_info *pm8001_ha, u8 phy_id);
+ int (*reg_dev_req)(struct pm8001_hba_info *pm8001_ha,
+ struct pm8001_device *pm8001_dev, u32 flag);
+ int (*dereg_dev_req)(struct pm8001_hba_info *pm8001_ha, u32 device_id);
+ int (*phy_ctl_req)(struct pm8001_hba_info *pm8001_ha,
+ u32 phy_id, u32 phy_op);
+ int (*task_abort)(struct pm8001_hba_info *pm8001_ha,
+ struct pm8001_device *pm8001_dev, u8 flag, u32 task_tag,
+ u32 cmd_tag);
+ int (*ssp_tm_req)(struct pm8001_hba_info *pm8001_ha,
+ struct pm8001_ccb_info *ccb, struct pm8001_tmf_task *tmf);
+ int (*get_nvmd_req)(struct pm8001_hba_info *pm8001_ha, void *payload);
+ int (*set_nvmd_req)(struct pm8001_hba_info *pm8001_ha, void *payload);
+ int (*fw_flash_update_req)(struct pm8001_hba_info *pm8001_ha,
+ void *payload);
+ int (*set_dev_state_req)(struct pm8001_hba_info *pm8001_ha,
+ struct pm8001_device *pm8001_dev, u32 state);
+ int (*sas_diag_start_end_req)(struct pm8001_hba_info *pm8001_ha,
+ u32 state);
+ int (*sas_diag_execute_req)(struct pm8001_hba_info *pm8001_ha,
+ u32 state);
+ int (*sas_re_init_req)(struct pm8001_hba_info *pm8001_ha);
+};
+
+struct pm8001_chip_info {
+ u32 n_phy;
+ const struct pm8001_dispatch *dispatch;
+};
+#define PM8001_CHIP_DISP (pm8001_ha->chip->dispatch)
+
+struct pm8001_port {
+ struct asd_sas_port sas_port;
+};
+
+struct pm8001_phy {
+ struct pm8001_hba_info *pm8001_ha;
+ struct pm8001_port *port;
+ struct asd_sas_phy sas_phy;
+ struct sas_identify identify;
+ struct scsi_device *sdev;
+ u64 dev_sas_addr;
+ u32 phy_type;
+ struct completion *enable_completion;
+ u32 frame_rcvd_size;
+ u8 frame_rcvd[32];
+ u8 phy_attached;
+ u8 phy_state;
+ enum sas_linkrate minimum_linkrate;
+ enum sas_linkrate maximum_linkrate;
+};
+
+struct pm8001_device {
+ enum sas_dev_type dev_type;
+ struct domain_device *sas_device;
+ u32 attached_phy;
+ u32 id;
+ struct completion *dcompletion;
+ struct completion *setds_completion;
+ u32 device_id;
+ u32 running_req;
+};
+
+struct pm8001_prd_imt {
+ __le32 len;
+ __le32 e;
+};
+
+struct pm8001_prd {
+ __le64 addr; /* 64-bit buffer address */
+ struct pm8001_prd_imt im_len; /* 64-bit length */
+} __attribute__ ((packed));
+/*
+ * CCB(Command Control Block)
+ */
+struct pm8001_ccb_info {
+ struct list_head entry;
+ struct sas_task *task;
+ u32 n_elem;
+ u32 ccb_tag;
+ dma_addr_t ccb_dma_handle;
+ struct pm8001_device *device;
+ struct pm8001_prd buf_prd[PM8001_MAX_DMA_SG];
+ struct fw_control_ex *fw_control_context;
+};
+
+struct mpi_mem {
+ void *virt_ptr;
+ dma_addr_t phys_addr;
+ u32 phys_addr_hi;
+ u32 phys_addr_lo;
+ u32 total_len;
+ u32 num_elements;
+ u32 element_size;
+ u32 alignment;
+};
+
+struct mpi_mem_req {
+ /* The number of element in the mpiMemory array */
+ u32 count;
+ /* The array of structures that define memroy regions*/
+ struct mpi_mem region[USI_MAX_MEMCNT];
+};
+
+struct main_cfg_table {
+ u32 signature;
+ u32 interface_rev;
+ u32 firmware_rev;
+ u32 max_out_io;
+ u32 max_sgl;
+ u32 ctrl_cap_flag;
+ u32 gst_offset;
+ u32 inbound_queue_offset;
+ u32 outbound_queue_offset;
+ u32 inbound_q_nppd_hppd;
+ u32 outbound_hw_event_pid0_3;
+ u32 outbound_hw_event_pid4_7;
+ u32 outbound_ncq_event_pid0_3;
+ u32 outbound_ncq_event_pid4_7;
+ u32 outbound_tgt_ITNexus_event_pid0_3;
+ u32 outbound_tgt_ITNexus_event_pid4_7;
+ u32 outbound_tgt_ssp_event_pid0_3;
+ u32 outbound_tgt_ssp_event_pid4_7;
+ u32 outbound_tgt_smp_event_pid0_3;
+ u32 outbound_tgt_smp_event_pid4_7;
+ u32 upper_event_log_addr;
+ u32 lower_event_log_addr;
+ u32 event_log_size;
+ u32 event_log_option;
+ u32 upper_iop_event_log_addr;
+ u32 lower_iop_event_log_addr;
+ u32 iop_event_log_size;
+ u32 iop_event_log_option;
+ u32 fatal_err_interrupt;
+ u32 fatal_err_dump_offset0;
+ u32 fatal_err_dump_length0;
+ u32 fatal_err_dump_offset1;
+ u32 fatal_err_dump_length1;
+ u32 hda_mode_flag;
+ u32 anolog_setup_table_offset;
+};
+struct general_status_table {
+ u32 gst_len_mpistate;
+ u32 iq_freeze_state0;
+ u32 iq_freeze_state1;
+ u32 msgu_tcnt;
+ u32 iop_tcnt;
+ u32 reserved;
+ u32 phy_state[8];
+ u32 reserved1;
+ u32 reserved2;
+ u32 reserved3;
+ u32 recover_err_info[8];
+};
+struct inbound_queue_table {
+ u32 element_pri_size_cnt;
+ u32 upper_base_addr;
+ u32 lower_base_addr;
+ u32 ci_upper_base_addr;
+ u32 ci_lower_base_addr;
+ u32 pi_pci_bar;
+ u32 pi_offset;
+ u32 total_length;
+ void *base_virt;
+ void *ci_virt;
+ u32 reserved;
+ __le32 consumer_index;
+ u32 producer_idx;
+};
+struct outbound_queue_table {
+ u32 element_size_cnt;
+ u32 upper_base_addr;
+ u32 lower_base_addr;
+ void *base_virt;
+ u32 pi_upper_base_addr;
+ u32 pi_lower_base_addr;
+ u32 ci_pci_bar;
+ u32 ci_offset;
+ u32 total_length;
+ void *pi_virt;
+ u32 interrup_vec_cnt_delay;
+ u32 dinterrup_to_pci_offset;
+ __le32 producer_index;
+ u32 consumer_idx;
+};
+struct pm8001_hba_memspace {
+ void __iomem *memvirtaddr;
+ u64 membase;
+ u32 memsize;
+};
+struct pm8001_hba_info {
+ char name[PM8001_NAME_LENGTH];
+ struct list_head list;
+ unsigned long flags;
+ spinlock_t lock;/* host-wide lock */
+ struct pci_dev *pdev;/* our device */
+ struct device *dev;
+ struct pm8001_hba_memspace io_mem[6];
+ struct mpi_mem_req memoryMap;
+ void __iomem *msg_unit_tbl_addr;/*Message Unit Table Addr*/
+ void __iomem *main_cfg_tbl_addr;/*Main Config Table Addr*/
+ void __iomem *general_stat_tbl_addr;/*General Status Table Addr*/
+ void __iomem *inbnd_q_tbl_addr;/*Inbound Queue Config Table Addr*/
+ void __iomem *outbnd_q_tbl_addr;/*Outbound Queue Config Table Addr*/
+ struct main_cfg_table main_cfg_tbl;
+ struct general_status_table gs_tbl;
+ struct inbound_queue_table inbnd_q_tbl[PM8001_MAX_INB_NUM];
+ struct outbound_queue_table outbnd_q_tbl[PM8001_MAX_OUTB_NUM];
+ u8 sas_addr[SAS_ADDR_SIZE];
+ struct sas_ha_struct *sas;/* SCSI/SAS glue */
+ struct Scsi_Host *shost;
+ u32 chip_id;
+ const struct pm8001_chip_info *chip;
+ struct completion *nvmd_completion;
+ int tags_num;
+ unsigned long *tags;
+ struct pm8001_phy phy[PM8001_MAX_PHYS];
+ struct pm8001_port port[PM8001_MAX_PHYS];
+ u32 id;
+ u32 irq;
+ struct pm8001_device *devices;
+ struct pm8001_ccb_info *ccb_info;
+#ifdef PM8001_USE_MSIX
+ struct msix_entry msix_entries[16];/*for msi-x interrupt*/
+ int number_of_intr;/*will be used in remove()*/
+#endif
+#ifdef PM8001_USE_TASKLET
+ struct tasklet_struct tasklet;
+#endif
+ struct list_head wq_list;
+ u32 logging_level;
+ u32 fw_status;
+ const struct firmware *fw_image;
+};
+
+struct pm8001_wq {
+ struct delayed_work work_q;
+ struct pm8001_hba_info *pm8001_ha;
+ void *data;
+ int handler;
+ struct list_head entry;
+};
+
+struct pm8001_fw_image_header {
+ u8 vender_id[8];
+ u8 product_id;
+ u8 hardware_rev;
+ u8 dest_partition;
+ u8 reserved;
+ u8 fw_rev[4];
+ __be32 image_length;
+ __be32 image_crc;
+ __be32 startup_entry;
+} __attribute__((packed, aligned(4)));
+
+/* define task management IU */
+struct pm8001_tmf_task {
+ u8 tmf;
+ u32 tag_of_task_to_be_managed;
+};
+/**
+ * FW Flash Update status values
+ */
+#define FLASH_UPDATE_COMPLETE_PENDING_REBOOT 0x00
+#define FLASH_UPDATE_IN_PROGRESS 0x01
+#define FLASH_UPDATE_HDR_ERR 0x02
+#define FLASH_UPDATE_OFFSET_ERR 0x03
+#define FLASH_UPDATE_CRC_ERR 0x04
+#define FLASH_UPDATE_LENGTH_ERR 0x05
+#define FLASH_UPDATE_HW_ERR 0x06
+#define FLASH_UPDATE_DNLD_NOT_SUPPORTED 0x10
+#define FLASH_UPDATE_DISABLED 0x11
+
+/**
+ * brief param structure for firmware flash update.
+ */
+struct fw_flash_updata_info {
+ u32 cur_image_offset;
+ u32 cur_image_len;
+ u32 total_image_len;
+ struct pm8001_prd sgl;
+};
+
+struct fw_control_info {
+ u32 retcode;/*ret code (status)*/
+ u32 phase;/*ret code phase*/
+ u32 phaseCmplt;/*percent complete for the current
+ update phase */
+ u32 version;/*Hex encoded firmware version number*/
+ u32 offset;/*Used for downloading firmware */
+ u32 len; /*len of buffer*/
+ u32 size;/* Used in OS VPD and Trace get size
+ operations.*/
+ u32 reserved;/* padding required for 64 bit
+ alignment */
+ u8 buffer[1];/* Start of buffer */
+};
+struct fw_control_ex {
+ struct fw_control_info *fw_control;
+ void *buffer;/* keep buffer pointer to be
+ freed when the responce comes*/
+ void *virtAddr;/* keep virtual address of the data */
+ void *usrAddr;/* keep virtual address of the
+ user data */
+ dma_addr_t phys_addr;
+ u32 len; /* len of buffer */
+ void *payload; /* pointer to IOCTL Payload */
+ u8 inProgress;/*if 1 - the IOCTL request is in
+ progress */
+ void *param1;
+ void *param2;
+ void *param3;
+};
+
+/******************** function prototype *********************/
+int pm8001_tag_alloc(struct pm8001_hba_info *pm8001_ha, u32 *tag_out);
+void pm8001_tag_init(struct pm8001_hba_info *pm8001_ha);
+u32 pm8001_get_ncq_tag(struct sas_task *task, u32 *tag);
+void pm8001_ccb_free(struct pm8001_hba_info *pm8001_ha, u32 ccb_idx);
+void pm8001_ccb_task_free(struct pm8001_hba_info *pm8001_ha,
+ struct sas_task *task, struct pm8001_ccb_info *ccb, u32 ccb_idx);
+int pm8001_phy_control(struct asd_sas_phy *sas_phy, enum phy_func func,
+ void *funcdata);
+int pm8001_slave_alloc(struct scsi_device *scsi_dev);
+int pm8001_slave_configure(struct scsi_device *sdev);
+void pm8001_scan_start(struct Scsi_Host *shost);
+int pm8001_scan_finished(struct Scsi_Host *shost, unsigned long time);
+int pm8001_queue_command(struct sas_task *task, const int num,
+ gfp_t gfp_flags);
+int pm8001_abort_task(struct sas_task *task);
+int pm8001_abort_task_set(struct domain_device *dev, u8 *lun);
+int pm8001_clear_aca(struct domain_device *dev, u8 *lun);
+int pm8001_clear_task_set(struct domain_device *dev, u8 *lun);
+int pm8001_dev_found(struct domain_device *dev);
+void pm8001_dev_gone(struct domain_device *dev);
+int pm8001_lu_reset(struct domain_device *dev, u8 *lun);
+int pm8001_I_T_nexus_reset(struct domain_device *dev);
+int pm8001_query_task(struct sas_task *task);
+int pm8001_mem_alloc(struct pci_dev *pdev, void **virt_addr,
+ dma_addr_t *pphys_addr, u32 *pphys_addr_hi, u32 *pphys_addr_lo,
+ u32 mem_size, u32 align);
+
+
+/* ctl shared API */
+extern struct device_attribute *pm8001_host_attrs[];
+
+#endif
+
diff --git a/drivers/scsi/pmcraid.c b/drivers/scsi/pmcraid.c
index 0e24abd0d30..34c6b896a91 100644
--- a/drivers/scsi/pmcraid.c
+++ b/drivers/scsi/pmcraid.c
@@ -278,12 +278,17 @@ static void pmcraid_slave_destroy(struct scsi_device *scsi_dev)
* pmcraid_change_queue_depth - Change the device's queue depth
* @scsi_dev: scsi device struct
* @depth: depth to set
+ * @reason: calling context
*
* Return value
* actual depth set
*/
-static int pmcraid_change_queue_depth(struct scsi_device *scsi_dev, int depth)
+static int pmcraid_change_queue_depth(struct scsi_device *scsi_dev, int depth,
+ int reason)
{
+ if (reason != SCSI_QDEPTH_DEFAULT)
+ return -EOPNOTSUPP;
+
if (depth > PMCRAID_MAX_CMD_PER_LUN)
depth = PMCRAID_MAX_CMD_PER_LUN;
diff --git a/drivers/scsi/qla2xxx/qla_attr.c b/drivers/scsi/qla2xxx/qla_attr.c
index fbcb82a2f7f..21e2bc4d740 100644
--- a/drivers/scsi/qla2xxx/qla_attr.c
+++ b/drivers/scsi/qla2xxx/qla_attr.c
@@ -1654,7 +1654,8 @@ qla24xx_vport_create(struct fc_vport *fc_vport, bool disable)
fc_vport_set_state(fc_vport, FC_VPORT_LINKDOWN);
}
- if (scsi_add_host(vha->host, &fc_vport->dev)) {
+ if (scsi_add_host_with_dma(vha->host, &fc_vport->dev,
+ &ha->pdev->dev)) {
DEBUG15(printk("scsi(%ld): scsi_add_host failure for VP[%d].\n",
vha->host_no, vha->vp_idx));
goto vport_create_failed_2;
diff --git a/drivers/scsi/qla2xxx/qla_dbg.c b/drivers/scsi/qla2xxx/qla_dbg.c
index cca8e4ab037..cb2eca4c26d 100644
--- a/drivers/scsi/qla2xxx/qla_dbg.c
+++ b/drivers/scsi/qla2xxx/qla_dbg.c
@@ -377,6 +377,24 @@ qla25xx_copy_mq(struct qla_hw_data *ha, void *ptr, uint32_t **last_chain)
return ptr + sizeof(struct qla2xxx_mq_chain);
}
+static void
+qla2xxx_dump_post_process(scsi_qla_host_t *vha, int rval)
+{
+ struct qla_hw_data *ha = vha->hw;
+
+ if (rval != QLA_SUCCESS) {
+ qla_printk(KERN_WARNING, ha,
+ "Failed to dump firmware (%x)!!!\n", rval);
+ ha->fw_dumped = 0;
+ } else {
+ qla_printk(KERN_INFO, ha,
+ "Firmware dump saved to temp buffer (%ld/%p).\n",
+ vha->host_no, ha->fw_dump);
+ ha->fw_dumped = 1;
+ qla2x00_post_uevent_work(vha, QLA_UEVENT_CODE_FW_DUMP);
+ }
+}
+
/**
* qla2300_fw_dump() - Dumps binary data from the 2300 firmware.
* @ha: HA context
@@ -530,17 +548,7 @@ qla2300_fw_dump(scsi_qla_host_t *vha, int hardware_locked)
if (rval == QLA_SUCCESS)
qla2xxx_copy_queues(ha, nxt);
- if (rval != QLA_SUCCESS) {
- qla_printk(KERN_WARNING, ha,
- "Failed to dump firmware (%x)!!!\n", rval);
- ha->fw_dumped = 0;
-
- } else {
- qla_printk(KERN_INFO, ha,
- "Firmware dump saved to temp buffer (%ld/%p).\n",
- base_vha->host_no, ha->fw_dump);
- ha->fw_dumped = 1;
- }
+ qla2xxx_dump_post_process(base_vha, rval);
qla2300_fw_dump_failed:
if (!hardware_locked)
@@ -737,17 +745,7 @@ qla2100_fw_dump(scsi_qla_host_t *vha, int hardware_locked)
if (rval == QLA_SUCCESS)
qla2xxx_copy_queues(ha, &fw->risc_ram[cnt]);
- if (rval != QLA_SUCCESS) {
- qla_printk(KERN_WARNING, ha,
- "Failed to dump firmware (%x)!!!\n", rval);
- ha->fw_dumped = 0;
-
- } else {
- qla_printk(KERN_INFO, ha,
- "Firmware dump saved to temp buffer (%ld/%p).\n",
- base_vha->host_no, ha->fw_dump);
- ha->fw_dumped = 1;
- }
+ qla2xxx_dump_post_process(base_vha, rval);
qla2100_fw_dump_failed:
if (!hardware_locked)
@@ -984,17 +982,7 @@ qla24xx_fw_dump(scsi_qla_host_t *vha, int hardware_locked)
qla24xx_copy_eft(ha, nxt);
qla24xx_fw_dump_failed_0:
- if (rval != QLA_SUCCESS) {
- qla_printk(KERN_WARNING, ha,
- "Failed to dump firmware (%x)!!!\n", rval);
- ha->fw_dumped = 0;
-
- } else {
- qla_printk(KERN_INFO, ha,
- "Firmware dump saved to temp buffer (%ld/%p).\n",
- base_vha->host_no, ha->fw_dump);
- ha->fw_dumped = 1;
- }
+ qla2xxx_dump_post_process(base_vha, rval);
qla24xx_fw_dump_failed:
if (!hardware_locked)
@@ -1305,17 +1293,7 @@ qla25xx_fw_dump(scsi_qla_host_t *vha, int hardware_locked)
}
qla25xx_fw_dump_failed_0:
- if (rval != QLA_SUCCESS) {
- qla_printk(KERN_WARNING, ha,
- "Failed to dump firmware (%x)!!!\n", rval);
- ha->fw_dumped = 0;
-
- } else {
- qla_printk(KERN_INFO, ha,
- "Firmware dump saved to temp buffer (%ld/%p).\n",
- base_vha->host_no, ha->fw_dump);
- ha->fw_dumped = 1;
- }
+ qla2xxx_dump_post_process(base_vha, rval);
qla25xx_fw_dump_failed:
if (!hardware_locked)
@@ -1628,17 +1606,7 @@ qla81xx_fw_dump(scsi_qla_host_t *vha, int hardware_locked)
}
qla81xx_fw_dump_failed_0:
- if (rval != QLA_SUCCESS) {
- qla_printk(KERN_WARNING, ha,
- "Failed to dump firmware (%x)!!!\n", rval);
- ha->fw_dumped = 0;
-
- } else {
- qla_printk(KERN_INFO, ha,
- "Firmware dump saved to temp buffer (%ld/%p).\n",
- base_vha->host_no, ha->fw_dump);
- ha->fw_dumped = 1;
- }
+ qla2xxx_dump_post_process(base_vha, rval);
qla81xx_fw_dump_failed:
if (!hardware_locked)
diff --git a/drivers/scsi/qla2xxx/qla_def.h b/drivers/scsi/qla2xxx/qla_def.h
index 21506186179..6b9bf23c773 100644
--- a/drivers/scsi/qla2xxx/qla_def.h
+++ b/drivers/scsi/qla2xxx/qla_def.h
@@ -2123,6 +2123,7 @@ enum qla_work_type {
QLA_EVT_ASYNC_LOGIN_DONE,
QLA_EVT_ASYNC_LOGOUT,
QLA_EVT_ASYNC_LOGOUT_DONE,
+ QLA_EVT_UEVENT,
};
@@ -2146,6 +2147,10 @@ struct qla_work_evt {
#define QLA_LOGIO_LOGIN_RETRIED BIT_0
u16 data[2];
} logio;
+ struct {
+ u32 code;
+#define QLA_UEVENT_CODE_FW_DUMP 0
+ } uevent;
} u;
};
@@ -2435,11 +2440,11 @@ struct qla_hw_data {
dma_addr_t edc_data_dma;
uint16_t edc_data_len;
-#define XGMAC_DATA_SIZE PAGE_SIZE
+#define XGMAC_DATA_SIZE 4096
void *xgmac_data;
dma_addr_t xgmac_data_dma;
-#define DCBX_TLV_DATA_SIZE PAGE_SIZE
+#define DCBX_TLV_DATA_SIZE 4096
void *dcbx_tlv;
dma_addr_t dcbx_tlv_dma;
diff --git a/drivers/scsi/qla2xxx/qla_gbl.h b/drivers/scsi/qla2xxx/qla_gbl.h
index f3d1d1afa95..e2185135850 100644
--- a/drivers/scsi/qla2xxx/qla_gbl.h
+++ b/drivers/scsi/qla2xxx/qla_gbl.h
@@ -92,6 +92,7 @@ extern int qla2x00_post_async_logout_work(struct scsi_qla_host *, fc_port_t *,
uint16_t *);
extern int qla2x00_post_async_logout_done_work(struct scsi_qla_host *,
fc_port_t *, uint16_t *);
+extern int qla2x00_post_uevent_work(struct scsi_qla_host *, u32);
extern int qla81xx_restart_mpi_firmware(scsi_qla_host_t *);
@@ -246,7 +247,7 @@ qla2x00_get_id_list(scsi_qla_host_t *, void *, dma_addr_t, uint16_t *);
extern int
qla2x00_get_resource_cnts(scsi_qla_host_t *, uint16_t *, uint16_t *,
- uint16_t *, uint16_t *, uint16_t *);
+ uint16_t *, uint16_t *, uint16_t *, uint16_t *);
extern int
qla2x00_get_fcal_position_map(scsi_qla_host_t *ha, char *pos_map);
diff --git a/drivers/scsi/qla2xxx/qla_init.c b/drivers/scsi/qla2xxx/qla_init.c
index 9e3eaac2559..b74924b279e 100644
--- a/drivers/scsi/qla2xxx/qla_init.c
+++ b/drivers/scsi/qla2xxx/qla_init.c
@@ -277,7 +277,6 @@ qla2x00_initialize_adapter(scsi_qla_host_t *vha)
vha->marker_needed = 0;
ha->isp_abort_cnt = 0;
ha->beacon_blink_led = 0;
- set_bit(REGISTER_FDMI_NEEDED, &vha->dpc_flags);
set_bit(0, ha->req_qid_map);
set_bit(0, ha->rsp_qid_map);
@@ -1203,7 +1202,7 @@ qla2x00_setup_chip(scsi_qla_host_t *vha)
}
qla2x00_get_resource_cnts(vha, NULL,
&ha->fw_xcb_count, NULL, NULL,
- &ha->max_npiv_vports);
+ &ha->max_npiv_vports, NULL);
if (!fw_major_version && ql2xallocfwdump)
qla2x00_alloc_fw_dump(vha);
@@ -3573,6 +3572,15 @@ qla2x00_abort_isp(scsi_qla_host_t *vha)
ha->isp_abort_cnt = 0;
clear_bit(ISP_ABORT_RETRY, &vha->dpc_flags);
+ if (IS_QLA81XX(ha))
+ qla2x00_get_fw_version(vha,
+ &ha->fw_major_version,
+ &ha->fw_minor_version,
+ &ha->fw_subminor_version,
+ &ha->fw_attributes, &ha->fw_memory_size,
+ ha->mpi_version, &ha->mpi_capabilities,
+ ha->phy_version);
+
if (ha->fce) {
ha->flags.fce_enabled = 1;
memset(ha->fce, 0,
diff --git a/drivers/scsi/qla2xxx/qla_isr.c b/drivers/scsi/qla2xxx/qla_isr.c
index b20a7169aac..804987397b7 100644
--- a/drivers/scsi/qla2xxx/qla_isr.c
+++ b/drivers/scsi/qla2xxx/qla_isr.c
@@ -313,10 +313,11 @@ qla2x00_async_event(scsi_qla_host_t *vha, struct rsp_que *rsp, uint16_t *mb)
static char *link_speeds[] = { "1", "2", "?", "4", "8", "10" };
char *link_speed;
uint16_t handle_cnt;
- uint16_t cnt;
+ uint16_t cnt, mbx;
uint32_t handles[5];
struct qla_hw_data *ha = vha->hw;
struct device_reg_2xxx __iomem *reg = &ha->iobase->isp;
+ struct device_reg_24xx __iomem *reg24 = &ha->iobase->isp24;
uint32_t rscn_entry, host_pid;
uint8_t rscn_queue_index;
unsigned long flags;
@@ -395,9 +396,10 @@ skip_rio:
break;
case MBA_SYSTEM_ERR: /* System Error */
+ mbx = IS_QLA81XX(ha) ? RD_REG_WORD(&reg24->mailbox7) : 0;
qla_printk(KERN_INFO, ha,
- "ISP System Error - mbx1=%xh mbx2=%xh mbx3=%xh.\n",
- mb[1], mb[2], mb[3]);
+ "ISP System Error - mbx1=%xh mbx2=%xh mbx3=%xh "
+ "mbx7=%xh.\n", mb[1], mb[2], mb[3], mbx);
ha->isp_ops->fw_dump(vha, 1);
@@ -419,9 +421,10 @@ skip_rio:
break;
case MBA_REQ_TRANSFER_ERR: /* Request Transfer Error */
- DEBUG2(printk("scsi(%ld): ISP Request Transfer Error.\n",
- vha->host_no));
- qla_printk(KERN_WARNING, ha, "ISP Request Transfer Error.\n");
+ DEBUG2(printk("scsi(%ld): ISP Request Transfer Error (%x).\n",
+ vha->host_no, mb[1]));
+ qla_printk(KERN_WARNING, ha,
+ "ISP Request Transfer Error (%x).\n", mb[1]);
set_bit(ISP_ABORT_NEEDED, &vha->dpc_flags);
break;
@@ -485,10 +488,13 @@ skip_rio:
break;
case MBA_LOOP_DOWN: /* Loop Down Event */
+ mbx = IS_QLA81XX(ha) ? RD_REG_WORD(&reg24->mailbox4) : 0;
DEBUG2(printk("scsi(%ld): Asynchronous LOOP DOWN "
- "(%x %x %x).\n", vha->host_no, mb[1], mb[2], mb[3]));
- qla_printk(KERN_INFO, ha, "LOOP DOWN detected (%x %x %x).\n",
- mb[1], mb[2], mb[3]);
+ "(%x %x %x %x).\n", vha->host_no, mb[1], mb[2], mb[3],
+ mbx));
+ qla_printk(KERN_INFO, ha,
+ "LOOP DOWN detected (%x %x %x %x).\n", mb[1], mb[2], mb[3],
+ mbx);
if (atomic_read(&vha->loop_state) != LOOP_DOWN) {
atomic_set(&vha->loop_state, LOOP_DOWN);
@@ -1347,16 +1353,22 @@ qla2x00_status_entry(scsi_qla_host_t *vha, struct rsp_que *rsp, void *pkt)
sense_len = rsp_info_len = resid_len = fw_resid_len = 0;
if (IS_FWI2_CAPABLE(ha)) {
- sense_len = le32_to_cpu(sts24->sense_len);
- rsp_info_len = le32_to_cpu(sts24->rsp_data_len);
- resid_len = le32_to_cpu(sts24->rsp_residual_count);
- fw_resid_len = le32_to_cpu(sts24->residual_len);
+ if (scsi_status & SS_SENSE_LEN_VALID)
+ sense_len = le32_to_cpu(sts24->sense_len);
+ if (scsi_status & SS_RESPONSE_INFO_LEN_VALID)
+ rsp_info_len = le32_to_cpu(sts24->rsp_data_len);
+ if (scsi_status & (SS_RESIDUAL_UNDER | SS_RESIDUAL_OVER))
+ resid_len = le32_to_cpu(sts24->rsp_residual_count);
+ if (comp_status == CS_DATA_UNDERRUN)
+ fw_resid_len = le32_to_cpu(sts24->residual_len);
rsp_info = sts24->data;
sense_data = sts24->data;
host_to_fcp_swap(sts24->data, sizeof(sts24->data));
} else {
- sense_len = le16_to_cpu(sts->req_sense_length);
- rsp_info_len = le16_to_cpu(sts->rsp_info_len);
+ if (scsi_status & SS_SENSE_LEN_VALID)
+ sense_len = le16_to_cpu(sts->req_sense_length);
+ if (scsi_status & SS_RESPONSE_INFO_LEN_VALID)
+ rsp_info_len = le16_to_cpu(sts->rsp_info_len);
resid_len = le32_to_cpu(sts->residual_length);
rsp_info = sts->rsp_info;
sense_data = sts->req_sense_data;
@@ -1443,38 +1455,62 @@ qla2x00_status_entry(scsi_qla_host_t *vha, struct rsp_que *rsp, void *pkt)
break;
case CS_DATA_UNDERRUN:
- resid = resid_len;
+ DEBUG2(printk(KERN_INFO
+ "scsi(%ld:%d:%d) UNDERRUN status detected 0x%x-0x%x. "
+ "resid=0x%x fw_resid=0x%x cdb=0x%x os_underflow=0x%x\n",
+ vha->host_no, cp->device->id, cp->device->lun, comp_status,
+ scsi_status, resid_len, fw_resid_len, cp->cmnd[0],
+ cp->underflow));
+
/* Use F/W calculated residual length. */
- if (IS_FWI2_CAPABLE(ha)) {
- if (!(scsi_status & SS_RESIDUAL_UNDER)) {
- lscsi_status = 0;
- } else if (resid != fw_resid_len) {
- scsi_status &= ~SS_RESIDUAL_UNDER;
- lscsi_status = 0;
+ resid = IS_FWI2_CAPABLE(ha) ? fw_resid_len : resid_len;
+ scsi_set_resid(cp, resid);
+ if (scsi_status & SS_RESIDUAL_UNDER) {
+ if (IS_FWI2_CAPABLE(ha) && fw_resid_len != resid_len) {
+ DEBUG2(printk(
+ "scsi(%ld:%d:%d:%d) Dropped frame(s) "
+ "detected (%x of %x bytes)...residual "
+ "length mismatch...retrying command.\n",
+ vha->host_no, cp->device->channel,
+ cp->device->id, cp->device->lun, resid,
+ scsi_bufflen(cp)));
+
+ cp->result = DID_ERROR << 16 | lscsi_status;
+ break;
}
- resid = fw_resid_len;
- }
- if (scsi_status & SS_RESIDUAL_UNDER) {
- scsi_set_resid(cp, resid);
- } else {
- DEBUG2(printk(KERN_INFO
- "scsi(%ld:%d:%d) UNDERRUN status detected "
- "0x%x-0x%x. resid=0x%x fw_resid=0x%x cdb=0x%x "
- "os_underflow=0x%x\n", vha->host_no,
- cp->device->id, cp->device->lun, comp_status,
- scsi_status, resid_len, resid, cp->cmnd[0],
- cp->underflow));
+ if (!lscsi_status &&
+ ((unsigned)(scsi_bufflen(cp) - resid) <
+ cp->underflow)) {
+ qla_printk(KERN_INFO, ha,
+ "scsi(%ld:%d:%d:%d): Mid-layer underflow "
+ "detected (%x of %x bytes)...returning "
+ "error status.\n", vha->host_no,
+ cp->device->channel, cp->device->id,
+ cp->device->lun, resid, scsi_bufflen(cp));
+
+ cp->result = DID_ERROR << 16;
+ break;
+ }
+ } else if (!lscsi_status) {
+ DEBUG2(printk(
+ "scsi(%ld:%d:%d:%d) Dropped frame(s) detected "
+ "(%x of %x bytes)...firmware reported underrun..."
+ "retrying command.\n", vha->host_no,
+ cp->device->channel, cp->device->id,
+ cp->device->lun, resid, scsi_bufflen(cp)));
+ cp->result = DID_ERROR << 16;
+ break;
}
+ cp->result = DID_OK << 16 | lscsi_status;
+
/*
* Check to see if SCSI Status is non zero. If so report SCSI
* Status.
*/
if (lscsi_status != 0) {
- cp->result = DID_OK << 16 | lscsi_status;
-
if (lscsi_status == SAM_STAT_TASK_SET_FULL) {
DEBUG2(printk(KERN_INFO
"scsi(%ld): QUEUE FULL status detected "
@@ -1501,42 +1537,6 @@ qla2x00_status_entry(scsi_qla_host_t *vha, struct rsp_que *rsp, void *pkt)
break;
qla2x00_handle_sense(sp, sense_data, sense_len, rsp);
- } else {
- /*
- * If RISC reports underrun and target does not report
- * it then we must have a lost frame, so tell upper
- * layer to retry it by reporting an error.
- */
- if (!(scsi_status & SS_RESIDUAL_UNDER)) {
- DEBUG2(printk("scsi(%ld:%d:%d:%d) Dropped "
- "frame(s) detected (%x of %x bytes)..."
- "retrying command.\n",
- vha->host_no, cp->device->channel,
- cp->device->id, cp->device->lun, resid,
- scsi_bufflen(cp)));
-
- scsi_set_resid(cp, resid);
- cp->result = DID_ERROR << 16;
- break;
- }
-
- /* Handle mid-layer underflow */
- if ((unsigned)(scsi_bufflen(cp) - resid) <
- cp->underflow) {
- qla_printk(KERN_INFO, ha,
- "scsi(%ld:%d:%d:%d): Mid-layer underflow "
- "detected (%x of %x bytes)...returning "
- "error status.\n", vha->host_no,
- cp->device->channel, cp->device->id,
- cp->device->lun, resid,
- scsi_bufflen(cp));
-
- cp->result = DID_ERROR << 16;
- break;
- }
-
- /* Everybody online, looking good... */
- cp->result = DID_OK << 16;
}
break;
diff --git a/drivers/scsi/qla2xxx/qla_mbx.c b/drivers/scsi/qla2xxx/qla_mbx.c
index b6202fe118a..05d595d9a7e 100644
--- a/drivers/scsi/qla2xxx/qla_mbx.c
+++ b/drivers/scsi/qla2xxx/qla_mbx.c
@@ -2006,7 +2006,7 @@ qla2x00_get_id_list(scsi_qla_host_t *vha, void *id_list, dma_addr_t id_list_dma,
int
qla2x00_get_resource_cnts(scsi_qla_host_t *vha, uint16_t *cur_xchg_cnt,
uint16_t *orig_xchg_cnt, uint16_t *cur_iocb_cnt,
- uint16_t *orig_iocb_cnt, uint16_t *max_npiv_vports)
+ uint16_t *orig_iocb_cnt, uint16_t *max_npiv_vports, uint16_t *max_fcfs)
{
int rval;
mbx_cmd_t mc;
@@ -2017,6 +2017,8 @@ qla2x00_get_resource_cnts(scsi_qla_host_t *vha, uint16_t *cur_xchg_cnt,
mcp->mb[0] = MBC_GET_RESOURCE_COUNTS;
mcp->out_mb = MBX_0;
mcp->in_mb = MBX_11|MBX_10|MBX_7|MBX_6|MBX_3|MBX_2|MBX_1|MBX_0;
+ if (IS_QLA81XX(vha->hw))
+ mcp->in_mb |= MBX_12;
mcp->tov = MBX_TOV_SECONDS;
mcp->flags = 0;
rval = qla2x00_mailbox_command(vha, mcp);
@@ -2027,9 +2029,10 @@ qla2x00_get_resource_cnts(scsi_qla_host_t *vha, uint16_t *cur_xchg_cnt,
vha->host_no, mcp->mb[0]));
} else {
DEBUG11(printk("%s(%ld): done. mb1=%x mb2=%x mb3=%x mb6=%x "
- "mb7=%x mb10=%x mb11=%x.\n", __func__, vha->host_no,
- mcp->mb[1], mcp->mb[2], mcp->mb[3], mcp->mb[6], mcp->mb[7],
- mcp->mb[10], mcp->mb[11]));
+ "mb7=%x mb10=%x mb11=%x mb12=%x.\n", __func__,
+ vha->host_no, mcp->mb[1], mcp->mb[2], mcp->mb[3],
+ mcp->mb[6], mcp->mb[7], mcp->mb[10], mcp->mb[11],
+ mcp->mb[12]));
if (cur_xchg_cnt)
*cur_xchg_cnt = mcp->mb[3];
@@ -2041,6 +2044,8 @@ qla2x00_get_resource_cnts(scsi_qla_host_t *vha, uint16_t *cur_xchg_cnt,
*orig_iocb_cnt = mcp->mb[10];
if (vha->hw->flags.npiv_supported && max_npiv_vports)
*max_npiv_vports = mcp->mb[11];
+ if (IS_QLA81XX(vha->hw) && max_fcfs)
+ *max_fcfs = mcp->mb[12];
}
return (rval);
@@ -2313,6 +2318,7 @@ __qla24xx_issue_tmf(char *name, uint32_t type, struct fc_port *fcport,
{
int rval, rval2;
struct tsk_mgmt_cmd *tsk;
+ struct sts_entry_24xx *sts;
dma_addr_t tsk_dma;
scsi_qla_host_t *vha;
struct qla_hw_data *ha;
@@ -2352,20 +2358,37 @@ __qla24xx_issue_tmf(char *name, uint32_t type, struct fc_port *fcport,
sizeof(tsk->p.tsk.lun));
}
+ sts = &tsk->p.sts;
rval = qla2x00_issue_iocb(vha, tsk, tsk_dma, 0);
if (rval != QLA_SUCCESS) {
DEBUG2_3_11(printk("%s(%ld): failed to issue %s Reset IOCB "
"(%x).\n", __func__, vha->host_no, name, rval));
- } else if (tsk->p.sts.entry_status != 0) {
+ } else if (sts->entry_status != 0) {
DEBUG2_3_11(printk("%s(%ld): failed to complete IOCB "
"-- error status (%x).\n", __func__, vha->host_no,
- tsk->p.sts.entry_status));
+ sts->entry_status));
rval = QLA_FUNCTION_FAILED;
- } else if (tsk->p.sts.comp_status !=
+ } else if (sts->comp_status !=
__constant_cpu_to_le16(CS_COMPLETE)) {
DEBUG2_3_11(printk("%s(%ld): failed to complete IOCB "
"-- completion status (%x).\n", __func__,
- vha->host_no, le16_to_cpu(tsk->p.sts.comp_status)));
+ vha->host_no, le16_to_cpu(sts->comp_status)));
+ rval = QLA_FUNCTION_FAILED;
+ } else if (!(le16_to_cpu(sts->scsi_status) &
+ SS_RESPONSE_INFO_LEN_VALID)) {
+ DEBUG2_3_11(printk("%s(%ld): failed to complete IOCB "
+ "-- no response info (%x).\n", __func__, vha->host_no,
+ le16_to_cpu(sts->scsi_status)));
+ rval = QLA_FUNCTION_FAILED;
+ } else if (le32_to_cpu(sts->rsp_data_len) < 4) {
+ DEBUG2_3_11(printk("%s(%ld): failed to complete IOCB "
+ "-- not enough response info (%d).\n", __func__,
+ vha->host_no, le32_to_cpu(sts->rsp_data_len)));
+ rval = QLA_FUNCTION_FAILED;
+ } else if (sts->data[3]) {
+ DEBUG2_3_11(printk("%s(%ld): failed to complete IOCB "
+ "-- response (%x).\n", __func__,
+ vha->host_no, sts->data[3]));
rval = QLA_FUNCTION_FAILED;
}
@@ -2759,8 +2782,10 @@ qla24xx_report_id_acquisition(scsi_qla_host_t *vha,
vp_idx, MSB(stat),
rptid_entry->port_id[2], rptid_entry->port_id[1],
rptid_entry->port_id[0]));
- if (vp_idx == 0)
- return;
+
+ vp = vha;
+ if (vp_idx == 0 && (MSB(stat) != 1))
+ goto reg_needed;
if (MSB(stat) == 1) {
DEBUG2(printk("scsi(%ld): Could not acquire ID for "
@@ -2783,8 +2808,11 @@ qla24xx_report_id_acquisition(scsi_qla_host_t *vha,
* response queue. Handle it in dpc context.
*/
set_bit(VP_IDX_ACQUIRED, &vp->vp_flags);
- set_bit(VP_DPC_NEEDED, &vha->dpc_flags);
+reg_needed:
+ set_bit(REGISTER_FC4_NEEDED, &vp->dpc_flags);
+ set_bit(REGISTER_FDMI_NEEDED, &vp->dpc_flags);
+ set_bit(VP_DPC_NEEDED, &vha->dpc_flags);
qla2xxx_wake_dpc(vha);
}
}
diff --git a/drivers/scsi/qla2xxx/qla_mid.c b/drivers/scsi/qla2xxx/qla_mid.c
index e07b3617f01..a47d34308a3 100644
--- a/drivers/scsi/qla2xxx/qla_mid.c
+++ b/drivers/scsi/qla2xxx/qla_mid.c
@@ -382,8 +382,6 @@ qla24xx_create_vhost(struct fc_vport *fc_vport)
vha->mgmt_svr_loop_id = 10 + vha->vp_idx;
vha->dpc_flags = 0L;
- set_bit(REGISTER_FDMI_NEEDED, &vha->dpc_flags);
- set_bit(REGISTER_FC4_NEEDED, &vha->dpc_flags);
/*
* To fix the issue of processing a parent's RSCN for the vport before
diff --git a/drivers/scsi/qla2xxx/qla_os.c b/drivers/scsi/qla2xxx/qla_os.c
index b79fca7d461..41669357b18 100644
--- a/drivers/scsi/qla2xxx/qla_os.c
+++ b/drivers/scsi/qla2xxx/qla_os.c
@@ -11,6 +11,7 @@
#include <linux/delay.h>
#include <linux/kthread.h>
#include <linux/mutex.h>
+#include <linux/kobject.h>
#include <scsi/scsi_tcq.h>
#include <scsi/scsicam.h>
@@ -137,7 +138,7 @@ static int qla2xxx_eh_target_reset(struct scsi_cmnd *);
static int qla2xxx_eh_bus_reset(struct scsi_cmnd *);
static int qla2xxx_eh_host_reset(struct scsi_cmnd *);
-static int qla2x00_change_queue_depth(struct scsi_device *, int);
+static int qla2x00_change_queue_depth(struct scsi_device *, int, int);
static int qla2x00_change_queue_type(struct scsi_device *, int);
struct scsi_host_template qla2xxx_driver_template = {
@@ -727,23 +728,6 @@ qla2x00_abort_fcport_cmds(fc_port_t *fcport)
spin_unlock_irqrestore(&ha->hardware_lock, flags);
}
-static void
-qla2x00_block_error_handler(struct scsi_cmnd *cmnd)
-{
- struct Scsi_Host *shost = cmnd->device->host;
- struct fc_rport *rport = starget_to_rport(scsi_target(cmnd->device));
- unsigned long flags;
-
- spin_lock_irqsave(shost->host_lock, flags);
- while (rport->port_state == FC_PORTSTATE_BLOCKED) {
- spin_unlock_irqrestore(shost->host_lock, flags);
- msleep(1000);
- spin_lock_irqsave(shost->host_lock, flags);
- }
- spin_unlock_irqrestore(shost->host_lock, flags);
- return;
-}
-
/**************************************************************************
* qla2xxx_eh_abort
*
@@ -773,7 +757,7 @@ qla2xxx_eh_abort(struct scsi_cmnd *cmd)
struct req_que *req = vha->req;
srb_t *spt;
- qla2x00_block_error_handler(cmd);
+ fc_block_scsi_eh(cmd);
if (!CMD_SP(cmd))
return SUCCESS;
@@ -904,7 +888,7 @@ __qla2xxx_eh_generic_reset(char *name, enum nexus_wait_type type,
fc_port_t *fcport = (struct fc_port *) cmd->device->hostdata;
int err;
- qla2x00_block_error_handler(cmd);
+ fc_block_scsi_eh(cmd);
if (!fcport)
return FAILED;
@@ -984,7 +968,7 @@ qla2xxx_eh_bus_reset(struct scsi_cmnd *cmd)
unsigned long serial;
srb_t *sp = (srb_t *) CMD_SP(cmd);
- qla2x00_block_error_handler(cmd);
+ fc_block_scsi_eh(cmd);
id = cmd->device->id;
lun = cmd->device->lun;
@@ -1047,7 +1031,7 @@ qla2xxx_eh_host_reset(struct scsi_cmnd *cmd)
srb_t *sp = (srb_t *) CMD_SP(cmd);
scsi_qla_host_t *base_vha = pci_get_drvdata(ha->pdev);
- qla2x00_block_error_handler(cmd);
+ fc_block_scsi_eh(cmd);
id = cmd->device->id;
lun = cmd->device->lun;
@@ -1234,8 +1218,11 @@ qla2xxx_slave_destroy(struct scsi_device *sdev)
}
static int
-qla2x00_change_queue_depth(struct scsi_device *sdev, int qdepth)
+qla2x00_change_queue_depth(struct scsi_device *sdev, int qdepth, int reason)
{
+ if (reason != SCSI_QDEPTH_DEFAULT)
+ return -EOPNOTSUPP;
+
scsi_adjust_queue_depth(sdev, scsi_get_tag_type(sdev), qdepth);
return sdev->queue_depth;
}
@@ -2653,6 +2640,37 @@ qla2x00_post_async_work(login_done, QLA_EVT_ASYNC_LOGIN_DONE);
qla2x00_post_async_work(logout, QLA_EVT_ASYNC_LOGOUT);
qla2x00_post_async_work(logout_done, QLA_EVT_ASYNC_LOGOUT_DONE);
+int
+qla2x00_post_uevent_work(struct scsi_qla_host *vha, u32 code)
+{
+ struct qla_work_evt *e;
+
+ e = qla2x00_alloc_work(vha, QLA_EVT_UEVENT);
+ if (!e)
+ return QLA_FUNCTION_FAILED;
+
+ e->u.uevent.code = code;
+ return qla2x00_post_work(vha, e);
+}
+
+static void
+qla2x00_uevent_emit(struct scsi_qla_host *vha, u32 code)
+{
+ char event_string[40];
+ char *envp[] = { event_string, NULL };
+
+ switch (code) {
+ case QLA_UEVENT_CODE_FW_DUMP:
+ snprintf(event_string, sizeof(event_string), "FW_DUMP=%ld",
+ vha->host_no);
+ break;
+ default:
+ /* do nothing */
+ break;
+ }
+ kobject_uevent_env(&vha->hw->pdev->dev.kobj, KOBJ_CHANGE, envp);
+}
+
void
qla2x00_do_work(struct scsi_qla_host *vha)
{
@@ -2690,6 +2708,9 @@ qla2x00_do_work(struct scsi_qla_host *vha)
qla2x00_async_logout_done(vha, e->u.logio.fcport,
e->u.logio.data);
break;
+ case QLA_EVT_UEVENT:
+ qla2x00_uevent_emit(vha, e->u.uevent.code);
+ break;
}
if (e->flags & QLA_EVT_FLAG_FREE)
kfree(e);
diff --git a/drivers/scsi/qla2xxx/qla_version.h b/drivers/scsi/qla2xxx/qla_version.h
index ac107a2c34a..807e0dbc67f 100644
--- a/drivers/scsi/qla2xxx/qla_version.h
+++ b/drivers/scsi/qla2xxx/qla_version.h
@@ -7,7 +7,7 @@
/*
* Driver version
*/
-#define QLA2XXX_VERSION "8.03.01-k6"
+#define QLA2XXX_VERSION "8.03.01-k7"
#define QLA_DRIVER_MAJOR_VER 8
#define QLA_DRIVER_MINOR_VER 3
diff --git a/drivers/scsi/qlogicpti.h b/drivers/scsi/qlogicpti.h
index 9c053bbaa87..e3c74d1ee2d 100644
--- a/drivers/scsi/qlogicpti.h
+++ b/drivers/scsi/qlogicpti.h
@@ -43,7 +43,7 @@
* determined for each queue request anew.
*/
#define QLOGICPTI_REQ_QUEUE_LEN 255 /* must be power of two - 1 */
-#define QLOGICPTI_MAX_SG(ql) (4 + ((ql) > 0) ? 7*((ql) - 1) : 0)
+#define QLOGICPTI_MAX_SG(ql) (4 + (((ql) > 0) ? 7*((ql) - 1) : 0))
/* mailbox command complete status codes */
#define MBOX_COMMAND_COMPLETE 0x4000
diff --git a/drivers/scsi/scsi.c b/drivers/scsi/scsi.c
index dd098cad337..a60da555557 100644
--- a/drivers/scsi/scsi.c
+++ b/drivers/scsi/scsi.c
@@ -940,10 +940,16 @@ EXPORT_SYMBOL(scsi_adjust_queue_depth);
*/
int scsi_track_queue_full(struct scsi_device *sdev, int depth)
{
- if ((jiffies >> 4) == sdev->last_queue_full_time)
+
+ /*
+ * Don't let QUEUE_FULLs on the same
+ * jiffies count, they could all be from
+ * same event.
+ */
+ if ((jiffies >> 4) == (sdev->last_queue_full_time >> 4))
return 0;
- sdev->last_queue_full_time = (jiffies >> 4);
+ sdev->last_queue_full_time = jiffies;
if (sdev->last_queue_full_depth != depth) {
sdev->last_queue_full_count = 1;
sdev->last_queue_full_depth = depth;
diff --git a/drivers/scsi/scsi_debug.c b/drivers/scsi/scsi_debug.c
index c4103bef41b..0b575c87100 100644
--- a/drivers/scsi/scsi_debug.c
+++ b/drivers/scsi/scsi_debug.c
@@ -44,6 +44,8 @@
#include <net/checksum.h>
+#include <asm/unaligned.h>
+
#include <scsi/scsi.h>
#include <scsi/scsi_cmnd.h>
#include <scsi/scsi_device.h>
@@ -105,6 +107,10 @@ static const char * scsi_debug_version_date = "20070104";
#define DEF_ATO 1
#define DEF_PHYSBLK_EXP 0
#define DEF_LOWEST_ALIGNED 0
+#define DEF_UNMAP_MAX_BLOCKS 0
+#define DEF_UNMAP_MAX_DESC 0
+#define DEF_UNMAP_GRANULARITY 0
+#define DEF_UNMAP_ALIGNMENT 0
/* bit mask values for scsi_debug_opts */
#define SCSI_DEBUG_OPT_NOISE 1
@@ -162,6 +168,10 @@ static int scsi_debug_guard = DEF_GUARD;
static int scsi_debug_ato = DEF_ATO;
static int scsi_debug_physblk_exp = DEF_PHYSBLK_EXP;
static int scsi_debug_lowest_aligned = DEF_LOWEST_ALIGNED;
+static int scsi_debug_unmap_max_desc = DEF_UNMAP_MAX_DESC;
+static int scsi_debug_unmap_max_blocks = DEF_UNMAP_MAX_BLOCKS;
+static int scsi_debug_unmap_granularity = DEF_UNMAP_GRANULARITY;
+static int scsi_debug_unmap_alignment = DEF_UNMAP_ALIGNMENT;
static int scsi_debug_cmnd_count = 0;
@@ -223,7 +233,9 @@ static struct sdebug_queued_cmd queued_arr[SCSI_DEBUG_CANQUEUE];
static unsigned char * fake_storep; /* ramdisk storage */
static unsigned char *dif_storep; /* protection info */
+static void *map_storep; /* provisioning map */
+static unsigned long map_size;
static int num_aborts = 0;
static int num_dev_resets = 0;
static int num_bus_resets = 0;
@@ -317,6 +329,7 @@ static void get_data_transfer_info(unsigned char *cmd,
(u32)cmd[28] << 24;
break;
+ case WRITE_SAME_16:
case WRITE_16:
case READ_16:
*lba = (u64)cmd[9] | (u64)cmd[8] << 8 |
@@ -335,6 +348,7 @@ static void get_data_transfer_info(unsigned char *cmd,
*num = (u32)cmd[9] | (u32)cmd[8] << 8 | (u32)cmd[7] << 16 |
(u32)cmd[6] << 24;
break;
+ case WRITE_SAME:
case WRITE_10:
case READ_10:
case XDWRITEREAD_10:
@@ -671,10 +685,12 @@ static int inquiry_evpd_89(unsigned char * arr)
}
+/* Block limits VPD page (SBC-3) */
static unsigned char vpdb0_data[] = {
- /* from 4th byte */ 0,0,0,4,
- 0,0,0x4,0,
- 0,0,0,64,
+ /* from 4th byte */ 0,0,0,4, 0,0,0x4,0, 0,0,0,64,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
};
static int inquiry_evpd_b0(unsigned char * arr)
@@ -691,14 +707,40 @@ static int inquiry_evpd_b0(unsigned char * arr)
arr[6] = (sdebug_store_sectors >> 8) & 0xff;
arr[7] = sdebug_store_sectors & 0xff;
}
+
+ if (scsi_debug_unmap_max_desc) {
+ unsigned int blocks;
+
+ if (scsi_debug_unmap_max_blocks)
+ blocks = scsi_debug_unmap_max_blocks;
+ else
+ blocks = 0xffffffff;
+
+ put_unaligned_be32(blocks, &arr[16]);
+ put_unaligned_be32(scsi_debug_unmap_max_desc, &arr[20]);
+ }
+
+ if (scsi_debug_unmap_alignment) {
+ put_unaligned_be32(scsi_debug_unmap_alignment, &arr[28]);
+ arr[28] |= 0x80; /* UGAVALID */
+ }
+
+ if (scsi_debug_unmap_granularity) {
+ put_unaligned_be32(scsi_debug_unmap_granularity, &arr[24]);
+ return 0x3c; /* Mandatory page length for thin provisioning */
+ }
+
return sizeof(vpdb0_data);
}
+/* Block device characteristics VPD page (SBC-3) */
static int inquiry_evpd_b1(unsigned char *arr)
{
memset(arr, 0, 0x3c);
arr[0] = 0;
- arr[1] = 1;
+ arr[1] = 1; /* non rotating medium (e.g. solid state) */
+ arr[2] = 0;
+ arr[3] = 5; /* less than 1.8" */
return 0x3c;
}
@@ -974,6 +1016,10 @@ static int resp_readcap16(struct scsi_cmnd * scp,
arr[11] = scsi_debug_sector_size & 0xff;
arr[13] = scsi_debug_physblk_exp & 0xf;
arr[14] = (scsi_debug_lowest_aligned >> 8) & 0x3f;
+
+ if (scsi_debug_unmap_granularity)
+ arr[14] |= 0x80; /* TPE */
+
arr[15] = scsi_debug_lowest_aligned & 0xff;
if (scsi_debug_dif) {
@@ -1887,6 +1933,70 @@ out:
return ret;
}
+static unsigned int map_state(sector_t lba, unsigned int *num)
+{
+ unsigned int granularity, alignment, mapped;
+ sector_t block, next, end;
+
+ granularity = scsi_debug_unmap_granularity;
+ alignment = granularity - scsi_debug_unmap_alignment;
+ block = lba + alignment;
+ do_div(block, granularity);
+
+ mapped = test_bit(block, map_storep);
+
+ if (mapped)
+ next = find_next_zero_bit(map_storep, map_size, block);
+ else
+ next = find_next_bit(map_storep, map_size, block);
+
+ end = next * granularity - scsi_debug_unmap_alignment;
+ *num = end - lba;
+
+ return mapped;
+}
+
+static void map_region(sector_t lba, unsigned int len)
+{
+ unsigned int granularity, alignment;
+ sector_t end = lba + len;
+
+ granularity = scsi_debug_unmap_granularity;
+ alignment = granularity - scsi_debug_unmap_alignment;
+
+ while (lba < end) {
+ sector_t block, rem;
+
+ block = lba + alignment;
+ rem = do_div(block, granularity);
+
+ set_bit(block, map_storep);
+
+ lba += granularity - rem;
+ }
+}
+
+static void unmap_region(sector_t lba, unsigned int len)
+{
+ unsigned int granularity, alignment;
+ sector_t end = lba + len;
+
+ granularity = scsi_debug_unmap_granularity;
+ alignment = granularity - scsi_debug_unmap_alignment;
+
+ while (lba < end) {
+ sector_t block, rem;
+
+ block = lba + alignment;
+ rem = do_div(block, granularity);
+
+ if (rem == 0 && lba + granularity <= end)
+ clear_bit(block, map_storep);
+
+ lba += granularity - rem;
+ }
+}
+
static int resp_write(struct scsi_cmnd *SCpnt, unsigned long long lba,
unsigned int num, struct sdebug_dev_info *devip,
u32 ei_lba)
@@ -1910,6 +2020,8 @@ static int resp_write(struct scsi_cmnd *SCpnt, unsigned long long lba,
write_lock_irqsave(&atomic_rw, iflags);
ret = do_device_access(SCpnt, devip, lba, num, 1);
+ if (scsi_debug_unmap_granularity)
+ map_region(lba, num);
write_unlock_irqrestore(&atomic_rw, iflags);
if (-1 == ret)
return (DID_ERROR << 16);
@@ -1917,9 +2029,143 @@ static int resp_write(struct scsi_cmnd *SCpnt, unsigned long long lba,
(SCSI_DEBUG_OPT_NOISE & scsi_debug_opts))
printk(KERN_INFO "scsi_debug: write: cdb indicated=%u, "
" IO sent=%d bytes\n", num * scsi_debug_sector_size, ret);
+
+ return 0;
+}
+
+static int resp_write_same(struct scsi_cmnd *scmd, unsigned long long lba,
+ unsigned int num, struct sdebug_dev_info *devip,
+ u32 ei_lba, unsigned int unmap)
+{
+ unsigned long iflags;
+ unsigned long long i;
+ int ret;
+
+ ret = check_device_access_params(devip, lba, num);
+ if (ret)
+ return ret;
+
+ write_lock_irqsave(&atomic_rw, iflags);
+
+ if (unmap && scsi_debug_unmap_granularity) {
+ unmap_region(lba, num);
+ goto out;
+ }
+
+ /* Else fetch one logical block */
+ ret = fetch_to_dev_buffer(scmd,
+ fake_storep + (lba * scsi_debug_sector_size),
+ scsi_debug_sector_size);
+
+ if (-1 == ret) {
+ write_unlock_irqrestore(&atomic_rw, iflags);
+ return (DID_ERROR << 16);
+ } else if ((ret < (num * scsi_debug_sector_size)) &&
+ (SCSI_DEBUG_OPT_NOISE & scsi_debug_opts))
+ printk(KERN_INFO "scsi_debug: write same: cdb indicated=%u, "
+ " IO sent=%d bytes\n", num * scsi_debug_sector_size, ret);
+
+ /* Copy first sector to remaining blocks */
+ for (i = 1 ; i < num ; i++)
+ memcpy(fake_storep + ((lba + i) * scsi_debug_sector_size),
+ fake_storep + (lba * scsi_debug_sector_size),
+ scsi_debug_sector_size);
+
+ if (scsi_debug_unmap_granularity)
+ map_region(lba, num);
+out:
+ write_unlock_irqrestore(&atomic_rw, iflags);
+
return 0;
}
+struct unmap_block_desc {
+ __be64 lba;
+ __be32 blocks;
+ __be32 __reserved;
+};
+
+static int resp_unmap(struct scsi_cmnd * scmd, struct sdebug_dev_info * devip)
+{
+ unsigned char *buf;
+ struct unmap_block_desc *desc;
+ unsigned int i, payload_len, descriptors;
+ int ret;
+
+ ret = check_readiness(scmd, 1, devip);
+ if (ret)
+ return ret;
+
+ payload_len = get_unaligned_be16(&scmd->cmnd[7]);
+ BUG_ON(scsi_bufflen(scmd) != payload_len);
+
+ descriptors = (payload_len - 8) / 16;
+
+ buf = kmalloc(scsi_bufflen(scmd), GFP_ATOMIC);
+ if (!buf)
+ return check_condition_result;
+
+ scsi_sg_copy_to_buffer(scmd, buf, scsi_bufflen(scmd));
+
+ BUG_ON(get_unaligned_be16(&buf[0]) != payload_len - 2);
+ BUG_ON(get_unaligned_be16(&buf[2]) != descriptors * 16);
+
+ desc = (void *)&buf[8];
+
+ for (i = 0 ; i < descriptors ; i++) {
+ unsigned long long lba = get_unaligned_be64(&desc[i].lba);
+ unsigned int num = get_unaligned_be32(&desc[i].blocks);
+
+ ret = check_device_access_params(devip, lba, num);
+ if (ret)
+ goto out;
+
+ unmap_region(lba, num);
+ }
+
+ ret = 0;
+
+out:
+ kfree(buf);
+
+ return ret;
+}
+
+#define SDEBUG_GET_LBA_STATUS_LEN 32
+
+static int resp_get_lba_status(struct scsi_cmnd * scmd,
+ struct sdebug_dev_info * devip)
+{
+ unsigned long long lba;
+ unsigned int alloc_len, mapped, num;
+ unsigned char arr[SDEBUG_GET_LBA_STATUS_LEN];
+ int ret;
+
+ ret = check_readiness(scmd, 1, devip);
+ if (ret)
+ return ret;
+
+ lba = get_unaligned_be64(&scmd->cmnd[2]);
+ alloc_len = get_unaligned_be32(&scmd->cmnd[10]);
+
+ if (alloc_len < 24)
+ return 0;
+
+ ret = check_device_access_params(devip, lba, 1);
+ if (ret)
+ return ret;
+
+ mapped = map_state(lba, &num);
+
+ memset(arr, 0, SDEBUG_GET_LBA_STATUS_LEN);
+ put_unaligned_be32(16, &arr[0]); /* Parameter Data Length */
+ put_unaligned_be64(lba, &arr[8]); /* LBA */
+ put_unaligned_be32(num, &arr[16]); /* Number of blocks */
+ arr[20] = !mapped; /* mapped = 0, unmapped = 1 */
+
+ return fill_from_dev_buffer(scmd, arr, SDEBUG_GET_LBA_STATUS_LEN);
+}
+
#define SDEBUG_RLUN_ARR_SZ 256
static int resp_report_luns(struct scsi_cmnd * scp,
@@ -2430,6 +2676,10 @@ module_param_named(guard, scsi_debug_guard, int, S_IRUGO);
module_param_named(ato, scsi_debug_ato, int, S_IRUGO);
module_param_named(physblk_exp, scsi_debug_physblk_exp, int, S_IRUGO);
module_param_named(lowest_aligned, scsi_debug_lowest_aligned, int, S_IRUGO);
+module_param_named(unmap_max_blocks, scsi_debug_unmap_max_blocks, int, S_IRUGO);
+module_param_named(unmap_max_desc, scsi_debug_unmap_max_desc, int, S_IRUGO);
+module_param_named(unmap_granularity, scsi_debug_unmap_granularity, int, S_IRUGO);
+module_param_named(unmap_alignment, scsi_debug_unmap_alignment, int, S_IRUGO);
MODULE_AUTHOR("Eric Youngdale + Douglas Gilbert");
MODULE_DESCRIPTION("SCSI debug adapter driver");
@@ -2458,6 +2708,10 @@ MODULE_PARM_DESC(dix, "data integrity extensions mask (def=0)");
MODULE_PARM_DESC(dif, "data integrity field type: 0-3 (def=0)");
MODULE_PARM_DESC(guard, "protection checksum: 0=crc, 1=ip (def=0)");
MODULE_PARM_DESC(ato, "application tag ownership: 0=disk 1=host (def=1)");
+MODULE_PARM_DESC(unmap_max_blocks, "max # of blocks can be unmapped in one cmd (def=0)");
+MODULE_PARM_DESC(unmap_max_desc, "max # of ranges that can be unmapped in one cmd (def=0)");
+MODULE_PARM_DESC(unmap_granularity, "thin provisioning granularity in blocks (def=0)");
+MODULE_PARM_DESC(unmap_alignment, "lowest aligned thin provisioning lba (def=0)");
static char sdebug_info[256];
@@ -2816,6 +3070,23 @@ static ssize_t sdebug_ato_show(struct device_driver *ddp, char *buf)
}
DRIVER_ATTR(ato, S_IRUGO, sdebug_ato_show, NULL);
+static ssize_t sdebug_map_show(struct device_driver *ddp, char *buf)
+{
+ ssize_t count;
+
+ if (scsi_debug_unmap_granularity == 0)
+ return scnprintf(buf, PAGE_SIZE, "0-%u\n",
+ sdebug_store_sectors);
+
+ count = bitmap_scnlistprintf(buf, PAGE_SIZE, map_storep, map_size);
+
+ buf[count++] = '\n';
+ buf[count++] = 0;
+
+ return count;
+}
+DRIVER_ATTR(map, S_IRUGO, sdebug_map_show, NULL);
+
/* Note: The following function creates attribute files in the
/sys/bus/pseudo/drivers/scsi_debug directory. The advantage of these
@@ -2847,11 +3118,13 @@ static int do_create_driverfs_files(void)
ret |= driver_create_file(&sdebug_driverfs_driver, &driver_attr_dif);
ret |= driver_create_file(&sdebug_driverfs_driver, &driver_attr_guard);
ret |= driver_create_file(&sdebug_driverfs_driver, &driver_attr_ato);
+ ret |= driver_create_file(&sdebug_driverfs_driver, &driver_attr_map);
return ret;
}
static void do_remove_driverfs_files(void)
{
+ driver_remove_file(&sdebug_driverfs_driver, &driver_attr_map);
driver_remove_file(&sdebug_driverfs_driver, &driver_attr_ato);
driver_remove_file(&sdebug_driverfs_driver, &driver_attr_guard);
driver_remove_file(&sdebug_driverfs_driver, &driver_attr_dif);
@@ -2989,6 +3262,36 @@ static int __init scsi_debug_init(void)
memset(dif_storep, 0xff, dif_size);
}
+ if (scsi_debug_unmap_granularity) {
+ unsigned int map_bytes;
+
+ if (scsi_debug_unmap_granularity < scsi_debug_unmap_alignment) {
+ printk(KERN_ERR
+ "%s: ERR: unmap_granularity < unmap_alignment\n",
+ __func__);
+ return -EINVAL;
+ }
+
+ map_size = (sdebug_store_sectors / scsi_debug_unmap_granularity);
+ map_bytes = map_size >> 3;
+ map_storep = vmalloc(map_bytes);
+
+ printk(KERN_INFO "scsi_debug_init: %lu provisioning blocks\n",
+ map_size);
+
+ if (map_storep == NULL) {
+ printk(KERN_ERR "scsi_debug_init: out of mem. (MAP)\n");
+ ret = -ENOMEM;
+ goto free_vm;
+ }
+
+ memset(map_storep, 0x0, map_bytes);
+
+ /* Map first 1KB for partition table */
+ if (scsi_debug_num_parts)
+ map_region(0, 2);
+ }
+
ret = device_register(&pseudo_primary);
if (ret < 0) {
printk(KERN_WARNING "scsi_debug: device_register error: %d\n",
@@ -3041,6 +3344,8 @@ bus_unreg:
dev_unreg:
device_unregister(&pseudo_primary);
free_vm:
+ if (map_storep)
+ vfree(map_storep);
if (dif_storep)
vfree(dif_storep);
vfree(fake_storep);
@@ -3167,6 +3472,7 @@ int scsi_debug_queuecommand(struct scsi_cmnd *SCpnt, done_funct_t done)
int inj_dif = 0;
int inj_dix = 0;
int delay_override = 0;
+ int unmap = 0;
scsi_set_resid(SCpnt, 0);
if ((SCSI_DEBUG_OPT_NOISE & scsi_debug_opts) && cmd) {
@@ -3272,13 +3578,21 @@ int scsi_debug_queuecommand(struct scsi_cmnd *SCpnt, done_funct_t done)
errsts = resp_readcap(SCpnt, devip);
break;
case SERVICE_ACTION_IN:
- if (SAI_READ_CAPACITY_16 != cmd[1]) {
+ if (cmd[1] == SAI_READ_CAPACITY_16)
+ errsts = resp_readcap16(SCpnt, devip);
+ else if (cmd[1] == SAI_GET_LBA_STATUS) {
+
+ if (scsi_debug_unmap_max_desc == 0) {
+ mk_sense_buffer(devip, ILLEGAL_REQUEST,
+ INVALID_COMMAND_OPCODE, 0);
+ errsts = check_condition_result;
+ } else
+ errsts = resp_get_lba_status(SCpnt, devip);
+ } else {
mk_sense_buffer(devip, ILLEGAL_REQUEST,
INVALID_OPCODE, 0);
errsts = check_condition_result;
- break;
}
- errsts = resp_readcap16(SCpnt, devip);
break;
case MAINTENANCE_IN:
if (MI_REPORT_TARGET_PGS != cmd[1]) {
@@ -3378,6 +3692,29 @@ write:
errsts = illegal_condition_result;
}
break;
+ case WRITE_SAME_16:
+ if (cmd[1] & 0x8)
+ unmap = 1;
+ /* fall through */
+ case WRITE_SAME:
+ errsts = check_readiness(SCpnt, 0, devip);
+ if (errsts)
+ break;
+ get_data_transfer_info(cmd, &lba, &num, &ei_lba);
+ errsts = resp_write_same(SCpnt, lba, num, devip, ei_lba, unmap);
+ break;
+ case UNMAP:
+ errsts = check_readiness(SCpnt, 0, devip);
+ if (errsts)
+ break;
+
+ if (scsi_debug_unmap_max_desc == 0) {
+ mk_sense_buffer(devip, ILLEGAL_REQUEST,
+ INVALID_COMMAND_OPCODE, 0);
+ errsts = check_condition_result;
+ } else
+ errsts = resp_unmap(SCpnt, devip);
+ break;
case MODE_SENSE:
case MODE_SENSE_10:
errsts = resp_mode_sense(SCpnt, target, devip);
diff --git a/drivers/scsi/scsi_devinfo.c b/drivers/scsi/scsi_devinfo.c
index 93c2622cb96..37af178b2d1 100644
--- a/drivers/scsi/scsi_devinfo.c
+++ b/drivers/scsi/scsi_devinfo.c
@@ -168,11 +168,10 @@ static struct {
{"Generic", "USB SD Reader", "1.00", BLIST_FORCELUN | BLIST_INQUIRY_36},
{"Generic", "USB Storage-SMC", "0180", BLIST_FORCELUN | BLIST_INQUIRY_36},
{"Generic", "USB Storage-SMC", "0207", BLIST_FORCELUN | BLIST_INQUIRY_36},
- {"HITACHI", "DF400", "*", BLIST_SPARSELUN},
- {"HITACHI", "DF500", "*", BLIST_SPARSELUN},
- {"HITACHI", "DF600", "*", BLIST_SPARSELUN},
- {"HITACHI", "DISK-SUBSYSTEM", "*", BLIST_ATTACH_PQ3 | BLIST_SPARSELUN | BLIST_LARGELUN},
- {"HITACHI", "OPEN-E", "*", BLIST_ATTACH_PQ3 | BLIST_SPARSELUN | BLIST_LARGELUN},
+ {"HITACHI", "DF400", "*", BLIST_REPORTLUN2},
+ {"HITACHI", "DF500", "*", BLIST_REPORTLUN2},
+ {"HITACHI", "DISK-SUBSYSTEM", "*", BLIST_REPORTLUN2},
+ {"HITACHI", "OPEN-", "*", BLIST_REPORTLUN2},
{"HITACHI", "OP-C-", "*", BLIST_SPARSELUN | BLIST_LARGELUN},
{"HITACHI", "3380-", "*", BLIST_SPARSELUN | BLIST_LARGELUN},
{"HITACHI", "3390-", "*", BLIST_SPARSELUN | BLIST_LARGELUN},
@@ -454,7 +453,7 @@ int scsi_get_device_flags(struct scsi_device *sdev,
/**
- * get_device_flags_keyed - get device specific flags from the dynamic device list.
+ * scsi_get_device_flags_keyed - get device specific flags from the dynamic device list
* @sdev: &scsi_device to get flags for
* @vendor: vendor name
* @model: model name
@@ -685,7 +684,7 @@ MODULE_PARM_DESC(default_dev_flags,
"scsi default device flag integer value");
/**
- * scsi_dev_info_list_delete - called from scsi.c:exit_scsi to remove the scsi_dev_info_list.
+ * scsi_exit_devinfo - remove /proc/scsi/device_info & the scsi_dev_info_list
**/
void scsi_exit_devinfo(void)
{
diff --git a/drivers/scsi/scsi_error.c b/drivers/scsi/scsi_error.c
index 1b0060b791e..08ed506e605 100644
--- a/drivers/scsi/scsi_error.c
+++ b/drivers/scsi/scsi_error.c
@@ -331,6 +331,64 @@ static int scsi_check_sense(struct scsi_cmnd *scmd)
}
}
+static void scsi_handle_queue_ramp_up(struct scsi_device *sdev)
+{
+ struct scsi_host_template *sht = sdev->host->hostt;
+ struct scsi_device *tmp_sdev;
+
+ if (!sht->change_queue_depth ||
+ sdev->queue_depth >= sdev->max_queue_depth)
+ return;
+
+ if (time_before(jiffies,
+ sdev->last_queue_ramp_up + sdev->queue_ramp_up_period))
+ return;
+
+ if (time_before(jiffies,
+ sdev->last_queue_full_time + sdev->queue_ramp_up_period))
+ return;
+
+ /*
+ * Walk all devices of a target and do
+ * ramp up on them.
+ */
+ shost_for_each_device(tmp_sdev, sdev->host) {
+ if (tmp_sdev->channel != sdev->channel ||
+ tmp_sdev->id != sdev->id ||
+ tmp_sdev->queue_depth == sdev->max_queue_depth)
+ continue;
+ /*
+ * call back into LLD to increase queue_depth by one
+ * with ramp up reason code.
+ */
+ sht->change_queue_depth(tmp_sdev, tmp_sdev->queue_depth + 1,
+ SCSI_QDEPTH_RAMP_UP);
+ sdev->last_queue_ramp_up = jiffies;
+ }
+}
+
+static void scsi_handle_queue_full(struct scsi_device *sdev)
+{
+ struct scsi_host_template *sht = sdev->host->hostt;
+ struct scsi_device *tmp_sdev;
+
+ if (!sht->change_queue_depth)
+ return;
+
+ shost_for_each_device(tmp_sdev, sdev->host) {
+ if (tmp_sdev->channel != sdev->channel ||
+ tmp_sdev->id != sdev->id)
+ continue;
+ /*
+ * We do not know the number of commands that were at
+ * the device when we got the queue full so we start
+ * from the highest possible value and work our way down.
+ */
+ sht->change_queue_depth(tmp_sdev, tmp_sdev->queue_depth - 1,
+ SCSI_QDEPTH_QFULL);
+ }
+}
+
/**
* scsi_eh_completed_normally - Disposition a eh cmd on return from LLD.
* @scmd: SCSI cmd to examine.
@@ -371,6 +429,7 @@ static int scsi_eh_completed_normally(struct scsi_cmnd *scmd)
*/
switch (status_byte(scmd->result)) {
case GOOD:
+ scsi_handle_queue_ramp_up(scmd->device);
case COMMAND_TERMINATED:
return SUCCESS;
case CHECK_CONDITION:
@@ -387,8 +446,10 @@ static int scsi_eh_completed_normally(struct scsi_cmnd *scmd)
* let issuer deal with this, it could be just fine
*/
return SUCCESS;
- case BUSY:
case QUEUE_FULL:
+ scsi_handle_queue_full(scmd->device);
+ /* fall through */
+ case BUSY:
default:
return FAILED;
}
@@ -1387,6 +1448,7 @@ int scsi_decide_disposition(struct scsi_cmnd *scmd)
*/
switch (status_byte(scmd->result)) {
case QUEUE_FULL:
+ scsi_handle_queue_full(scmd->device);
/*
* the case of trying to send too many commands to a
* tagged queueing device.
@@ -1400,6 +1462,7 @@ int scsi_decide_disposition(struct scsi_cmnd *scmd)
*/
return ADD_TO_MLQUEUE;
case GOOD:
+ scsi_handle_queue_ramp_up(scmd->device);
case COMMAND_TERMINATED:
return SUCCESS;
case TASK_ABORTED:
diff --git a/drivers/scsi/scsi_ioctl.c b/drivers/scsi/scsi_ioctl.c
index b98f763931c..d9564fb04f6 100644
--- a/drivers/scsi/scsi_ioctl.c
+++ b/drivers/scsi/scsi_ioctl.c
@@ -308,6 +308,9 @@ int scsi_nonblockable_ioctl(struct scsi_device *sdev, int cmd,
case SG_SCSI_RESET_DEVICE:
val = SCSI_TRY_RESET_DEVICE;
break;
+ case SG_SCSI_RESET_TARGET:
+ val = SCSI_TRY_RESET_TARGET;
+ break;
case SG_SCSI_RESET_BUS:
val = SCSI_TRY_RESET_BUS;
break;
diff --git a/drivers/scsi/scsi_lib.c b/drivers/scsi/scsi_lib.c
index 5987da85710..e495d381394 100644
--- a/drivers/scsi/scsi_lib.c
+++ b/drivers/scsi/scsi_lib.c
@@ -898,7 +898,7 @@ void scsi_io_completion(struct scsi_cmnd *cmd, unsigned int good_bytes)
scsi_print_sense("", cmd);
scsi_print_command(cmd);
}
- if (blk_end_request_err(req, -EIO))
+ if (blk_end_request_err(req, error))
scsi_requeue_command(q, cmd);
else
scsi_next_command(cmd);
@@ -1359,9 +1359,9 @@ static int scsi_lld_busy(struct request_queue *q)
static void scsi_kill_request(struct request *req, struct request_queue *q)
{
struct scsi_cmnd *cmd = req->special;
- struct scsi_device *sdev = cmd->device;
- struct scsi_target *starget = scsi_target(sdev);
- struct Scsi_Host *shost = sdev->host;
+ struct scsi_device *sdev;
+ struct scsi_target *starget;
+ struct Scsi_Host *shost;
blk_start_request(req);
@@ -1371,6 +1371,9 @@ static void scsi_kill_request(struct request *req, struct request_queue *q)
BUG();
}
+ sdev = cmd->device;
+ starget = scsi_target(sdev);
+ shost = sdev->host;
scsi_init_cmd_errh(cmd);
cmd->result = DID_NO_CONNECT << 16;
atomic_inc(&cmd->device->iorequest_cnt);
diff --git a/drivers/scsi/scsi_lib_dma.c b/drivers/scsi/scsi_lib_dma.c
index ac6855cd265..dcd128583b8 100644
--- a/drivers/scsi/scsi_lib_dma.c
+++ b/drivers/scsi/scsi_lib_dma.c
@@ -23,7 +23,7 @@ int scsi_dma_map(struct scsi_cmnd *cmd)
int nseg = 0;
if (scsi_sg_count(cmd)) {
- struct device *dev = cmd->device->host->shost_gendev.parent;
+ struct device *dev = cmd->device->host->dma_dev;
nseg = dma_map_sg(dev, scsi_sglist(cmd), scsi_sg_count(cmd),
cmd->sc_data_direction);
@@ -41,7 +41,7 @@ EXPORT_SYMBOL(scsi_dma_map);
void scsi_dma_unmap(struct scsi_cmnd *cmd)
{
if (scsi_sg_count(cmd)) {
- struct device *dev = cmd->device->host->shost_gendev.parent;
+ struct device *dev = cmd->device->host->dma_dev;
dma_unmap_sg(dev, scsi_sglist(cmd), scsi_sg_count(cmd),
cmd->sc_data_direction);
diff --git a/drivers/scsi/scsi_scan.c b/drivers/scsi/scsi_scan.c
index 47291bcff0d..012f73a9688 100644
--- a/drivers/scsi/scsi_scan.c
+++ b/drivers/scsi/scsi_scan.c
@@ -251,6 +251,7 @@ static struct scsi_device *scsi_alloc_sdev(struct scsi_target *starget,
sdev->model = scsi_null_device_strs;
sdev->rev = scsi_null_device_strs;
sdev->host = shost;
+ sdev->queue_ramp_up_period = SCSI_DEFAULT_RAMP_UP_PERIOD;
sdev->id = starget->id;
sdev->lun = lun;
sdev->channel = starget->channel;
@@ -941,6 +942,8 @@ static int scsi_add_lun(struct scsi_device *sdev, unsigned char *inq_result,
}
}
+ sdev->max_queue_depth = sdev->queue_depth;
+
/*
* Ok, the device is now all set up, we can
* register it and tell the rest of the kernel
diff --git a/drivers/scsi/scsi_sysctl.c b/drivers/scsi/scsi_sysctl.c
index 63a30f566f3..2b6b93f7d8e 100644
--- a/drivers/scsi/scsi_sysctl.c
+++ b/drivers/scsi/scsi_sysctl.c
@@ -13,26 +13,23 @@
static ctl_table scsi_table[] = {
- { .ctl_name = DEV_SCSI_LOGGING_LEVEL,
- .procname = "logging_level",
+ { .procname = "logging_level",
.data = &scsi_logging_level,
.maxlen = sizeof(scsi_logging_level),
.mode = 0644,
- .proc_handler = &proc_dointvec },
+ .proc_handler = proc_dointvec },
{ }
};
static ctl_table scsi_dir_table[] = {
- { .ctl_name = DEV_SCSI,
- .procname = "scsi",
+ { .procname = "scsi",
.mode = 0555,
.child = scsi_table },
{ }
};
static ctl_table scsi_root_table[] = {
- { .ctl_name = CTL_DEV,
- .procname = "dev",
+ { .procname = "dev",
.mode = 0555,
.child = scsi_dir_table },
{ }
diff --git a/drivers/scsi/scsi_sysfs.c b/drivers/scsi/scsi_sysfs.c
index 392d8db3390..5a065055e68 100644
--- a/drivers/scsi/scsi_sysfs.c
+++ b/drivers/scsi/scsi_sysfs.c
@@ -766,10 +766,13 @@ sdev_store_queue_depth_rw(struct device *dev, struct device_attribute *attr,
if (depth < 1)
return -EINVAL;
- retval = sht->change_queue_depth(sdev, depth);
+ retval = sht->change_queue_depth(sdev, depth,
+ SCSI_QDEPTH_DEFAULT);
if (retval < 0)
return retval;
+ sdev->max_queue_depth = sdev->queue_depth;
+
return count;
}
@@ -778,6 +781,37 @@ static struct device_attribute sdev_attr_queue_depth_rw =
sdev_store_queue_depth_rw);
static ssize_t
+sdev_show_queue_ramp_up_period(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct scsi_device *sdev;
+ sdev = to_scsi_device(dev);
+ return snprintf(buf, 20, "%u\n",
+ jiffies_to_msecs(sdev->queue_ramp_up_period));
+}
+
+static ssize_t
+sdev_store_queue_ramp_up_period(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct scsi_device *sdev = to_scsi_device(dev);
+ unsigned long period;
+
+ if (strict_strtoul(buf, 10, &period))
+ return -EINVAL;
+
+ sdev->queue_ramp_up_period = msecs_to_jiffies(period);
+ return period;
+}
+
+static struct device_attribute sdev_attr_queue_ramp_up_period =
+ __ATTR(queue_ramp_up_period, S_IRUGO | S_IWUSR,
+ sdev_show_queue_ramp_up_period,
+ sdev_store_queue_ramp_up_period);
+
+static ssize_t
sdev_store_queue_type_rw(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count)
{
@@ -867,8 +901,12 @@ int scsi_sysfs_add_sdev(struct scsi_device *sdev)
sdev->is_visible = 1;
/* create queue files, which may be writable, depending on the host */
- if (sdev->host->hostt->change_queue_depth)
- error = device_create_file(&sdev->sdev_gendev, &sdev_attr_queue_depth_rw);
+ if (sdev->host->hostt->change_queue_depth) {
+ error = device_create_file(&sdev->sdev_gendev,
+ &sdev_attr_queue_depth_rw);
+ error = device_create_file(&sdev->sdev_gendev,
+ &sdev_attr_queue_ramp_up_period);
+ }
else
error = device_create_file(&sdev->sdev_gendev, &dev_attr_queue_depth);
if (error)
diff --git a/drivers/scsi/scsi_transport_fc.c b/drivers/scsi/scsi_transport_fc.c
index c6f70dae9b2..6531c91501b 100644
--- a/drivers/scsi/scsi_transport_fc.c
+++ b/drivers/scsi/scsi_transport_fc.c
@@ -27,6 +27,7 @@
*/
#include <linux/module.h>
#include <linux/init.h>
+#include <linux/delay.h>
#include <scsi/scsi_device.h>
#include <scsi/scsi_host.h>
#include <scsi/scsi_transport.h>
@@ -2384,6 +2385,7 @@ fc_rport_final_delete(struct work_struct *work)
struct Scsi_Host *shost = rport_to_shost(rport);
struct fc_internal *i = to_fc_internal(shost->transportt);
unsigned long flags;
+ int do_callback = 0;
/*
* if a scan is pending, flush the SCSI Host work_q so that
@@ -2422,8 +2424,15 @@ fc_rport_final_delete(struct work_struct *work)
* Avoid this call if we already called it when we preserved the
* rport for the binding.
*/
+ spin_lock_irqsave(shost->host_lock, flags);
if (!(rport->flags & FC_RPORT_DEVLOSS_CALLBK_DONE) &&
- (i->f->dev_loss_tmo_callbk))
+ (i->f->dev_loss_tmo_callbk)) {
+ rport->flags |= FC_RPORT_DEVLOSS_CALLBK_DONE;
+ do_callback = 1;
+ }
+ spin_unlock_irqrestore(shost->host_lock, flags);
+
+ if (do_callback)
i->f->dev_loss_tmo_callbk(rport);
fc_bsg_remove(rport->rqst_q);
@@ -2970,6 +2979,7 @@ fc_timeout_deleted_rport(struct work_struct *work)
struct fc_internal *i = to_fc_internal(shost->transportt);
struct fc_host_attrs *fc_host = shost_to_fc_host(shost);
unsigned long flags;
+ int do_callback = 0;
spin_lock_irqsave(shost->host_lock, flags);
@@ -3035,7 +3045,6 @@ fc_timeout_deleted_rport(struct work_struct *work)
rport->roles = FC_PORT_ROLE_UNKNOWN;
rport->port_state = FC_PORTSTATE_NOTPRESENT;
rport->flags &= ~FC_RPORT_FAST_FAIL_TIMEDOUT;
- rport->flags |= FC_RPORT_DEVLOSS_CALLBK_DONE;
/*
* Pre-emptively kill I/O rather than waiting for the work queue
@@ -3045,32 +3054,40 @@ fc_timeout_deleted_rport(struct work_struct *work)
spin_unlock_irqrestore(shost->host_lock, flags);
fc_terminate_rport_io(rport);
- BUG_ON(rport->port_state != FC_PORTSTATE_NOTPRESENT);
+ spin_lock_irqsave(shost->host_lock, flags);
+
+ if (rport->port_state == FC_PORTSTATE_NOTPRESENT) { /* still missing */
- /* remove the identifiers that aren't used in the consisting binding */
- switch (fc_host->tgtid_bind_type) {
- case FC_TGTID_BIND_BY_WWPN:
- rport->node_name = -1;
- rport->port_id = -1;
- break;
- case FC_TGTID_BIND_BY_WWNN:
- rport->port_name = -1;
- rport->port_id = -1;
- break;
- case FC_TGTID_BIND_BY_ID:
- rport->node_name = -1;
- rport->port_name = -1;
- break;
- case FC_TGTID_BIND_NONE: /* to keep compiler happy */
- break;
+ /* remove the identifiers that aren't used in the consisting binding */
+ switch (fc_host->tgtid_bind_type) {
+ case FC_TGTID_BIND_BY_WWPN:
+ rport->node_name = -1;
+ rport->port_id = -1;
+ break;
+ case FC_TGTID_BIND_BY_WWNN:
+ rport->port_name = -1;
+ rport->port_id = -1;
+ break;
+ case FC_TGTID_BIND_BY_ID:
+ rport->node_name = -1;
+ rport->port_name = -1;
+ break;
+ case FC_TGTID_BIND_NONE: /* to keep compiler happy */
+ break;
+ }
+
+ /*
+ * As this only occurs if the remote port (scsi target)
+ * went away and didn't come back - we'll remove
+ * all attached scsi devices.
+ */
+ rport->flags |= FC_RPORT_DEVLOSS_CALLBK_DONE;
+ fc_queue_work(shost, &rport->stgt_delete_work);
+
+ do_callback = 1;
}
- /*
- * As this only occurs if the remote port (scsi target)
- * went away and didn't come back - we'll remove
- * all attached scsi devices.
- */
- fc_queue_work(shost, &rport->stgt_delete_work);
+ spin_unlock_irqrestore(shost->host_lock, flags);
/*
* Notify the driver that the rport is now dead. The LLDD will
@@ -3078,7 +3095,7 @@ fc_timeout_deleted_rport(struct work_struct *work)
*
* Note: we set the CALLBK_DONE flag above to correspond
*/
- if (i->f->dev_loss_tmo_callbk)
+ if (do_callback && i->f->dev_loss_tmo_callbk)
i->f->dev_loss_tmo_callbk(rport);
}
@@ -3128,6 +3145,31 @@ fc_scsi_scan_rport(struct work_struct *work)
spin_unlock_irqrestore(shost->host_lock, flags);
}
+/**
+ * fc_block_scsi_eh - Block SCSI eh thread for blocked fc_rport
+ * @cmnd: SCSI command that scsi_eh is trying to recover
+ *
+ * This routine can be called from a FC LLD scsi_eh callback. It
+ * blocks the scsi_eh thread until the fc_rport leaves the
+ * FC_PORTSTATE_BLOCKED. This is necessary to avoid the scsi_eh
+ * failing recovery actions for blocked rports which would lead to
+ * offlined SCSI devices.
+ */
+void fc_block_scsi_eh(struct scsi_cmnd *cmnd)
+{
+ struct Scsi_Host *shost = cmnd->device->host;
+ struct fc_rport *rport = starget_to_rport(scsi_target(cmnd->device));
+ unsigned long flags;
+
+ spin_lock_irqsave(shost->host_lock, flags);
+ while (rport->port_state == FC_PORTSTATE_BLOCKED) {
+ spin_unlock_irqrestore(shost->host_lock, flags);
+ msleep(1000);
+ spin_lock_irqsave(shost->host_lock, flags);
+ }
+ spin_unlock_irqrestore(shost->host_lock, flags);
+}
+EXPORT_SYMBOL(fc_block_scsi_eh);
/**
* fc_vport_setup - allocates and creates a FC virtual port.
@@ -3769,8 +3811,9 @@ fc_bsg_request_handler(struct request_queue *q, struct Scsi_Host *shost,
return;
while (!blk_queue_plugged(q)) {
- if (rport && (rport->port_state == FC_PORTSTATE_BLOCKED))
- break;
+ if (rport && (rport->port_state == FC_PORTSTATE_BLOCKED) &&
+ !(rport->flags & FC_RPORT_FAST_FAIL_TIMEDOUT))
+ break;
req = blk_fetch_request(q);
if (!req)
diff --git a/drivers/scsi/scsi_transport_iscsi.c b/drivers/scsi/scsi_transport_iscsi.c
index ad897df3661..ea3892e7e0f 100644
--- a/drivers/scsi/scsi_transport_iscsi.c
+++ b/drivers/scsi/scsi_transport_iscsi.c
@@ -30,7 +30,7 @@
#include <scsi/scsi_transport_iscsi.h>
#include <scsi/iscsi_if.h>
-#define ISCSI_SESSION_ATTRS 21
+#define ISCSI_SESSION_ATTRS 22
#define ISCSI_CONN_ATTRS 13
#define ISCSI_HOST_ATTRS 4
@@ -627,8 +627,10 @@ static void __iscsi_block_session(struct work_struct *work)
spin_unlock_irqrestore(&session->lock, flags);
scsi_target_block(&session->dev);
ISCSI_DBG_TRANS_SESSION(session, "Completed SCSI target blocking\n");
- queue_delayed_work(iscsi_eh_timer_workq, &session->recovery_work,
- session->recovery_tmo * HZ);
+ if (session->recovery_tmo >= 0)
+ queue_delayed_work(iscsi_eh_timer_workq,
+ &session->recovery_work,
+ session->recovery_tmo * HZ);
}
void iscsi_block_session(struct iscsi_cls_session *session)
@@ -1348,8 +1350,7 @@ iscsi_set_param(struct iscsi_transport *transport, struct iscsi_uevent *ev)
switch (ev->u.set_param.param) {
case ISCSI_PARAM_SESS_RECOVERY_TMO:
sscanf(data, "%d", &value);
- if (value != 0)
- session->recovery_tmo = value;
+ session->recovery_tmo = value;
break;
default:
err = transport->set_param(conn, ev->u.set_param.param,
@@ -1759,6 +1760,7 @@ iscsi_session_attr(password_in, ISCSI_PARAM_PASSWORD_IN, 1);
iscsi_session_attr(fast_abort, ISCSI_PARAM_FAST_ABORT, 0);
iscsi_session_attr(abort_tmo, ISCSI_PARAM_ABORT_TMO, 0);
iscsi_session_attr(lu_reset_tmo, ISCSI_PARAM_LU_RESET_TMO, 0);
+iscsi_session_attr(tgt_reset_tmo, ISCSI_PARAM_TGT_RESET_TMO, 0);
iscsi_session_attr(ifacename, ISCSI_PARAM_IFACE_NAME, 0);
iscsi_session_attr(initiatorname, ISCSI_PARAM_INITIATOR_NAME, 0)
@@ -2000,6 +2002,7 @@ iscsi_register_transport(struct iscsi_transport *tt)
SETUP_SESSION_RD_ATTR(fast_abort, ISCSI_FAST_ABORT);
SETUP_SESSION_RD_ATTR(abort_tmo, ISCSI_ABORT_TMO);
SETUP_SESSION_RD_ATTR(lu_reset_tmo,ISCSI_LU_RESET_TMO);
+ SETUP_SESSION_RD_ATTR(tgt_reset_tmo,ISCSI_TGT_RESET_TMO);
SETUP_SESSION_RD_ATTR(ifacename, ISCSI_IFACE_NAME);
SETUP_SESSION_RD_ATTR(initiatorname, ISCSI_INITIATOR_NAME);
SETUP_PRIV_SESSION_RD_ATTR(recovery_tmo);
diff --git a/drivers/scsi/st.c b/drivers/scsi/st.c
index 12d58a7ed6b..ad59abb4772 100644
--- a/drivers/scsi/st.c
+++ b/drivers/scsi/st.c
@@ -2280,7 +2280,8 @@ static int st_set_options(struct scsi_tape *STp, long options)
} else if (code == MT_ST_SET_CLN) {
value = (options & ~MT_ST_OPTIONS) & 0xff;
if (value != 0 &&
- value < EXTENDED_SENSE_START && value >= SCSI_SENSE_BUFFERSIZE)
+ (value < EXTENDED_SENSE_START ||
+ value >= SCSI_SENSE_BUFFERSIZE))
return (-EINVAL);
STp->cln_mode = value;
STp->cln_sense_mask = (options >> 8) & 0xff;
diff --git a/drivers/scsi/stex.c b/drivers/scsi/stex.c
index 09fa8861fc5..3058bb1aff9 100644
--- a/drivers/scsi/stex.c
+++ b/drivers/scsi/stex.c
@@ -36,11 +36,11 @@
#include <scsi/scsi_eh.h>
#define DRV_NAME "stex"
-#define ST_DRIVER_VERSION "4.6.0000.3"
+#define ST_DRIVER_VERSION "4.6.0000.4"
#define ST_VER_MAJOR 4
#define ST_VER_MINOR 6
#define ST_OEM 0
-#define ST_BUILD_VER 3
+#define ST_BUILD_VER 4
enum {
/* MU register offset */
@@ -64,24 +64,24 @@ enum {
YH2I_REQ_HI = 0xc4,
/* MU register value */
- MU_INBOUND_DOORBELL_HANDSHAKE = 1,
- MU_INBOUND_DOORBELL_REQHEADCHANGED = 2,
- MU_INBOUND_DOORBELL_STATUSTAILCHANGED = 4,
- MU_INBOUND_DOORBELL_HMUSTOPPED = 8,
- MU_INBOUND_DOORBELL_RESET = 16,
-
- MU_OUTBOUND_DOORBELL_HANDSHAKE = 1,
- MU_OUTBOUND_DOORBELL_REQUESTTAILCHANGED = 2,
- MU_OUTBOUND_DOORBELL_STATUSHEADCHANGED = 4,
- MU_OUTBOUND_DOORBELL_BUSCHANGE = 8,
- MU_OUTBOUND_DOORBELL_HASEVENT = 16,
+ MU_INBOUND_DOORBELL_HANDSHAKE = (1 << 0),
+ MU_INBOUND_DOORBELL_REQHEADCHANGED = (1 << 1),
+ MU_INBOUND_DOORBELL_STATUSTAILCHANGED = (1 << 2),
+ MU_INBOUND_DOORBELL_HMUSTOPPED = (1 << 3),
+ MU_INBOUND_DOORBELL_RESET = (1 << 4),
+
+ MU_OUTBOUND_DOORBELL_HANDSHAKE = (1 << 0),
+ MU_OUTBOUND_DOORBELL_REQUESTTAILCHANGED = (1 << 1),
+ MU_OUTBOUND_DOORBELL_STATUSHEADCHANGED = (1 << 2),
+ MU_OUTBOUND_DOORBELL_BUSCHANGE = (1 << 3),
+ MU_OUTBOUND_DOORBELL_HASEVENT = (1 << 4),
+ MU_OUTBOUND_DOORBELL_REQUEST_RESET = (1 << 27),
/* MU status code */
MU_STATE_STARTING = 1,
- MU_STATE_FMU_READY_FOR_HANDSHAKE = 2,
- MU_STATE_SEND_HANDSHAKE_FRAME = 3,
- MU_STATE_STARTED = 4,
- MU_STATE_RESETTING = 5,
+ MU_STATE_STARTED = 2,
+ MU_STATE_RESETTING = 3,
+ MU_STATE_FAILED = 4,
MU_MAX_DELAY = 120,
MU_HANDSHAKE_SIGNATURE = 0x55aaaa55,
@@ -111,6 +111,8 @@ enum {
SS_H2I_INT_RESET = 0x100,
+ SS_I2H_REQUEST_RESET = 0x2000,
+
SS_MU_OPERATIONAL = 0x80000000,
STEX_CDB_LENGTH = 16,
@@ -160,6 +162,7 @@ enum {
INQUIRY_EVPD = 0x01,
ST_ADDITIONAL_MEM = 0x200000,
+ ST_ADDITIONAL_MEM_MIN = 0x80000,
};
struct st_sgitem {
@@ -311,6 +314,10 @@ struct st_hba {
struct st_ccb *wait_ccb;
__le32 *scratch;
+ char work_q_name[20];
+ struct workqueue_struct *work_q;
+ struct work_struct reset_work;
+ wait_queue_head_t reset_waitq;
unsigned int mu_status;
unsigned int cardtype;
int msi_enabled;
@@ -577,6 +584,9 @@ stex_queuecommand(struct scsi_cmnd *cmd, void (* done)(struct scsi_cmnd *))
lun = cmd->device->lun;
hba = (struct st_hba *) &host->hostdata[0];
+ if (unlikely(hba->mu_status == MU_STATE_RESETTING))
+ return SCSI_MLQUEUE_HOST_BUSY;
+
switch (cmd->cmnd[0]) {
case MODE_SENSE_10:
{
@@ -841,7 +851,6 @@ static irqreturn_t stex_intr(int irq, void *__hba)
void __iomem *base = hba->mmio_base;
u32 data;
unsigned long flags;
- int handled = 0;
spin_lock_irqsave(hba->host->host_lock, flags);
@@ -852,12 +861,16 @@ static irqreturn_t stex_intr(int irq, void *__hba)
writel(data, base + ODBL);
readl(base + ODBL); /* flush */
stex_mu_intr(hba, data);
- handled = 1;
+ spin_unlock_irqrestore(hba->host->host_lock, flags);
+ if (unlikely(data & MU_OUTBOUND_DOORBELL_REQUEST_RESET &&
+ hba->cardtype == st_shasta))
+ queue_work(hba->work_q, &hba->reset_work);
+ return IRQ_HANDLED;
}
spin_unlock_irqrestore(hba->host->host_lock, flags);
- return IRQ_RETVAL(handled);
+ return IRQ_NONE;
}
static void stex_ss_mu_intr(struct st_hba *hba)
@@ -939,7 +952,6 @@ static irqreturn_t stex_ss_intr(int irq, void *__hba)
void __iomem *base = hba->mmio_base;
u32 data;
unsigned long flags;
- int handled = 0;
spin_lock_irqsave(hba->host->host_lock, flags);
@@ -948,12 +960,15 @@ static irqreturn_t stex_ss_intr(int irq, void *__hba)
/* clear the interrupt */
writel(data, base + YI2H_INT_C);
stex_ss_mu_intr(hba);
- handled = 1;
+ spin_unlock_irqrestore(hba->host->host_lock, flags);
+ if (unlikely(data & SS_I2H_REQUEST_RESET))
+ queue_work(hba->work_q, &hba->reset_work);
+ return IRQ_HANDLED;
}
spin_unlock_irqrestore(hba->host->host_lock, flags);
- return IRQ_RETVAL(handled);
+ return IRQ_NONE;
}
static int stex_common_handshake(struct st_hba *hba)
@@ -1001,7 +1016,7 @@ static int stex_common_handshake(struct st_hba *hba)
h->partner_type = HMU_PARTNER_TYPE;
if (hba->extra_offset) {
h->extra_offset = cpu_to_le32(hba->extra_offset);
- h->extra_size = cpu_to_le32(ST_ADDITIONAL_MEM);
+ h->extra_size = cpu_to_le32(hba->dma_size - hba->extra_offset);
} else
h->extra_offset = h->extra_size = 0;
@@ -1046,7 +1061,7 @@ static int stex_ss_handshake(struct st_hba *hba)
struct st_msg_header *msg_h;
struct handshake_frame *h;
__le32 *scratch;
- u32 data;
+ u32 data, scratch_size;
unsigned long before;
int ret = 0;
@@ -1074,13 +1089,16 @@ static int stex_ss_handshake(struct st_hba *hba)
stex_gettime(&h->hosttime);
h->partner_type = HMU_PARTNER_TYPE;
h->extra_offset = h->extra_size = 0;
- h->scratch_size = cpu_to_le32((hba->sts_count+1)*sizeof(u32));
+ scratch_size = (hba->sts_count+1)*sizeof(u32);
+ h->scratch_size = cpu_to_le32(scratch_size);
data = readl(base + YINT_EN);
data &= ~4;
writel(data, base + YINT_EN);
writel((hba->dma_handle >> 16) >> 16, base + YH2I_REQ_HI);
+ readl(base + YH2I_REQ_HI);
writel(hba->dma_handle, base + YH2I_REQ);
+ readl(base + YH2I_REQ); /* flush */
scratch = hba->scratch;
before = jiffies;
@@ -1096,7 +1114,7 @@ static int stex_ss_handshake(struct st_hba *hba)
msleep(1);
}
- *scratch = 0;
+ memset(scratch, 0, scratch_size);
msg_h->flag = 0;
return ret;
}
@@ -1105,19 +1123,24 @@ static int stex_handshake(struct st_hba *hba)
{
int err;
unsigned long flags;
+ unsigned int mu_status;
err = (hba->cardtype == st_yel) ?
stex_ss_handshake(hba) : stex_common_handshake(hba);
+ spin_lock_irqsave(hba->host->host_lock, flags);
+ mu_status = hba->mu_status;
if (err == 0) {
- spin_lock_irqsave(hba->host->host_lock, flags);
hba->req_head = 0;
hba->req_tail = 0;
hba->status_head = 0;
hba->status_tail = 0;
hba->out_req_cnt = 0;
hba->mu_status = MU_STATE_STARTED;
- spin_unlock_irqrestore(hba->host->host_lock, flags);
- }
+ } else
+ hba->mu_status = MU_STATE_FAILED;
+ if (mu_status == MU_STATE_RESETTING)
+ wake_up_all(&hba->reset_waitq);
+ spin_unlock_irqrestore(hba->host->host_lock, flags);
return err;
}
@@ -1137,17 +1160,11 @@ static int stex_abort(struct scsi_cmnd *cmd)
base = hba->mmio_base;
spin_lock_irqsave(host->host_lock, flags);
- if (tag < host->can_queue && hba->ccb[tag].cmd == cmd)
+ if (tag < host->can_queue &&
+ hba->ccb[tag].req && hba->ccb[tag].cmd == cmd)
hba->wait_ccb = &hba->ccb[tag];
- else {
- for (tag = 0; tag < host->can_queue; tag++)
- if (hba->ccb[tag].cmd == cmd) {
- hba->wait_ccb = &hba->ccb[tag];
- break;
- }
- if (tag >= host->can_queue)
- goto out;
- }
+ else
+ goto out;
if (hba->cardtype == st_yel) {
data = readl(base + YI2H_INT);
@@ -1221,6 +1238,37 @@ static void stex_hard_reset(struct st_hba *hba)
hba->pdev->saved_config_space[i]);
}
+static int stex_yos_reset(struct st_hba *hba)
+{
+ void __iomem *base;
+ unsigned long flags, before;
+ int ret = 0;
+
+ base = hba->mmio_base;
+ writel(MU_INBOUND_DOORBELL_RESET, base + IDBL);
+ readl(base + IDBL); /* flush */
+ before = jiffies;
+ while (hba->out_req_cnt > 0) {
+ if (time_after(jiffies, before + ST_INTERNAL_TIMEOUT * HZ)) {
+ printk(KERN_WARNING DRV_NAME
+ "(%s): reset timeout\n", pci_name(hba->pdev));
+ ret = -1;
+ break;
+ }
+ msleep(1);
+ }
+
+ spin_lock_irqsave(hba->host->host_lock, flags);
+ if (ret == -1)
+ hba->mu_status = MU_STATE_FAILED;
+ else
+ hba->mu_status = MU_STATE_STARTED;
+ wake_up_all(&hba->reset_waitq);
+ spin_unlock_irqrestore(hba->host->host_lock, flags);
+
+ return ret;
+}
+
static void stex_ss_reset(struct st_hba *hba)
{
writel(SS_H2I_INT_RESET, hba->mmio_base + YH2I_INT);
@@ -1228,66 +1276,86 @@ static void stex_ss_reset(struct st_hba *hba)
ssleep(5);
}
-static int stex_reset(struct scsi_cmnd *cmd)
+static int stex_do_reset(struct st_hba *hba)
{
- struct st_hba *hba;
- void __iomem *base;
- unsigned long flags, before;
+ struct st_ccb *ccb;
+ unsigned long flags;
+ unsigned int mu_status = MU_STATE_RESETTING;
+ u16 tag;
- hba = (struct st_hba *) &cmd->device->host->hostdata[0];
+ spin_lock_irqsave(hba->host->host_lock, flags);
+ if (hba->mu_status == MU_STATE_STARTING) {
+ spin_unlock_irqrestore(hba->host->host_lock, flags);
+ printk(KERN_INFO DRV_NAME "(%s): request reset during init\n",
+ pci_name(hba->pdev));
+ return 0;
+ }
+ while (hba->mu_status == MU_STATE_RESETTING) {
+ spin_unlock_irqrestore(hba->host->host_lock, flags);
+ wait_event_timeout(hba->reset_waitq,
+ hba->mu_status != MU_STATE_RESETTING,
+ MU_MAX_DELAY * HZ);
+ spin_lock_irqsave(hba->host->host_lock, flags);
+ mu_status = hba->mu_status;
+ }
- printk(KERN_INFO DRV_NAME
- "(%s): resetting host\n", pci_name(hba->pdev));
- scsi_print_command(cmd);
+ if (mu_status != MU_STATE_RESETTING) {
+ spin_unlock_irqrestore(hba->host->host_lock, flags);
+ return (mu_status == MU_STATE_STARTED) ? 0 : -1;
+ }
hba->mu_status = MU_STATE_RESETTING;
+ spin_unlock_irqrestore(hba->host->host_lock, flags);
+
+ if (hba->cardtype == st_yosemite)
+ return stex_yos_reset(hba);
if (hba->cardtype == st_shasta)
stex_hard_reset(hba);
else if (hba->cardtype == st_yel)
stex_ss_reset(hba);
- if (hba->cardtype != st_yosemite) {
- if (stex_handshake(hba)) {
- printk(KERN_WARNING DRV_NAME
- "(%s): resetting: handshake failed\n",
- pci_name(hba->pdev));
- return FAILED;
+ spin_lock_irqsave(hba->host->host_lock, flags);
+ for (tag = 0; tag < hba->host->can_queue; tag++) {
+ ccb = &hba->ccb[tag];
+ if (ccb->req == NULL)
+ continue;
+ ccb->req = NULL;
+ if (ccb->cmd) {
+ scsi_dma_unmap(ccb->cmd);
+ ccb->cmd->result = DID_RESET << 16;
+ ccb->cmd->scsi_done(ccb->cmd);
+ ccb->cmd = NULL;
}
- return SUCCESS;
}
+ spin_unlock_irqrestore(hba->host->host_lock, flags);
- /* st_yosemite */
- writel(MU_INBOUND_DOORBELL_RESET, hba->mmio_base + IDBL);
- readl(hba->mmio_base + IDBL); /* flush */
- before = jiffies;
- while (hba->out_req_cnt > 0) {
- if (time_after(jiffies, before + ST_INTERNAL_TIMEOUT * HZ)) {
- printk(KERN_WARNING DRV_NAME
- "(%s): reset timeout\n", pci_name(hba->pdev));
- return FAILED;
- }
- msleep(1);
- }
+ if (stex_handshake(hba) == 0)
+ return 0;
- base = hba->mmio_base;
- writel(0, base + IMR0);
- readl(base + IMR0);
- writel(0, base + OMR0);
- readl(base + OMR0);
- writel(0, base + IMR1);
- readl(base + IMR1);
- writel(0, base + OMR1);
- readl(base + OMR1); /* flush */
- spin_lock_irqsave(hba->host->host_lock, flags);
- hba->req_head = 0;
- hba->req_tail = 0;
- hba->status_head = 0;
- hba->status_tail = 0;
- hba->out_req_cnt = 0;
- hba->mu_status = MU_STATE_STARTED;
- spin_unlock_irqrestore(hba->host->host_lock, flags);
- return SUCCESS;
+ printk(KERN_WARNING DRV_NAME "(%s): resetting: handshake failed\n",
+ pci_name(hba->pdev));
+ return -1;
+}
+
+static int stex_reset(struct scsi_cmnd *cmd)
+{
+ struct st_hba *hba;
+
+ hba = (struct st_hba *) &cmd->device->host->hostdata[0];
+
+ printk(KERN_INFO DRV_NAME
+ "(%s): resetting host\n", pci_name(hba->pdev));
+ scsi_print_command(cmd);
+
+ return stex_do_reset(hba) ? FAILED : SUCCESS;
+}
+
+static void stex_reset_work(struct work_struct *work)
+{
+ struct st_hba *hba = container_of(work, struct st_hba, reset_work);
+
+ stex_do_reset(hba);
}
static int stex_biosparam(struct scsi_device *sdev,
@@ -1420,8 +1488,8 @@ static int stex_set_dma_mask(struct pci_dev * pdev)
{
int ret;
- if (!pci_set_dma_mask(pdev, DMA_BIT_MASK(64))
- && !pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(64)))
+ if (!pci_set_dma_mask(pdev, DMA_BIT_MASK(64))
+ && !pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(64)))
return 0;
ret = pci_set_dma_mask(pdev, DMA_BIT_MASK(32));
if (!ret)
@@ -1528,10 +1596,24 @@ stex_probe(struct pci_dev *pdev, const struct pci_device_id *id)
hba->dma_mem = dma_alloc_coherent(&pdev->dev,
hba->dma_size, &hba->dma_handle, GFP_KERNEL);
if (!hba->dma_mem) {
- err = -ENOMEM;
- printk(KERN_ERR DRV_NAME "(%s): dma mem alloc failed\n",
- pci_name(pdev));
- goto out_iounmap;
+ /* Retry minimum coherent mapping for st_seq and st_vsc */
+ if (hba->cardtype == st_seq ||
+ (hba->cardtype == st_vsc && (pdev->subsystem_device & 1))) {
+ printk(KERN_WARNING DRV_NAME
+ "(%s): allocating min buffer for controller\n",
+ pci_name(pdev));
+ hba->dma_size = hba->extra_offset
+ + ST_ADDITIONAL_MEM_MIN;
+ hba->dma_mem = dma_alloc_coherent(&pdev->dev,
+ hba->dma_size, &hba->dma_handle, GFP_KERNEL);
+ }
+
+ if (!hba->dma_mem) {
+ err = -ENOMEM;
+ printk(KERN_ERR DRV_NAME "(%s): dma mem alloc failed\n",
+ pci_name(pdev));
+ goto out_iounmap;
+ }
}
hba->ccb = kcalloc(ci->rq_count, sizeof(struct st_ccb), GFP_KERNEL);
@@ -1568,12 +1650,24 @@ stex_probe(struct pci_dev *pdev, const struct pci_device_id *id)
hba->host = host;
hba->pdev = pdev;
+ init_waitqueue_head(&hba->reset_waitq);
+
+ snprintf(hba->work_q_name, sizeof(hba->work_q_name),
+ "stex_wq_%d", host->host_no);
+ hba->work_q = create_singlethread_workqueue(hba->work_q_name);
+ if (!hba->work_q) {
+ printk(KERN_ERR DRV_NAME "(%s): create workqueue failed\n",
+ pci_name(pdev));
+ err = -ENOMEM;
+ goto out_ccb_free;
+ }
+ INIT_WORK(&hba->reset_work, stex_reset_work);
err = stex_request_irq(hba);
if (err) {
printk(KERN_ERR DRV_NAME "(%s): request irq failed\n",
pci_name(pdev));
- goto out_ccb_free;
+ goto out_free_wq;
}
err = stex_handshake(hba);
@@ -1602,6 +1696,8 @@ stex_probe(struct pci_dev *pdev, const struct pci_device_id *id)
out_free_irq:
stex_free_irq(hba);
+out_free_wq:
+ destroy_workqueue(hba->work_q);
out_ccb_free:
kfree(hba->ccb);
out_pci_free:
@@ -1669,6 +1765,8 @@ static void stex_hba_free(struct st_hba *hba)
{
stex_free_irq(hba);
+ destroy_workqueue(hba->work_q);
+
iounmap(hba->mmio_base);
pci_release_regions(hba->pdev);
diff --git a/drivers/scsi/vmw_pvscsi.c b/drivers/scsi/vmw_pvscsi.c
new file mode 100644
index 00000000000..d2604c813a2
--- /dev/null
+++ b/drivers/scsi/vmw_pvscsi.c
@@ -0,0 +1,1407 @@
+/*
+ * Linux driver for VMware's para-virtualized SCSI HBA.
+ *
+ * Copyright (C) 2008-2009, VMware, Inc. All Rights Reserved.
+ *
+ * This program is free software; you can 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 and no later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or
+ * NON INFRINGEMENT. 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 by: Alok N Kataria <akataria@vmware.com>
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <linux/workqueue.h>
+#include <linux/pci.h>
+
+#include <scsi/scsi.h>
+#include <scsi/scsi_host.h>
+#include <scsi/scsi_cmnd.h>
+#include <scsi/scsi_device.h>
+
+#include "vmw_pvscsi.h"
+
+#define PVSCSI_LINUX_DRIVER_DESC "VMware PVSCSI driver"
+
+MODULE_DESCRIPTION(PVSCSI_LINUX_DRIVER_DESC);
+MODULE_AUTHOR("VMware, Inc.");
+MODULE_LICENSE("GPL");
+MODULE_VERSION(PVSCSI_DRIVER_VERSION_STRING);
+
+#define PVSCSI_DEFAULT_NUM_PAGES_PER_RING 8
+#define PVSCSI_DEFAULT_NUM_PAGES_MSG_RING 1
+#define PVSCSI_DEFAULT_QUEUE_DEPTH 64
+#define SGL_SIZE PAGE_SIZE
+
+struct pvscsi_sg_list {
+ struct PVSCSISGElement sge[PVSCSI_MAX_NUM_SG_ENTRIES_PER_SEGMENT];
+};
+
+struct pvscsi_ctx {
+ /*
+ * The index of the context in cmd_map serves as the context ID for a
+ * 1-to-1 mapping completions back to requests.
+ */
+ struct scsi_cmnd *cmd;
+ struct pvscsi_sg_list *sgl;
+ struct list_head list;
+ dma_addr_t dataPA;
+ dma_addr_t sensePA;
+ dma_addr_t sglPA;
+};
+
+struct pvscsi_adapter {
+ char *mmioBase;
+ unsigned int irq;
+ u8 rev;
+ bool use_msi;
+ bool use_msix;
+ bool use_msg;
+
+ spinlock_t hw_lock;
+
+ struct workqueue_struct *workqueue;
+ struct work_struct work;
+
+ struct PVSCSIRingReqDesc *req_ring;
+ unsigned req_pages;
+ unsigned req_depth;
+ dma_addr_t reqRingPA;
+
+ struct PVSCSIRingCmpDesc *cmp_ring;
+ unsigned cmp_pages;
+ dma_addr_t cmpRingPA;
+
+ struct PVSCSIRingMsgDesc *msg_ring;
+ unsigned msg_pages;
+ dma_addr_t msgRingPA;
+
+ struct PVSCSIRingsState *rings_state;
+ dma_addr_t ringStatePA;
+
+ struct pci_dev *dev;
+ struct Scsi_Host *host;
+
+ struct list_head cmd_pool;
+ struct pvscsi_ctx *cmd_map;
+};
+
+
+/* Command line parameters */
+static int pvscsi_ring_pages = PVSCSI_DEFAULT_NUM_PAGES_PER_RING;
+static int pvscsi_msg_ring_pages = PVSCSI_DEFAULT_NUM_PAGES_MSG_RING;
+static int pvscsi_cmd_per_lun = PVSCSI_DEFAULT_QUEUE_DEPTH;
+static bool pvscsi_disable_msi;
+static bool pvscsi_disable_msix;
+static bool pvscsi_use_msg = true;
+
+#define PVSCSI_RW (S_IRUSR | S_IWUSR)
+
+module_param_named(ring_pages, pvscsi_ring_pages, int, PVSCSI_RW);
+MODULE_PARM_DESC(ring_pages, "Number of pages per req/cmp ring - (default="
+ __stringify(PVSCSI_DEFAULT_NUM_PAGES_PER_RING) ")");
+
+module_param_named(msg_ring_pages, pvscsi_msg_ring_pages, int, PVSCSI_RW);
+MODULE_PARM_DESC(msg_ring_pages, "Number of pages for the msg ring - (default="
+ __stringify(PVSCSI_DEFAULT_NUM_PAGES_MSG_RING) ")");
+
+module_param_named(cmd_per_lun, pvscsi_cmd_per_lun, int, PVSCSI_RW);
+MODULE_PARM_DESC(cmd_per_lun, "Maximum commands per lun - (default="
+ __stringify(PVSCSI_MAX_REQ_QUEUE_DEPTH) ")");
+
+module_param_named(disable_msi, pvscsi_disable_msi, bool, PVSCSI_RW);
+MODULE_PARM_DESC(disable_msi, "Disable MSI use in driver - (default=0)");
+
+module_param_named(disable_msix, pvscsi_disable_msix, bool, PVSCSI_RW);
+MODULE_PARM_DESC(disable_msix, "Disable MSI-X use in driver - (default=0)");
+
+module_param_named(use_msg, pvscsi_use_msg, bool, PVSCSI_RW);
+MODULE_PARM_DESC(use_msg, "Use msg ring when available - (default=1)");
+
+static const struct pci_device_id pvscsi_pci_tbl[] = {
+ { PCI_VDEVICE(VMWARE, PCI_DEVICE_ID_VMWARE_PVSCSI) },
+ { 0 }
+};
+
+MODULE_DEVICE_TABLE(pci, pvscsi_pci_tbl);
+
+static struct device *
+pvscsi_dev(const struct pvscsi_adapter *adapter)
+{
+ return &(adapter->dev->dev);
+}
+
+static struct pvscsi_ctx *
+pvscsi_find_context(const struct pvscsi_adapter *adapter, struct scsi_cmnd *cmd)
+{
+ struct pvscsi_ctx *ctx, *end;
+
+ end = &adapter->cmd_map[adapter->req_depth];
+ for (ctx = adapter->cmd_map; ctx < end; ctx++)
+ if (ctx->cmd == cmd)
+ return ctx;
+
+ return NULL;
+}
+
+static struct pvscsi_ctx *
+pvscsi_acquire_context(struct pvscsi_adapter *adapter, struct scsi_cmnd *cmd)
+{
+ struct pvscsi_ctx *ctx;
+
+ if (list_empty(&adapter->cmd_pool))
+ return NULL;
+
+ ctx = list_first_entry(&adapter->cmd_pool, struct pvscsi_ctx, list);
+ ctx->cmd = cmd;
+ list_del(&ctx->list);
+
+ return ctx;
+}
+
+static void pvscsi_release_context(struct pvscsi_adapter *adapter,
+ struct pvscsi_ctx *ctx)
+{
+ ctx->cmd = NULL;
+ list_add(&ctx->list, &adapter->cmd_pool);
+}
+
+/*
+ * Map a pvscsi_ctx struct to a context ID field value; we map to a simple
+ * non-zero integer. ctx always points to an entry in cmd_map array, hence
+ * the return value is always >=1.
+ */
+static u64 pvscsi_map_context(const struct pvscsi_adapter *adapter,
+ const struct pvscsi_ctx *ctx)
+{
+ return ctx - adapter->cmd_map + 1;
+}
+
+static struct pvscsi_ctx *
+pvscsi_get_context(const struct pvscsi_adapter *adapter, u64 context)
+{
+ return &adapter->cmd_map[context - 1];
+}
+
+static void pvscsi_reg_write(const struct pvscsi_adapter *adapter,
+ u32 offset, u32 val)
+{
+ writel(val, adapter->mmioBase + offset);
+}
+
+static u32 pvscsi_reg_read(const struct pvscsi_adapter *adapter, u32 offset)
+{
+ return readl(adapter->mmioBase + offset);
+}
+
+static u32 pvscsi_read_intr_status(const struct pvscsi_adapter *adapter)
+{
+ return pvscsi_reg_read(adapter, PVSCSI_REG_OFFSET_INTR_STATUS);
+}
+
+static void pvscsi_write_intr_status(const struct pvscsi_adapter *adapter,
+ u32 val)
+{
+ pvscsi_reg_write(adapter, PVSCSI_REG_OFFSET_INTR_STATUS, val);
+}
+
+static void pvscsi_unmask_intr(const struct pvscsi_adapter *adapter)
+{
+ u32 intr_bits;
+
+ intr_bits = PVSCSI_INTR_CMPL_MASK;
+ if (adapter->use_msg)
+ intr_bits |= PVSCSI_INTR_MSG_MASK;
+
+ pvscsi_reg_write(adapter, PVSCSI_REG_OFFSET_INTR_MASK, intr_bits);
+}
+
+static void pvscsi_mask_intr(const struct pvscsi_adapter *adapter)
+{
+ pvscsi_reg_write(adapter, PVSCSI_REG_OFFSET_INTR_MASK, 0);
+}
+
+static void pvscsi_write_cmd_desc(const struct pvscsi_adapter *adapter,
+ u32 cmd, const void *desc, size_t len)
+{
+ const u32 *ptr = desc;
+ size_t i;
+
+ len /= sizeof(*ptr);
+ pvscsi_reg_write(adapter, PVSCSI_REG_OFFSET_COMMAND, cmd);
+ for (i = 0; i < len; i++)
+ pvscsi_reg_write(adapter,
+ PVSCSI_REG_OFFSET_COMMAND_DATA, ptr[i]);
+}
+
+static void pvscsi_abort_cmd(const struct pvscsi_adapter *adapter,
+ const struct pvscsi_ctx *ctx)
+{
+ struct PVSCSICmdDescAbortCmd cmd = { 0 };
+
+ cmd.target = ctx->cmd->device->id;
+ cmd.context = pvscsi_map_context(adapter, ctx);
+
+ pvscsi_write_cmd_desc(adapter, PVSCSI_CMD_ABORT_CMD, &cmd, sizeof(cmd));
+}
+
+static void pvscsi_kick_rw_io(const struct pvscsi_adapter *adapter)
+{
+ pvscsi_reg_write(adapter, PVSCSI_REG_OFFSET_KICK_RW_IO, 0);
+}
+
+static void pvscsi_process_request_ring(const struct pvscsi_adapter *adapter)
+{
+ pvscsi_reg_write(adapter, PVSCSI_REG_OFFSET_KICK_NON_RW_IO, 0);
+}
+
+static int scsi_is_rw(unsigned char op)
+{
+ return op == READ_6 || op == WRITE_6 ||
+ op == READ_10 || op == WRITE_10 ||
+ op == READ_12 || op == WRITE_12 ||
+ op == READ_16 || op == WRITE_16;
+}
+
+static void pvscsi_kick_io(const struct pvscsi_adapter *adapter,
+ unsigned char op)
+{
+ if (scsi_is_rw(op))
+ pvscsi_kick_rw_io(adapter);
+ else
+ pvscsi_process_request_ring(adapter);
+}
+
+static void ll_adapter_reset(const struct pvscsi_adapter *adapter)
+{
+ dev_dbg(pvscsi_dev(adapter), "Adapter Reset on %p\n", adapter);
+
+ pvscsi_write_cmd_desc(adapter, PVSCSI_CMD_ADAPTER_RESET, NULL, 0);
+}
+
+static void ll_bus_reset(const struct pvscsi_adapter *adapter)
+{
+ dev_dbg(pvscsi_dev(adapter), "Reseting bus on %p\n", adapter);
+
+ pvscsi_write_cmd_desc(adapter, PVSCSI_CMD_RESET_BUS, NULL, 0);
+}
+
+static void ll_device_reset(const struct pvscsi_adapter *adapter, u32 target)
+{
+ struct PVSCSICmdDescResetDevice cmd = { 0 };
+
+ dev_dbg(pvscsi_dev(adapter), "Reseting device: target=%u\n", target);
+
+ cmd.target = target;
+
+ pvscsi_write_cmd_desc(adapter, PVSCSI_CMD_RESET_DEVICE,
+ &cmd, sizeof(cmd));
+}
+
+static void pvscsi_create_sg(struct pvscsi_ctx *ctx,
+ struct scatterlist *sg, unsigned count)
+{
+ unsigned i;
+ struct PVSCSISGElement *sge;
+
+ BUG_ON(count > PVSCSI_MAX_NUM_SG_ENTRIES_PER_SEGMENT);
+
+ sge = &ctx->sgl->sge[0];
+ for (i = 0; i < count; i++, sg++) {
+ sge[i].addr = sg_dma_address(sg);
+ sge[i].length = sg_dma_len(sg);
+ sge[i].flags = 0;
+ }
+}
+
+/*
+ * Map all data buffers for a command into PCI space and
+ * setup the scatter/gather list if needed.
+ */
+static void pvscsi_map_buffers(struct pvscsi_adapter *adapter,
+ struct pvscsi_ctx *ctx, struct scsi_cmnd *cmd,
+ struct PVSCSIRingReqDesc *e)
+{
+ unsigned count;
+ unsigned bufflen = scsi_bufflen(cmd);
+ struct scatterlist *sg;
+
+ e->dataLen = bufflen;
+ e->dataAddr = 0;
+ if (bufflen == 0)
+ return;
+
+ sg = scsi_sglist(cmd);
+ count = scsi_sg_count(cmd);
+ if (count != 0) {
+ int segs = scsi_dma_map(cmd);
+ if (segs > 1) {
+ pvscsi_create_sg(ctx, sg, segs);
+
+ e->flags |= PVSCSI_FLAG_CMD_WITH_SG_LIST;
+ ctx->sglPA = pci_map_single(adapter->dev, ctx->sgl,
+ SGL_SIZE, PCI_DMA_TODEVICE);
+ e->dataAddr = ctx->sglPA;
+ } else
+ e->dataAddr = sg_dma_address(sg);
+ } else {
+ /*
+ * In case there is no S/G list, scsi_sglist points
+ * directly to the buffer.
+ */
+ ctx->dataPA = pci_map_single(adapter->dev, sg, bufflen,
+ cmd->sc_data_direction);
+ e->dataAddr = ctx->dataPA;
+ }
+}
+
+static void pvscsi_unmap_buffers(const struct pvscsi_adapter *adapter,
+ struct pvscsi_ctx *ctx)
+{
+ struct scsi_cmnd *cmd;
+ unsigned bufflen;
+
+ cmd = ctx->cmd;
+ bufflen = scsi_bufflen(cmd);
+
+ if (bufflen != 0) {
+ unsigned count = scsi_sg_count(cmd);
+
+ if (count != 0) {
+ scsi_dma_unmap(cmd);
+ if (ctx->sglPA) {
+ pci_unmap_single(adapter->dev, ctx->sglPA,
+ SGL_SIZE, PCI_DMA_TODEVICE);
+ ctx->sglPA = 0;
+ }
+ } else
+ pci_unmap_single(adapter->dev, ctx->dataPA, bufflen,
+ cmd->sc_data_direction);
+ }
+ if (cmd->sense_buffer)
+ pci_unmap_single(adapter->dev, ctx->sensePA,
+ SCSI_SENSE_BUFFERSIZE, PCI_DMA_FROMDEVICE);
+}
+
+static int __devinit pvscsi_allocate_rings(struct pvscsi_adapter *adapter)
+{
+ adapter->rings_state = pci_alloc_consistent(adapter->dev, PAGE_SIZE,
+ &adapter->ringStatePA);
+ if (!adapter->rings_state)
+ return -ENOMEM;
+
+ adapter->req_pages = min(PVSCSI_MAX_NUM_PAGES_REQ_RING,
+ pvscsi_ring_pages);
+ adapter->req_depth = adapter->req_pages
+ * PVSCSI_MAX_NUM_REQ_ENTRIES_PER_PAGE;
+ adapter->req_ring = pci_alloc_consistent(adapter->dev,
+ adapter->req_pages * PAGE_SIZE,
+ &adapter->reqRingPA);
+ if (!adapter->req_ring)
+ return -ENOMEM;
+
+ adapter->cmp_pages = min(PVSCSI_MAX_NUM_PAGES_CMP_RING,
+ pvscsi_ring_pages);
+ adapter->cmp_ring = pci_alloc_consistent(adapter->dev,
+ adapter->cmp_pages * PAGE_SIZE,
+ &adapter->cmpRingPA);
+ if (!adapter->cmp_ring)
+ return -ENOMEM;
+
+ BUG_ON(!IS_ALIGNED(adapter->ringStatePA, PAGE_SIZE));
+ BUG_ON(!IS_ALIGNED(adapter->reqRingPA, PAGE_SIZE));
+ BUG_ON(!IS_ALIGNED(adapter->cmpRingPA, PAGE_SIZE));
+
+ if (!adapter->use_msg)
+ return 0;
+
+ adapter->msg_pages = min(PVSCSI_MAX_NUM_PAGES_MSG_RING,
+ pvscsi_msg_ring_pages);
+ adapter->msg_ring = pci_alloc_consistent(adapter->dev,
+ adapter->msg_pages * PAGE_SIZE,
+ &adapter->msgRingPA);
+ if (!adapter->msg_ring)
+ return -ENOMEM;
+ BUG_ON(!IS_ALIGNED(adapter->msgRingPA, PAGE_SIZE));
+
+ return 0;
+}
+
+static void pvscsi_setup_all_rings(const struct pvscsi_adapter *adapter)
+{
+ struct PVSCSICmdDescSetupRings cmd = { 0 };
+ dma_addr_t base;
+ unsigned i;
+
+ cmd.ringsStatePPN = adapter->ringStatePA >> PAGE_SHIFT;
+ cmd.reqRingNumPages = adapter->req_pages;
+ cmd.cmpRingNumPages = adapter->cmp_pages;
+
+ base = adapter->reqRingPA;
+ for (i = 0; i < adapter->req_pages; i++) {
+ cmd.reqRingPPNs[i] = base >> PAGE_SHIFT;
+ base += PAGE_SIZE;
+ }
+
+ base = adapter->cmpRingPA;
+ for (i = 0; i < adapter->cmp_pages; i++) {
+ cmd.cmpRingPPNs[i] = base >> PAGE_SHIFT;
+ base += PAGE_SIZE;
+ }
+
+ memset(adapter->rings_state, 0, PAGE_SIZE);
+ memset(adapter->req_ring, 0, adapter->req_pages * PAGE_SIZE);
+ memset(adapter->cmp_ring, 0, adapter->cmp_pages * PAGE_SIZE);
+
+ pvscsi_write_cmd_desc(adapter, PVSCSI_CMD_SETUP_RINGS,
+ &cmd, sizeof(cmd));
+
+ if (adapter->use_msg) {
+ struct PVSCSICmdDescSetupMsgRing cmd_msg = { 0 };
+
+ cmd_msg.numPages = adapter->msg_pages;
+
+ base = adapter->msgRingPA;
+ for (i = 0; i < adapter->msg_pages; i++) {
+ cmd_msg.ringPPNs[i] = base >> PAGE_SHIFT;
+ base += PAGE_SIZE;
+ }
+ memset(adapter->msg_ring, 0, adapter->msg_pages * PAGE_SIZE);
+
+ pvscsi_write_cmd_desc(adapter, PVSCSI_CMD_SETUP_MSG_RING,
+ &cmd_msg, sizeof(cmd_msg));
+ }
+}
+
+/*
+ * Pull a completion descriptor off and pass the completion back
+ * to the SCSI mid layer.
+ */
+static void pvscsi_complete_request(struct pvscsi_adapter *adapter,
+ const struct PVSCSIRingCmpDesc *e)
+{
+ struct pvscsi_ctx *ctx;
+ struct scsi_cmnd *cmd;
+ u32 btstat = e->hostStatus;
+ u32 sdstat = e->scsiStatus;
+
+ ctx = pvscsi_get_context(adapter, e->context);
+ cmd = ctx->cmd;
+ pvscsi_unmap_buffers(adapter, ctx);
+ pvscsi_release_context(adapter, ctx);
+ cmd->result = 0;
+
+ if (sdstat != SAM_STAT_GOOD &&
+ (btstat == BTSTAT_SUCCESS ||
+ btstat == BTSTAT_LINKED_COMMAND_COMPLETED ||
+ btstat == BTSTAT_LINKED_COMMAND_COMPLETED_WITH_FLAG)) {
+ cmd->result = (DID_OK << 16) | sdstat;
+ if (sdstat == SAM_STAT_CHECK_CONDITION && cmd->sense_buffer)
+ cmd->result |= (DRIVER_SENSE << 24);
+ } else
+ switch (btstat) {
+ case BTSTAT_SUCCESS:
+ case BTSTAT_LINKED_COMMAND_COMPLETED:
+ case BTSTAT_LINKED_COMMAND_COMPLETED_WITH_FLAG:
+ /* If everything went fine, let's move on.. */
+ cmd->result = (DID_OK << 16);
+ break;
+
+ case BTSTAT_DATARUN:
+ case BTSTAT_DATA_UNDERRUN:
+ /* Report residual data in underruns */
+ scsi_set_resid(cmd, scsi_bufflen(cmd) - e->dataLen);
+ cmd->result = (DID_ERROR << 16);
+ break;
+
+ case BTSTAT_SELTIMEO:
+ /* Our emulation returns this for non-connected devs */
+ cmd->result = (DID_BAD_TARGET << 16);
+ break;
+
+ case BTSTAT_LUNMISMATCH:
+ case BTSTAT_TAGREJECT:
+ case BTSTAT_BADMSG:
+ cmd->result = (DRIVER_INVALID << 24);
+ /* fall through */
+
+ case BTSTAT_HAHARDWARE:
+ case BTSTAT_INVPHASE:
+ case BTSTAT_HATIMEOUT:
+ case BTSTAT_NORESPONSE:
+ case BTSTAT_DISCONNECT:
+ case BTSTAT_HASOFTWARE:
+ case BTSTAT_BUSFREE:
+ case BTSTAT_SENSFAILED:
+ cmd->result |= (DID_ERROR << 16);
+ break;
+
+ case BTSTAT_SENTRST:
+ case BTSTAT_RECVRST:
+ case BTSTAT_BUSRESET:
+ cmd->result = (DID_RESET << 16);
+ break;
+
+ case BTSTAT_ABORTQUEUE:
+ cmd->result = (DID_ABORT << 16);
+ break;
+
+ case BTSTAT_SCSIPARITY:
+ cmd->result = (DID_PARITY << 16);
+ break;
+
+ default:
+ cmd->result = (DID_ERROR << 16);
+ scmd_printk(KERN_DEBUG, cmd,
+ "Unknown completion status: 0x%x\n",
+ btstat);
+ }
+
+ dev_dbg(&cmd->device->sdev_gendev,
+ "cmd=%p %x ctx=%p result=0x%x status=0x%x,%x\n",
+ cmd, cmd->cmnd[0], ctx, cmd->result, btstat, sdstat);
+
+ cmd->scsi_done(cmd);
+}
+
+/*
+ * barrier usage : Since the PVSCSI device is emulated, there could be cases
+ * where we may want to serialize some accesses between the driver and the
+ * emulation layer. We use compiler barriers instead of the more expensive
+ * memory barriers because PVSCSI is only supported on X86 which has strong
+ * memory access ordering.
+ */
+static void pvscsi_process_completion_ring(struct pvscsi_adapter *adapter)
+{
+ struct PVSCSIRingsState *s = adapter->rings_state;
+ struct PVSCSIRingCmpDesc *ring = adapter->cmp_ring;
+ u32 cmp_entries = s->cmpNumEntriesLog2;
+
+ while (s->cmpConsIdx != s->cmpProdIdx) {
+ struct PVSCSIRingCmpDesc *e = ring + (s->cmpConsIdx &
+ MASK(cmp_entries));
+ /*
+ * This barrier() ensures that *e is not dereferenced while
+ * the device emulation still writes data into the slot.
+ * Since the device emulation advances s->cmpProdIdx only after
+ * updating the slot we want to check it first.
+ */
+ barrier();
+ pvscsi_complete_request(adapter, e);
+ /*
+ * This barrier() ensures that compiler doesn't reorder write
+ * to s->cmpConsIdx before the read of (*e) inside
+ * pvscsi_complete_request. Otherwise, device emulation may
+ * overwrite *e before we had a chance to read it.
+ */
+ barrier();
+ s->cmpConsIdx++;
+ }
+}
+
+/*
+ * Translate a Linux SCSI request into a request ring entry.
+ */
+static int pvscsi_queue_ring(struct pvscsi_adapter *adapter,
+ struct pvscsi_ctx *ctx, struct scsi_cmnd *cmd)
+{
+ struct PVSCSIRingsState *s;
+ struct PVSCSIRingReqDesc *e;
+ struct scsi_device *sdev;
+ u32 req_entries;
+
+ s = adapter->rings_state;
+ sdev = cmd->device;
+ req_entries = s->reqNumEntriesLog2;
+
+ /*
+ * If this condition holds, we might have room on the request ring, but
+ * we might not have room on the completion ring for the response.
+ * However, we have already ruled out this possibility - we would not
+ * have successfully allocated a context if it were true, since we only
+ * have one context per request entry. Check for it anyway, since it
+ * would be a serious bug.
+ */
+ if (s->reqProdIdx - s->cmpConsIdx >= 1 << req_entries) {
+ scmd_printk(KERN_ERR, cmd, "vmw_pvscsi: "
+ "ring full: reqProdIdx=%d cmpConsIdx=%d\n",
+ s->reqProdIdx, s->cmpConsIdx);
+ return -1;
+ }
+
+ e = adapter->req_ring + (s->reqProdIdx & MASK(req_entries));
+
+ e->bus = sdev->channel;
+ e->target = sdev->id;
+ memset(e->lun, 0, sizeof(e->lun));
+ e->lun[1] = sdev->lun;
+
+ if (cmd->sense_buffer) {
+ ctx->sensePA = pci_map_single(adapter->dev, cmd->sense_buffer,
+ SCSI_SENSE_BUFFERSIZE,
+ PCI_DMA_FROMDEVICE);
+ e->senseAddr = ctx->sensePA;
+ e->senseLen = SCSI_SENSE_BUFFERSIZE;
+ } else {
+ e->senseLen = 0;
+ e->senseAddr = 0;
+ }
+ e->cdbLen = cmd->cmd_len;
+ e->vcpuHint = smp_processor_id();
+ memcpy(e->cdb, cmd->cmnd, e->cdbLen);
+
+ e->tag = SIMPLE_QUEUE_TAG;
+ if (sdev->tagged_supported &&
+ (cmd->tag == HEAD_OF_QUEUE_TAG ||
+ cmd->tag == ORDERED_QUEUE_TAG))
+ e->tag = cmd->tag;
+
+ if (cmd->sc_data_direction == DMA_FROM_DEVICE)
+ e->flags = PVSCSI_FLAG_CMD_DIR_TOHOST;
+ else if (cmd->sc_data_direction == DMA_TO_DEVICE)
+ e->flags = PVSCSI_FLAG_CMD_DIR_TODEVICE;
+ else if (cmd->sc_data_direction == DMA_NONE)
+ e->flags = PVSCSI_FLAG_CMD_DIR_NONE;
+ else
+ e->flags = 0;
+
+ pvscsi_map_buffers(adapter, ctx, cmd, e);
+
+ e->context = pvscsi_map_context(adapter, ctx);
+
+ barrier();
+
+ s->reqProdIdx++;
+
+ return 0;
+}
+
+static int pvscsi_queue(struct scsi_cmnd *cmd, void (*done)(struct scsi_cmnd *))
+{
+ struct Scsi_Host *host = cmd->device->host;
+ struct pvscsi_adapter *adapter = shost_priv(host);
+ struct pvscsi_ctx *ctx;
+ unsigned long flags;
+
+ spin_lock_irqsave(&adapter->hw_lock, flags);
+
+ ctx = pvscsi_acquire_context(adapter, cmd);
+ if (!ctx || pvscsi_queue_ring(adapter, ctx, cmd) != 0) {
+ if (ctx)
+ pvscsi_release_context(adapter, ctx);
+ spin_unlock_irqrestore(&adapter->hw_lock, flags);
+ return SCSI_MLQUEUE_HOST_BUSY;
+ }
+
+ cmd->scsi_done = done;
+
+ dev_dbg(&cmd->device->sdev_gendev,
+ "queued cmd %p, ctx %p, op=%x\n", cmd, ctx, cmd->cmnd[0]);
+
+ spin_unlock_irqrestore(&adapter->hw_lock, flags);
+
+ pvscsi_kick_io(adapter, cmd->cmnd[0]);
+
+ return 0;
+}
+
+static int pvscsi_abort(struct scsi_cmnd *cmd)
+{
+ struct pvscsi_adapter *adapter = shost_priv(cmd->device->host);
+ struct pvscsi_ctx *ctx;
+ unsigned long flags;
+
+ scmd_printk(KERN_DEBUG, cmd, "task abort on host %u, %p\n",
+ adapter->host->host_no, cmd);
+
+ spin_lock_irqsave(&adapter->hw_lock, flags);
+
+ /*
+ * Poll the completion ring first - we might be trying to abort
+ * a command that is waiting to be dispatched in the completion ring.
+ */
+ pvscsi_process_completion_ring(adapter);
+
+ /*
+ * If there is no context for the command, it either already succeeded
+ * or else was never properly issued. Not our problem.
+ */
+ ctx = pvscsi_find_context(adapter, cmd);
+ if (!ctx) {
+ scmd_printk(KERN_DEBUG, cmd, "Failed to abort cmd %p\n", cmd);
+ goto out;
+ }
+
+ pvscsi_abort_cmd(adapter, ctx);
+
+ pvscsi_process_completion_ring(adapter);
+
+out:
+ spin_unlock_irqrestore(&adapter->hw_lock, flags);
+ return SUCCESS;
+}
+
+/*
+ * Abort all outstanding requests. This is only safe to use if the completion
+ * ring will never be walked again or the device has been reset, because it
+ * destroys the 1-1 mapping between context field passed to emulation and our
+ * request structure.
+ */
+static void pvscsi_reset_all(struct pvscsi_adapter *adapter)
+{
+ unsigned i;
+
+ for (i = 0; i < adapter->req_depth; i++) {
+ struct pvscsi_ctx *ctx = &adapter->cmd_map[i];
+ struct scsi_cmnd *cmd = ctx->cmd;
+ if (cmd) {
+ scmd_printk(KERN_ERR, cmd,
+ "Forced reset on cmd %p\n", cmd);
+ pvscsi_unmap_buffers(adapter, ctx);
+ pvscsi_release_context(adapter, ctx);
+ cmd->result = (DID_RESET << 16);
+ cmd->scsi_done(cmd);
+ }
+ }
+}
+
+static int pvscsi_host_reset(struct scsi_cmnd *cmd)
+{
+ struct Scsi_Host *host = cmd->device->host;
+ struct pvscsi_adapter *adapter = shost_priv(host);
+ unsigned long flags;
+ bool use_msg;
+
+ scmd_printk(KERN_INFO, cmd, "SCSI Host reset\n");
+
+ spin_lock_irqsave(&adapter->hw_lock, flags);
+
+ use_msg = adapter->use_msg;
+
+ if (use_msg) {
+ adapter->use_msg = 0;
+ spin_unlock_irqrestore(&adapter->hw_lock, flags);
+
+ /*
+ * Now that we know that the ISR won't add more work on the
+ * workqueue we can safely flush any outstanding work.
+ */
+ flush_workqueue(adapter->workqueue);
+ spin_lock_irqsave(&adapter->hw_lock, flags);
+ }
+
+ /*
+ * We're going to tear down the entire ring structure and set it back
+ * up, so stalling new requests until all completions are flushed and
+ * the rings are back in place.
+ */
+
+ pvscsi_process_request_ring(adapter);
+
+ ll_adapter_reset(adapter);
+
+ /*
+ * Now process any completions. Note we do this AFTER adapter reset,
+ * which is strange, but stops races where completions get posted
+ * between processing the ring and issuing the reset. The backend will
+ * not touch the ring memory after reset, so the immediately pre-reset
+ * completion ring state is still valid.
+ */
+ pvscsi_process_completion_ring(adapter);
+
+ pvscsi_reset_all(adapter);
+ adapter->use_msg = use_msg;
+ pvscsi_setup_all_rings(adapter);
+ pvscsi_unmask_intr(adapter);
+
+ spin_unlock_irqrestore(&adapter->hw_lock, flags);
+
+ return SUCCESS;
+}
+
+static int pvscsi_bus_reset(struct scsi_cmnd *cmd)
+{
+ struct Scsi_Host *host = cmd->device->host;
+ struct pvscsi_adapter *adapter = shost_priv(host);
+ unsigned long flags;
+
+ scmd_printk(KERN_INFO, cmd, "SCSI Bus reset\n");
+
+ /*
+ * We don't want to queue new requests for this bus after
+ * flushing all pending requests to emulation, since new
+ * requests could then sneak in during this bus reset phase,
+ * so take the lock now.
+ */
+ spin_lock_irqsave(&adapter->hw_lock, flags);
+
+ pvscsi_process_request_ring(adapter);
+ ll_bus_reset(adapter);
+ pvscsi_process_completion_ring(adapter);
+
+ spin_unlock_irqrestore(&adapter->hw_lock, flags);
+
+ return SUCCESS;
+}
+
+static int pvscsi_device_reset(struct scsi_cmnd *cmd)
+{
+ struct Scsi_Host *host = cmd->device->host;
+ struct pvscsi_adapter *adapter = shost_priv(host);
+ unsigned long flags;
+
+ scmd_printk(KERN_INFO, cmd, "SCSI device reset on scsi%u:%u\n",
+ host->host_no, cmd->device->id);
+
+ /*
+ * We don't want to queue new requests for this device after flushing
+ * all pending requests to emulation, since new requests could then
+ * sneak in during this device reset phase, so take the lock now.
+ */
+ spin_lock_irqsave(&adapter->hw_lock, flags);
+
+ pvscsi_process_request_ring(adapter);
+ ll_device_reset(adapter, cmd->device->id);
+ pvscsi_process_completion_ring(adapter);
+
+ spin_unlock_irqrestore(&adapter->hw_lock, flags);
+
+ return SUCCESS;
+}
+
+static struct scsi_host_template pvscsi_template;
+
+static const char *pvscsi_info(struct Scsi_Host *host)
+{
+ struct pvscsi_adapter *adapter = shost_priv(host);
+ static char buf[256];
+
+ sprintf(buf, "VMware PVSCSI storage adapter rev %d, req/cmp/msg rings: "
+ "%u/%u/%u pages, cmd_per_lun=%u", adapter->rev,
+ adapter->req_pages, adapter->cmp_pages, adapter->msg_pages,
+ pvscsi_template.cmd_per_lun);
+
+ return buf;
+}
+
+static struct scsi_host_template pvscsi_template = {
+ .module = THIS_MODULE,
+ .name = "VMware PVSCSI Host Adapter",
+ .proc_name = "vmw_pvscsi",
+ .info = pvscsi_info,
+ .queuecommand = pvscsi_queue,
+ .this_id = -1,
+ .sg_tablesize = PVSCSI_MAX_NUM_SG_ENTRIES_PER_SEGMENT,
+ .dma_boundary = UINT_MAX,
+ .max_sectors = 0xffff,
+ .use_clustering = ENABLE_CLUSTERING,
+ .eh_abort_handler = pvscsi_abort,
+ .eh_device_reset_handler = pvscsi_device_reset,
+ .eh_bus_reset_handler = pvscsi_bus_reset,
+ .eh_host_reset_handler = pvscsi_host_reset,
+};
+
+static void pvscsi_process_msg(const struct pvscsi_adapter *adapter,
+ const struct PVSCSIRingMsgDesc *e)
+{
+ struct PVSCSIRingsState *s = adapter->rings_state;
+ struct Scsi_Host *host = adapter->host;
+ struct scsi_device *sdev;
+
+ printk(KERN_INFO "vmw_pvscsi: msg type: 0x%x - MSG RING: %u/%u (%u) \n",
+ e->type, s->msgProdIdx, s->msgConsIdx, s->msgNumEntriesLog2);
+
+ BUILD_BUG_ON(PVSCSI_MSG_LAST != 2);
+
+ if (e->type == PVSCSI_MSG_DEV_ADDED) {
+ struct PVSCSIMsgDescDevStatusChanged *desc;
+ desc = (struct PVSCSIMsgDescDevStatusChanged *)e;
+
+ printk(KERN_INFO
+ "vmw_pvscsi: msg: device added at scsi%u:%u:%u\n",
+ desc->bus, desc->target, desc->lun[1]);
+
+ if (!scsi_host_get(host))
+ return;
+
+ sdev = scsi_device_lookup(host, desc->bus, desc->target,
+ desc->lun[1]);
+ if (sdev) {
+ printk(KERN_INFO "vmw_pvscsi: device already exists\n");
+ scsi_device_put(sdev);
+ } else
+ scsi_add_device(adapter->host, desc->bus,
+ desc->target, desc->lun[1]);
+
+ scsi_host_put(host);
+ } else if (e->type == PVSCSI_MSG_DEV_REMOVED) {
+ struct PVSCSIMsgDescDevStatusChanged *desc;
+ desc = (struct PVSCSIMsgDescDevStatusChanged *)e;
+
+ printk(KERN_INFO
+ "vmw_pvscsi: msg: device removed at scsi%u:%u:%u\n",
+ desc->bus, desc->target, desc->lun[1]);
+
+ if (!scsi_host_get(host))
+ return;
+
+ sdev = scsi_device_lookup(host, desc->bus, desc->target,
+ desc->lun[1]);
+ if (sdev) {
+ scsi_remove_device(sdev);
+ scsi_device_put(sdev);
+ } else
+ printk(KERN_INFO
+ "vmw_pvscsi: failed to lookup scsi%u:%u:%u\n",
+ desc->bus, desc->target, desc->lun[1]);
+
+ scsi_host_put(host);
+ }
+}
+
+static int pvscsi_msg_pending(const struct pvscsi_adapter *adapter)
+{
+ struct PVSCSIRingsState *s = adapter->rings_state;
+
+ return s->msgProdIdx != s->msgConsIdx;
+}
+
+static void pvscsi_process_msg_ring(const struct pvscsi_adapter *adapter)
+{
+ struct PVSCSIRingsState *s = adapter->rings_state;
+ struct PVSCSIRingMsgDesc *ring = adapter->msg_ring;
+ u32 msg_entries = s->msgNumEntriesLog2;
+
+ while (pvscsi_msg_pending(adapter)) {
+ struct PVSCSIRingMsgDesc *e = ring + (s->msgConsIdx &
+ MASK(msg_entries));
+
+ barrier();
+ pvscsi_process_msg(adapter, e);
+ barrier();
+ s->msgConsIdx++;
+ }
+}
+
+static void pvscsi_msg_workqueue_handler(struct work_struct *data)
+{
+ struct pvscsi_adapter *adapter;
+
+ adapter = container_of(data, struct pvscsi_adapter, work);
+
+ pvscsi_process_msg_ring(adapter);
+}
+
+static int pvscsi_setup_msg_workqueue(struct pvscsi_adapter *adapter)
+{
+ char name[32];
+
+ if (!pvscsi_use_msg)
+ return 0;
+
+ pvscsi_reg_write(adapter, PVSCSI_REG_OFFSET_COMMAND,
+ PVSCSI_CMD_SETUP_MSG_RING);
+
+ if (pvscsi_reg_read(adapter, PVSCSI_REG_OFFSET_COMMAND_STATUS) == -1)
+ return 0;
+
+ snprintf(name, sizeof(name),
+ "vmw_pvscsi_wq_%u", adapter->host->host_no);
+
+ adapter->workqueue = create_singlethread_workqueue(name);
+ if (!adapter->workqueue) {
+ printk(KERN_ERR "vmw_pvscsi: failed to create work queue\n");
+ return 0;
+ }
+ INIT_WORK(&adapter->work, pvscsi_msg_workqueue_handler);
+
+ return 1;
+}
+
+static irqreturn_t pvscsi_isr(int irq, void *devp)
+{
+ struct pvscsi_adapter *adapter = devp;
+ int handled;
+
+ if (adapter->use_msi || adapter->use_msix)
+ handled = true;
+ else {
+ u32 val = pvscsi_read_intr_status(adapter);
+ handled = (val & PVSCSI_INTR_ALL_SUPPORTED) != 0;
+ if (handled)
+ pvscsi_write_intr_status(devp, val);
+ }
+
+ if (handled) {
+ unsigned long flags;
+
+ spin_lock_irqsave(&adapter->hw_lock, flags);
+
+ pvscsi_process_completion_ring(adapter);
+ if (adapter->use_msg && pvscsi_msg_pending(adapter))
+ queue_work(adapter->workqueue, &adapter->work);
+
+ spin_unlock_irqrestore(&adapter->hw_lock, flags);
+ }
+
+ return IRQ_RETVAL(handled);
+}
+
+static void pvscsi_free_sgls(const struct pvscsi_adapter *adapter)
+{
+ struct pvscsi_ctx *ctx = adapter->cmd_map;
+ unsigned i;
+
+ for (i = 0; i < adapter->req_depth; ++i, ++ctx)
+ free_pages((unsigned long)ctx->sgl, get_order(SGL_SIZE));
+}
+
+static int pvscsi_setup_msix(const struct pvscsi_adapter *adapter, int *irq)
+{
+ struct msix_entry entry = { 0, PVSCSI_VECTOR_COMPLETION };
+ int ret;
+
+ ret = pci_enable_msix(adapter->dev, &entry, 1);
+ if (ret)
+ return ret;
+
+ *irq = entry.vector;
+
+ return 0;
+}
+
+static void pvscsi_shutdown_intr(struct pvscsi_adapter *adapter)
+{
+ if (adapter->irq) {
+ free_irq(adapter->irq, adapter);
+ adapter->irq = 0;
+ }
+ if (adapter->use_msi) {
+ pci_disable_msi(adapter->dev);
+ adapter->use_msi = 0;
+ } else if (adapter->use_msix) {
+ pci_disable_msix(adapter->dev);
+ adapter->use_msix = 0;
+ }
+}
+
+static void pvscsi_release_resources(struct pvscsi_adapter *adapter)
+{
+ pvscsi_shutdown_intr(adapter);
+
+ if (adapter->workqueue)
+ destroy_workqueue(adapter->workqueue);
+
+ if (adapter->mmioBase)
+ pci_iounmap(adapter->dev, adapter->mmioBase);
+
+ pci_release_regions(adapter->dev);
+
+ if (adapter->cmd_map) {
+ pvscsi_free_sgls(adapter);
+ kfree(adapter->cmd_map);
+ }
+
+ if (adapter->rings_state)
+ pci_free_consistent(adapter->dev, PAGE_SIZE,
+ adapter->rings_state, adapter->ringStatePA);
+
+ if (adapter->req_ring)
+ pci_free_consistent(adapter->dev,
+ adapter->req_pages * PAGE_SIZE,
+ adapter->req_ring, adapter->reqRingPA);
+
+ if (adapter->cmp_ring)
+ pci_free_consistent(adapter->dev,
+ adapter->cmp_pages * PAGE_SIZE,
+ adapter->cmp_ring, adapter->cmpRingPA);
+
+ if (adapter->msg_ring)
+ pci_free_consistent(adapter->dev,
+ adapter->msg_pages * PAGE_SIZE,
+ adapter->msg_ring, adapter->msgRingPA);
+}
+
+/*
+ * Allocate scatter gather lists.
+ *
+ * These are statically allocated. Trying to be clever was not worth it.
+ *
+ * Dynamic allocation can fail, and we can't go deeep into the memory
+ * allocator, since we're a SCSI driver, and trying too hard to allocate
+ * memory might generate disk I/O. We also don't want to fail disk I/O
+ * in that case because we can't get an allocation - the I/O could be
+ * trying to swap out data to free memory. Since that is pathological,
+ * just use a statically allocated scatter list.
+ *
+ */
+static int __devinit pvscsi_allocate_sg(struct pvscsi_adapter *adapter)
+{
+ struct pvscsi_ctx *ctx;
+ int i;
+
+ ctx = adapter->cmd_map;
+ BUILD_BUG_ON(sizeof(struct pvscsi_sg_list) > SGL_SIZE);
+
+ for (i = 0; i < adapter->req_depth; ++i, ++ctx) {
+ ctx->sgl = (void *)__get_free_pages(GFP_KERNEL,
+ get_order(SGL_SIZE));
+ ctx->sglPA = 0;
+ BUG_ON(!IS_ALIGNED(((unsigned long)ctx->sgl), PAGE_SIZE));
+ if (!ctx->sgl) {
+ for (; i >= 0; --i, --ctx) {
+ free_pages((unsigned long)ctx->sgl,
+ get_order(SGL_SIZE));
+ ctx->sgl = NULL;
+ }
+ return -ENOMEM;
+ }
+ }
+
+ return 0;
+}
+
+static int __devinit pvscsi_probe(struct pci_dev *pdev,
+ const struct pci_device_id *id)
+{
+ struct pvscsi_adapter *adapter;
+ struct Scsi_Host *host;
+ unsigned int i;
+ unsigned long flags = 0;
+ int error;
+
+ error = -ENODEV;
+
+ if (pci_enable_device(pdev))
+ return error;
+
+ if (pci_set_dma_mask(pdev, DMA_BIT_MASK(64)) == 0 &&
+ pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(64)) == 0) {
+ printk(KERN_INFO "vmw_pvscsi: using 64bit dma\n");
+ } else if (pci_set_dma_mask(pdev, DMA_BIT_MASK(32)) == 0 &&
+ pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(32)) == 0) {
+ printk(KERN_INFO "vmw_pvscsi: using 32bit dma\n");
+ } else {
+ printk(KERN_ERR "vmw_pvscsi: failed to set DMA mask\n");
+ goto out_disable_device;
+ }
+
+ pvscsi_template.can_queue =
+ min(PVSCSI_MAX_NUM_PAGES_REQ_RING, pvscsi_ring_pages) *
+ PVSCSI_MAX_NUM_REQ_ENTRIES_PER_PAGE;
+ pvscsi_template.cmd_per_lun =
+ min(pvscsi_template.can_queue, pvscsi_cmd_per_lun);
+ host = scsi_host_alloc(&pvscsi_template, sizeof(struct pvscsi_adapter));
+ if (!host) {
+ printk(KERN_ERR "vmw_pvscsi: failed to allocate host\n");
+ goto out_disable_device;
+ }
+
+ adapter = shost_priv(host);
+ memset(adapter, 0, sizeof(*adapter));
+ adapter->dev = pdev;
+ adapter->host = host;
+
+ spin_lock_init(&adapter->hw_lock);
+
+ host->max_channel = 0;
+ host->max_id = 16;
+ host->max_lun = 1;
+ host->max_cmd_len = 16;
+
+ adapter->rev = pdev->revision;
+
+ if (pci_request_regions(pdev, "vmw_pvscsi")) {
+ printk(KERN_ERR "vmw_pvscsi: pci memory selection failed\n");
+ goto out_free_host;
+ }
+
+ for (i = 0; i < DEVICE_COUNT_RESOURCE; i++) {
+ if ((pci_resource_flags(pdev, i) & PCI_BASE_ADDRESS_SPACE_IO))
+ continue;
+
+ if (pci_resource_len(pdev, i) < PVSCSI_MEM_SPACE_SIZE)
+ continue;
+
+ break;
+ }
+
+ if (i == DEVICE_COUNT_RESOURCE) {
+ printk(KERN_ERR
+ "vmw_pvscsi: adapter has no suitable MMIO region\n");
+ goto out_release_resources;
+ }
+
+ adapter->mmioBase = pci_iomap(pdev, i, PVSCSI_MEM_SPACE_SIZE);
+
+ if (!adapter->mmioBase) {
+ printk(KERN_ERR
+ "vmw_pvscsi: can't iomap for BAR %d memsize %lu\n",
+ i, PVSCSI_MEM_SPACE_SIZE);
+ goto out_release_resources;
+ }
+
+ pci_set_master(pdev);
+ pci_set_drvdata(pdev, host);
+
+ ll_adapter_reset(adapter);
+
+ adapter->use_msg = pvscsi_setup_msg_workqueue(adapter);
+
+ error = pvscsi_allocate_rings(adapter);
+ if (error) {
+ printk(KERN_ERR "vmw_pvscsi: unable to allocate ring memory\n");
+ goto out_release_resources;
+ }
+
+ /*
+ * From this point on we should reset the adapter if anything goes
+ * wrong.
+ */
+ pvscsi_setup_all_rings(adapter);
+
+ adapter->cmd_map = kcalloc(adapter->req_depth,
+ sizeof(struct pvscsi_ctx), GFP_KERNEL);
+ if (!adapter->cmd_map) {
+ printk(KERN_ERR "vmw_pvscsi: failed to allocate memory.\n");
+ error = -ENOMEM;
+ goto out_reset_adapter;
+ }
+
+ INIT_LIST_HEAD(&adapter->cmd_pool);
+ for (i = 0; i < adapter->req_depth; i++) {
+ struct pvscsi_ctx *ctx = adapter->cmd_map + i;
+ list_add(&ctx->list, &adapter->cmd_pool);
+ }
+
+ error = pvscsi_allocate_sg(adapter);
+ if (error) {
+ printk(KERN_ERR "vmw_pvscsi: unable to allocate s/g table\n");
+ goto out_reset_adapter;
+ }
+
+ if (!pvscsi_disable_msix &&
+ pvscsi_setup_msix(adapter, &adapter->irq) == 0) {
+ printk(KERN_INFO "vmw_pvscsi: using MSI-X\n");
+ adapter->use_msix = 1;
+ } else if (!pvscsi_disable_msi && pci_enable_msi(pdev) == 0) {
+ printk(KERN_INFO "vmw_pvscsi: using MSI\n");
+ adapter->use_msi = 1;
+ adapter->irq = pdev->irq;
+ } else {
+ printk(KERN_INFO "vmw_pvscsi: using INTx\n");
+ adapter->irq = pdev->irq;
+ flags = IRQF_SHARED;
+ }
+
+ error = request_irq(adapter->irq, pvscsi_isr, flags,
+ "vmw_pvscsi", adapter);
+ if (error) {
+ printk(KERN_ERR
+ "vmw_pvscsi: unable to request IRQ: %d\n", error);
+ adapter->irq = 0;
+ goto out_reset_adapter;
+ }
+
+ error = scsi_add_host(host, &pdev->dev);
+ if (error) {
+ printk(KERN_ERR
+ "vmw_pvscsi: scsi_add_host failed: %d\n", error);
+ goto out_reset_adapter;
+ }
+
+ dev_info(&pdev->dev, "VMware PVSCSI rev %d host #%u\n",
+ adapter->rev, host->host_no);
+
+ pvscsi_unmask_intr(adapter);
+
+ scsi_scan_host(host);
+
+ return 0;
+
+out_reset_adapter:
+ ll_adapter_reset(adapter);
+out_release_resources:
+ pvscsi_release_resources(adapter);
+out_free_host:
+ scsi_host_put(host);
+out_disable_device:
+ pci_set_drvdata(pdev, NULL);
+ pci_disable_device(pdev);
+
+ return error;
+}
+
+static void __pvscsi_shutdown(struct pvscsi_adapter *adapter)
+{
+ pvscsi_mask_intr(adapter);
+
+ if (adapter->workqueue)
+ flush_workqueue(adapter->workqueue);
+
+ pvscsi_shutdown_intr(adapter);
+
+ pvscsi_process_request_ring(adapter);
+ pvscsi_process_completion_ring(adapter);
+ ll_adapter_reset(adapter);
+}
+
+static void pvscsi_shutdown(struct pci_dev *dev)
+{
+ struct Scsi_Host *host = pci_get_drvdata(dev);
+ struct pvscsi_adapter *adapter = shost_priv(host);
+
+ __pvscsi_shutdown(adapter);
+}
+
+static void pvscsi_remove(struct pci_dev *pdev)
+{
+ struct Scsi_Host *host = pci_get_drvdata(pdev);
+ struct pvscsi_adapter *adapter = shost_priv(host);
+
+ scsi_remove_host(host);
+
+ __pvscsi_shutdown(adapter);
+ pvscsi_release_resources(adapter);
+
+ scsi_host_put(host);
+
+ pci_set_drvdata(pdev, NULL);
+ pci_disable_device(pdev);
+}
+
+static struct pci_driver pvscsi_pci_driver = {
+ .name = "vmw_pvscsi",
+ .id_table = pvscsi_pci_tbl,
+ .probe = pvscsi_probe,
+ .remove = __devexit_p(pvscsi_remove),
+ .shutdown = pvscsi_shutdown,
+};
+
+static int __init pvscsi_init(void)
+{
+ pr_info("%s - version %s\n",
+ PVSCSI_LINUX_DRIVER_DESC, PVSCSI_DRIVER_VERSION_STRING);
+ return pci_register_driver(&pvscsi_pci_driver);
+}
+
+static void __exit pvscsi_exit(void)
+{
+ pci_unregister_driver(&pvscsi_pci_driver);
+}
+
+module_init(pvscsi_init);
+module_exit(pvscsi_exit);
diff --git a/drivers/scsi/vmw_pvscsi.h b/drivers/scsi/vmw_pvscsi.h
new file mode 100644
index 00000000000..62e36e75715
--- /dev/null
+++ b/drivers/scsi/vmw_pvscsi.h
@@ -0,0 +1,397 @@
+/*
+ * VMware PVSCSI header file
+ *
+ * Copyright (C) 2008-2009, VMware, Inc. All Rights Reserved.
+ *
+ * This program is free software; you can 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 and no later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or
+ * NON INFRINGEMENT. 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 by: Alok N Kataria <akataria@vmware.com>
+ *
+ */
+
+#ifndef _VMW_PVSCSI_H_
+#define _VMW_PVSCSI_H_
+
+#include <linux/types.h>
+
+#define PVSCSI_DRIVER_VERSION_STRING "1.0.1.0-k"
+
+#define PVSCSI_MAX_NUM_SG_ENTRIES_PER_SEGMENT 128
+
+#define MASK(n) ((1 << (n)) - 1) /* make an n-bit mask */
+
+#define PCI_VENDOR_ID_VMWARE 0x15AD
+#define PCI_DEVICE_ID_VMWARE_PVSCSI 0x07C0
+
+/*
+ * host adapter status/error codes
+ */
+enum HostBusAdapterStatus {
+ BTSTAT_SUCCESS = 0x00, /* CCB complete normally with no errors */
+ BTSTAT_LINKED_COMMAND_COMPLETED = 0x0a,
+ BTSTAT_LINKED_COMMAND_COMPLETED_WITH_FLAG = 0x0b,
+ BTSTAT_DATA_UNDERRUN = 0x0c,
+ BTSTAT_SELTIMEO = 0x11, /* SCSI selection timeout */
+ BTSTAT_DATARUN = 0x12, /* data overrun/underrun */
+ BTSTAT_BUSFREE = 0x13, /* unexpected bus free */
+ BTSTAT_INVPHASE = 0x14, /* invalid bus phase or sequence requested by target */
+ BTSTAT_LUNMISMATCH = 0x17, /* linked CCB has different LUN from first CCB */
+ BTSTAT_SENSFAILED = 0x1b, /* auto request sense failed */
+ BTSTAT_TAGREJECT = 0x1c, /* SCSI II tagged queueing message rejected by target */
+ BTSTAT_BADMSG = 0x1d, /* unsupported message received by the host adapter */
+ BTSTAT_HAHARDWARE = 0x20, /* host adapter hardware failed */
+ BTSTAT_NORESPONSE = 0x21, /* target did not respond to SCSI ATN, sent a SCSI RST */
+ BTSTAT_SENTRST = 0x22, /* host adapter asserted a SCSI RST */
+ BTSTAT_RECVRST = 0x23, /* other SCSI devices asserted a SCSI RST */
+ BTSTAT_DISCONNECT = 0x24, /* target device reconnected improperly (w/o tag) */
+ BTSTAT_BUSRESET = 0x25, /* host adapter issued BUS device reset */
+ BTSTAT_ABORTQUEUE = 0x26, /* abort queue generated */
+ BTSTAT_HASOFTWARE = 0x27, /* host adapter software error */
+ BTSTAT_HATIMEOUT = 0x30, /* host adapter hardware timeout error */
+ BTSTAT_SCSIPARITY = 0x34, /* SCSI parity error detected */
+};
+
+/*
+ * Register offsets.
+ *
+ * These registers are accessible both via i/o space and mm i/o.
+ */
+
+enum PVSCSIRegOffset {
+ PVSCSI_REG_OFFSET_COMMAND = 0x0,
+ PVSCSI_REG_OFFSET_COMMAND_DATA = 0x4,
+ PVSCSI_REG_OFFSET_COMMAND_STATUS = 0x8,
+ PVSCSI_REG_OFFSET_LAST_STS_0 = 0x100,
+ PVSCSI_REG_OFFSET_LAST_STS_1 = 0x104,
+ PVSCSI_REG_OFFSET_LAST_STS_2 = 0x108,
+ PVSCSI_REG_OFFSET_LAST_STS_3 = 0x10c,
+ PVSCSI_REG_OFFSET_INTR_STATUS = 0x100c,
+ PVSCSI_REG_OFFSET_INTR_MASK = 0x2010,
+ PVSCSI_REG_OFFSET_KICK_NON_RW_IO = 0x3014,
+ PVSCSI_REG_OFFSET_DEBUG = 0x3018,
+ PVSCSI_REG_OFFSET_KICK_RW_IO = 0x4018,
+};
+
+/*
+ * Virtual h/w commands.
+ */
+
+enum PVSCSICommands {
+ PVSCSI_CMD_FIRST = 0, /* has to be first */
+
+ PVSCSI_CMD_ADAPTER_RESET = 1,
+ PVSCSI_CMD_ISSUE_SCSI = 2,
+ PVSCSI_CMD_SETUP_RINGS = 3,
+ PVSCSI_CMD_RESET_BUS = 4,
+ PVSCSI_CMD_RESET_DEVICE = 5,
+ PVSCSI_CMD_ABORT_CMD = 6,
+ PVSCSI_CMD_CONFIG = 7,
+ PVSCSI_CMD_SETUP_MSG_RING = 8,
+ PVSCSI_CMD_DEVICE_UNPLUG = 9,
+
+ PVSCSI_CMD_LAST = 10 /* has to be last */
+};
+
+/*
+ * Command descriptor for PVSCSI_CMD_RESET_DEVICE --
+ */
+
+struct PVSCSICmdDescResetDevice {
+ u32 target;
+ u8 lun[8];
+} __packed;
+
+/*
+ * Command descriptor for PVSCSI_CMD_ABORT_CMD --
+ *
+ * - currently does not support specifying the LUN.
+ * - _pad should be 0.
+ */
+
+struct PVSCSICmdDescAbortCmd {
+ u64 context;
+ u32 target;
+ u32 _pad;
+} __packed;
+
+/*
+ * Command descriptor for PVSCSI_CMD_SETUP_RINGS --
+ *
+ * Notes:
+ * - reqRingNumPages and cmpRingNumPages need to be power of two.
+ * - reqRingNumPages and cmpRingNumPages need to be different from 0,
+ * - reqRingNumPages and cmpRingNumPages need to be inferior to
+ * PVSCSI_SETUP_RINGS_MAX_NUM_PAGES.
+ */
+
+#define PVSCSI_SETUP_RINGS_MAX_NUM_PAGES 32
+struct PVSCSICmdDescSetupRings {
+ u32 reqRingNumPages;
+ u32 cmpRingNumPages;
+ u64 ringsStatePPN;
+ u64 reqRingPPNs[PVSCSI_SETUP_RINGS_MAX_NUM_PAGES];
+ u64 cmpRingPPNs[PVSCSI_SETUP_RINGS_MAX_NUM_PAGES];
+} __packed;
+
+/*
+ * Command descriptor for PVSCSI_CMD_SETUP_MSG_RING --
+ *
+ * Notes:
+ * - this command was not supported in the initial revision of the h/w
+ * interface. Before using it, you need to check that it is supported by
+ * writing PVSCSI_CMD_SETUP_MSG_RING to the 'command' register, then
+ * immediately after read the 'command status' register:
+ * * a value of -1 means that the cmd is NOT supported,
+ * * a value != -1 means that the cmd IS supported.
+ * If it's supported the 'command status' register should return:
+ * sizeof(PVSCSICmdDescSetupMsgRing) / sizeof(u32).
+ * - this command should be issued _after_ the usual SETUP_RINGS so that the
+ * RingsState page is already setup. If not, the command is a nop.
+ * - numPages needs to be a power of two,
+ * - numPages needs to be different from 0,
+ * - _pad should be zero.
+ */
+
+#define PVSCSI_SETUP_MSG_RING_MAX_NUM_PAGES 16
+
+struct PVSCSICmdDescSetupMsgRing {
+ u32 numPages;
+ u32 _pad;
+ u64 ringPPNs[PVSCSI_SETUP_MSG_RING_MAX_NUM_PAGES];
+} __packed;
+
+enum PVSCSIMsgType {
+ PVSCSI_MSG_DEV_ADDED = 0,
+ PVSCSI_MSG_DEV_REMOVED = 1,
+ PVSCSI_MSG_LAST = 2,
+};
+
+/*
+ * Msg descriptor.
+ *
+ * sizeof(struct PVSCSIRingMsgDesc) == 128.
+ *
+ * - type is of type enum PVSCSIMsgType.
+ * - the content of args depend on the type of event being delivered.
+ */
+
+struct PVSCSIRingMsgDesc {
+ u32 type;
+ u32 args[31];
+} __packed;
+
+struct PVSCSIMsgDescDevStatusChanged {
+ u32 type; /* PVSCSI_MSG_DEV _ADDED / _REMOVED */
+ u32 bus;
+ u32 target;
+ u8 lun[8];
+ u32 pad[27];
+} __packed;
+
+/*
+ * Rings state.
+ *
+ * - the fields:
+ * . msgProdIdx,
+ * . msgConsIdx,
+ * . msgNumEntriesLog2,
+ * .. are only used once the SETUP_MSG_RING cmd has been issued.
+ * - '_pad' helps to ensure that the msg related fields are on their own
+ * cache-line.
+ */
+
+struct PVSCSIRingsState {
+ u32 reqProdIdx;
+ u32 reqConsIdx;
+ u32 reqNumEntriesLog2;
+
+ u32 cmpProdIdx;
+ u32 cmpConsIdx;
+ u32 cmpNumEntriesLog2;
+
+ u8 _pad[104];
+
+ u32 msgProdIdx;
+ u32 msgConsIdx;
+ u32 msgNumEntriesLog2;
+} __packed;
+
+/*
+ * Request descriptor.
+ *
+ * sizeof(RingReqDesc) = 128
+ *
+ * - context: is a unique identifier of a command. It could normally be any
+ * 64bit value, however we currently store it in the serialNumber variable
+ * of struct SCSI_Command, so we have the following restrictions due to the
+ * way this field is handled in the vmkernel storage stack:
+ * * this value can't be 0,
+ * * the upper 32bit need to be 0 since serialNumber is as a u32.
+ * Currently tracked as PR 292060.
+ * - dataLen: contains the total number of bytes that need to be transferred.
+ * - dataAddr:
+ * * if PVSCSI_FLAG_CMD_WITH_SG_LIST is set: dataAddr is the PA of the first
+ * s/g table segment, each s/g segment is entirely contained on a single
+ * page of physical memory,
+ * * if PVSCSI_FLAG_CMD_WITH_SG_LIST is NOT set, then dataAddr is the PA of
+ * the buffer used for the DMA transfer,
+ * - flags:
+ * * PVSCSI_FLAG_CMD_WITH_SG_LIST: see dataAddr above,
+ * * PVSCSI_FLAG_CMD_DIR_NONE: no DMA involved,
+ * * PVSCSI_FLAG_CMD_DIR_TOHOST: transfer from device to main memory,
+ * * PVSCSI_FLAG_CMD_DIR_TODEVICE: transfer from main memory to device,
+ * * PVSCSI_FLAG_CMD_OUT_OF_BAND_CDB: reserved to handle CDBs larger than
+ * 16bytes. To be specified.
+ * - vcpuHint: vcpuId of the processor that will be most likely waiting for the
+ * completion of the i/o. For guest OSes that use lowest priority message
+ * delivery mode (such as windows), we use this "hint" to deliver the
+ * completion action to the proper vcpu. For now, we can use the vcpuId of
+ * the processor that initiated the i/o as a likely candidate for the vcpu
+ * that will be waiting for the completion..
+ * - bus should be 0: we currently only support bus 0 for now.
+ * - unused should be zero'd.
+ */
+
+#define PVSCSI_FLAG_CMD_WITH_SG_LIST (1 << 0)
+#define PVSCSI_FLAG_CMD_OUT_OF_BAND_CDB (1 << 1)
+#define PVSCSI_FLAG_CMD_DIR_NONE (1 << 2)
+#define PVSCSI_FLAG_CMD_DIR_TOHOST (1 << 3)
+#define PVSCSI_FLAG_CMD_DIR_TODEVICE (1 << 4)
+
+struct PVSCSIRingReqDesc {
+ u64 context;
+ u64 dataAddr;
+ u64 dataLen;
+ u64 senseAddr;
+ u32 senseLen;
+ u32 flags;
+ u8 cdb[16];
+ u8 cdbLen;
+ u8 lun[8];
+ u8 tag;
+ u8 bus;
+ u8 target;
+ u8 vcpuHint;
+ u8 unused[59];
+} __packed;
+
+/*
+ * Scatter-gather list management.
+ *
+ * As described above, when PVSCSI_FLAG_CMD_WITH_SG_LIST is set in the
+ * RingReqDesc.flags, then RingReqDesc.dataAddr is the PA of the first s/g
+ * table segment.
+ *
+ * - each segment of the s/g table contain a succession of struct
+ * PVSCSISGElement.
+ * - each segment is entirely contained on a single physical page of memory.
+ * - a "chain" s/g element has the flag PVSCSI_SGE_FLAG_CHAIN_ELEMENT set in
+ * PVSCSISGElement.flags and in this case:
+ * * addr is the PA of the next s/g segment,
+ * * length is undefined, assumed to be 0.
+ */
+
+struct PVSCSISGElement {
+ u64 addr;
+ u32 length;
+ u32 flags;
+} __packed;
+
+/*
+ * Completion descriptor.
+ *
+ * sizeof(RingCmpDesc) = 32
+ *
+ * - context: identifier of the command. The same thing that was specified
+ * under "context" as part of struct RingReqDesc at initiation time,
+ * - dataLen: number of bytes transferred for the actual i/o operation,
+ * - senseLen: number of bytes written into the sense buffer,
+ * - hostStatus: adapter status,
+ * - scsiStatus: device status,
+ * - _pad should be zero.
+ */
+
+struct PVSCSIRingCmpDesc {
+ u64 context;
+ u64 dataLen;
+ u32 senseLen;
+ u16 hostStatus;
+ u16 scsiStatus;
+ u32 _pad[2];
+} __packed;
+
+/*
+ * Interrupt status / IRQ bits.
+ */
+
+#define PVSCSI_INTR_CMPL_0 (1 << 0)
+#define PVSCSI_INTR_CMPL_1 (1 << 1)
+#define PVSCSI_INTR_CMPL_MASK MASK(2)
+
+#define PVSCSI_INTR_MSG_0 (1 << 2)
+#define PVSCSI_INTR_MSG_1 (1 << 3)
+#define PVSCSI_INTR_MSG_MASK (MASK(2) << 2)
+
+#define PVSCSI_INTR_ALL_SUPPORTED MASK(4)
+
+/*
+ * Number of MSI-X vectors supported.
+ */
+#define PVSCSI_MAX_INTRS 24
+
+/*
+ * Enumeration of supported MSI-X vectors
+ */
+#define PVSCSI_VECTOR_COMPLETION 0
+
+/*
+ * Misc constants for the rings.
+ */
+
+#define PVSCSI_MAX_NUM_PAGES_REQ_RING PVSCSI_SETUP_RINGS_MAX_NUM_PAGES
+#define PVSCSI_MAX_NUM_PAGES_CMP_RING PVSCSI_SETUP_RINGS_MAX_NUM_PAGES
+#define PVSCSI_MAX_NUM_PAGES_MSG_RING PVSCSI_SETUP_MSG_RING_MAX_NUM_PAGES
+
+#define PVSCSI_MAX_NUM_REQ_ENTRIES_PER_PAGE \
+ (PAGE_SIZE / sizeof(struct PVSCSIRingReqDesc))
+
+#define PVSCSI_MAX_REQ_QUEUE_DEPTH \
+ (PVSCSI_MAX_NUM_PAGES_REQ_RING * PVSCSI_MAX_NUM_REQ_ENTRIES_PER_PAGE)
+
+#define PVSCSI_MEM_SPACE_COMMAND_NUM_PAGES 1
+#define PVSCSI_MEM_SPACE_INTR_STATUS_NUM_PAGES 1
+#define PVSCSI_MEM_SPACE_MISC_NUM_PAGES 2
+#define PVSCSI_MEM_SPACE_KICK_IO_NUM_PAGES 2
+#define PVSCSI_MEM_SPACE_MSIX_NUM_PAGES 2
+
+enum PVSCSIMemSpace {
+ PVSCSI_MEM_SPACE_COMMAND_PAGE = 0,
+ PVSCSI_MEM_SPACE_INTR_STATUS_PAGE = 1,
+ PVSCSI_MEM_SPACE_MISC_PAGE = 2,
+ PVSCSI_MEM_SPACE_KICK_IO_PAGE = 4,
+ PVSCSI_MEM_SPACE_MSIX_TABLE_PAGE = 6,
+ PVSCSI_MEM_SPACE_MSIX_PBA_PAGE = 7,
+};
+
+#define PVSCSI_MEM_SPACE_NUM_PAGES \
+ (PVSCSI_MEM_SPACE_COMMAND_NUM_PAGES + \
+ PVSCSI_MEM_SPACE_INTR_STATUS_NUM_PAGES + \
+ PVSCSI_MEM_SPACE_MISC_NUM_PAGES + \
+ PVSCSI_MEM_SPACE_KICK_IO_NUM_PAGES + \
+ PVSCSI_MEM_SPACE_MSIX_NUM_PAGES)
+
+#define PVSCSI_MEM_SPACE_SIZE (PVSCSI_MEM_SPACE_NUM_PAGES * PAGE_SIZE)
+
+#endif /* _VMW_PVSCSI_H_ */