summaryrefslogtreecommitdiff
path: root/drivers/scsi/lpfc
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/scsi/lpfc')
-rw-r--r--drivers/scsi/lpfc/lpfc.h22
-rw-r--r--drivers/scsi/lpfc/lpfc_attr.c169
-rw-r--r--drivers/scsi/lpfc/lpfc_crtn.h15
-rw-r--r--drivers/scsi/lpfc/lpfc_ct.c17
-rw-r--r--drivers/scsi/lpfc/lpfc_debugfs.c164
-rw-r--r--drivers/scsi/lpfc/lpfc_debugfs.h2
-rw-r--r--drivers/scsi/lpfc/lpfc_els.c160
-rw-r--r--drivers/scsi/lpfc/lpfc_hbadisc.c69
-rw-r--r--drivers/scsi/lpfc/lpfc_hw.h249
-rw-r--r--drivers/scsi/lpfc/lpfc_init.c685
-rw-r--r--drivers/scsi/lpfc/lpfc_logmsg.h1
-rw-r--r--drivers/scsi/lpfc/lpfc_mbox.c54
-rw-r--r--drivers/scsi/lpfc/lpfc_nl.h30
-rw-r--r--drivers/scsi/lpfc/lpfc_nportdisc.c2
-rw-r--r--drivers/scsi/lpfc/lpfc_scsi.c1235
-rw-r--r--drivers/scsi/lpfc/lpfc_scsi.h2
-rw-r--r--drivers/scsi/lpfc/lpfc_sli.c159
-rw-r--r--drivers/scsi/lpfc/lpfc_version.h2
-rw-r--r--drivers/scsi/lpfc/lpfc_vport.c28
19 files changed, 2623 insertions, 442 deletions
diff --git a/drivers/scsi/lpfc/lpfc.h b/drivers/scsi/lpfc/lpfc.h
index 60a9e6e9384b..dcba267db711 100644
--- a/drivers/scsi/lpfc/lpfc.h
+++ b/drivers/scsi/lpfc/lpfc.h
@@ -29,8 +29,10 @@ struct lpfc_sli2_slim;
#define LPFC_MAX_NS_RETRY 3 /* Number of retry attempts to contact
the NameServer before giving up. */
#define LPFC_CMD_PER_LUN 3 /* max outstanding cmds per lun */
-#define LPFC_DEFAULT_SG_SEG_CNT 64 /* sg element count per scsi cmnd */
-#define LPFC_MAX_SG_SEG_CNT 256 /* sg element count per scsi cmnd */
+#define LPFC_DEFAULT_SG_SEG_CNT 64 /* sg element count per scsi cmnd */
+#define LPFC_DEFAULT_PROT_SG_SEG_CNT 4096 /* sg protection elements count */
+#define LPFC_MAX_SG_SEG_CNT 4096 /* sg element count per scsi cmnd */
+#define LPFC_MAX_PROT_SG_SEG_CNT 4096 /* prot sg element count per scsi cmd*/
#define LPFC_IOCB_LIST_CNT 2250 /* list of IOCBs for fast-path usage. */
#define LPFC_Q_RAMP_UP_INTERVAL 120 /* lun q_depth ramp up interval */
#define LPFC_VNAME_LEN 100 /* vport symbolic name length */
@@ -354,8 +356,6 @@ struct lpfc_vport {
uint8_t load_flag;
#define FC_LOADING 0x1 /* HBA in process of loading drvr */
#define FC_UNLOADING 0x2 /* HBA in process of unloading drvr */
- char *vname; /* Application assigned name */
-
/* Vport Config Parameters */
uint32_t cfg_scan_down;
uint32_t cfg_lun_queue_depth;
@@ -376,7 +376,7 @@ struct lpfc_vport {
struct fc_vport *fc_vport;
-#ifdef CONFIG_LPFC_DEBUG_FS
+#ifdef CONFIG_SCSI_LPFC_DEBUG_FS
struct dentry *debug_disc_trc;
struct dentry *debug_nodelist;
struct dentry *vport_debugfs_root;
@@ -428,6 +428,7 @@ struct lpfc_hba {
#define LPFC_SLI3_VPORT_TEARDOWN 0x04
#define LPFC_SLI3_CRP_ENABLED 0x08
#define LPFC_SLI3_INB_ENABLED 0x10
+#define LPFC_SLI3_BG_ENABLED 0x20
uint32_t iocb_cmd_size;
uint32_t iocb_rsp_size;
@@ -501,12 +502,14 @@ struct lpfc_hba {
uint32_t cfg_poll_tmo;
uint32_t cfg_use_msi;
uint32_t cfg_sg_seg_cnt;
+ uint32_t cfg_prot_sg_seg_cnt;
uint32_t cfg_sg_dma_buf_size;
uint64_t cfg_soft_wwnn;
uint64_t cfg_soft_wwpn;
uint32_t cfg_hba_queue_depth;
uint32_t cfg_enable_hba_reset;
uint32_t cfg_enable_hba_heartbeat;
+ uint32_t cfg_enable_bg;
lpfc_vpd_t vpd; /* vital product data */
@@ -572,6 +575,9 @@ struct lpfc_hba {
uint64_t fc4InputRequests;
uint64_t fc4OutputRequests;
uint64_t fc4ControlRequests;
+ uint64_t bg_guard_err_cnt;
+ uint64_t bg_apptag_err_cnt;
+ uint64_t bg_reftag_err_cnt;
struct lpfc_sysfs_mbox sysfs_mbox;
@@ -594,6 +600,8 @@ struct lpfc_hba {
struct fc_host_statistics link_stats;
enum intr_type_t intr_type;
+ uint32_t intr_mode;
+#define LPFC_INTR_ERROR 0xFFFFFFFF
struct msix_entry msix_entries[LPFC_MSIX_VECTORS];
struct list_head port_list;
@@ -613,12 +621,14 @@ struct lpfc_hba {
unsigned long last_rsrc_error_time;
unsigned long last_ramp_down_time;
unsigned long last_ramp_up_time;
-#ifdef CONFIG_LPFC_DEBUG_FS
+#ifdef CONFIG_SCSI_LPFC_DEBUG_FS
struct dentry *hba_debugfs_root;
atomic_t debugfs_vport_count;
struct dentry *debug_hbqinfo;
struct dentry *debug_dumpHostSlim;
struct dentry *debug_dumpHBASlim;
+ struct dentry *debug_dumpData; /* BlockGuard BPL*/
+ struct dentry *debug_dumpDif; /* BlockGuard BPL*/
struct dentry *debug_slow_ring_trc;
struct lpfc_debugfs_trc *slow_ring_trc;
atomic_t slow_ring_trc_cnt;
diff --git a/drivers/scsi/lpfc/lpfc_attr.c b/drivers/scsi/lpfc/lpfc_attr.c
index aa3d6277581d..40cf0f4f327f 100644
--- a/drivers/scsi/lpfc/lpfc_attr.c
+++ b/drivers/scsi/lpfc/lpfc_attr.c
@@ -96,6 +96,61 @@ lpfc_drvr_version_show(struct device *dev, struct device_attribute *attr,
return snprintf(buf, PAGE_SIZE, LPFC_MODULE_DESC "\n");
}
+static ssize_t
+lpfc_bg_info_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->cfg_enable_bg)
+ if (phba->sli3_options & LPFC_SLI3_BG_ENABLED)
+ return snprintf(buf, PAGE_SIZE, "BlockGuard Enabled\n");
+ else
+ return snprintf(buf, PAGE_SIZE,
+ "BlockGuard Not Supported\n");
+ else
+ return snprintf(buf, PAGE_SIZE,
+ "BlockGuard Disabled\n");
+}
+
+static ssize_t
+lpfc_bg_guard_err_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;
+
+ return snprintf(buf, PAGE_SIZE, "%llu\n",
+ (unsigned long long)phba->bg_guard_err_cnt);
+}
+
+static ssize_t
+lpfc_bg_apptag_err_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;
+
+ return snprintf(buf, PAGE_SIZE, "%llu\n",
+ (unsigned long long)phba->bg_apptag_err_cnt);
+}
+
+static ssize_t
+lpfc_bg_reftag_err_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;
+
+ return snprintf(buf, PAGE_SIZE, "%llu\n",
+ (unsigned long long)phba->bg_reftag_err_cnt);
+}
+
/**
* lpfc_info_show: Return some pci info about the host in ascii.
* @dev: class converted to a Scsi_host structure.
@@ -1485,6 +1540,10 @@ lpfc_vport_param_store(name)\
static DEVICE_ATTR(lpfc_##name, S_IRUGO | S_IWUSR,\
lpfc_##name##_show, lpfc_##name##_store)
+static DEVICE_ATTR(bg_info, S_IRUGO, lpfc_bg_info_show, NULL);
+static DEVICE_ATTR(bg_guard_err, S_IRUGO, lpfc_bg_guard_err_show, NULL);
+static DEVICE_ATTR(bg_apptag_err, S_IRUGO, lpfc_bg_apptag_err_show, NULL);
+static DEVICE_ATTR(bg_reftag_err, S_IRUGO, lpfc_bg_reftag_err_show, NULL);
static DEVICE_ATTR(info, S_IRUGO, lpfc_info_show, NULL);
static DEVICE_ATTR(serialnum, S_IRUGO, lpfc_serialnum_show, NULL);
static DEVICE_ATTR(modeldesc, S_IRUGO, lpfc_modeldesc_show, NULL);
@@ -1970,6 +2029,7 @@ static DEVICE_ATTR(lpfc_devloss_tmo, S_IRUGO | S_IWUSR,
# LOG_LINK_EVENT 0x10 Link events
# LOG_FCP 0x40 FCP traffic history
# LOG_NODE 0x80 Node table events
+# LOG_BG 0x200 BlockBuard events
# LOG_MISC 0x400 Miscellaneous events
# LOG_SLI 0x800 SLI events
# LOG_FCP_ERROR 0x1000 Only log FCP errors
@@ -2769,6 +2829,42 @@ LPFC_ATTR_R(enable_hba_reset, 1, 0, 1, "Enable HBA resets from the driver.");
LPFC_ATTR_R(enable_hba_heartbeat, 1, 0, 1, "Enable HBA Heartbeat.");
/*
+# lpfc_enable_bg: Enable BlockGuard (Emulex's Implementation of T10-DIF)
+# 0 = BlockGuard disabled (default)
+# 1 = BlockGuard enabled
+# Value range is [0,1]. Default value is 0.
+*/
+LPFC_ATTR_R(enable_bg, 0, 0, 1, "Enable BlockGuard Support");
+
+
+/*
+# lpfc_prot_mask: i
+# - Bit mask of host protection capabilities used to register with the
+# SCSI mid-layer
+# - Only meaningful if BG is turned on (lpfc_enable_bg=1).
+# - Allows you to ultimately specify which profiles to use
+# - Default will result in registering capabilities for all profiles.
+#
+*/
+unsigned int lpfc_prot_mask = SHOST_DIX_TYPE0_PROTECTION;
+
+module_param(lpfc_prot_mask, uint, 0);
+MODULE_PARM_DESC(lpfc_prot_mask, "host protection mask");
+
+/*
+# lpfc_prot_guard: i
+# - Bit mask of protection guard types to register with the SCSI mid-layer
+# - Guard types are currently either 1) IP checksum 2) T10-DIF CRC
+# - Allows you to ultimately specify which profiles to use
+# - Default will result in registering capabilities for all guard types
+#
+*/
+unsigned char lpfc_prot_guard = SHOST_DIX_GUARD_IP;
+module_param(lpfc_prot_guard, byte, 0);
+MODULE_PARM_DESC(lpfc_prot_guard, "host protection guard type");
+
+
+/*
* lpfc_sg_seg_cnt: Initial Maximum DMA Segment Count
* This value can be set to values between 64 and 256. The default value is
* 64, but may be increased to allow for larger Max I/O sizes. The scsi layer
@@ -2777,7 +2873,15 @@ LPFC_ATTR_R(enable_hba_heartbeat, 1, 0, 1, "Enable HBA Heartbeat.");
LPFC_ATTR_R(sg_seg_cnt, LPFC_DEFAULT_SG_SEG_CNT, LPFC_DEFAULT_SG_SEG_CNT,
LPFC_MAX_SG_SEG_CNT, "Max Scatter Gather Segment Count");
+LPFC_ATTR_R(prot_sg_seg_cnt, LPFC_DEFAULT_PROT_SG_SEG_CNT,
+ LPFC_DEFAULT_PROT_SG_SEG_CNT, LPFC_MAX_PROT_SG_SEG_CNT,
+ "Max Protection Scatter Gather Segment Count");
+
struct device_attribute *lpfc_hba_attrs[] = {
+ &dev_attr_bg_info,
+ &dev_attr_bg_guard_err,
+ &dev_attr_bg_apptag_err,
+ &dev_attr_bg_reftag_err,
&dev_attr_info,
&dev_attr_serialnum,
&dev_attr_modeldesc,
@@ -2825,6 +2929,7 @@ struct device_attribute *lpfc_hba_attrs[] = {
&dev_attr_lpfc_poll,
&dev_attr_lpfc_poll_tmo,
&dev_attr_lpfc_use_msi,
+ &dev_attr_lpfc_enable_bg,
&dev_attr_lpfc_soft_wwnn,
&dev_attr_lpfc_soft_wwpn,
&dev_attr_lpfc_soft_wwn_enable,
@@ -2833,6 +2938,7 @@ struct device_attribute *lpfc_hba_attrs[] = {
&dev_attr_lpfc_sg_seg_cnt,
&dev_attr_lpfc_max_scsicmpl_time,
&dev_attr_lpfc_stat_data_ctrl,
+ &dev_attr_lpfc_prot_sg_seg_cnt,
NULL,
};
@@ -3282,25 +3388,28 @@ lpfc_alloc_sysfs_attr(struct lpfc_vport *vport)
int error;
error = sysfs_create_bin_file(&shost->shost_dev.kobj,
- &sysfs_ctlreg_attr);
- if (error)
+ &sysfs_drvr_stat_data_attr);
+
+ /* Virtual ports do not need ctrl_reg and mbox */
+ if (error || vport->port_type == LPFC_NPIV_PORT)
goto out;
error = sysfs_create_bin_file(&shost->shost_dev.kobj,
- &sysfs_mbox_attr);
+ &sysfs_ctlreg_attr);
if (error)
- goto out_remove_ctlreg_attr;
+ goto out_remove_stat_attr;
error = sysfs_create_bin_file(&shost->shost_dev.kobj,
- &sysfs_drvr_stat_data_attr);
+ &sysfs_mbox_attr);
if (error)
- goto out_remove_mbox_attr;
+ goto out_remove_ctlreg_attr;
return 0;
-out_remove_mbox_attr:
- sysfs_remove_bin_file(&shost->shost_dev.kobj, &sysfs_mbox_attr);
out_remove_ctlreg_attr:
sysfs_remove_bin_file(&shost->shost_dev.kobj, &sysfs_ctlreg_attr);
+out_remove_stat_attr:
+ sysfs_remove_bin_file(&shost->shost_dev.kobj,
+ &sysfs_drvr_stat_data_attr);
out:
return error;
}
@@ -3315,6 +3424,9 @@ lpfc_free_sysfs_attr(struct lpfc_vport *vport)
struct Scsi_Host *shost = lpfc_shost_from_vport(vport);
sysfs_remove_bin_file(&shost->shost_dev.kobj,
&sysfs_drvr_stat_data_attr);
+ /* Virtual ports do not need ctrl_reg and mbox */
+ if (vport->port_type == LPFC_NPIV_PORT)
+ return;
sysfs_remove_bin_file(&shost->shost_dev.kobj, &sysfs_mbox_attr);
sysfs_remove_bin_file(&shost->shost_dev.kobj, &sysfs_ctlreg_attr);
}
@@ -3792,6 +3904,23 @@ lpfc_show_rport_##field (struct device *dev, \
lpfc_rport_show_function(field, format_string, sz, ) \
static FC_RPORT_ATTR(field, S_IRUGO, lpfc_show_rport_##field, NULL)
+/**
+ * lpfc_set_vport_symbolic_name: Set the vport's symbolic name.
+ * @fc_vport: The fc_vport who's symbolic name has been changed.
+ *
+ * Description:
+ * This function is called by the transport after the @fc_vport's symbolic name
+ * has been changed. This function re-registers the symbolic name with the
+ * switch to propogate the change into the fabric if the vport is active.
+ **/
+static void
+lpfc_set_vport_symbolic_name(struct fc_vport *fc_vport)
+{
+ struct lpfc_vport *vport = *(struct lpfc_vport **)fc_vport->dd_data;
+
+ if (vport->port_state == LPFC_VPORT_READY)
+ lpfc_ns_cmd(vport, SLI_CTNS_RSPN_ID, 0, 0);
+}
struct fc_function_template lpfc_transport_functions = {
/* fixed attributes the driver supports */
@@ -3801,6 +3930,7 @@ struct fc_function_template lpfc_transport_functions = {
.show_host_supported_fc4s = 1,
.show_host_supported_speeds = 1,
.show_host_maxframe_size = 1,
+ .show_host_symbolic_name = 1,
/* dynamic attributes the driver supports */
.get_host_port_id = lpfc_get_host_port_id,
@@ -3850,6 +3980,10 @@ struct fc_function_template lpfc_transport_functions = {
.terminate_rport_io = lpfc_terminate_rport_io,
.dd_fcvport_size = sizeof(struct lpfc_vport *),
+
+ .vport_disable = lpfc_vport_disable,
+
+ .set_vport_symbolic_name = lpfc_set_vport_symbolic_name,
};
struct fc_function_template lpfc_vport_transport_functions = {
@@ -3860,6 +3994,7 @@ struct fc_function_template lpfc_vport_transport_functions = {
.show_host_supported_fc4s = 1,
.show_host_supported_speeds = 1,
.show_host_maxframe_size = 1,
+ .show_host_symbolic_name = 1,
/* dynamic attributes the driver supports */
.get_host_port_id = lpfc_get_host_port_id,
@@ -3908,6 +4043,8 @@ struct fc_function_template lpfc_vport_transport_functions = {
.terminate_rport_io = lpfc_terminate_rport_io,
.vport_disable = lpfc_vport_disable,
+
+ .set_vport_symbolic_name = lpfc_set_vport_symbolic_name,
};
/**
@@ -3930,13 +4067,12 @@ lpfc_get_cfgparam(struct lpfc_hba *phba)
lpfc_use_msi_init(phba, lpfc_use_msi);
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);
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);
- /* Also reinitialize the host templates with new values. */
- lpfc_vport_template.sg_tablesize = phba->cfg_sg_seg_cnt;
- lpfc_template.sg_tablesize = phba->cfg_sg_seg_cnt;
+ lpfc_prot_sg_seg_cnt_init(phba, lpfc_prot_sg_seg_cnt);
/*
* Since the sg_tablesize is module parameter, the sg_dma_buf_size
* used to create the sg_dma_buf_pool must be dynamically calculated.
@@ -3945,6 +4081,17 @@ lpfc_get_cfgparam(struct lpfc_hba *phba)
phba->cfg_sg_dma_buf_size = sizeof(struct fcp_cmnd) +
sizeof(struct fcp_rsp) +
((phba->cfg_sg_seg_cnt + 2) * sizeof(struct ulp_bde64));
+
+ if (phba->cfg_enable_bg) {
+ phba->cfg_sg_seg_cnt = LPFC_MAX_SG_SEG_CNT;
+ phba->cfg_sg_dma_buf_size +=
+ phba->cfg_prot_sg_seg_cnt * sizeof(struct ulp_bde64);
+ }
+
+ /* Also reinitialize the host templates with new values. */
+ lpfc_vport_template.sg_tablesize = phba->cfg_sg_seg_cnt;
+ lpfc_template.sg_tablesize = phba->cfg_sg_seg_cnt;
+
lpfc_hba_queue_depth_init(phba, lpfc_hba_queue_depth);
return;
}
diff --git a/drivers/scsi/lpfc/lpfc_crtn.h b/drivers/scsi/lpfc/lpfc_crtn.h
index 044ef4057d28..07f4976319a5 100644
--- a/drivers/scsi/lpfc/lpfc_crtn.h
+++ b/drivers/scsi/lpfc/lpfc_crtn.h
@@ -22,6 +22,7 @@ typedef int (*node_filter)(struct lpfc_nodelist *, void *);
struct fc_rport;
void lpfc_dump_mem(struct lpfc_hba *, LPFC_MBOXQ_t *, uint16_t);
+void lpfc_dump_wakeup_param(struct lpfc_hba *, LPFC_MBOXQ_t *);
void lpfc_read_nv(struct lpfc_hba *, LPFC_MBOXQ_t *);
void lpfc_config_async(struct lpfc_hba *, LPFC_MBOXQ_t *, uint32_t);
@@ -284,12 +285,24 @@ extern void lpfc_debugfs_slow_ring_trc(struct lpfc_hba *, char *, uint32_t,
uint32_t, uint32_t);
extern struct lpfc_hbq_init *lpfc_hbq_defs[];
+/* externs BlockGuard */
+extern char *_dump_buf_data;
+extern unsigned long _dump_buf_data_order;
+extern char *_dump_buf_dif;
+extern unsigned long _dump_buf_dif_order;
+extern spinlock_t _dump_buf_lock;
+extern int _dump_buf_done;
+extern spinlock_t pgcnt_lock;
+extern unsigned int pgcnt;
+extern unsigned int lpfc_prot_mask;
+extern unsigned char lpfc_prot_guard;
+
/* Interface exported by fabric iocb scheduler */
void lpfc_fabric_abort_nport(struct lpfc_nodelist *);
void lpfc_fabric_abort_hba(struct lpfc_hba *);
void lpfc_fabric_block_timeout(unsigned long);
void lpfc_unblock_fabric_iocbs(struct lpfc_hba *);
-void lpfc_adjust_queue_depth(struct lpfc_hba *);
+void lpfc_rampdown_queue_depth(struct lpfc_hba *);
void lpfc_ramp_down_queue_handler(struct lpfc_hba *);
void lpfc_ramp_up_queue_handler(struct lpfc_hba *);
void lpfc_scsi_dev_block(struct lpfc_hba *);
diff --git a/drivers/scsi/lpfc/lpfc_ct.c b/drivers/scsi/lpfc/lpfc_ct.c
index 26dae8bae2d1..896c7b0351e5 100644
--- a/drivers/scsi/lpfc/lpfc_ct.c
+++ b/drivers/scsi/lpfc/lpfc_ct.c
@@ -560,18 +560,25 @@ lpfc_cmpl_ct_cmd_gid_ft(struct lpfc_hba *phba, struct lpfc_iocbq *cmdiocb,
irsp->ulpStatus, irsp->un.ulpWord[4], vport->fc_ns_retry);
/* Don't bother processing response if vport is being torn down. */
- if (vport->load_flag & FC_UNLOADING)
+ if (vport->load_flag & FC_UNLOADING) {
+ if (vport->fc_flag & FC_RSCN_MODE)
+ lpfc_els_flush_rscn(vport);
goto out;
+ }
if (lpfc_els_chk_latt(vport)) {
lpfc_printf_vlog(vport, KERN_INFO, LOG_DISCOVERY,
"0216 Link event during NS query\n");
+ if (vport->fc_flag & FC_RSCN_MODE)
+ lpfc_els_flush_rscn(vport);
lpfc_vport_set_state(vport, FC_VPORT_FAILED);
goto out;
}
if (lpfc_error_lost_link(irsp)) {
lpfc_printf_vlog(vport, KERN_INFO, LOG_DISCOVERY,
"0226 NS query failed due to link event\n");
+ if (vport->fc_flag & FC_RSCN_MODE)
+ lpfc_els_flush_rscn(vport);
goto out;
}
if (irsp->ulpStatus) {
@@ -587,6 +594,8 @@ lpfc_cmpl_ct_cmd_gid_ft(struct lpfc_hba *phba, struct lpfc_iocbq *cmdiocb,
if (rc == 0)
goto out;
}
+ if (vport->fc_flag & FC_RSCN_MODE)
+ lpfc_els_flush_rscn(vport);
lpfc_vport_set_state(vport, FC_VPORT_FAILED);
lpfc_printf_vlog(vport, KERN_ERR, LOG_ELS,
"0257 GID_FT Query error: 0x%x 0x%x\n",
@@ -1008,8 +1017,10 @@ lpfc_vport_symbolic_port_name(struct lpfc_vport *vport, char *symbol,
if (n < size)
n += snprintf(symbol + n, size - n, " VPort-%d", vport->vpi);
- if (n < size && vport->vname)
- n += snprintf(symbol + n, size - n, " VName-%s", vport->vname);
+ if (n < size &&
+ strlen(vport->fc_vport->symbolic_name))
+ n += snprintf(symbol + n, size - n, " VName-%s",
+ vport->fc_vport->symbolic_name);
return n;
}
diff --git a/drivers/scsi/lpfc/lpfc_debugfs.c b/drivers/scsi/lpfc/lpfc_debugfs.c
index 771920bdde44..b615eda361d5 100644
--- a/drivers/scsi/lpfc/lpfc_debugfs.c
+++ b/drivers/scsi/lpfc/lpfc_debugfs.c
@@ -46,7 +46,7 @@
#include "lpfc_compat.h"
#include "lpfc_debugfs.h"
-#ifdef CONFIG_LPFC_DEBUG_FS
+#ifdef CONFIG_SCSI_LPFC_DEBUG_FS
/**
* debugfs interface
*
@@ -618,7 +618,7 @@ inline void
lpfc_debugfs_disc_trc(struct lpfc_vport *vport, int mask, char *fmt,
uint32_t data1, uint32_t data2, uint32_t data3)
{
-#ifdef CONFIG_LPFC_DEBUG_FS
+#ifdef CONFIG_SCSI_LPFC_DEBUG_FS
struct lpfc_debugfs_trc *dtp;
int index;
@@ -659,7 +659,7 @@ inline void
lpfc_debugfs_slow_ring_trc(struct lpfc_hba *phba, char *fmt,
uint32_t data1, uint32_t data2, uint32_t data3)
{
-#ifdef CONFIG_LPFC_DEBUG_FS
+#ifdef CONFIG_SCSI_LPFC_DEBUG_FS
struct lpfc_debugfs_trc *dtp;
int index;
@@ -680,7 +680,7 @@ lpfc_debugfs_slow_ring_trc(struct lpfc_hba *phba, char *fmt,
return;
}
-#ifdef CONFIG_LPFC_DEBUG_FS
+#ifdef CONFIG_SCSI_LPFC_DEBUG_FS
/**
* lpfc_debugfs_disc_trc_open - Open the discovery trace log.
* @inode: The inode pointer that contains a vport pointer.
@@ -907,6 +907,91 @@ out:
return rc;
}
+static int
+lpfc_debugfs_dumpData_open(struct inode *inode, struct file *file)
+{
+ struct lpfc_debug *debug;
+ int rc = -ENOMEM;
+
+ if (!_dump_buf_data)
+ return -EBUSY;
+
+ debug = kmalloc(sizeof(*debug), GFP_KERNEL);
+ if (!debug)
+ goto out;
+
+ /* Round to page boundry */
+ printk(KERN_ERR "BLKGRD %s: _dump_buf_data=0x%p\n",
+ __func__, _dump_buf_data);
+ debug->buffer = _dump_buf_data;
+ if (!debug->buffer) {
+ kfree(debug);
+ goto out;
+ }
+
+ debug->len = (1 << _dump_buf_data_order) << PAGE_SHIFT;
+ file->private_data = debug;
+
+ rc = 0;
+out:
+ return rc;
+}
+
+static int
+lpfc_debugfs_dumpDif_open(struct inode *inode, struct file *file)
+{
+ struct lpfc_debug *debug;
+ int rc = -ENOMEM;
+
+ if (!_dump_buf_dif)
+ return -EBUSY;
+
+ debug = kmalloc(sizeof(*debug), GFP_KERNEL);
+ if (!debug)
+ 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);
+ debug->buffer = _dump_buf_dif;
+ if (!debug->buffer) {
+ kfree(debug);
+ goto out;
+ }
+
+ debug->len = (1 << _dump_buf_dif_order) << PAGE_SHIFT;
+ file->private_data = debug;
+
+ rc = 0;
+out:
+ return rc;
+}
+
+static ssize_t
+lpfc_debugfs_dumpDataDif_write(struct file *file, const char __user *buf,
+ size_t nbytes, loff_t *ppos)
+{
+ /*
+ * The Data/DIF buffers only save one failing IO
+ * The write op is used as a reset mechanism after an IO has
+ * already been saved to the next one can be saved
+ */
+ spin_lock(&_dump_buf_lock);
+
+ memset((void *)_dump_buf_data, 0,
+ ((1 << PAGE_SHIFT) << _dump_buf_data_order));
+ memset((void *)_dump_buf_dif, 0,
+ ((1 << PAGE_SHIFT) << _dump_buf_dif_order));
+
+ _dump_buf_done = 0;
+
+ spin_unlock(&_dump_buf_lock);
+
+ return nbytes;
+}
+
+
+
/**
* lpfc_debugfs_nodelist_open - Open the nodelist debugfs file.
* @inode: The inode pointer that contains a vport pointer.
@@ -1035,6 +1120,17 @@ lpfc_debugfs_release(struct inode *inode, struct file *file)
return 0;
}
+static int
+lpfc_debugfs_dumpDataDif_release(struct inode *inode, struct file *file)
+{
+ struct lpfc_debug *debug = file->private_data;
+
+ debug->buffer = NULL;
+ kfree(debug);
+
+ return 0;
+}
+
#undef lpfc_debugfs_op_disc_trc
static struct file_operations lpfc_debugfs_op_disc_trc = {
.owner = THIS_MODULE,
@@ -1080,6 +1176,26 @@ static struct file_operations lpfc_debugfs_op_dumpHostSlim = {
.release = lpfc_debugfs_release,
};
+#undef lpfc_debugfs_op_dumpData
+static struct file_operations lpfc_debugfs_op_dumpData = {
+ .owner = THIS_MODULE,
+ .open = lpfc_debugfs_dumpData_open,
+ .llseek = lpfc_debugfs_lseek,
+ .read = lpfc_debugfs_read,
+ .write = lpfc_debugfs_dumpDataDif_write,
+ .release = lpfc_debugfs_dumpDataDif_release,
+};
+
+#undef lpfc_debugfs_op_dumpDif
+static struct file_operations lpfc_debugfs_op_dumpDif = {
+ .owner = THIS_MODULE,
+ .open = lpfc_debugfs_dumpDif_open,
+ .llseek = lpfc_debugfs_lseek,
+ .read = lpfc_debugfs_read,
+ .write = lpfc_debugfs_dumpDataDif_write,
+ .release = lpfc_debugfs_dumpDataDif_release,
+};
+
#undef lpfc_debugfs_op_slow_ring_trc
static struct file_operations lpfc_debugfs_op_slow_ring_trc = {
.owner = THIS_MODULE,
@@ -1106,7 +1222,7 @@ static atomic_t lpfc_debugfs_hba_count;
inline void
lpfc_debugfs_initialize(struct lpfc_vport *vport)
{
-#ifdef CONFIG_LPFC_DEBUG_FS
+#ifdef CONFIG_SCSI_LPFC_DEBUG_FS
struct lpfc_hba *phba = vport->phba;
char name[64];
uint32_t num, i;
@@ -1176,6 +1292,32 @@ lpfc_debugfs_initialize(struct lpfc_vport *vport)
goto debug_failed;
}
+ /* Setup dumpData */
+ snprintf(name, sizeof(name), "dumpData");
+ phba->debug_dumpData =
+ debugfs_create_file(name, S_IFREG|S_IRUGO|S_IWUSR,
+ phba->hba_debugfs_root,
+ phba, &lpfc_debugfs_op_dumpData);
+ if (!phba->debug_dumpData) {
+ lpfc_printf_vlog(vport, KERN_ERR, LOG_INIT,
+ "0800 Cannot create debugfs dumpData\n");
+ goto debug_failed;
+ }
+
+ /* Setup dumpDif */
+ snprintf(name, sizeof(name), "dumpDif");
+ phba->debug_dumpDif =
+ debugfs_create_file(name, S_IFREG|S_IRUGO|S_IWUSR,
+ phba->hba_debugfs_root,
+ phba, &lpfc_debugfs_op_dumpDif);
+ if (!phba->debug_dumpDif) {
+ lpfc_printf_vlog(vport, KERN_ERR, LOG_INIT,
+ "0801 Cannot create debugfs dumpDif\n");
+ goto debug_failed;
+ }
+
+
+
/* Setup slow ring trace */
if (lpfc_debugfs_max_slow_ring_trc) {
num = lpfc_debugfs_max_slow_ring_trc - 1;
@@ -1305,7 +1447,7 @@ debug_failed:
inline void
lpfc_debugfs_terminate(struct lpfc_vport *vport)
{
-#ifdef CONFIG_LPFC_DEBUG_FS
+#ifdef CONFIG_SCSI_LPFC_DEBUG_FS
struct lpfc_hba *phba = vport->phba;
if (vport->disc_trc) {
@@ -1340,6 +1482,16 @@ lpfc_debugfs_terminate(struct lpfc_vport *vport)
debugfs_remove(phba->debug_dumpHostSlim); /* HostSlim */
phba->debug_dumpHostSlim = NULL;
}
+ if (phba->debug_dumpData) {
+ debugfs_remove(phba->debug_dumpData); /* dumpData */
+ phba->debug_dumpData = NULL;
+ }
+
+ if (phba->debug_dumpDif) {
+ debugfs_remove(phba->debug_dumpDif); /* dumpDif */
+ phba->debug_dumpDif = NULL;
+ }
+
if (phba->slow_ring_trc) {
kfree(phba->slow_ring_trc);
phba->slow_ring_trc = NULL;
diff --git a/drivers/scsi/lpfc/lpfc_debugfs.h b/drivers/scsi/lpfc/lpfc_debugfs.h
index 31e86a55391d..03c7313a1012 100644
--- a/drivers/scsi/lpfc/lpfc_debugfs.h
+++ b/drivers/scsi/lpfc/lpfc_debugfs.h
@@ -21,7 +21,7 @@
#ifndef _H_LPFC_DEBUG_FS
#define _H_LPFC_DEBUG_FS
-#ifdef CONFIG_LPFC_DEBUG_FS
+#ifdef CONFIG_SCSI_LPFC_DEBUG_FS
struct lpfc_debugfs_trc {
char *fmt;
uint32_t data1;
diff --git a/drivers/scsi/lpfc/lpfc_els.c b/drivers/scsi/lpfc/lpfc_els.c
index 630bd28fb997..a8f30bdaff69 100644
--- a/drivers/scsi/lpfc/lpfc_els.c
+++ b/drivers/scsi/lpfc/lpfc_els.c
@@ -221,7 +221,11 @@ lpfc_prep_els_iocb(struct lpfc_vport *vport, uint8_t expectRsp,
/* For ELS_REQUEST64_CR, use the VPI by default */
icmd->ulpContext = vport->vpi;
icmd->ulpCt_h = 0;
- icmd->ulpCt_l = 1;
+ /* The CT field must be 0=INVALID_RPI for the ECHO cmd */
+ if (elscmd == ELS_CMD_ECHO)
+ icmd->ulpCt_l = 0; /* context = invalid RPI */
+ else
+ icmd->ulpCt_l = 1; /* context = VPI */
}
bpl = (struct ulp_bde64 *) pbuflist->virt;
@@ -271,7 +275,8 @@ lpfc_prep_els_iocb(struct lpfc_vport *vport, uint8_t expectRsp,
return elsiocb;
els_iocb_free_pbuf_exit:
- lpfc_mbuf_free(phba, prsp->virt, prsp->phys);
+ if (expectRsp)
+ lpfc_mbuf_free(phba, prsp->virt, prsp->phys);
kfree(pbuflist);
els_iocb_free_prsp_exit:
@@ -2468,6 +2473,15 @@ lpfc_els_retry(struct lpfc_hba *phba, struct lpfc_iocbq *cmdiocb,
case IOSTAT_LOCAL_REJECT:
switch ((irsp->un.ulpWord[4] & 0xff)) {
case IOERR_LOOP_OPEN_FAILURE:
+ if (cmd == ELS_CMD_FLOGI) {
+ if (PCI_DEVICE_ID_HORNET ==
+ phba->pcidev->device) {
+ phba->fc_topology = TOPOLOGY_LOOP;
+ phba->pport->fc_myDID = 0;
+ phba->alpa_map[0] = 0;
+ phba->alpa_map[1] = 0;
+ }
+ }
if (cmd == ELS_CMD_PLOGI && cmdiocb->retry == 0)
delay = 1000;
retry = 1;
@@ -3823,27 +3837,21 @@ lpfc_rscn_payload_check(struct lpfc_vport *vport, uint32_t did)
while (payload_len) {
rscn_did.un.word = be32_to_cpu(*lp++);
payload_len -= sizeof(uint32_t);
- switch (rscn_did.un.b.resv) {
- case 0: /* Single N_Port ID effected */
+ switch (rscn_did.un.b.resv & RSCN_ADDRESS_FORMAT_MASK) {
+ case RSCN_ADDRESS_FORMAT_PORT:
if (ns_did.un.word == rscn_did.un.word)
goto return_did_out;
break;
- case 1: /* Whole N_Port Area effected */
+ case RSCN_ADDRESS_FORMAT_AREA:
if ((ns_did.un.b.domain == rscn_did.un.b.domain)
&& (ns_did.un.b.area == rscn_did.un.b.area))
goto return_did_out;
break;
- case 2: /* Whole N_Port Domain effected */
+ case RSCN_ADDRESS_FORMAT_DOMAIN:
if (ns_did.un.b.domain == rscn_did.un.b.domain)
goto return_did_out;
break;
- default:
- /* Unknown Identifier in RSCN node */
- lpfc_printf_vlog(vport, KERN_ERR, LOG_DISCOVERY,
- "0217 Unknown Identifier in "
- "RSCN payload Data: x%x\n",
- rscn_did.un.word);
- case 3: /* Whole Fabric effected */
+ case RSCN_ADDRESS_FORMAT_FABRIC:
goto return_did_out;
}
}
@@ -3887,6 +3895,49 @@ lpfc_rscn_recovery_check(struct lpfc_vport *vport)
}
/**
+ * lpfc_send_rscn_event: Send an RSCN event to management application.
+ * @vport: pointer to a host virtual N_Port data structure.
+ * @cmdiocb: pointer to lpfc command iocb data structure.
+ *
+ * lpfc_send_rscn_event sends an RSCN netlink event to management
+ * applications.
+ */
+static void
+lpfc_send_rscn_event(struct lpfc_vport *vport,
+ struct lpfc_iocbq *cmdiocb)
+{
+ struct lpfc_dmabuf *pcmd;
+ struct Scsi_Host *shost = lpfc_shost_from_vport(vport);
+ uint32_t *payload_ptr;
+ uint32_t payload_len;
+ struct lpfc_rscn_event_header *rscn_event_data;
+
+ pcmd = (struct lpfc_dmabuf *) cmdiocb->context2;
+ payload_ptr = (uint32_t *) pcmd->virt;
+ payload_len = be32_to_cpu(*payload_ptr & ~ELS_CMD_MASK);
+
+ rscn_event_data = kmalloc(sizeof(struct lpfc_rscn_event_header) +
+ payload_len, GFP_KERNEL);
+ if (!rscn_event_data) {
+ lpfc_printf_vlog(vport, KERN_ERR, LOG_ELS,
+ "0147 Failed to allocate memory for RSCN event\n");
+ return;
+ }
+ rscn_event_data->event_type = FC_REG_RSCN_EVENT;
+ rscn_event_data->payload_length = payload_len;
+ memcpy(rscn_event_data->rscn_payload, payload_ptr,
+ payload_len);
+
+ fc_host_post_vendor_event(shost,
+ fc_get_event_number(),
+ sizeof(struct lpfc_els_event_header) + payload_len,
+ (char *)rscn_event_data,
+ LPFC_NL_VENDOR_ID);
+
+ kfree(rscn_event_data);
+}
+
+/**
* lpfc_els_rcv_rscn: Process an unsolicited rscn iocb.
* @vport: pointer to a host virtual N_Port data structure.
* @cmdiocb: pointer to lpfc command iocb data structure.
@@ -3933,6 +3984,10 @@ lpfc_els_rcv_rscn(struct lpfc_vport *vport, struct lpfc_iocbq *cmdiocb,
"0214 RSCN received Data: x%x x%x x%x x%x\n",
vport->fc_flag, payload_len, *lp,
vport->fc_rscn_id_cnt);
+
+ /* Send an RSCN event to the management application */
+ lpfc_send_rscn_event(vport, cmdiocb);
+
for (i = 0; i < payload_len/sizeof(uint32_t); i++)
fc_host_post_event(shost, fc_get_event_number(),
FCH_EVT_RSCN, lp[i]);
@@ -4884,10 +4939,6 @@ lpfc_els_timeout_handler(struct lpfc_vport *vport)
uint32_t timeout;
uint32_t remote_ID = 0xffffffff;
- /* If the timer is already canceled do nothing */
- if ((vport->work_port_events & WORKER_ELS_TMO) == 0) {
- return;
- }
spin_lock_irq(&phba->hbalock);
timeout = (uint32_t)(phba->fc_ratov << 1);
@@ -5128,7 +5179,7 @@ lpfc_send_els_failure_event(struct lpfc_hba *phba,
fc_get_event_number(),
sizeof(lsrjt_event),
(char *)&lsrjt_event,
- SCSI_NL_VID_TYPE_PCI | PCI_VENDOR_ID_EMULEX);
+ LPFC_NL_VENDOR_ID);
return;
}
if ((rspiocbp->iocb.ulpStatus == IOSTAT_NPORT_BSY) ||
@@ -5146,7 +5197,7 @@ lpfc_send_els_failure_event(struct lpfc_hba *phba,
fc_get_event_number(),
sizeof(fabric_event),
(char *)&fabric_event,
- SCSI_NL_VID_TYPE_PCI | PCI_VENDOR_ID_EMULEX);
+ LPFC_NL_VENDOR_ID);
return;
}
@@ -5164,32 +5215,68 @@ lpfc_send_els_failure_event(struct lpfc_hba *phba,
static void
lpfc_send_els_event(struct lpfc_vport *vport,
struct lpfc_nodelist *ndlp,
- uint32_t cmd)
+ uint32_t *payload)
{
- struct lpfc_els_event_header els_data;
+ struct lpfc_els_event_header *els_data = NULL;
+ struct lpfc_logo_event *logo_data = NULL;
struct Scsi_Host *shost = lpfc_shost_from_vport(vport);
- els_data.event_type = FC_REG_ELS_EVENT;
- switch (cmd) {
+ if (*payload == ELS_CMD_LOGO) {
+ logo_data = kmalloc(sizeof(struct lpfc_logo_event), GFP_KERNEL);
+ if (!logo_data) {
+ lpfc_printf_vlog(vport, KERN_ERR, LOG_ELS,
+ "0148 Failed to allocate memory "
+ "for LOGO event\n");
+ return;
+ }
+ els_data = &logo_data->header;
+ } else {
+ els_data = kmalloc(sizeof(struct lpfc_els_event_header),
+ GFP_KERNEL);
+ if (!els_data) {
+ lpfc_printf_vlog(vport, KERN_ERR, LOG_ELS,
+ "0149 Failed to allocate memory "
+ "for ELS event\n");
+ return;
+ }
+ }
+ els_data->event_type = FC_REG_ELS_EVENT;
+ switch (*payload) {
case ELS_CMD_PLOGI:
- els_data.subcategory = LPFC_EVENT_PLOGI_RCV;
+ els_data->subcategory = LPFC_EVENT_PLOGI_RCV;
break;
case ELS_CMD_PRLO:
- els_data.subcategory = LPFC_EVENT_PRLO_RCV;
+ els_data->subcategory = LPFC_EVENT_PRLO_RCV;
break;
case ELS_CMD_ADISC:
- els_data.subcategory = LPFC_EVENT_ADISC_RCV;
+ els_data->subcategory = LPFC_EVENT_ADISC_RCV;
+ break;
+ case ELS_CMD_LOGO:
+ els_data->subcategory = LPFC_EVENT_LOGO_RCV;
+ /* Copy the WWPN in the LOGO payload */
+ memcpy(logo_data->logo_wwpn, &payload[2],
+ sizeof(struct lpfc_name));
break;
default:
return;
}
- memcpy(els_data.wwpn, &ndlp->nlp_portname, sizeof(struct lpfc_name));
- memcpy(els_data.wwnn, &ndlp->nlp_nodename, sizeof(struct lpfc_name));
- fc_host_post_vendor_event(shost,
- fc_get_event_number(),
- sizeof(els_data),
- (char *)&els_data,
- SCSI_NL_VID_TYPE_PCI | PCI_VENDOR_ID_EMULEX);
+ memcpy(els_data->wwpn, &ndlp->nlp_portname, sizeof(struct lpfc_name));
+ memcpy(els_data->wwnn, &ndlp->nlp_nodename, sizeof(struct lpfc_name));
+ if (*payload == ELS_CMD_LOGO) {
+ fc_host_post_vendor_event(shost,
+ fc_get_event_number(),
+ sizeof(struct lpfc_logo_event),
+ (char *)logo_data,
+ LPFC_NL_VENDOR_ID);
+ kfree(logo_data);
+ } else {
+ fc_host_post_vendor_event(shost,
+ fc_get_event_number(),
+ sizeof(struct lpfc_els_event_header),
+ (char *)els_data,
+ LPFC_NL_VENDOR_ID);
+ kfree(els_data);
+ }
return;
}
@@ -5296,7 +5383,7 @@ lpfc_els_unsol_buffer(struct lpfc_hba *phba, struct lpfc_sli_ring *pring,
phba->fc_stat.elsRcvPLOGI++;
ndlp = lpfc_plogi_confirm_nport(phba, payload, ndlp);
- lpfc_send_els_event(vport, ndlp, cmd);
+ lpfc_send_els_event(vport, ndlp, payload);
if (vport->port_state < LPFC_DISC_AUTH) {
if (!(phba->pport->fc_flag & FC_PT2PT) ||
(phba->pport->fc_flag & FC_PT2PT_PLOGI)) {
@@ -5334,6 +5421,7 @@ lpfc_els_unsol_buffer(struct lpfc_hba *phba, struct lpfc_sli_ring *pring,
did, vport->port_state, ndlp->nlp_flag);
phba->fc_stat.elsRcvLOGO++;
+ lpfc_send_els_event(vport, ndlp, payload);
if (vport->port_state < LPFC_DISC_AUTH) {
rjt_err = LSRJT_UNABLE_TPC;
break;
@@ -5346,7 +5434,7 @@ lpfc_els_unsol_buffer(struct lpfc_hba *phba, struct lpfc_sli_ring *pring,
did, vport->port_state, ndlp->nlp_flag);
phba->fc_stat.elsRcvPRLO++;
- lpfc_send_els_event(vport, ndlp, cmd);
+ lpfc_send_els_event(vport, ndlp, payload);
if (vport->port_state < LPFC_DISC_AUTH) {
rjt_err = LSRJT_UNABLE_TPC;
break;
@@ -5364,7 +5452,7 @@ lpfc_els_unsol_buffer(struct lpfc_hba *phba, struct lpfc_sli_ring *pring,
"RCV ADISC: did:x%x/ste:x%x flg:x%x",
did, vport->port_state, ndlp->nlp_flag);
- lpfc_send_els_event(vport, ndlp, cmd);
+ lpfc_send_els_event(vport, ndlp, payload);
phba->fc_stat.elsRcvADISC++;
if (vport->port_state < LPFC_DISC_AUTH) {
rjt_err = LSRJT_UNABLE_TPC;
diff --git a/drivers/scsi/lpfc/lpfc_hbadisc.c b/drivers/scsi/lpfc/lpfc_hbadisc.c
index a1a70d9ffc2a..8c64494444bf 100644
--- a/drivers/scsi/lpfc/lpfc_hbadisc.c
+++ b/drivers/scsi/lpfc/lpfc_hbadisc.c
@@ -350,7 +350,7 @@ lpfc_send_fastpath_evt(struct lpfc_hba *phba,
evt_data_size = sizeof(fast_evt_data->un.
read_check_error);
} else if ((evt_sub_category == LPFC_EVENT_FABRIC_BUSY) ||
- (evt_sub_category == IOSTAT_NPORT_BSY)) {
+ (evt_sub_category == LPFC_EVENT_PORT_BUSY)) {
evt_data = (char *) &fast_evt_data->un.fabric_evt;
evt_data_size = sizeof(fast_evt_data->un.fabric_evt);
} else {
@@ -387,7 +387,7 @@ lpfc_send_fastpath_evt(struct lpfc_hba *phba,
fc_get_event_number(),
evt_data_size,
evt_data,
- SCSI_NL_VID_TYPE_PCI | PCI_VENDOR_ID_EMULEX);
+ LPFC_NL_VENDOR_ID);
lpfc_free_fast_evt(phba, fast_evt_data);
return;
@@ -585,20 +585,25 @@ lpfc_do_work(void *p)
set_user_nice(current, -20);
phba->data_flags = 0;
- while (1) {
+ while (!kthread_should_stop()) {
/* wait and check worker queue activities */
rc = wait_event_interruptible(phba->work_waitq,
(test_and_clear_bit(LPFC_DATA_READY,
&phba->data_flags)
|| kthread_should_stop()));
- BUG_ON(rc);
-
- if (kthread_should_stop())
+ /* Signal wakeup shall terminate the worker thread */
+ if (rc) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_ELS,
+ "0433 Wakeup on signal: rc=x%x\n", rc);
break;
+ }
/* Attend pending lpfc data processing */
lpfc_work_done(phba);
}
+ phba->worker_thread = NULL;
+ lpfc_printf_log(phba, KERN_INFO, LOG_ELS,
+ "0432 Worker thread stopped.\n");
return 0;
}
@@ -1852,6 +1857,32 @@ lpfc_disable_node(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp)
lpfc_nlp_state_cleanup(vport, ndlp, ndlp->nlp_state,
NLP_STE_UNUSED_NODE);
}
+/**
+ * lpfc_initialize_node: Initialize all fields of node object.
+ * @vport: Pointer to Virtual Port object.
+ * @ndlp: Pointer to FC node object.
+ * @did: FC_ID of the node.
+ * This function is always called when node object need to
+ * be initialized. It initializes all the fields of the node
+ * object.
+ **/
+static inline void
+lpfc_initialize_node(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp,
+ uint32_t did)
+{
+ INIT_LIST_HEAD(&ndlp->els_retry_evt.evt_listp);
+ INIT_LIST_HEAD(&ndlp->dev_loss_evt.evt_listp);
+ init_timer(&ndlp->nlp_delayfunc);
+ ndlp->nlp_delayfunc.function = lpfc_els_retry_delay;
+ ndlp->nlp_delayfunc.data = (unsigned long)ndlp;
+ ndlp->nlp_DID = did;
+ ndlp->vport = vport;
+ ndlp->nlp_sid = NLP_NO_SID;
+ kref_init(&ndlp->kref);
+ NLP_INT_NODE_ACT(ndlp);
+ atomic_set(&ndlp->cmd_pending, 0);
+ ndlp->cmd_qdepth = LPFC_MAX_TGT_QDEPTH;
+}
struct lpfc_nodelist *
lpfc_enable_node(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp,
@@ -1892,17 +1923,7 @@ lpfc_enable_node(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp,
/* re-initialize ndlp except of ndlp linked list pointer */
memset((((char *)ndlp) + sizeof (struct list_head)), 0,
sizeof (struct lpfc_nodelist) - sizeof (struct list_head));
- INIT_LIST_HEAD(&ndlp->els_retry_evt.evt_listp);
- INIT_LIST_HEAD(&ndlp->dev_loss_evt.evt_listp);
- init_timer(&ndlp->nlp_delayfunc);
- ndlp->nlp_delayfunc.function = lpfc_els_retry_delay;
- ndlp->nlp_delayfunc.data = (unsigned long)ndlp;
- ndlp->nlp_DID = did;
- ndlp->vport = vport;
- ndlp->nlp_sid = NLP_NO_SID;
- /* ndlp management re-initialize */
- kref_init(&ndlp->kref);
- NLP_INT_NODE_ACT(ndlp);
+ lpfc_initialize_node(vport, ndlp, did);
spin_unlock_irqrestore(&phba->ndlp_lock, flags);
@@ -3116,19 +3137,9 @@ lpfc_nlp_init(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp,
uint32_t did)
{
memset(ndlp, 0, sizeof (struct lpfc_nodelist));
- INIT_LIST_HEAD(&ndlp->els_retry_evt.evt_listp);
- INIT_LIST_HEAD(&ndlp->dev_loss_evt.evt_listp);
- init_timer(&ndlp->nlp_delayfunc);
- ndlp->nlp_delayfunc.function = lpfc_els_retry_delay;
- ndlp->nlp_delayfunc.data = (unsigned long)ndlp;
- ndlp->nlp_DID = did;
- ndlp->vport = vport;
- ndlp->nlp_sid = NLP_NO_SID;
+
+ lpfc_initialize_node(vport, ndlp, did);
INIT_LIST_HEAD(&ndlp->nlp_listp);
- kref_init(&ndlp->kref);
- NLP_INT_NODE_ACT(ndlp);
- atomic_set(&ndlp->cmd_pending, 0);
- ndlp->cmd_qdepth = LPFC_MAX_TGT_QDEPTH;
lpfc_debugfs_disc_trc(vport, LPFC_DISC_TRC_NODE,
"node init: did:x%x",
diff --git a/drivers/scsi/lpfc/lpfc_hw.h b/drivers/scsi/lpfc/lpfc_hw.h
index 5de5dabbbee6..4168c7b498b8 100644
--- a/drivers/scsi/lpfc/lpfc_hw.h
+++ b/drivers/scsi/lpfc/lpfc_hw.h
@@ -65,6 +65,9 @@
#define SLI3_IOCB_RSP_SIZE 64
+/* vendor ID used in SCSI netlink calls */
+#define LPFC_NL_VENDOR_ID (SCSI_NL_VID_TYPE_PCI | PCI_VENDOR_ID_EMULEX)
+
/* Common Transport structures and definitions */
union CtRevisionId {
@@ -866,6 +869,12 @@ typedef struct _D_ID { /* Structure is in Big Endian format */
} un;
} D_ID;
+#define RSCN_ADDRESS_FORMAT_PORT 0x0
+#define RSCN_ADDRESS_FORMAT_AREA 0x1
+#define RSCN_ADDRESS_FORMAT_DOMAIN 0x2
+#define RSCN_ADDRESS_FORMAT_FABRIC 0x3
+#define RSCN_ADDRESS_FORMAT_MASK 0x3
+
/*
* Structure to define all ELS Payload types
*/
@@ -1535,6 +1544,108 @@ typedef struct ULP_BDL { /* SLI-2 */
uint32_t ulpIoTag32; /* Can be used for 32 bit I/O Tag */
} ULP_BDL;
+/*
+ * BlockGuard Definitions
+ */
+
+enum lpfc_protgrp_type {
+ LPFC_PG_TYPE_INVALID = 0, /* used to indicate errors */
+ LPFC_PG_TYPE_NO_DIF, /* no DIF data pointed to by prot grp */
+ LPFC_PG_TYPE_EMBD_DIF, /* DIF is embedded (inline) with data */
+ LPFC_PG_TYPE_DIF_BUF /* DIF has its own scatter/gather list */
+};
+
+/* PDE Descriptors */
+#define LPFC_PDE1_DESCRIPTOR 0x81
+#define LPFC_PDE2_DESCRIPTOR 0x82
+#define LPFC_PDE3_DESCRIPTOR 0x83
+
+/* BlockGuard Profiles */
+enum lpfc_bg_prof_codes {
+ LPFC_PROF_INVALID,
+ LPFC_PROF_A1 = 128, /* Full Protection */
+ LPFC_PROF_A2, /* Disabled Protection Checks:A2~A4 */
+ LPFC_PROF_A3,
+ LPFC_PROF_A4,
+ LPFC_PROF_B1, /* Embedded DIFs: B1~B3 */
+ LPFC_PROF_B2,
+ LPFC_PROF_B3,
+ LPFC_PROF_C1, /* Separate DIFs: C1~C3 */
+ LPFC_PROF_C2,
+ LPFC_PROF_C3,
+ LPFC_PROF_D1, /* Full Protection */
+ LPFC_PROF_D2, /* Partial Protection & Check Disabling */
+ LPFC_PROF_D3,
+ LPFC_PROF_E1, /* E1~E4:out - check-only, in - update apptag */
+ LPFC_PROF_E2,
+ LPFC_PROF_E3,
+ LPFC_PROF_E4,
+ LPFC_PROF_F1, /* Full Translation - F1 Prot Descriptor */
+ /* F1 Translation BDE */
+ LPFC_PROF_ANT1, /* TCP checksum, DIF inline with data buffers */
+ LPFC_PROF_AST1, /* TCP checksum, DIF split from data buffer */
+ LPFC_PROF_ANT2,
+ LPFC_PROF_AST2
+};
+
+/* BlockGuard error-control defines */
+#define BG_EC_STOP_ERR 0x00
+#define BG_EC_CONT_ERR 0x01
+#define BG_EC_IGN_UNINIT_STOP_ERR 0x10
+#define BG_EC_IGN_UNINIT_CONT_ERR 0x11
+
+/* PDE (Protection Descriptor Entry) word 0 bit masks and shifts */
+#define PDE_DESC_TYPE_MASK 0xff000000
+#define PDE_DESC_TYPE_SHIFT 24
+#define PDE_BG_PROFILE_MASK 0x00ff0000
+#define PDE_BG_PROFILE_SHIFT 16
+#define PDE_BLOCK_LEN_MASK 0x0000fffc
+#define PDE_BLOCK_LEN_SHIFT 2
+#define PDE_ERR_CTRL_MASK 0x00000003
+#define PDE_ERR_CTRL_SHIFT 0
+/* PDE word 1 bit masks and shifts */
+#define PDE_APPTAG_MASK_MASK 0xffff0000
+#define PDE_APPTAG_MASK_SHIFT 16
+#define PDE_APPTAG_VAL_MASK 0x0000ffff
+#define PDE_APPTAG_VAL_SHIFT 0
+struct lpfc_pde {
+ uint32_t parms; /* bitfields of descriptor, prof, len, and ec */
+ uint32_t apptag; /* bitfields of app tag maskand app tag value */
+ uint32_t reftag; /* reference tag occupying all 32 bits */
+};
+
+/* inline function to set fields in parms of PDE */
+static inline void
+lpfc_pde_set_bg_parms(struct lpfc_pde *p, u8 desc, u8 prof, u16 len, u8 ec)
+{
+ uint32_t *wp = &p->parms;
+
+ /* spec indicates that adapter appends two 0's to length field */
+ len = len >> 2;
+
+ *wp &= 0;
+ *wp |= ((desc << PDE_DESC_TYPE_SHIFT) & PDE_DESC_TYPE_MASK);
+ *wp |= ((prof << PDE_BG_PROFILE_SHIFT) & PDE_BG_PROFILE_MASK);
+ *wp |= ((len << PDE_BLOCK_LEN_SHIFT) & PDE_BLOCK_LEN_MASK);
+ *wp |= ((ec << PDE_ERR_CTRL_SHIFT) & PDE_ERR_CTRL_MASK);
+ *wp = le32_to_cpu(*wp);
+}
+
+/* inline function to set apptag and reftag fields of PDE */
+static inline void
+lpfc_pde_set_dif_parms(struct lpfc_pde *p, u16 apptagmask, u16 apptagval,
+ u32 reftag)
+{
+ uint32_t *wp = &p->apptag;
+ *wp &= 0;
+ *wp |= ((apptagmask << PDE_APPTAG_MASK_SHIFT) & PDE_APPTAG_MASK_MASK);
+ *wp |= ((apptagval << PDE_APPTAG_VAL_SHIFT) & PDE_APPTAG_VAL_MASK);
+ *wp = le32_to_cpu(*wp);
+ wp = &p->reftag;
+ *wp = le32_to_cpu(reftag);
+}
+
+
/* Structure for MB Command LOAD_SM and DOWN_LOAD */
typedef struct {
@@ -2359,6 +2470,30 @@ typedef struct {
#define DMP_RSP_OFFSET 0x14 /* word 5 contains first word of rsp */
#define DMP_RSP_SIZE 0x6C /* maximum of 27 words of rsp data */
+#define WAKE_UP_PARMS_REGION_ID 4
+#define WAKE_UP_PARMS_WORD_SIZE 15
+
+/* Option rom version structure */
+struct prog_id {
+#ifdef __BIG_ENDIAN_BITFIELD
+ uint8_t type;
+ uint8_t id;
+ uint32_t ver:4; /* Major Version */
+ uint32_t rev:4; /* Revision */
+ uint32_t lev:2; /* Level */
+ uint32_t dist:2; /* Dist Type */
+ uint32_t num:4; /* number after dist type */
+#else /* __LITTLE_ENDIAN_BITFIELD */
+ uint32_t num:4; /* number after dist type */
+ uint32_t dist:2; /* Dist Type */
+ uint32_t lev:2; /* Level */
+ uint32_t rev:4; /* Revision */
+ uint32_t ver:4; /* Major Version */
+ uint8_t id;
+ uint8_t type;
+#endif
+};
+
/* Structure for MB Command UPDATE_CFG (0x1B) */
struct update_cfg_var {
@@ -2552,11 +2687,19 @@ typedef struct {
uint32_t pcbLow; /* bit 31:0 of memory based port config block */
uint32_t pcbHigh; /* bit 63:32 of memory based port config block */
- uint32_t hbainit[6];
+ uint32_t hbainit[5];
+#ifdef __BIG_ENDIAN_BITFIELD
+ uint32_t hps : 1; /* bit 31 word9 Host Pointer in slim */
+ uint32_t rsvd : 31; /* least significant 31 bits of word 9 */
+#else /* __LITTLE_ENDIAN */
+ uint32_t rsvd : 31; /* least significant 31 bits of word 9 */
+ uint32_t hps : 1; /* bit 31 word9 Host Pointer in slim */
+#endif
#ifdef __BIG_ENDIAN_BITFIELD
- uint32_t rsvd : 24; /* Reserved */
- uint32_t cmv : 1; /* Configure Max VPIs */
+ uint32_t rsvd1 : 23; /* Reserved */
+ uint32_t cbg : 1; /* Configure BlockGuard */
+ uint32_t cmv : 1; /* Configure Max VPIs */
uint32_t ccrp : 1; /* Config Command Ring Polling */
uint32_t csah : 1; /* Configure Synchronous Abort Handling */
uint32_t chbs : 1; /* Cofigure Host Backing store */
@@ -2573,10 +2716,12 @@ typedef struct {
uint32_t csah : 1; /* Configure Synchronous Abort Handling */
uint32_t ccrp : 1; /* Config Command Ring Polling */
uint32_t cmv : 1; /* Configure Max VPIs */
- uint32_t rsvd : 24; /* Reserved */
+ uint32_t cbg : 1; /* Configure BlockGuard */
+ uint32_t rsvd1 : 23; /* Reserved */
#endif
#ifdef __BIG_ENDIAN_BITFIELD
- uint32_t rsvd2 : 24; /* Reserved */
+ uint32_t rsvd2 : 23; /* Reserved */
+ uint32_t gbg : 1; /* Grant BlockGuard */
uint32_t gmv : 1; /* Grant Max VPIs */
uint32_t gcrp : 1; /* Grant Command Ring Polling */
uint32_t gsah : 1; /* Grant Synchronous Abort Handling */
@@ -2594,7 +2739,8 @@ typedef struct {
uint32_t gsah : 1; /* Grant Synchronous Abort Handling */
uint32_t gcrp : 1; /* Grant Command Ring Polling */
uint32_t gmv : 1; /* Grant Max VPIs */
- uint32_t rsvd2 : 24; /* Reserved */
+ uint32_t gbg : 1; /* Grant BlockGuard */
+ uint32_t rsvd2 : 23; /* Reserved */
#endif
#ifdef __BIG_ENDIAN_BITFIELD
@@ -3214,6 +3360,94 @@ struct que_xri64cx_ext_fields {
struct lpfc_hbq_entry buff[5];
};
+struct sli3_bg_fields {
+ uint32_t filler[6]; /* word 8-13 in IOCB */
+ uint32_t bghm; /* word 14 - BlockGuard High Water Mark */
+/* Bitfields for bgstat (BlockGuard Status - word 15 of IOCB) */
+#define BGS_BIDIR_BG_PROF_MASK 0xff000000
+#define BGS_BIDIR_BG_PROF_SHIFT 24
+#define BGS_BIDIR_ERR_COND_FLAGS_MASK 0x003f0000
+#define BGS_BIDIR_ERR_COND_SHIFT 16
+#define BGS_BG_PROFILE_MASK 0x0000ff00
+#define BGS_BG_PROFILE_SHIFT 8
+#define BGS_INVALID_PROF_MASK 0x00000020
+#define BGS_INVALID_PROF_SHIFT 5
+#define BGS_UNINIT_DIF_BLOCK_MASK 0x00000010
+#define BGS_UNINIT_DIF_BLOCK_SHIFT 4
+#define BGS_HI_WATER_MARK_PRESENT_MASK 0x00000008
+#define BGS_HI_WATER_MARK_PRESENT_SHIFT 3
+#define BGS_REFTAG_ERR_MASK 0x00000004
+#define BGS_REFTAG_ERR_SHIFT 2
+#define BGS_APPTAG_ERR_MASK 0x00000002
+#define BGS_APPTAG_ERR_SHIFT 1
+#define BGS_GUARD_ERR_MASK 0x00000001
+#define BGS_GUARD_ERR_SHIFT 0
+ uint32_t bgstat; /* word 15 - BlockGuard Status */
+};
+
+static inline uint32_t
+lpfc_bgs_get_bidir_bg_prof(uint32_t bgstat)
+{
+ return (le32_to_cpu(bgstat) & BGS_BIDIR_BG_PROF_MASK) >>
+ BGS_BIDIR_BG_PROF_SHIFT;
+}
+
+static inline uint32_t
+lpfc_bgs_get_bidir_err_cond(uint32_t bgstat)
+{
+ return (le32_to_cpu(bgstat) & BGS_BIDIR_ERR_COND_FLAGS_MASK) >>
+ BGS_BIDIR_ERR_COND_SHIFT;
+}
+
+static inline uint32_t
+lpfc_bgs_get_bg_prof(uint32_t bgstat)
+{
+ return (le32_to_cpu(bgstat) & BGS_BG_PROFILE_MASK) >>
+ BGS_BG_PROFILE_SHIFT;
+}
+
+static inline uint32_t
+lpfc_bgs_get_invalid_prof(uint32_t bgstat)
+{
+ return (le32_to_cpu(bgstat) & BGS_INVALID_PROF_MASK) >>
+ BGS_INVALID_PROF_SHIFT;
+}
+
+static inline uint32_t
+lpfc_bgs_get_uninit_dif_block(uint32_t bgstat)
+{
+ return (le32_to_cpu(bgstat) & BGS_UNINIT_DIF_BLOCK_MASK) >>
+ BGS_UNINIT_DIF_BLOCK_SHIFT;
+}
+
+static inline uint32_t
+lpfc_bgs_get_hi_water_mark_present(uint32_t bgstat)
+{
+ return (le32_to_cpu(bgstat) & BGS_HI_WATER_MARK_PRESENT_MASK) >>
+ BGS_HI_WATER_MARK_PRESENT_SHIFT;
+}
+
+static inline uint32_t
+lpfc_bgs_get_reftag_err(uint32_t bgstat)
+{
+ return (le32_to_cpu(bgstat) & BGS_REFTAG_ERR_MASK) >>
+ BGS_REFTAG_ERR_SHIFT;
+}
+
+static inline uint32_t
+lpfc_bgs_get_apptag_err(uint32_t bgstat)
+{
+ return (le32_to_cpu(bgstat) & BGS_APPTAG_ERR_MASK) >>
+ BGS_APPTAG_ERR_SHIFT;
+}
+
+static inline uint32_t
+lpfc_bgs_get_guard_err(uint32_t bgstat)
+{
+ return (le32_to_cpu(bgstat) & BGS_GUARD_ERR_MASK) >>
+ BGS_GUARD_ERR_SHIFT;
+}
+
#define LPFC_EXT_DATA_BDE_COUNT 3
struct fcp_irw_ext {
uint32_t io_tag64_low;
@@ -3322,6 +3556,9 @@ typedef struct _IOCB { /* IOCB structure */
struct que_xri64cx_ext_fields que_xri64cx_ext_words;
struct fcp_irw_ext fcp_ext;
uint32_t sli3Words[24]; /* 96 extra bytes for SLI-3 */
+
+ /* words 8-15 for BlockGuard */
+ struct sli3_bg_fields sli3_bg;
} unsli3;
#define ulpCt_h ulpXS
diff --git a/drivers/scsi/lpfc/lpfc_init.c b/drivers/scsi/lpfc/lpfc_init.c
index 909be3301bba..4c77038c8f1c 100644
--- a/drivers/scsi/lpfc/lpfc_init.c
+++ b/drivers/scsi/lpfc/lpfc_init.c
@@ -45,6 +45,12 @@
#include "lpfc_vport.h"
#include "lpfc_version.h"
+char *_dump_buf_data;
+unsigned long _dump_buf_data_order;
+char *_dump_buf_dif;
+unsigned long _dump_buf_dif_order;
+spinlock_t _dump_buf_lock;
+
static int lpfc_parse_vpd(struct lpfc_hba *, uint8_t *, int);
static void lpfc_get_hba_model_desc(struct lpfc_hba *, uint8_t *, uint8_t *);
static int lpfc_post_rcv_buf(struct lpfc_hba *);
@@ -236,6 +242,51 @@ lpfc_config_async_cmpl(struct lpfc_hba * phba, LPFC_MBOXQ_t * pmboxq)
}
/**
+ * lpfc_dump_wakeup_param_cmpl: Completion handler for dump memory mailbox
+ * command used for getting wake up parameters.
+ * @phba: pointer to lpfc hba data structure.
+ * @pmboxq: pointer to the driver internal queue element for mailbox command.
+ *
+ * This is the completion handler for dump mailbox command for getting
+ * wake up parameters. When this command complete, the response contain
+ * Option rom version of the HBA. This function translate the version number
+ * into a human readable string and store it in OptionROMVersion.
+ **/
+static void
+lpfc_dump_wakeup_param_cmpl(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmboxq)
+{
+ struct prog_id *prg;
+ uint32_t prog_id_word;
+ char dist = ' ';
+ /* character array used for decoding dist type. */
+ char dist_char[] = "nabx";
+
+ if (pmboxq->mb.mbxStatus != MBX_SUCCESS) {
+ mempool_free(pmboxq, phba->mbox_mem_pool);
+ return;
+ }
+
+ prg = (struct prog_id *) &prog_id_word;
+
+ /* word 7 contain option rom version */
+ prog_id_word = pmboxq->mb.un.varWords[7];
+
+ /* Decode the Option rom version word to a readable string */
+ if (prg->dist < 4)
+ dist = dist_char[prg->dist];
+
+ if ((prg->dist == 3) && (prg->num == 0))
+ sprintf(phba->OptionROMVersion, "%d.%d%d",
+ prg->ver, prg->rev, prg->lev);
+ else
+ sprintf(phba->OptionROMVersion, "%d.%d%d%c%d",
+ prg->ver, prg->rev, prg->lev,
+ dist, prg->num);
+ mempool_free(pmboxq, phba->mbox_mem_pool);
+ return;
+}
+
+/**
* lpfc_config_port_post: Perform lpfc initialization after config port.
* @phba: pointer to lpfc hba data structure.
*
@@ -482,6 +533,20 @@ lpfc_config_port_post(struct lpfc_hba *phba)
rc);
mempool_free(pmb, phba->mbox_mem_pool);
}
+
+ /* Get Option rom version */
+ pmb = mempool_alloc(phba->mbox_mem_pool, GFP_KERNEL);
+ lpfc_dump_wakeup_param(phba, pmb);
+ pmb->mbox_cmpl = lpfc_dump_wakeup_param_cmpl;
+ pmb->vport = phba->pport;
+ rc = lpfc_sli_issue_mbox(phba, pmb, MBX_NOWAIT);
+
+ if ((rc != MBX_BUSY) && (rc != MBX_SUCCESS)) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_INIT, "0435 Adapter failed "
+ "to get Option ROM version status x%x\n.", rc);
+ mempool_free(pmb, phba->mbox_mem_pool);
+ }
+
return 0;
}
@@ -686,11 +751,6 @@ lpfc_hb_timeout_handler(struct lpfc_hba *phba)
return;
spin_lock_irq(&phba->pport->work_port_lock);
- /* If the timer is already canceled do nothing */
- if (!(phba->pport->work_port_events & WORKER_HB_TMO)) {
- spin_unlock_irq(&phba->pport->work_port_lock);
- return;
- }
if (time_after(phba->last_completion_time + LPFC_HB_MBOX_INTERVAL * HZ,
jiffies)) {
@@ -833,8 +893,7 @@ lpfc_handle_eratt(struct lpfc_hba *phba)
fc_host_post_vendor_event(shost, fc_get_event_number(),
sizeof(board_event),
(char *) &board_event,
- SCSI_NL_VID_TYPE_PCI
- | PCI_VENDOR_ID_EMULEX);
+ LPFC_NL_VENDOR_ID);
if (phba->work_hs & HS_FFER6) {
/* Re-establishing Link */
@@ -1984,6 +2043,7 @@ lpfc_create_port(struct lpfc_hba *phba, int instance, struct device *dev)
shost->max_lun = vport->cfg_max_luns;
shost->this_id = -1;
shost->max_cmd_len = 16;
+
/*
* Set initial can_queue value since 0 is no longer supported and
* scsi_add_host will fail. This will be adjusted later based on the
@@ -2042,8 +2102,6 @@ destroy_port(struct lpfc_vport *vport)
struct Scsi_Host *shost = lpfc_shost_from_vport(vport);
struct lpfc_hba *phba = vport->phba;
- kfree(vport->vname);
-
lpfc_debugfs_terminate(vport);
fc_remove_host(shost);
scsi_remove_host(shost);
@@ -2226,8 +2284,7 @@ lpfc_enable_msix(struct lpfc_hba *phba)
ARRAY_SIZE(phba->msix_entries));
if (rc) {
lpfc_printf_log(phba, KERN_INFO, LOG_INIT,
- "0420 Enable MSI-X failed (%d), continuing "
- "with MSI\n", rc);
+ "0420 PCI enable MSI-X failed (%d)\n", rc);
goto msi_fail_out;
} else
for (i = 0; i < LPFC_MSIX_VECTORS; i++)
@@ -2244,9 +2301,9 @@ lpfc_enable_msix(struct lpfc_hba *phba)
rc = request_irq(phba->msix_entries[0].vector, &lpfc_sp_intr_handler,
IRQF_SHARED, LPFC_SP_DRIVER_HANDLER_NAME, phba);
if (rc) {
- lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+ lpfc_printf_log(phba, KERN_WARNING, LOG_INIT,
"0421 MSI-X slow-path request_irq failed "
- "(%d), continuing with MSI\n", rc);
+ "(%d)\n", rc);
goto msi_fail_out;
}
@@ -2255,9 +2312,9 @@ lpfc_enable_msix(struct lpfc_hba *phba)
IRQF_SHARED, LPFC_FP_DRIVER_HANDLER_NAME, phba);
if (rc) {
- lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+ lpfc_printf_log(phba, KERN_WARNING, LOG_INIT,
"0429 MSI-X fast-path request_irq failed "
- "(%d), continuing with MSI\n", rc);
+ "(%d)\n", rc);
goto irq_fail_out;
}
@@ -2278,7 +2335,7 @@ lpfc_enable_msix(struct lpfc_hba *phba)
goto mbx_fail_out;
rc = lpfc_sli_issue_mbox(phba, pmb, MBX_POLL);
if (rc != MBX_SUCCESS) {
- lpfc_printf_log(phba, KERN_ERR, LOG_MBOX,
+ lpfc_printf_log(phba, KERN_WARNING, LOG_MBOX,
"0351 Config MSI mailbox command failed, "
"mbxCmd x%x, mbxStatus x%x\n",
pmb->mb.mbxCommand, pmb->mb.mbxStatus);
@@ -2327,6 +2384,195 @@ lpfc_disable_msix(struct lpfc_hba *phba)
}
/**
+ * lpfc_enable_msi: Enable MSI interrupt mode.
+ * @phba: pointer to lpfc hba data structure.
+ *
+ * This routine is invoked to enable the MSI interrupt mode. The kernel
+ * function pci_enable_msi() is called to enable the MSI vector. The
+ * device driver is responsible for calling the request_irq() to register
+ * MSI vector with a interrupt the handler, which is done in this function.
+ *
+ * Return codes
+ * 0 - sucessful
+ * other values - error
+ */
+static int
+lpfc_enable_msi(struct lpfc_hba *phba)
+{
+ int rc;
+
+ rc = pci_enable_msi(phba->pcidev);
+ if (!rc)
+ lpfc_printf_log(phba, KERN_INFO, LOG_INIT,
+ "0462 PCI enable MSI mode success.\n");
+ else {
+ lpfc_printf_log(phba, KERN_INFO, LOG_INIT,
+ "0471 PCI enable MSI mode failed (%d)\n", rc);
+ return rc;
+ }
+
+ rc = request_irq(phba->pcidev->irq, lpfc_intr_handler,
+ IRQF_SHARED, LPFC_DRIVER_NAME, phba);
+ if (rc) {
+ pci_disable_msi(phba->pcidev);
+ lpfc_printf_log(phba, KERN_WARNING, LOG_INIT,
+ "0478 MSI request_irq failed (%d)\n", rc);
+ }
+ return rc;
+}
+
+/**
+ * lpfc_disable_msi: Disable MSI interrupt mode.
+ * @phba: pointer to lpfc hba data structure.
+ *
+ * This routine is invoked to disable the MSI interrupt mode. The driver
+ * calls free_irq() on MSI vector it has done request_irq() on before
+ * calling pci_disable_msi(). Failure to do so results in a BUG_ON() and
+ * a device will be left with MSI enabled and leaks its vector.
+ */
+
+static void
+lpfc_disable_msi(struct lpfc_hba *phba)
+{
+ free_irq(phba->pcidev->irq, phba);
+ pci_disable_msi(phba->pcidev);
+ return;
+}
+
+/**
+ * lpfc_log_intr_mode: Log the active interrupt mode
+ * @phba: pointer to lpfc hba data structure.
+ * @intr_mode: active interrupt mode adopted.
+ *
+ * This routine it invoked to log the currently used active interrupt mode
+ * to the device.
+ */
+static void
+lpfc_log_intr_mode(struct lpfc_hba *phba, uint32_t intr_mode)
+{
+ switch (intr_mode) {
+ case 0:
+ lpfc_printf_log(phba, KERN_INFO, LOG_INIT,
+ "0470 Enable INTx interrupt mode.\n");
+ break;
+ case 1:
+ lpfc_printf_log(phba, KERN_INFO, LOG_INIT,
+ "0481 Enabled MSI interrupt mode.\n");
+ break;
+ case 2:
+ lpfc_printf_log(phba, KERN_INFO, LOG_INIT,
+ "0480 Enabled MSI-X interrupt mode.\n");
+ break;
+ default:
+ lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+ "0482 Illegal interrupt mode.\n");
+ break;
+ }
+ return;
+}
+
+static void
+lpfc_stop_port(struct lpfc_hba *phba)
+{
+ /* Clear all interrupt enable conditions */
+ writel(0, phba->HCregaddr);
+ readl(phba->HCregaddr); /* flush */
+ /* Clear all pending interrupts */
+ writel(0xffffffff, phba->HAregaddr);
+ readl(phba->HAregaddr); /* flush */
+
+ /* Reset some HBA SLI setup states */
+ lpfc_stop_phba_timers(phba);
+ phba->pport->work_port_events = 0;
+
+ return;
+}
+
+/**
+ * lpfc_enable_intr: Enable device interrupt.
+ * @phba: pointer to lpfc hba data structure.
+ *
+ * This routine is invoked to enable device interrupt and associate driver's
+ * interrupt handler(s) to interrupt vector(s). Depends on the interrupt
+ * mode configured to the driver, the driver will try to fallback from the
+ * configured interrupt mode to an interrupt mode which is supported by the
+ * platform, kernel, and device in the order of: MSI-X -> MSI -> IRQ.
+ *
+ * Return codes
+ * 0 - sucessful
+ * other values - error
+ **/
+static uint32_t
+lpfc_enable_intr(struct lpfc_hba *phba, uint32_t cfg_mode)
+{
+ uint32_t intr_mode = LPFC_INTR_ERROR;
+ int retval;
+
+ if (cfg_mode == 2) {
+ /* Need to issue conf_port mbox cmd before conf_msi mbox cmd */
+ retval = lpfc_sli_config_port(phba, 3);
+ if (!retval) {
+ /* Now, try to enable MSI-X interrupt mode */
+ retval = lpfc_enable_msix(phba);
+ if (!retval) {
+ /* Indicate initialization to MSI-X mode */
+ phba->intr_type = MSIX;
+ intr_mode = 2;
+ }
+ }
+ }
+
+ /* Fallback to MSI if MSI-X initialization failed */
+ if (cfg_mode >= 1 && phba->intr_type == NONE) {
+ retval = lpfc_enable_msi(phba);
+ if (!retval) {
+ /* Indicate initialization to MSI mode */
+ phba->intr_type = MSI;
+ intr_mode = 1;
+ }
+ }
+
+ /* Fallback to INTx if both MSI-X/MSI initalization failed */
+ if (phba->intr_type == NONE) {
+ retval = request_irq(phba->pcidev->irq, lpfc_intr_handler,
+ IRQF_SHARED, LPFC_DRIVER_NAME, phba);
+ if (!retval) {
+ /* Indicate initialization to INTx mode */
+ phba->intr_type = INTx;
+ intr_mode = 0;
+ }
+ }
+ return intr_mode;
+}
+
+/**
+ * lpfc_disable_intr: Disable device interrupt.
+ * @phba: pointer to lpfc hba data structure.
+ *
+ * This routine is invoked to disable device interrupt and disassociate the
+ * driver's interrupt handler(s) from interrupt vector(s). Depending on the
+ * interrupt mode, the driver will release the interrupt vector(s) for the
+ * message signaled interrupt.
+ **/
+static void
+lpfc_disable_intr(struct lpfc_hba *phba)
+{
+ /* Disable the currently initialized interrupt mode */
+ if (phba->intr_type == MSIX)
+ lpfc_disable_msix(phba);
+ else if (phba->intr_type == MSI)
+ lpfc_disable_msi(phba);
+ else if (phba->intr_type == INTx)
+ free_irq(phba->pcidev->irq, phba);
+
+ /* Reset interrupt management states */
+ phba->intr_type = NONE;
+ phba->sli.slistat.sli_intr = 0;
+
+ return;
+}
+
+/**
* lpfc_pci_probe_one: lpfc PCI probe func to register device to PCI subsystem.
* @pdev: pointer to PCI device
* @pid: pointer to PCI device identifier
@@ -2356,6 +2602,7 @@ lpfc_pci_probe_one(struct pci_dev *pdev, const struct pci_device_id *pid)
int error = -ENODEV, retval;
int i, hbq_count;
uint16_t iotag;
+ uint32_t cfg_mode, intr_mode;
int bars = pci_select_bars(pdev, IORESOURCE_MEM);
struct lpfc_adapter_event_header adapter_event;
@@ -2409,6 +2656,7 @@ lpfc_pci_probe_one(struct pci_dev *pdev, const struct pci_device_id *pid)
phba->eratt_poll.data = (unsigned long) phba;
pci_set_master(pdev);
+ pci_save_state(pdev);
pci_try_set_mwi(pdev);
if (pci_set_dma_mask(phba->pcidev, DMA_64BIT_MASK) != 0)
@@ -2557,7 +2805,6 @@ lpfc_pci_probe_one(struct pci_dev *pdev, const struct pci_device_id *pid)
lpfc_debugfs_initialize(vport);
pci_set_drvdata(pdev, shost);
- phba->intr_type = NONE;
phba->MBslimaddr = phba->slim_memmap_p;
phba->HAregaddr = phba->ctrl_regs_memmap_p + HA_REG_OFFSET;
@@ -2565,63 +2812,58 @@ lpfc_pci_probe_one(struct pci_dev *pdev, const struct pci_device_id *pid)
phba->HSregaddr = phba->ctrl_regs_memmap_p + HS_REG_OFFSET;
phba->HCregaddr = phba->ctrl_regs_memmap_p + HC_REG_OFFSET;
- /* Configure and enable interrupt */
- if (phba->cfg_use_msi == 2) {
- /* Need to issue conf_port mbox cmd before conf_msi mbox cmd */
- error = lpfc_sli_config_port(phba, 3);
- if (error)
- lpfc_printf_log(phba, KERN_INFO, LOG_INIT,
- "0427 Firmware not capable of SLI 3 mode.\n");
- else {
- lpfc_printf_log(phba, KERN_INFO, LOG_INIT,
- "0426 Firmware capable of SLI 3 mode.\n");
- /* Now, try to enable MSI-X interrupt mode */
- error = lpfc_enable_msix(phba);
- if (!error) {
- phba->intr_type = MSIX;
- lpfc_printf_log(phba, KERN_INFO, LOG_INIT,
- "0430 enable MSI-X mode.\n");
- }
- }
- }
-
- /* Fallback to MSI if MSI-X initialization failed */
- if (phba->cfg_use_msi >= 1 && phba->intr_type == NONE) {
- retval = pci_enable_msi(phba->pcidev);
- if (!retval) {
- phba->intr_type = MSI;
- lpfc_printf_log(phba, KERN_INFO, LOG_INIT,
- "0473 enable MSI mode.\n");
- } else
- lpfc_printf_log(phba, KERN_INFO, LOG_INIT,
- "0452 enable IRQ mode.\n");
- }
-
- /* MSI-X is the only case the doesn't need to call request_irq */
- if (phba->intr_type != MSIX) {
- retval = request_irq(phba->pcidev->irq, lpfc_intr_handler,
- IRQF_SHARED, LPFC_DRIVER_NAME, phba);
- if (retval) {
- lpfc_printf_log(phba, KERN_ERR, LOG_INIT, "0451 Enable "
- "interrupt handler failed\n");
- error = retval;
- goto out_disable_msi;
- } else if (phba->intr_type != MSI)
- phba->intr_type = INTx;
- }
-
+ /* Configure sysfs attributes */
if (lpfc_alloc_sysfs_attr(vport)) {
lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
"1476 Failed to allocate sysfs attr\n");
error = -ENOMEM;
- goto out_free_irq;
+ goto out_destroy_port;
}
- if (lpfc_sli_hba_setup(phba)) {
- lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
- "1477 Failed to set up hba\n");
- error = -ENODEV;
- goto out_remove_device;
+ cfg_mode = phba->cfg_use_msi;
+ while (true) {
+ /* Configure and enable interrupt */
+ intr_mode = lpfc_enable_intr(phba, cfg_mode);
+ if (intr_mode == LPFC_INTR_ERROR) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+ "0426 Failed to enable interrupt.\n");
+ goto out_free_sysfs_attr;
+ }
+ /* HBA SLI setup */
+ if (lpfc_sli_hba_setup(phba)) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+ "1477 Failed to set up hba\n");
+ error = -ENODEV;
+ goto out_remove_device;
+ }
+
+ /* Wait 50ms for the interrupts of previous mailbox commands */
+ msleep(50);
+ /* Check active interrupts received */
+ if (phba->sli.slistat.sli_intr > LPFC_MSIX_VECTORS) {
+ /* Log the current active interrupt mode */
+ phba->intr_mode = intr_mode;
+ lpfc_log_intr_mode(phba, intr_mode);
+ break;
+ } else {
+ lpfc_printf_log(phba, KERN_INFO, LOG_INIT,
+ "0451 Configure interrupt mode (%d) "
+ "failed active interrupt test.\n",
+ intr_mode);
+ if (intr_mode == 0) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+ "0479 Failed to enable "
+ "interrupt.\n");
+ error = -ENODEV;
+ goto out_remove_device;
+ }
+ /* Stop HBA SLI setups */
+ lpfc_stop_port(phba);
+ /* Disable the current interrupt mode */
+ lpfc_disable_intr(phba);
+ /* Try next level of interrupt mode */
+ cfg_mode = --intr_mode;
+ }
}
/*
@@ -2629,6 +2871,75 @@ lpfc_pci_probe_one(struct pci_dev *pdev, const struct pci_device_id *pid)
* the value of can_queue.
*/
shost->can_queue = phba->cfg_hba_queue_depth - 10;
+ if (phba->sli3_options & LPFC_SLI3_BG_ENABLED) {
+
+ if (lpfc_prot_mask && lpfc_prot_guard) {
+ lpfc_printf_log(phba, KERN_INFO, LOG_INIT,
+ "1478 Registering BlockGuard with the "
+ "SCSI layer\n");
+
+ scsi_host_set_prot(shost, lpfc_prot_mask);
+ scsi_host_set_guard(shost, lpfc_prot_guard);
+ }
+ }
+
+ if (!_dump_buf_data) {
+ int pagecnt = 10;
+ while (pagecnt) {
+ spin_lock_init(&_dump_buf_lock);
+ _dump_buf_data =
+ (char *) __get_free_pages(GFP_KERNEL, pagecnt);
+ if (_dump_buf_data) {
+ printk(KERN_ERR "BLKGRD allocated %d pages for "
+ "_dump_buf_data at 0x%p\n",
+ (1 << pagecnt), _dump_buf_data);
+ _dump_buf_data_order = pagecnt;
+ memset(_dump_buf_data, 0, ((1 << PAGE_SHIFT)
+ << pagecnt));
+ break;
+ } else {
+ --pagecnt;
+ }
+
+ }
+
+ if (!_dump_buf_data_order)
+ printk(KERN_ERR "BLKGRD ERROR unable to allocate "
+ "memory for hexdump\n");
+
+ } else {
+ printk(KERN_ERR "BLKGRD already allocated _dump_buf_data=0x%p"
+ "\n", _dump_buf_data);
+ }
+
+
+ if (!_dump_buf_dif) {
+ int pagecnt = 10;
+ while (pagecnt) {
+ _dump_buf_dif =
+ (char *) __get_free_pages(GFP_KERNEL, pagecnt);
+ if (_dump_buf_dif) {
+ printk(KERN_ERR "BLKGRD allocated %d pages for "
+ "_dump_buf_dif at 0x%p\n",
+ (1 << pagecnt), _dump_buf_dif);
+ _dump_buf_dif_order = pagecnt;
+ memset(_dump_buf_dif, 0, ((1 << PAGE_SHIFT)
+ << pagecnt));
+ break;
+ } else {
+ --pagecnt;
+ }
+
+ }
+
+ if (!_dump_buf_dif_order)
+ printk(KERN_ERR "BLKGRD ERROR unable to allocate "
+ "memory for hexdump\n");
+
+ } else {
+ printk(KERN_ERR "BLKGRD already allocated _dump_buf_dif=0x%p\n",
+ _dump_buf_dif);
+ }
lpfc_host_attrib_init(shost);
@@ -2646,29 +2957,22 @@ lpfc_pci_probe_one(struct pci_dev *pdev, const struct pci_device_id *pid)
fc_host_post_vendor_event(shost, fc_get_event_number(),
sizeof(adapter_event),
(char *) &adapter_event,
- SCSI_NL_VID_TYPE_PCI | PCI_VENDOR_ID_EMULEX);
-
- scsi_scan_host(shost);
+ LPFC_NL_VENDOR_ID);
return 0;
out_remove_device:
- lpfc_free_sysfs_attr(vport);
spin_lock_irq(shost->host_lock);
vport->load_flag |= FC_UNLOADING;
spin_unlock_irq(shost->host_lock);
-out_free_irq:
lpfc_stop_phba_timers(phba);
phba->pport->work_port_events = 0;
-
- if (phba->intr_type == MSIX)
- lpfc_disable_msix(phba);
- else
- free_irq(phba->pcidev->irq, phba);
-
-out_disable_msi:
- if (phba->intr_type == MSI)
- pci_disable_msi(phba->pcidev);
+ lpfc_disable_intr(phba);
+ lpfc_sli_hba_down(phba);
+ lpfc_sli_brdrestart(phba);
+out_free_sysfs_attr:
+ lpfc_free_sysfs_attr(vport);
+out_destroy_port:
destroy_port(vport);
out_kthread_stop:
kthread_stop(phba->worker_thread);
@@ -2709,7 +3013,7 @@ out:
* @pdev: pointer to PCI device
*
* This routine is to be registered to the kernel's PCI subsystem. When an
- * Emulex HBA is removed from PCI bus. It perform all the necessary cleanup
+ * Emulex HBA is removed from PCI bus, it performs all the necessary cleanup
* for the HBA device to be removed from the PCI subsystem properly.
**/
static void __devexit
@@ -2717,18 +3021,27 @@ lpfc_pci_remove_one(struct pci_dev *pdev)
{
struct Scsi_Host *shost = pci_get_drvdata(pdev);
struct lpfc_vport *vport = (struct lpfc_vport *) shost->hostdata;
+ struct lpfc_vport **vports;
struct lpfc_hba *phba = vport->phba;
+ int i;
int bars = pci_select_bars(pdev, IORESOURCE_MEM);
spin_lock_irq(&phba->hbalock);
vport->load_flag |= FC_UNLOADING;
spin_unlock_irq(&phba->hbalock);
- kfree(vport->vname);
lpfc_free_sysfs_attr(vport);
kthread_stop(phba->worker_thread);
+ /* Release all the vports against this physical port */
+ vports = lpfc_create_vport_work_array(phba);
+ if (vports != NULL)
+ for (i = 1; i <= phba->max_vpi && vports[i] != NULL; i++)
+ fc_vport_terminate(vports[i]->fc_vport);
+ lpfc_destroy_vport_work_array(phba, vports);
+
+ /* Remove FC host and then SCSI host with the physical port */
fc_remove_host(shost);
scsi_remove_host(shost);
lpfc_cleanup(vport);
@@ -2748,13 +3061,8 @@ lpfc_pci_remove_one(struct pci_dev *pdev)
lpfc_debugfs_terminate(vport);
- if (phba->intr_type == MSIX)
- lpfc_disable_msix(phba);
- else {
- free_irq(phba->pcidev->irq, phba);
- if (phba->intr_type == MSI)
- pci_disable_msi(phba->pcidev);
- }
+ /* Disable interrupt */
+ lpfc_disable_intr(phba);
pci_set_drvdata(pdev, NULL);
scsi_host_put(shost);
@@ -2786,6 +3094,115 @@ lpfc_pci_remove_one(struct pci_dev *pdev)
}
/**
+ * lpfc_pci_suspend_one: lpfc PCI func to suspend device for power management.
+ * @pdev: pointer to PCI device
+ * @msg: power management message
+ *
+ * This routine is to be registered to the kernel's PCI subsystem to support
+ * system Power Management (PM). When PM invokes this method, it quiesces the
+ * device by stopping the driver's worker thread for the device, turning off
+ * device's interrupt and DMA, and bring the device offline. Note that as the
+ * driver implements the minimum PM requirements to a power-aware driver's PM
+ * support for suspend/resume -- all the possible PM messages (SUSPEND,
+ * HIBERNATE, FREEZE) to the suspend() method call will be treated as SUSPEND
+ * and the driver will fully reinitialize its device during resume() method
+ * call, the driver will set device to PCI_D3hot state in PCI config space
+ * instead of setting it according to the @msg provided by the PM.
+ *
+ * Return code
+ * 0 - driver suspended the device
+ * Error otherwise
+ **/
+static int
+lpfc_pci_suspend_one(struct pci_dev *pdev, pm_message_t msg)
+{
+ struct Scsi_Host *shost = pci_get_drvdata(pdev);
+ struct lpfc_hba *phba = ((struct lpfc_vport *)shost->hostdata)->phba;
+
+ lpfc_printf_log(phba, KERN_INFO, LOG_INIT,
+ "0473 PCI device Power Management suspend.\n");
+
+ /* Bring down the device */
+ lpfc_offline_prep(phba);
+ lpfc_offline(phba);
+ kthread_stop(phba->worker_thread);
+
+ /* Disable interrupt from device */
+ lpfc_disable_intr(phba);
+
+ /* Save device state to PCI config space */
+ pci_save_state(pdev);
+ pci_set_power_state(pdev, PCI_D3hot);
+
+ return 0;
+}
+
+/**
+ * lpfc_pci_resume_one: lpfc PCI func to resume device for power management.
+ * @pdev: pointer to PCI device
+ *
+ * This routine is to be registered to the kernel's PCI subsystem to support
+ * system Power Management (PM). When PM invokes this method, it restores
+ * the device's PCI config space state and fully reinitializes the device
+ * and brings it online. Note that as the driver implements the minimum PM
+ * requirements to a power-aware driver's PM for suspend/resume -- all
+ * the possible PM messages (SUSPEND, HIBERNATE, FREEZE) to the suspend()
+ * method call will be treated as SUSPEND and the driver will fully
+ * reinitialize its device during resume() method call, the device will be
+ * set to PCI_D0 directly in PCI config space before restoring the state.
+ *
+ * Return code
+ * 0 - driver suspended the device
+ * Error otherwise
+ **/
+static int
+lpfc_pci_resume_one(struct pci_dev *pdev)
+{
+ struct Scsi_Host *shost = pci_get_drvdata(pdev);
+ struct lpfc_hba *phba = ((struct lpfc_vport *)shost->hostdata)->phba;
+ uint32_t intr_mode;
+ int error;
+
+ lpfc_printf_log(phba, KERN_INFO, LOG_INIT,
+ "0452 PCI device Power Management resume.\n");
+
+ /* 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);
+
+ /* Startup the kernel thread for this host adapter. */
+ phba->worker_thread = kthread_run(lpfc_do_work, phba,
+ "lpfc_worker_%d", phba->brd_no);
+ if (IS_ERR(phba->worker_thread)) {
+ error = PTR_ERR(phba->worker_thread);
+ lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+ "0434 PM resume failed to start worker "
+ "thread: error=x%x.\n", error);
+ return error;
+ }
+
+ /* Configure and enable interrupt */
+ intr_mode = lpfc_enable_intr(phba, phba->intr_mode);
+ if (intr_mode == LPFC_INTR_ERROR) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+ "0430 PM resume Failed to enable interrupt\n");
+ return -EIO;
+ } else
+ phba->intr_mode = intr_mode;
+
+ /* Restart HBA and bring it online */
+ lpfc_sli_brdrestart(phba);
+ lpfc_online(phba);
+
+ /* Log the current active interrupt mode */
+ lpfc_log_intr_mode(phba, phba->intr_mode);
+
+ return 0;
+}
+
+/**
* lpfc_io_error_detected: Driver method for handling PCI I/O error detected.
* @pdev: pointer to PCI device.
* @state: the current PCI connection state.
@@ -2828,13 +3245,8 @@ static pci_ers_result_t lpfc_io_error_detected(struct pci_dev *pdev,
pring = &psli->ring[psli->fcp_ring];
lpfc_sli_abort_iocb_ring(phba, pring);
- if (phba->intr_type == MSIX)
- lpfc_disable_msix(phba);
- else {
- free_irq(phba->pcidev->irq, phba);
- if (phba->intr_type == MSI)
- pci_disable_msi(phba->pcidev);
- }
+ /* Disable interrupt */
+ lpfc_disable_intr(phba);
/* Request a slot reset. */
return PCI_ERS_RESULT_NEED_RESET;
@@ -2862,7 +3274,7 @@ static pci_ers_result_t lpfc_io_slot_reset(struct pci_dev *pdev)
struct Scsi_Host *shost = pci_get_drvdata(pdev);
struct lpfc_hba *phba = ((struct lpfc_vport *)shost->hostdata)->phba;
struct lpfc_sli *psli = &phba->sli;
- int error, retval;
+ uint32_t intr_mode;
dev_printk(KERN_INFO, &pdev->dev, "recovering from a slot reset.\n");
if (pci_enable_device_mem(pdev)) {
@@ -2871,61 +3283,31 @@ static pci_ers_result_t lpfc_io_slot_reset(struct pci_dev *pdev)
return PCI_ERS_RESULT_DISCONNECT;
}
- pci_set_master(pdev);
+ pci_restore_state(pdev);
+ if (pdev->is_busmaster)
+ pci_set_master(pdev);
spin_lock_irq(&phba->hbalock);
psli->sli_flag &= ~LPFC_SLI2_ACTIVE;
spin_unlock_irq(&phba->hbalock);
- /* Enable configured interrupt method */
- phba->intr_type = NONE;
- if (phba->cfg_use_msi == 2) {
- /* Need to issue conf_port mbox cmd before conf_msi mbox cmd */
- error = lpfc_sli_config_port(phba, 3);
- if (error)
- lpfc_printf_log(phba, KERN_INFO, LOG_INIT,
- "0478 Firmware not capable of SLI 3 mode.\n");
- else {
- lpfc_printf_log(phba, KERN_INFO, LOG_INIT,
- "0479 Firmware capable of SLI 3 mode.\n");
- /* Now, try to enable MSI-X interrupt mode */
- error = lpfc_enable_msix(phba);
- if (!error) {
- phba->intr_type = MSIX;
- lpfc_printf_log(phba, KERN_INFO, LOG_INIT,
- "0480 enable MSI-X mode.\n");
- }
- }
- }
-
- /* Fallback to MSI if MSI-X initialization failed */
- if (phba->cfg_use_msi >= 1 && phba->intr_type == NONE) {
- retval = pci_enable_msi(phba->pcidev);
- if (!retval) {
- phba->intr_type = MSI;
- lpfc_printf_log(phba, KERN_INFO, LOG_INIT,
- "0481 enable MSI mode.\n");
- } else
- lpfc_printf_log(phba, KERN_INFO, LOG_INIT,
- "0470 enable IRQ mode.\n");
- }
-
- /* MSI-X is the only case the doesn't need to call request_irq */
- if (phba->intr_type != MSIX) {
- retval = request_irq(phba->pcidev->irq, lpfc_intr_handler,
- IRQF_SHARED, LPFC_DRIVER_NAME, phba);
- if (retval) {
- lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
- "0471 Enable interrupt handler "
- "failed\n");
- } else if (phba->intr_type != MSI)
- phba->intr_type = INTx;
- }
+ /* Configure and enable interrupt */
+ intr_mode = lpfc_enable_intr(phba, phba->intr_mode);
+ if (intr_mode == LPFC_INTR_ERROR) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+ "0427 Cannot re-enable interrupt after "
+ "slot reset.\n");
+ return PCI_ERS_RESULT_DISCONNECT;
+ } else
+ phba->intr_mode = intr_mode;
/* Take device offline; this will perform cleanup */
lpfc_offline(phba);
lpfc_sli_brdrestart(phba);
+ /* Log the current active interrupt mode */
+ lpfc_log_intr_mode(phba, phba->intr_mode);
+
return PCI_ERS_RESULT_RECOVERED;
}
@@ -3037,6 +3419,8 @@ static struct pci_driver lpfc_driver = {
.id_table = lpfc_id_table,
.probe = lpfc_pci_probe_one,
.remove = __devexit_p(lpfc_pci_remove_one),
+ .suspend = lpfc_pci_suspend_one,
+ .resume = lpfc_pci_resume_one,
.err_handler = &lpfc_err_handler,
};
@@ -3100,6 +3484,19 @@ lpfc_exit(void)
fc_release_transport(lpfc_transport_template);
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",
+ (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",
+ (1L << _dump_buf_dif_order), _dump_buf_dif);
+ free_pages((unsigned long)_dump_buf_dif, _dump_buf_dif_order);
+ }
}
module_init(lpfc_init);
diff --git a/drivers/scsi/lpfc/lpfc_logmsg.h b/drivers/scsi/lpfc/lpfc_logmsg.h
index 39fd2b843bec..a85b7c196bbc 100644
--- a/drivers/scsi/lpfc/lpfc_logmsg.h
+++ b/drivers/scsi/lpfc/lpfc_logmsg.h
@@ -27,6 +27,7 @@
#define LOG_FCP 0x40 /* FCP traffic history */
#define LOG_NODE 0x80 /* Node table events */
#define LOG_TEMP 0x100 /* Temperature sensor events */
+#define LOG_BG 0x200 /* BlockBuard events */
#define LOG_MISC 0x400 /* Miscellaneous events */
#define LOG_SLI 0x800 /* SLI events */
#define LOG_FCP_ERROR 0x1000 /* log errors, not underruns */
diff --git a/drivers/scsi/lpfc/lpfc_mbox.c b/drivers/scsi/lpfc/lpfc_mbox.c
index 7465fe746fe9..34eeb086a667 100644
--- a/drivers/scsi/lpfc/lpfc_mbox.c
+++ b/drivers/scsi/lpfc/lpfc_mbox.c
@@ -77,6 +77,38 @@ lpfc_dump_mem(struct lpfc_hba * phba, LPFC_MBOXQ_t * pmb, uint16_t offset)
}
/**
+ * lpfc_dump_mem: Prepare a mailbox command for retrieving wakeup params.
+ * @phba: pointer to lpfc hba data structure.
+ * @pmb: pointer to the driver internal queue element for mailbox command.
+ * This function create a dump memory mailbox command to dump wake up
+ * parameters.
+ */
+void
+lpfc_dump_wakeup_param(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmb)
+{
+ MAILBOX_t *mb;
+ void *ctx;
+
+ mb = &pmb->mb;
+ /* Save context so that we can restore after memset */
+ ctx = pmb->context2;
+
+ /* Setup to dump VPD region */
+ memset(pmb, 0, sizeof(LPFC_MBOXQ_t));
+ mb->mbxCommand = MBX_DUMP_MEMORY;
+ mb->mbxOwner = OWN_HOST;
+ mb->un.varDmp.cv = 1;
+ mb->un.varDmp.type = DMP_NV_PARAMS;
+ mb->un.varDmp.entry_index = 0;
+ mb->un.varDmp.region_id = WAKE_UP_PARMS_REGION_ID;
+ mb->un.varDmp.word_cnt = WAKE_UP_PARMS_WORD_SIZE;
+ mb->un.varDmp.co = 0;
+ mb->un.varDmp.resp_offset = 0;
+ pmb->context2 = ctx;
+ return;
+}
+
+/**
* lpfc_read_nv: Prepare a mailbox command for reading HBA's NVRAM param.
* @phba: pointer to lpfc hba data structure.
* @pmb: pointer to the driver internal queue element for mailbox command.
@@ -1061,9 +1093,14 @@ lpfc_config_port(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmb)
mb->un.varCfgPort.pcbLow = putPaddrLow(pdma_addr);
mb->un.varCfgPort.pcbHigh = putPaddrHigh(pdma_addr);
+ /* Always Host Group Pointer is in SLIM */
+ mb->un.varCfgPort.hps = 1;
+
/* If HBA supports SLI=3 ask for it */
if (phba->sli_rev == 3 && phba->vpd.sli3Feat.cerbm) {
+ if (phba->cfg_enable_bg)
+ mb->un.varCfgPort.cbg = 1; /* configure BlockGuard */
mb->un.varCfgPort.cerbm = 1; /* Request HBQs */
mb->un.varCfgPort.ccrp = 1; /* Command Ring Polling */
mb->un.varCfgPort.cinb = 1; /* Interrupt Notification Block */
@@ -1163,16 +1200,11 @@ lpfc_config_port(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmb)
sizeof(*phba->host_gp));
}
- /* Setup Port Group ring pointer */
- if (phba->sli3_options & LPFC_SLI3_INB_ENABLED) {
- pgp_offset = offsetof(struct lpfc_sli2_slim,
- mbx.us.s3_inb_pgp.port);
- phba->hbq_get = phba->mbox->us.s3_inb_pgp.hbq_get;
- } else if (phba->sli_rev == 3) {
+ /* Setup Port Group offset */
+ if (phba->sli_rev == 3)
pgp_offset = offsetof(struct lpfc_sli2_slim,
mbx.us.s3_pgp.port);
- phba->hbq_get = phba->mbox->us.s3_pgp.hbq_get;
- } else
+ else
pgp_offset = offsetof(struct lpfc_sli2_slim, mbx.us.s2.port);
pdma_addr = phba->slim2p.phys + pgp_offset;
phba->pcb->pgpAddrHigh = putPaddrHigh(pdma_addr);
@@ -1285,10 +1317,12 @@ lpfc_mbox_get(struct lpfc_hba * phba)
void
lpfc_mbox_cmpl_put(struct lpfc_hba * phba, LPFC_MBOXQ_t * mbq)
{
+ unsigned long iflag;
+
/* This function expects to be called from interrupt context */
- spin_lock(&phba->hbalock);
+ spin_lock_irqsave(&phba->hbalock, iflag);
list_add_tail(&mbq->list, &phba->sli.mboxq_cmpl);
- spin_unlock(&phba->hbalock);
+ spin_unlock_irqrestore(&phba->hbalock, iflag);
return;
}
diff --git a/drivers/scsi/lpfc/lpfc_nl.h b/drivers/scsi/lpfc/lpfc_nl.h
index 1accb5a9f4e6..27d1a88a98fe 100644
--- a/drivers/scsi/lpfc/lpfc_nl.h
+++ b/drivers/scsi/lpfc/lpfc_nl.h
@@ -22,18 +22,20 @@
#define FC_REG_LINK_EVENT 0x0001 /* link up / down events */
#define FC_REG_RSCN_EVENT 0x0002 /* RSCN events */
#define FC_REG_CT_EVENT 0x0004 /* CT request events */
-#define FC_REG_DUMP_EVENT 0x0008 /* Dump events */
-#define FC_REG_TEMPERATURE_EVENT 0x0010 /* temperature events */
-#define FC_REG_ELS_EVENT 0x0020 /* lpfc els events */
-#define FC_REG_FABRIC_EVENT 0x0040 /* lpfc fabric events */
-#define FC_REG_SCSI_EVENT 0x0080 /* lpfc scsi events */
-#define FC_REG_BOARD_EVENT 0x0100 /* lpfc board events */
-#define FC_REG_ADAPTER_EVENT 0x0200 /* lpfc adapter events */
+#define FC_REG_DUMP_EVENT 0x0010 /* Dump events */
+#define FC_REG_TEMPERATURE_EVENT 0x0020 /* temperature events */
+#define FC_REG_VPORTRSCN_EVENT 0x0040 /* Vport RSCN events */
+#define FC_REG_ELS_EVENT 0x0080 /* lpfc els events */
+#define FC_REG_FABRIC_EVENT 0x0100 /* lpfc fabric events */
+#define FC_REG_SCSI_EVENT 0x0200 /* lpfc scsi events */
+#define FC_REG_BOARD_EVENT 0x0400 /* lpfc board events */
+#define FC_REG_ADAPTER_EVENT 0x0800 /* lpfc adapter events */
#define FC_REG_EVENT_MASK (FC_REG_LINK_EVENT | \
FC_REG_RSCN_EVENT | \
FC_REG_CT_EVENT | \
FC_REG_DUMP_EVENT | \
FC_REG_TEMPERATURE_EVENT | \
+ FC_REG_VPORTRSCN_EVENT | \
FC_REG_ELS_EVENT | \
FC_REG_FABRIC_EVENT | \
FC_REG_SCSI_EVENT | \
@@ -52,6 +54,13 @@
* The payload sent via the fc transport is one-way driver->application.
*/
+/* RSCN event header */
+struct lpfc_rscn_event_header {
+ uint32_t event_type;
+ uint32_t payload_length; /* RSCN data length in bytes */
+ uint32_t rscn_payload[];
+};
+
/* els event header */
struct lpfc_els_event_header {
uint32_t event_type;
@@ -65,6 +74,7 @@ struct lpfc_els_event_header {
#define LPFC_EVENT_PRLO_RCV 0x02
#define LPFC_EVENT_ADISC_RCV 0x04
#define LPFC_EVENT_LSRJT_RCV 0x08
+#define LPFC_EVENT_LOGO_RCV 0x10
/* special els lsrjt event */
struct lpfc_lsrjt_event {
@@ -74,6 +84,11 @@ struct lpfc_lsrjt_event {
uint32_t explanation;
};
+/* special els logo event */
+struct lpfc_logo_event {
+ struct lpfc_els_event_header header;
+ uint8_t logo_wwpn[8];
+};
/* fabric event header */
struct lpfc_fabric_event_header {
@@ -125,6 +140,7 @@ struct lpfc_scsi_varqueuedepth_event {
/* special case scsi check condition event */
struct lpfc_scsi_check_condition_event {
struct lpfc_scsi_event_header scsi_event;
+ uint8_t opcode;
uint8_t sense_key;
uint8_t asc;
uint8_t ascq;
diff --git a/drivers/scsi/lpfc/lpfc_nportdisc.c b/drivers/scsi/lpfc/lpfc_nportdisc.c
index 0c25d97acb42..8f548adae9cc 100644
--- a/drivers/scsi/lpfc/lpfc_nportdisc.c
+++ b/drivers/scsi/lpfc/lpfc_nportdisc.c
@@ -1929,10 +1929,10 @@ lpfc_device_recov_npr_node(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp,
if (vport->fc_flag & FC_RSCN_DEFERRED)
return ndlp->nlp_state;
+ lpfc_cancel_retry_delay_tmo(vport, ndlp);
spin_lock_irq(shost->host_lock);
ndlp->nlp_flag &= ~(NLP_NODEV_REMOVE | NLP_NPR_2B_DISC);
spin_unlock_irq(shost->host_lock);
- lpfc_cancel_retry_delay_tmo(vport, ndlp);
return ndlp->nlp_state;
}
diff --git a/drivers/scsi/lpfc/lpfc_scsi.c b/drivers/scsi/lpfc/lpfc_scsi.c
index bd1867411821..b103b6ed4970 100644
--- a/drivers/scsi/lpfc/lpfc_scsi.c
+++ b/drivers/scsi/lpfc/lpfc_scsi.c
@@ -18,13 +18,14 @@
* more details, a copy of which can be found in the file COPYING *
* included with this package. *
*******************************************************************/
-
#include <linux/pci.h>
#include <linux/interrupt.h>
#include <linux/delay.h>
+#include <asm/unaligned.h>
#include <scsi/scsi.h>
#include <scsi/scsi_device.h>
+#include <scsi/scsi_eh.h>
#include <scsi/scsi_host.h>
#include <scsi/scsi_tcq.h>
#include <scsi/scsi_transport_fc.h>
@@ -43,6 +44,73 @@
#define LPFC_RESET_WAIT 2
#define LPFC_ABORT_WAIT 2
+int _dump_buf_done;
+
+static char *dif_op_str[] = {
+ "SCSI_PROT_NORMAL",
+ "SCSI_PROT_READ_INSERT",
+ "SCSI_PROT_WRITE_STRIP",
+ "SCSI_PROT_READ_STRIP",
+ "SCSI_PROT_WRITE_INSERT",
+ "SCSI_PROT_READ_PASS",
+ "SCSI_PROT_WRITE_PASS",
+ "SCSI_PROT_READ_CONVERT",
+ "SCSI_PROT_WRITE_CONVERT"
+};
+
+static void
+lpfc_debug_save_data(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",
+ __func__);
+ return;
+ }
+
+
+ if (!sgde) {
+ printk(KERN_ERR "BLKGRD ERROR: data scatterlist is null\n");
+ return;
+ }
+
+ dst = (void *) _dump_buf_data;
+ while (sgde) {
+ src = sg_virt(sgde);
+ memcpy(dst, src, sgde->length);
+ dst += sgde->length;
+ sgde = sg_next(sgde);
+ }
+}
+
+static void
+lpfc_debug_save_dif(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",
+ __func__);
+ return;
+ }
+
+ if (!sgde) {
+ printk(KERN_ERR "BLKGRD ERROR: prot scatterlist is null\n");
+ return;
+ }
+
+ dst = _dump_buf_dif;
+ while (sgde) {
+ src = sg_virt(sgde);
+ memcpy(dst, src, sgde->length);
+ dst += sgde->length;
+ sgde = sg_next(sgde);
+ }
+}
+
/**
* lpfc_update_stats: Update statistical data for the command completion.
* @phba: Pointer to HBA object.
@@ -66,6 +134,8 @@ lpfc_update_stats(struct lpfc_hba *phba, struct lpfc_scsi_buf *lpfc_cmd)
if (cmd->result)
return;
+ latency = jiffies_to_msecs((long)jiffies - (long)lpfc_cmd->start_time);
+
spin_lock_irqsave(shost->host_lock, flags);
if (!vport->stat_data_enabled ||
vport->stat_data_blocked ||
@@ -74,13 +144,15 @@ lpfc_update_stats(struct lpfc_hba *phba, struct lpfc_scsi_buf *lpfc_cmd)
spin_unlock_irqrestore(shost->host_lock, flags);
return;
}
- latency = jiffies_to_msecs(jiffies - lpfc_cmd->start_time);
if (phba->bucket_type == LPFC_LINEAR_BUCKET) {
i = (latency + phba->bucket_step - 1 - phba->bucket_base)/
phba->bucket_step;
- if (i >= LPFC_MAX_BUCKET_COUNT)
- i = LPFC_MAX_BUCKET_COUNT;
+ /* check array subscript bounds */
+ if (i < 0)
+ i = 0;
+ else if (i >= LPFC_MAX_BUCKET_COUNT)
+ i = LPFC_MAX_BUCKET_COUNT - 1;
} else {
for (i = 0; i < LPFC_MAX_BUCKET_COUNT-1; i++)
if (latency <= (phba->bucket_base +
@@ -92,7 +164,6 @@ lpfc_update_stats(struct lpfc_hba *phba, struct lpfc_scsi_buf *lpfc_cmd)
spin_unlock_irqrestore(shost->host_lock, flags);
}
-
/**
* lpfc_send_sdev_queuedepth_change_event: Posts a queuedepth change
* event.
@@ -148,12 +219,19 @@ lpfc_send_sdev_queuedepth_change_event(struct lpfc_hba *phba,
return;
}
-/*
- * This function is called with no lock held when there is a resource
- * error in driver or in firmware.
- */
+/**
+ * lpfc_rampdown_queue_depth: Post RAMP_DOWN_QUEUE event to worker thread.
+ * @phba: The Hba for which this call is being executed.
+ *
+ * This routine is called when there is resource error in driver or firmware.
+ * This routine posts WORKER_RAMP_DOWN_QUEUE event for @phba. This routine
+ * posts at most 1 event each second. This routine wakes up worker thread of
+ * @phba to process WORKER_RAM_DOWN_EVENT event.
+ *
+ * This routine should be called with no lock held.
+ **/
void
-lpfc_adjust_queue_depth(struct lpfc_hba *phba)
+lpfc_rampdown_queue_depth(struct lpfc_hba *phba)
{
unsigned long flags;
uint32_t evt_posted;
@@ -182,10 +260,17 @@ lpfc_adjust_queue_depth(struct lpfc_hba *phba)
return;
}
-/*
- * This function is called with no lock held when there is a successful
- * SCSI command completion.
- */
+/**
+ * lpfc_rampup_queue_depth: Post RAMP_UP_QUEUE event for worker thread.
+ * @phba: The Hba for which this call is being executed.
+ *
+ * This routine post WORKER_RAMP_UP_QUEUE event for @phba vport. This routine
+ * post at most 1 event every 5 minute after last_ramp_up_time or
+ * last_rsrc_error_time. This routine wakes up worker thread of @phba
+ * to process WORKER_RAM_DOWN_EVENT event.
+ *
+ * This routine should be called with no lock held.
+ **/
static inline void
lpfc_rampup_queue_depth(struct lpfc_vport *vport,
struct scsi_device *sdev)
@@ -217,6 +302,14 @@ lpfc_rampup_queue_depth(struct lpfc_vport *vport,
return;
}
+/**
+ * lpfc_ramp_down_queue_handler: WORKER_RAMP_DOWN_QUEUE event handler.
+ * @phba: The Hba for which this call is being executed.
+ *
+ * This routine is called to process WORKER_RAMP_DOWN_QUEUE event for worker
+ * thread.This routine reduces queue depth for all scsi device on each vport
+ * associated with @phba.
+ **/
void
lpfc_ramp_down_queue_handler(struct lpfc_hba *phba)
{
@@ -267,6 +360,15 @@ lpfc_ramp_down_queue_handler(struct lpfc_hba *phba)
atomic_set(&phba->num_cmd_success, 0);
}
+/**
+ * lpfc_ramp_up_queue_handler: WORKER_RAMP_UP_QUEUE event handler.
+ * @phba: The Hba for which this call is being executed.
+ *
+ * This routine is called to process WORKER_RAMP_UP_QUEUE event for worker
+ * thread.This routine increases queue depth for all scsi device on each vport
+ * associated with @phba by 1. This routine also sets @phba num_rsrc_err and
+ * num_cmd_success to zero.
+ **/
void
lpfc_ramp_up_queue_handler(struct lpfc_hba *phba)
{
@@ -336,14 +438,21 @@ lpfc_scsi_dev_block(struct lpfc_hba *phba)
lpfc_destroy_vport_work_array(phba, vports);
}
-/*
+/**
+ * lpfc_new_scsi_buf: Scsi buffer allocator.
+ * @vport: The virtual port for which this call being executed.
+ *
* This routine allocates a scsi buffer, which contains all the necessary
* information needed to initiate a SCSI I/O. The non-DMAable buffer region
* contains information to build the IOCB. The DMAable region contains
- * memory for the FCP CMND, FCP RSP, and the inital BPL. In addition to
- * allocating memeory, the FCP CMND and FCP RSP BDEs are setup in the BPL
+ * memory for the FCP CMND, FCP RSP, and the initial BPL. In addition to
+ * allocating memory, the FCP CMND and FCP RSP BDEs are setup in the BPL
* and the BPL BDE is setup in the IOCB.
- */
+ *
+ * Return codes:
+ * NULL - Error
+ * Pointer to lpfc_scsi_buf data structure - Success
+ **/
static struct lpfc_scsi_buf *
lpfc_new_scsi_buf(struct lpfc_vport *vport)
{
@@ -407,14 +516,14 @@ lpfc_new_scsi_buf(struct lpfc_vport *vport)
bpl[0].addrLow = le32_to_cpu(putPaddrLow(pdma_phys_fcp_cmd));
bpl[0].tus.f.bdeSize = sizeof(struct fcp_cmnd);
bpl[0].tus.f.bdeFlags = BUFF_TYPE_BDE_64;
- bpl[0].tus.w = le32_to_cpu(bpl->tus.w);
+ bpl[0].tus.w = le32_to_cpu(bpl[0].tus.w);
/* Setup the physical region for the FCP RSP */
bpl[1].addrHigh = le32_to_cpu(putPaddrHigh(pdma_phys_fcp_rsp));
bpl[1].addrLow = le32_to_cpu(putPaddrLow(pdma_phys_fcp_rsp));
bpl[1].tus.f.bdeSize = sizeof(struct fcp_rsp);
bpl[1].tus.f.bdeFlags = BUFF_TYPE_BDE_64;
- bpl[1].tus.w = le32_to_cpu(bpl->tus.w);
+ bpl[1].tus.w = le32_to_cpu(bpl[1].tus.w);
/*
* Since the IOCB for the FCP I/O is built into this lpfc_scsi_buf,
@@ -422,7 +531,8 @@ lpfc_new_scsi_buf(struct lpfc_vport *vport)
*/
iocb = &psb->cur_iocbq.iocb;
iocb->un.fcpi64.bdl.ulpIoTag32 = 0;
- if (phba->sli_rev == 3) {
+ if ((phba->sli_rev == 3) &&
+ !(phba->sli3_options & LPFC_SLI3_BG_ENABLED)) {
/* fill in immediate fcp command BDE */
iocb->un.fcpi64.bdl.bdeFlags = BUFF_TYPE_BDE_IMMED;
iocb->un.fcpi64.bdl.bdeSize = sizeof(struct fcp_cmnd);
@@ -452,6 +562,17 @@ lpfc_new_scsi_buf(struct lpfc_vport *vport)
return psb;
}
+/**
+ * lpfc_get_scsi_buf: Get a scsi buffer from lpfc_scsi_buf_list list of Hba.
+ * @phba: The Hba for which this call is being executed.
+ *
+ * This routine removes a scsi buffer from head of @phba lpfc_scsi_buf_list list
+ * and returns to caller.
+ *
+ * Return codes:
+ * NULL - Error
+ * Pointer to lpfc_scsi_buf - Success
+ **/
static struct lpfc_scsi_buf*
lpfc_get_scsi_buf(struct lpfc_hba * phba)
{
@@ -464,11 +585,20 @@ lpfc_get_scsi_buf(struct lpfc_hba * phba)
if (lpfc_cmd) {
lpfc_cmd->seg_cnt = 0;
lpfc_cmd->nonsg_phys = 0;
+ lpfc_cmd->prot_seg_cnt = 0;
}
spin_unlock_irqrestore(&phba->scsi_buf_list_lock, iflag);
return lpfc_cmd;
}
+/**
+ * lpfc_release_scsi_buf: Return a scsi buffer back to hba lpfc_scsi_buf_list list.
+ * @phba: The Hba for which this call is being executed.
+ * @psb: The scsi buffer which is being released.
+ *
+ * This routine releases @psb scsi buffer by adding it to tail of @phba
+ * lpfc_scsi_buf_list list.
+ **/
static void
lpfc_release_scsi_buf(struct lpfc_hba *phba, struct lpfc_scsi_buf *psb)
{
@@ -480,6 +610,20 @@ lpfc_release_scsi_buf(struct lpfc_hba *phba, struct lpfc_scsi_buf *psb)
spin_unlock_irqrestore(&phba->scsi_buf_list_lock, iflag);
}
+/**
+ * lpfc_scsi_prep_dma_buf: Routine to do DMA mapping for scsi buffer.
+ * @phba: The Hba for which this call is being executed.
+ * @lpfc_cmd: The scsi buffer which is going to be mapped.
+ *
+ * This routine does the pci dma mapping for scatter-gather list of scsi cmnd
+ * field of @lpfc_cmd. This routine scans through sg elements and format the
+ * bdea. This routine also initializes all IOCB fields which are dependent on
+ * scsi command request buffer.
+ *
+ * Return codes:
+ * 1 - Error
+ * 0 - Success
+ **/
static int
lpfc_scsi_prep_dma_buf(struct lpfc_hba *phba, struct lpfc_scsi_buf *lpfc_cmd)
{
@@ -516,7 +660,7 @@ lpfc_scsi_prep_dma_buf(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",
+ "dma_map_sg. Config %d, seg_cnt %d\n",
__func__, phba->cfg_sg_seg_cnt,
lpfc_cmd->seg_cnt);
scsi_dma_unmap(scsi_cmnd);
@@ -535,6 +679,7 @@ lpfc_scsi_prep_dma_buf(struct lpfc_hba *phba, struct lpfc_scsi_buf *lpfc_cmd)
scsi_for_each_sg(scsi_cmnd, sgel, nseg, num_bde) {
physaddr = sg_dma_address(sgel);
if (phba->sli_rev == 3 &&
+ !(phba->sli3_options & LPFC_SLI3_BG_ENABLED) &&
nseg <= LPFC_EXT_DATA_BDE_COUNT) {
data_bde->tus.f.bdeFlags = BUFF_TYPE_BDE_64;
data_bde->tus.f.bdeSize = sg_dma_len(sgel);
@@ -560,7 +705,8 @@ lpfc_scsi_prep_dma_buf(struct lpfc_hba *phba, struct lpfc_scsi_buf *lpfc_cmd)
* explicitly reinitialized and for SLI-3 the extended bde count is
* explicitly reinitialized since all iocb memory resources are reused.
*/
- if (phba->sli_rev == 3) {
+ if (phba->sli_rev == 3 &&
+ !(phba->sli3_options & LPFC_SLI3_BG_ENABLED)) {
if (num_bde > LPFC_EXT_DATA_BDE_COUNT) {
/*
* The extended IOCB format can only fit 3 BDE or a BPL.
@@ -587,7 +733,683 @@ lpfc_scsi_prep_dma_buf(struct lpfc_hba *phba, struct lpfc_scsi_buf *lpfc_cmd)
((num_bde + 2) * sizeof(struct ulp_bde64));
}
fcp_cmnd->fcpDl = cpu_to_be32(scsi_bufflen(scsi_cmnd));
+
+ /*
+ * Due to difference in data length between DIF/non-DIF paths,
+ * we need to set word 4 of IOCB here
+ */
+ iocb_cmd->un.fcpi.fcpi_parm = le32_to_cpu(scsi_bufflen(scsi_cmnd));
+ return 0;
+}
+
+/*
+ * Given a scsi cmnd, determine the BlockGuard profile to be used
+ * with the cmd
+ */
+static int
+lpfc_sc_to_sli_prof(struct scsi_cmnd *sc)
+{
+ uint8_t guard_type = scsi_host_get_guard(sc->device->host);
+ uint8_t ret_prof = LPFC_PROF_INVALID;
+
+ if (guard_type == SHOST_DIX_GUARD_IP) {
+ switch (scsi_get_prot_op(sc)) {
+ case SCSI_PROT_READ_INSERT:
+ case SCSI_PROT_WRITE_STRIP:
+ ret_prof = LPFC_PROF_AST2;
+ break;
+
+ case SCSI_PROT_READ_STRIP:
+ case SCSI_PROT_WRITE_INSERT:
+ ret_prof = LPFC_PROF_A1;
+ break;
+
+ case SCSI_PROT_READ_CONVERT:
+ case SCSI_PROT_WRITE_CONVERT:
+ ret_prof = LPFC_PROF_AST1;
+ break;
+
+ case SCSI_PROT_READ_PASS:
+ case SCSI_PROT_WRITE_PASS:
+ case SCSI_PROT_NORMAL:
+ default:
+ printk(KERN_ERR "Bad op/guard:%d/%d combination\n",
+ scsi_get_prot_op(sc), guard_type);
+ break;
+
+ }
+ } else if (guard_type == SHOST_DIX_GUARD_CRC) {
+ switch (scsi_get_prot_op(sc)) {
+ case SCSI_PROT_READ_STRIP:
+ case SCSI_PROT_WRITE_INSERT:
+ ret_prof = LPFC_PROF_A1;
+ break;
+
+ case SCSI_PROT_READ_PASS:
+ case SCSI_PROT_WRITE_PASS:
+ ret_prof = LPFC_PROF_C1;
+ break;
+
+ case SCSI_PROT_READ_CONVERT:
+ case SCSI_PROT_WRITE_CONVERT:
+ case SCSI_PROT_READ_INSERT:
+ case SCSI_PROT_WRITE_STRIP:
+ case SCSI_PROT_NORMAL:
+ default:
+ printk(KERN_ERR "Bad op/guard:%d/%d combination\n",
+ scsi_get_prot_op(sc), guard_type);
+ break;
+ }
+ } else {
+ /* unsupported format */
+ BUG();
+ }
+
+ return ret_prof;
+}
+
+struct scsi_dif_tuple {
+ __be16 guard_tag; /* Checksum */
+ __be16 app_tag; /* Opaque storage */
+ __be32 ref_tag; /* Target LBA or indirect LBA */
+};
+
+static inline unsigned
+lpfc_cmd_blksize(struct scsi_cmnd *sc)
+{
+ return sc->device->sector_size;
+}
+
+/**
+ * lpfc_get_cmd_dif_parms - Extract DIF parameters from SCSI command
+ * @sc: in: SCSI command
+ * @apptagmask out: app tag mask
+ * @apptagval out: app tag value
+ * @reftag out: ref tag (reference tag)
+ *
+ * Description:
+ * Extract DIF paramters from the command if possible. Otherwise,
+ * use default paratmers.
+ *
+ **/
+static inline void
+lpfc_get_cmd_dif_parms(struct scsi_cmnd *sc, uint16_t *apptagmask,
+ uint16_t *apptagval, uint32_t *reftag)
+{
+ struct scsi_dif_tuple *spt;
+ unsigned char op = scsi_get_prot_op(sc);
+ unsigned int protcnt = scsi_prot_sg_count(sc);
+ static int cnt;
+
+ if (protcnt && (op == SCSI_PROT_WRITE_STRIP ||
+ op == SCSI_PROT_WRITE_PASS ||
+ op == SCSI_PROT_WRITE_CONVERT)) {
+
+ cnt++;
+ spt = page_address(sg_page(scsi_prot_sglist(sc))) +
+ scsi_prot_sglist(sc)[0].offset;
+ *apptagmask = 0;
+ *apptagval = 0;
+ *reftag = cpu_to_be32(spt->ref_tag);
+
+ } else {
+ /* SBC defines ref tag to be lower 32bits of LBA */
+ *reftag = (uint32_t) (0xffffffff & scsi_get_lba(sc));
+ *apptagmask = 0;
+ *apptagval = 0;
+ }
+}
+
+/*
+ * This function sets up buffer list for protection groups of
+ * type LPFC_PG_TYPE_NO_DIF
+ *
+ * This is usually used when the HBA is instructed to generate
+ * DIFs and insert them into data stream (or strip DIF from
+ * incoming data stream)
+ *
+ * The buffer list consists of just one protection group described
+ * below:
+ * +-------------------------+
+ * start of prot group --> | PDE_1 |
+ * +-------------------------+
+ * | Data BDE |
+ * +-------------------------+
+ * |more Data BDE's ... (opt)|
+ * +-------------------------+
+ *
+ * @sc: pointer to scsi command we're working on
+ * @bpl: pointer to buffer list for protection groups
+ * @datacnt: number of segments of data that have been dma mapped
+ *
+ * Note: Data s/g buffers have been dma mapped
+ */
+static int
+lpfc_bg_setup_bpl(struct lpfc_hba *phba, struct scsi_cmnd *sc,
+ struct ulp_bde64 *bpl, int datasegcnt)
+{
+ struct scatterlist *sgde = NULL; /* s/g data entry */
+ struct lpfc_pde *pde1 = NULL;
+ dma_addr_t physaddr;
+ int i = 0, num_bde = 0;
+ int datadir = sc->sc_data_direction;
+ int prof = LPFC_PROF_INVALID;
+ unsigned blksize;
+ uint32_t reftag;
+ uint16_t apptagmask, apptagval;
+
+ pde1 = (struct lpfc_pde *) bpl;
+ prof = lpfc_sc_to_sli_prof(sc);
+
+ if (prof == LPFC_PROF_INVALID)
+ goto out;
+
+ /* extract some info from the scsi command for PDE1*/
+ blksize = lpfc_cmd_blksize(sc);
+ lpfc_get_cmd_dif_parms(sc, &apptagmask, &apptagval, &reftag);
+
+ /* setup PDE1 with what we have */
+ lpfc_pde_set_bg_parms(pde1, LPFC_PDE1_DESCRIPTOR, prof, blksize,
+ BG_EC_STOP_ERR);
+ lpfc_pde_set_dif_parms(pde1, apptagmask, apptagval, reftag);
+
+ num_bde++;
+ bpl++;
+
+ /* assumption: caller has already run dma_map_sg on command data */
+ scsi_for_each_sg(sc, sgde, datasegcnt, i) {
+ physaddr = sg_dma_address(sgde);
+ bpl->addrLow = le32_to_cpu(putPaddrLow(physaddr));
+ bpl->addrHigh = le32_to_cpu(putPaddrHigh(physaddr));
+ bpl->tus.f.bdeSize = sg_dma_len(sgde);
+ if (datadir == DMA_TO_DEVICE)
+ bpl->tus.f.bdeFlags = BUFF_TYPE_BDE_64;
+ else
+ bpl->tus.f.bdeFlags = BUFF_TYPE_BDE_64I;
+ bpl->tus.w = le32_to_cpu(bpl->tus.w);
+ bpl++;
+ num_bde++;
+ }
+
+out:
+ return num_bde;
+}
+
+/*
+ * This function sets up buffer list for protection groups of
+ * type LPFC_PG_TYPE_DIF_BUF
+ *
+ * This is usually used when DIFs are in their own buffers,
+ * separate from the data. The HBA can then by instructed
+ * to place the DIFs in the outgoing stream. For read operations,
+ * The HBA could extract the DIFs and place it in DIF buffers.
+ *
+ * The buffer list for this type consists of one or more of the
+ * protection groups described below:
+ * +-------------------------+
+ * start of first prot group --> | PDE_1 |
+ * +-------------------------+
+ * | PDE_3 (Prot BDE) |
+ * +-------------------------+
+ * | Data BDE |
+ * +-------------------------+
+ * |more Data BDE's ... (opt)|
+ * +-------------------------+
+ * start of new prot group --> | PDE_1 |
+ * +-------------------------+
+ * | ... |
+ * +-------------------------+
+ *
+ * @sc: pointer to scsi command we're working on
+ * @bpl: pointer to buffer list for protection groups
+ * @datacnt: number of segments of data that have been dma mapped
+ * @protcnt: number of segment of protection data that have been dma mapped
+ *
+ * Note: It is assumed that both data and protection s/g buffers have been
+ * mapped for DMA
+ */
+static int
+lpfc_bg_setup_bpl_prot(struct lpfc_hba *phba, struct scsi_cmnd *sc,
+ struct ulp_bde64 *bpl, int datacnt, int protcnt)
+{
+ struct scatterlist *sgde = NULL; /* s/g data entry */
+ struct scatterlist *sgpe = NULL; /* s/g prot entry */
+ struct lpfc_pde *pde1 = NULL;
+ struct ulp_bde64 *prot_bde = NULL;
+ dma_addr_t dataphysaddr, protphysaddr;
+ unsigned short curr_data = 0, curr_prot = 0;
+ unsigned int split_offset, protgroup_len;
+ unsigned int protgrp_blks, protgrp_bytes;
+ unsigned int remainder, subtotal;
+ int prof = LPFC_PROF_INVALID;
+ int datadir = sc->sc_data_direction;
+ unsigned char pgdone = 0, alldone = 0;
+ unsigned blksize;
+ uint32_t reftag;
+ uint16_t apptagmask, apptagval;
+ int num_bde = 0;
+
+ sgpe = scsi_prot_sglist(sc);
+ sgde = scsi_sglist(sc);
+
+ if (!sgpe || !sgde) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_FCP,
+ "9020 Invalid s/g entry: data=0x%p prot=0x%p\n",
+ sgpe, sgde);
+ return 0;
+ }
+
+ prof = lpfc_sc_to_sli_prof(sc);
+ if (prof == LPFC_PROF_INVALID)
+ goto out;
+
+ /* extract some info from the scsi command for PDE1*/
+ blksize = lpfc_cmd_blksize(sc);
+ lpfc_get_cmd_dif_parms(sc, &apptagmask, &apptagval, &reftag);
+
+ split_offset = 0;
+ do {
+ /* setup the first PDE_1 */
+ pde1 = (struct lpfc_pde *) bpl;
+
+ lpfc_pde_set_bg_parms(pde1, LPFC_PDE1_DESCRIPTOR, prof, blksize,
+ BG_EC_STOP_ERR);
+ lpfc_pde_set_dif_parms(pde1, apptagmask, apptagval, reftag);
+
+ num_bde++;
+ bpl++;
+
+ /* setup the first BDE that points to protection buffer */
+ prot_bde = (struct ulp_bde64 *) bpl;
+ protphysaddr = sg_dma_address(sgpe);
+ prot_bde->addrLow = le32_to_cpu(putPaddrLow(protphysaddr));
+ prot_bde->addrHigh = le32_to_cpu(putPaddrHigh(protphysaddr));
+ protgroup_len = sg_dma_len(sgpe);
+
+
+ /* must be integer multiple of the DIF block length */
+ BUG_ON(protgroup_len % 8);
+
+ protgrp_blks = protgroup_len / 8;
+ protgrp_bytes = protgrp_blks * blksize;
+
+ prot_bde->tus.f.bdeSize = protgroup_len;
+ if (datadir == DMA_TO_DEVICE)
+ prot_bde->tus.f.bdeFlags = BUFF_TYPE_BDE_64;
+ else
+ prot_bde->tus.f.bdeFlags = BUFF_TYPE_BDE_64I;
+ prot_bde->tus.w = le32_to_cpu(bpl->tus.w);
+
+ curr_prot++;
+ num_bde++;
+
+ /* setup BDE's for data blocks associated with DIF data */
+ pgdone = 0;
+ subtotal = 0; /* total bytes processed for current prot grp */
+ while (!pgdone) {
+ if (!sgde) {
+ printk(KERN_ERR "%s Invalid data segment\n",
+ __func__);
+ return 0;
+ }
+ bpl++;
+ dataphysaddr = sg_dma_address(sgde) + split_offset;
+ bpl->addrLow = le32_to_cpu(putPaddrLow(dataphysaddr));
+ bpl->addrHigh = le32_to_cpu(putPaddrHigh(dataphysaddr));
+
+ remainder = sg_dma_len(sgde) - split_offset;
+
+ if ((subtotal + remainder) <= protgrp_bytes) {
+ /* we can use this whole buffer */
+ bpl->tus.f.bdeSize = remainder;
+ split_offset = 0;
+
+ if ((subtotal + remainder) == protgrp_bytes)
+ pgdone = 1;
+ } else {
+ /* must split this buffer with next prot grp */
+ bpl->tus.f.bdeSize = protgrp_bytes - subtotal;
+ split_offset += bpl->tus.f.bdeSize;
+ }
+
+ subtotal += bpl->tus.f.bdeSize;
+
+ if (datadir == DMA_TO_DEVICE)
+ bpl->tus.f.bdeFlags = BUFF_TYPE_BDE_64;
+ else
+ bpl->tus.f.bdeFlags = BUFF_TYPE_BDE_64I;
+ bpl->tus.w = le32_to_cpu(bpl->tus.w);
+
+ num_bde++;
+ curr_data++;
+
+ if (split_offset)
+ break;
+
+ /* Move to the next s/g segment if possible */
+ sgde = sg_next(sgde);
+ }
+
+ /* are we done ? */
+ if (curr_prot == protcnt) {
+ alldone = 1;
+ } else if (curr_prot < protcnt) {
+ /* advance to next prot buffer */
+ sgpe = sg_next(sgpe);
+ bpl++;
+
+ /* update the reference tag */
+ reftag += protgrp_blks;
+ } else {
+ /* if we're here, we have a bug */
+ printk(KERN_ERR "BLKGRD: bug in %s\n", __func__);
+ }
+
+ } while (!alldone);
+
+out:
+
+
+ return num_bde;
+}
+/*
+ * Given a SCSI command that supports DIF, determine composition of protection
+ * groups involved in setting up buffer lists
+ *
+ * Returns:
+ * for DIF (for both read and write)
+ * */
+static int
+lpfc_prot_group_type(struct lpfc_hba *phba, struct scsi_cmnd *sc)
+{
+ int ret = LPFC_PG_TYPE_INVALID;
+ unsigned char op = scsi_get_prot_op(sc);
+
+ switch (op) {
+ case SCSI_PROT_READ_STRIP:
+ case SCSI_PROT_WRITE_INSERT:
+ ret = LPFC_PG_TYPE_NO_DIF;
+ break;
+ case SCSI_PROT_READ_INSERT:
+ case SCSI_PROT_WRITE_STRIP:
+ case SCSI_PROT_READ_PASS:
+ case SCSI_PROT_WRITE_PASS:
+ case SCSI_PROT_WRITE_CONVERT:
+ case SCSI_PROT_READ_CONVERT:
+ ret = LPFC_PG_TYPE_DIF_BUF;
+ break;
+ default:
+ lpfc_printf_log(phba, KERN_ERR, LOG_FCP,
+ "9021 Unsupported protection op:%d\n", op);
+ break;
+ }
+
+ return ret;
+}
+
+/*
+ * This is the protection/DIF aware version of
+ * lpfc_scsi_prep_dma_buf(). It may be a good idea to combine the
+ * two functions eventually, but for now, it's here
+ */
+static int
+lpfc_bg_scsi_prep_dma_buf(struct lpfc_hba *phba,
+ struct lpfc_scsi_buf *lpfc_cmd)
+{
+ struct scsi_cmnd *scsi_cmnd = lpfc_cmd->pCmd;
+ struct fcp_cmnd *fcp_cmnd = lpfc_cmd->fcp_cmnd;
+ struct ulp_bde64 *bpl = lpfc_cmd->fcp_bpl;
+ IOCB_t *iocb_cmd = &lpfc_cmd->cur_iocbq.iocb;
+ uint32_t num_bde = 0;
+ int datasegcnt, protsegcnt, datadir = scsi_cmnd->sc_data_direction;
+ int prot_group_type = 0;
+ int diflen, fcpdl;
+ unsigned blksize;
+
+ /*
+ * Start the lpfc command prep by bumping the bpl beyond fcp_cmnd
+ * fcp_rsp regions to the first data bde entry
+ */
+ bpl += 2;
+ if (scsi_sg_count(scsi_cmnd)) {
+ /*
+ * The driver stores the segment count returned from pci_map_sg
+ * because this a count of dma-mappings used to map the use_sg
+ * pages. They are not guaranteed to be the same for those
+ * architectures that implement an IOMMU.
+ */
+ datasegcnt = dma_map_sg(&phba->pcidev->dev,
+ scsi_sglist(scsi_cmnd),
+ scsi_sg_count(scsi_cmnd), datadir);
+ if (unlikely(!datasegcnt))
+ return 1;
+
+ 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",
+ __func__, phba->cfg_sg_seg_cnt,
+ lpfc_cmd->seg_cnt);
+ scsi_dma_unmap(scsi_cmnd);
+ return 1;
+ }
+
+ prot_group_type = lpfc_prot_group_type(phba, scsi_cmnd);
+
+ switch (prot_group_type) {
+ case LPFC_PG_TYPE_NO_DIF:
+ num_bde = lpfc_bg_setup_bpl(phba, scsi_cmnd, bpl,
+ datasegcnt);
+ /* we shoud have 2 or more entries in buffer list */
+ if (num_bde < 2)
+ goto err;
+ break;
+ case LPFC_PG_TYPE_DIF_BUF:{
+ /*
+ * This type indicates that protection buffers are
+ * passed to the driver, so that needs to be prepared
+ * for DMA
+ */
+ protsegcnt = dma_map_sg(&phba->pcidev->dev,
+ scsi_prot_sglist(scsi_cmnd),
+ scsi_prot_sg_count(scsi_cmnd), datadir);
+ if (unlikely(!protsegcnt)) {
+ scsi_dma_unmap(scsi_cmnd);
+ return 1;
+ }
+
+ 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,"
+ "prot_seg_cnt %d\n", __func__,
+ phba->cfg_prot_sg_seg_cnt,
+ lpfc_cmd->prot_seg_cnt);
+ dma_unmap_sg(&phba->pcidev->dev,
+ scsi_prot_sglist(scsi_cmnd),
+ scsi_prot_sg_count(scsi_cmnd),
+ datadir);
+ scsi_dma_unmap(scsi_cmnd);
+ return 1;
+ }
+
+ num_bde = lpfc_bg_setup_bpl_prot(phba, scsi_cmnd, bpl,
+ datasegcnt, protsegcnt);
+ /* we shoud have 3 or more entries in buffer list */
+ if (num_bde < 3)
+ goto err;
+ break;
+ }
+ case LPFC_PG_TYPE_INVALID:
+ default:
+ lpfc_printf_log(phba, KERN_ERR, LOG_FCP,
+ "9022 Unexpected protection group %i\n",
+ prot_group_type);
+ return 1;
+ }
+ }
+
+ /*
+ * Finish initializing those IOCB fields that are dependent on the
+ * scsi_cmnd request_buffer. Note that the bdeSize is explicitly
+ * reinitialized since all iocb memory resources are used many times
+ * for transmit, receive, and continuation bpl's.
+ */
+ iocb_cmd->un.fcpi64.bdl.bdeSize = (2 * sizeof(struct ulp_bde64));
+ iocb_cmd->un.fcpi64.bdl.bdeSize += (num_bde * sizeof(struct ulp_bde64));
+ iocb_cmd->ulpBdeCount = 1;
+ iocb_cmd->ulpLe = 1;
+
+ fcpdl = scsi_bufflen(scsi_cmnd);
+
+ if (scsi_get_prot_type(scsi_cmnd) == SCSI_PROT_DIF_TYPE1) {
+ /*
+ * We are in DIF Type 1 mode
+ * Every data block has a 8 byte DIF (trailer)
+ * attached to it. Must ajust FCP data length
+ */
+ blksize = lpfc_cmd_blksize(scsi_cmnd);
+ diflen = (fcpdl / blksize) * 8;
+ fcpdl += diflen;
+ }
+ fcp_cmnd->fcpDl = be32_to_cpu(fcpdl);
+
+ /*
+ * Due to difference in data length between DIF/non-DIF paths,
+ * we need to set word 4 of IOCB here
+ */
+ iocb_cmd->un.fcpi.fcpi_parm = fcpdl;
+
return 0;
+err:
+ lpfc_printf_log(phba, KERN_ERR, LOG_FCP,
+ "9023 Could not setup all needed BDE's"
+ "prot_group_type=%d, num_bde=%d\n",
+ prot_group_type, num_bde);
+ return 1;
+}
+
+/*
+ * This function checks for BlockGuard errors detected by
+ * the HBA. In case of errors, the ASC/ASCQ fields in the
+ * sense buffer will be set accordingly, paired with
+ * ILLEGAL_REQUEST to signal to the kernel that the HBA
+ * detected corruption.
+ *
+ * Returns:
+ * 0 - No error found
+ * 1 - BlockGuard error found
+ * -1 - Internal error (bad profile, ...etc)
+ */
+static int
+lpfc_parse_bg_err(struct lpfc_hba *phba, struct lpfc_scsi_buf *lpfc_cmd,
+ struct lpfc_iocbq *pIocbOut)
+{
+ struct scsi_cmnd *cmd = lpfc_cmd->pCmd;
+ struct sli3_bg_fields *bgf = &pIocbOut->iocb.unsli3.sli3_bg;
+ int ret = 0;
+ uint32_t bghm = bgf->bghm;
+ 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%lx "
+ "bgstat=0x%x bghm=0x%x\n",
+ cmd->cmnd[0], (unsigned long long)scsi_get_lba(cmd),
+ cmd->request->nr_sectors, bgstat, bghm);
+
+ spin_lock(&_dump_buf_lock);
+ if (!_dump_buf_done) {
+ printk(KERN_ERR "Saving Data for %u blocks to debugfs\n",
+ (cmd->cmnd[7] << 8 | cmd->cmnd[8]));
+ lpfc_debug_save_data(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);
+ }
+
+ _dump_buf_done = 1;
+ }
+ spin_unlock(&_dump_buf_lock);
+
+ if (lpfc_bgs_get_invalid_prof(bgstat)) {
+ cmd->result = ScsiResult(DID_ERROR, 0);
+ printk(KERN_ERR "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",
+ bgstat);
+ ret = (-1);
+ goto out;
+ }
+
+ if (lpfc_bgs_get_guard_err(bgstat)) {
+ ret = 1;
+
+ scsi_build_sense_buffer(1, cmd->sense_buffer, ILLEGAL_REQUEST,
+ 0x10, 0x1);
+ cmd->result = (DRIVER_SENSE|SUGGEST_DIE) << 24
+ | ScsiResult(DID_ABORT, SAM_STAT_CHECK_CONDITION);
+ phba->bg_guard_err_cnt++;
+ printk(KERN_ERR "BLKGRD: guard_tag error\n");
+ }
+
+ if (lpfc_bgs_get_reftag_err(bgstat)) {
+ ret = 1;
+
+ scsi_build_sense_buffer(1, cmd->sense_buffer, ILLEGAL_REQUEST,
+ 0x10, 0x3);
+ cmd->result = (DRIVER_SENSE|SUGGEST_DIE) << 24
+ | ScsiResult(DID_ABORT, SAM_STAT_CHECK_CONDITION);
+
+ phba->bg_reftag_err_cnt++;
+ printk(KERN_ERR "BLKGRD: ref_tag error\n");
+ }
+
+ if (lpfc_bgs_get_apptag_err(bgstat)) {
+ ret = 1;
+
+ scsi_build_sense_buffer(1, cmd->sense_buffer, ILLEGAL_REQUEST,
+ 0x10, 0x2);
+ cmd->result = (DRIVER_SENSE|SUGGEST_DIE) << 24
+ | ScsiResult(DID_ABORT, SAM_STAT_CHECK_CONDITION);
+
+ phba->bg_apptag_err_cnt++;
+ printk(KERN_ERR "BLKGRD: app_tag error\n");
+ }
+
+ if (lpfc_bgs_get_hi_water_mark_present(bgstat)) {
+ /*
+ * setup sense data descriptor 0 per SPC-4 as an information
+ * field, and put the failing LBA in it
+ */
+ cmd->sense_buffer[8] = 0; /* Information */
+ cmd->sense_buffer[9] = 0xa; /* Add. length */
+ do_div(bghm, cmd->device->sector_size);
+
+ failing_sector = scsi_get_lba(cmd);
+ failing_sector += bghm;
+
+ put_unaligned_be64(failing_sector, &cmd->sense_buffer[10]);
+ }
+
+ if (!ret) {
+ /* No error was reported - problem in FW? */
+ cmd->result = ScsiResult(DID_ERROR, 0);
+ printk(KERN_ERR "BLKGRD: no errors reported!\n");
+ }
+
+out:
+ return ret;
}
/**
@@ -681,6 +1503,15 @@ lpfc_send_scsi_error_event(struct lpfc_hba *phba, struct lpfc_vport *vport,
lpfc_worker_wake_up(phba);
return;
}
+
+/**
+ * lpfc_scsi_unprep_dma_buf: Routine to un-map DMA mapping of scatter gather.
+ * @phba: The Hba for which this call is being executed.
+ * @psb: The scsi buffer which is going to be un-mapped.
+ *
+ * This routine does DMA un-mapping of scatter gather list of scsi command
+ * field of @lpfc_cmd.
+ **/
static void
lpfc_scsi_unprep_dma_buf(struct lpfc_hba * phba, struct lpfc_scsi_buf * psb)
{
@@ -692,8 +1523,22 @@ lpfc_scsi_unprep_dma_buf(struct lpfc_hba * phba, struct lpfc_scsi_buf * psb)
*/
if (psb->seg_cnt > 0)
scsi_dma_unmap(psb->pCmd);
+ if (psb->prot_seg_cnt > 0)
+ dma_unmap_sg(&phba->pcidev->dev, scsi_prot_sglist(psb->pCmd),
+ scsi_prot_sg_count(psb->pCmd),
+ psb->pCmd->sc_data_direction);
}
+/**
+ * lpfc_handler_fcp_err: FCP response handler.
+ * @vport: The virtual port for which this call is being executed.
+ * @lpfc_cmd: Pointer to lpfc_scsi_buf data structure.
+ * @rsp_iocb: The response IOCB which contains FCP error.
+ *
+ * This routine is called to process response IOCB with status field
+ * IOSTAT_FCP_RSP_ERROR. This routine sets result field of scsi command
+ * based upon SCSI and FCP error.
+ **/
static void
lpfc_handle_fcp_err(struct lpfc_vport *vport, struct lpfc_scsi_buf *lpfc_cmd,
struct lpfc_iocbq *rsp_iocb)
@@ -735,7 +1580,7 @@ lpfc_handle_fcp_err(struct lpfc_vport *vport, struct lpfc_scsi_buf *lpfc_cmd,
logit = LOG_FCP;
lpfc_printf_vlog(vport, KERN_WARNING, logit,
- "0730 FCP command x%x failed: x%x SNS x%x x%x "
+ "9024 FCP command x%x failed: x%x SNS x%x x%x "
"Data: x%x x%x x%x x%x x%x\n",
cmnd->cmnd[0], scsi_status,
be32_to_cpu(*lp), be32_to_cpu(*(lp + 3)), resp_info,
@@ -758,7 +1603,7 @@ lpfc_handle_fcp_err(struct lpfc_vport *vport, struct lpfc_scsi_buf *lpfc_cmd,
scsi_set_resid(cmnd, be32_to_cpu(fcprsp->rspResId));
lpfc_printf_vlog(vport, KERN_INFO, LOG_FCP,
- "0716 FCP Read Underrun, expected %d, "
+ "9025 FCP Read Underrun, expected %d, "
"residual %d Data: x%x x%x x%x\n",
be32_to_cpu(fcpcmd->fcpDl),
scsi_get_resid(cmnd), fcpi_parm, cmnd->cmnd[0],
@@ -774,7 +1619,7 @@ lpfc_handle_fcp_err(struct lpfc_vport *vport, struct lpfc_scsi_buf *lpfc_cmd,
(scsi_get_resid(cmnd) != fcpi_parm)) {
lpfc_printf_vlog(vport, KERN_WARNING,
LOG_FCP | LOG_FCP_ERROR,
- "0735 FCP Read Check Error "
+ "9026 FCP Read Check Error "
"and Underrun Data: x%x x%x x%x x%x\n",
be32_to_cpu(fcpcmd->fcpDl),
scsi_get_resid(cmnd), fcpi_parm,
@@ -793,7 +1638,7 @@ lpfc_handle_fcp_err(struct lpfc_vport *vport, struct lpfc_scsi_buf *lpfc_cmd,
(scsi_bufflen(cmnd) - scsi_get_resid(cmnd)
< cmnd->underflow)) {
lpfc_printf_vlog(vport, KERN_INFO, LOG_FCP,
- "0717 FCP command x%x residual "
+ "9027 FCP command x%x residual "
"underrun converted to error "
"Data: x%x x%x x%x\n",
cmnd->cmnd[0], scsi_bufflen(cmnd),
@@ -802,7 +1647,7 @@ lpfc_handle_fcp_err(struct lpfc_vport *vport, struct lpfc_scsi_buf *lpfc_cmd,
}
} else if (resp_info & RESID_OVER) {
lpfc_printf_vlog(vport, KERN_WARNING, LOG_FCP,
- "0720 FCP command x%x residual overrun error. "
+ "9028 FCP command x%x residual overrun error. "
"Data: x%x x%x \n", cmnd->cmnd[0],
scsi_bufflen(cmnd), scsi_get_resid(cmnd));
host_status = DID_ERROR;
@@ -814,7 +1659,7 @@ lpfc_handle_fcp_err(struct lpfc_vport *vport, struct lpfc_scsi_buf *lpfc_cmd,
} else if ((scsi_status == SAM_STAT_GOOD) && fcpi_parm &&
(cmnd->sc_data_direction == DMA_FROM_DEVICE)) {
lpfc_printf_vlog(vport, KERN_WARNING, LOG_FCP | LOG_FCP_ERROR,
- "0734 FCP Read Check Error Data: "
+ "9029 FCP Read Check Error Data: "
"x%x x%x x%x x%x\n",
be32_to_cpu(fcpcmd->fcpDl),
be32_to_cpu(fcprsp->rspResId),
@@ -828,6 +1673,16 @@ lpfc_handle_fcp_err(struct lpfc_vport *vport, struct lpfc_scsi_buf *lpfc_cmd,
lpfc_send_scsi_error_event(vport->phba, vport, lpfc_cmd, rsp_iocb);
}
+/**
+ * lpfc_scsi_cmd_iocb_cmpl: Scsi cmnd IOCB completion routine.
+ * @phba: The Hba for which this call is being executed.
+ * @pIocbIn: The command IOCBQ for the scsi cmnd.
+ * @pIocbOut: The response IOCBQ for the scsi cmnd .
+ *
+ * This routine assigns scsi command result by looking into response IOCB
+ * status field appropriately. This routine handles QUEUE FULL condition as
+ * well by ramping down device queue depth.
+ **/
static void
lpfc_scsi_cmd_iocb_cmpl(struct lpfc_hba *phba, struct lpfc_iocbq *pIocbIn,
struct lpfc_iocbq *pIocbOut)
@@ -846,7 +1701,8 @@ lpfc_scsi_cmd_iocb_cmpl(struct lpfc_hba *phba, struct lpfc_iocbq *pIocbIn,
lpfc_cmd->result = pIocbOut->iocb.un.ulpWord[4];
lpfc_cmd->status = pIocbOut->iocb.ulpStatus;
- atomic_dec(&pnode->cmd_pending);
+ if (pnode && NLP_CHK_NODE_ACT(pnode))
+ atomic_dec(&pnode->cmd_pending);
if (lpfc_cmd->status) {
if (lpfc_cmd->status == IOSTAT_LOCAL_REJECT &&
@@ -856,7 +1712,7 @@ lpfc_scsi_cmd_iocb_cmpl(struct lpfc_hba *phba, struct lpfc_iocbq *pIocbIn,
lpfc_cmd->status = IOSTAT_DEFAULT;
lpfc_printf_vlog(vport, KERN_WARNING, LOG_FCP,
- "0729 FCP cmd x%x failed <%d/%d> "
+ "9030 FCP cmd x%x failed <%d/%d> "
"status: x%x result: x%x Data: x%x x%x\n",
cmd->cmnd[0],
cmd->device ? cmd->device->id : 0xffff,
@@ -904,7 +1760,28 @@ lpfc_scsi_cmd_iocb_cmpl(struct lpfc_hba *phba, struct lpfc_iocbq *pIocbIn,
lpfc_cmd->result == IOERR_ABORT_REQUESTED) {
cmd->result = ScsiResult(DID_REQUEUE, 0);
break;
- } /* else: fall through */
+ }
+
+ if ((lpfc_cmd->result == IOERR_RX_DMA_FAILED ||
+ lpfc_cmd->result == IOERR_TX_DMA_FAILED) &&
+ pIocbOut->iocb.unsli3.sli3_bg.bgstat) {
+ if (scsi_get_prot_op(cmd) != SCSI_PROT_NORMAL) {
+ /*
+ * This is a response for a BG enabled
+ * cmd. Parse BG error
+ */
+ lpfc_parse_bg_err(phba, lpfc_cmd,
+ pIocbOut);
+ break;
+ } else {
+ lpfc_printf_vlog(vport, KERN_WARNING,
+ LOG_BG,
+ "9031 non-zero BGSTAT "
+ "on unprotected cmd");
+ }
+ }
+
+ /* else: fall through */
default:
cmd->result = ScsiResult(DID_ERROR, 0);
break;
@@ -936,23 +1813,31 @@ lpfc_scsi_cmd_iocb_cmpl(struct lpfc_hba *phba, struct lpfc_iocbq *pIocbIn,
time_after(jiffies, lpfc_cmd->start_time +
msecs_to_jiffies(vport->cfg_max_scsicmpl_time))) {
spin_lock_irqsave(sdev->host->host_lock, flags);
- if ((pnode->cmd_qdepth > atomic_read(&pnode->cmd_pending) &&
- (atomic_read(&pnode->cmd_pending) > LPFC_MIN_TGT_QDEPTH) &&
- ((cmd->cmnd[0] == READ_10) || (cmd->cmnd[0] == WRITE_10))))
- pnode->cmd_qdepth = atomic_read(&pnode->cmd_pending);
-
- pnode->last_change_time = jiffies;
+ if (pnode && NLP_CHK_NODE_ACT(pnode)) {
+ if (pnode->cmd_qdepth >
+ atomic_read(&pnode->cmd_pending) &&
+ (atomic_read(&pnode->cmd_pending) >
+ LPFC_MIN_TGT_QDEPTH) &&
+ ((cmd->cmnd[0] == READ_10) ||
+ (cmd->cmnd[0] == WRITE_10)))
+ pnode->cmd_qdepth =
+ atomic_read(&pnode->cmd_pending);
+
+ pnode->last_change_time = jiffies;
+ }
spin_unlock_irqrestore(sdev->host->host_lock, flags);
- } else if ((pnode->cmd_qdepth < LPFC_MAX_TGT_QDEPTH) &&
+ } else if (pnode && NLP_CHK_NODE_ACT(pnode)) {
+ if ((pnode->cmd_qdepth < LPFC_MAX_TGT_QDEPTH) &&
time_after(jiffies, pnode->last_change_time +
- msecs_to_jiffies(LPFC_TGTQ_INTERVAL))) {
- spin_lock_irqsave(sdev->host->host_lock, flags);
- pnode->cmd_qdepth += pnode->cmd_qdepth *
- LPFC_TGTQ_RAMPUP_PCENT / 100;
- if (pnode->cmd_qdepth > LPFC_MAX_TGT_QDEPTH)
- pnode->cmd_qdepth = LPFC_MAX_TGT_QDEPTH;
- pnode->last_change_time = jiffies;
- spin_unlock_irqrestore(sdev->host->host_lock, flags);
+ msecs_to_jiffies(LPFC_TGTQ_INTERVAL))) {
+ spin_lock_irqsave(sdev->host->host_lock, flags);
+ pnode->cmd_qdepth += pnode->cmd_qdepth *
+ LPFC_TGTQ_RAMPUP_PCENT / 100;
+ if (pnode->cmd_qdepth > LPFC_MAX_TGT_QDEPTH)
+ pnode->cmd_qdepth = LPFC_MAX_TGT_QDEPTH;
+ pnode->last_change_time = jiffies;
+ spin_unlock_irqrestore(sdev->host->host_lock, flags);
+ }
}
lpfc_scsi_unprep_dma_buf(phba, lpfc_cmd);
@@ -1067,6 +1952,15 @@ lpfc_fcpcmd_to_iocb(uint8_t *data, struct fcp_cmnd *fcp_cmnd)
}
}
+/**
+ * lpfc_scsi_prep_cmnd: Routine to convert scsi cmnd to FCP information unit.
+ * @vport: The virtual port for which this call is being executed.
+ * @lpfc_cmd: The scsi command which needs to send.
+ * @pnode: Pointer to lpfc_nodelist.
+ *
+ * This routine initializes fcp_cmnd and iocb data structure from scsi command
+ * to transfer.
+ **/
static void
lpfc_scsi_prep_cmnd(struct lpfc_vport *vport, struct lpfc_scsi_buf *lpfc_cmd,
struct lpfc_nodelist *pnode)
@@ -1122,7 +2016,6 @@ lpfc_scsi_prep_cmnd(struct lpfc_vport *vport, struct lpfc_scsi_buf *lpfc_cmd,
} else {
iocb_cmd->ulpCommand = CMD_FCP_IREAD64_CR;
iocb_cmd->ulpPU = PARM_READ_CHECK;
- iocb_cmd->un.fcpi.fcpi_parm = scsi_bufflen(scsi_cmnd);
fcp_cmnd->fcpCntl3 = READ_DATA;
phba->fc4InputRequests++;
}
@@ -1133,7 +2026,8 @@ lpfc_scsi_prep_cmnd(struct lpfc_vport *vport, struct lpfc_scsi_buf *lpfc_cmd,
fcp_cmnd->fcpCntl3 = 0;
phba->fc4ControlRequests++;
}
- if (phba->sli_rev == 3)
+ if (phba->sli_rev == 3 &&
+ !(phba->sli3_options & LPFC_SLI3_BG_ENABLED))
lpfc_fcpcmd_to_iocb(iocb_cmd->unsli3.fcp_ext.icd, fcp_cmnd);
/*
* Finish initializing those IOCB fields that are independent
@@ -1152,6 +2046,19 @@ lpfc_scsi_prep_cmnd(struct lpfc_vport *vport, struct lpfc_scsi_buf *lpfc_cmd,
piocbq->vport = vport;
}
+/**
+ * lpfc_scsi_prep_task_mgmt_cmnd: Convert scsi TM cmnd to FCP information unit.
+ * @vport: The virtual port for which this call is being executed.
+ * @lpfc_cmd: Pointer to lpfc_scsi_buf data structure.
+ * @lun: Logical unit number.
+ * @task_mgmt_cmd: SCSI task management command.
+ *
+ * This routine creates FCP information unit corresponding to @task_mgmt_cmd.
+ *
+ * Return codes:
+ * 0 - Error
+ * 1 - Success
+ **/
static int
lpfc_scsi_prep_task_mgmt_cmd(struct lpfc_vport *vport,
struct lpfc_scsi_buf *lpfc_cmd,
@@ -1178,7 +2085,8 @@ lpfc_scsi_prep_task_mgmt_cmd(struct lpfc_vport *vport,
memset(fcp_cmnd, 0, sizeof(struct fcp_cmnd));
int_to_scsilun(lun, &fcp_cmnd->fcp_lun);
fcp_cmnd->fcpCntl2 = task_mgmt_cmd;
- if (vport->phba->sli_rev == 3)
+ if (vport->phba->sli_rev == 3 &&
+ !(vport->phba->sli3_options & LPFC_SLI3_BG_ENABLED))
lpfc_fcpcmd_to_iocb(piocb->unsli3.fcp_ext.icd, fcp_cmnd);
piocb->ulpCommand = CMD_FCP_ICMND64_CR;
piocb->ulpContext = ndlp->nlp_rpi;
@@ -1201,6 +2109,15 @@ lpfc_scsi_prep_task_mgmt_cmd(struct lpfc_vport *vport,
return 1;
}
+/**
+ * lpc_taskmgmt_def_cmpl: IOCB completion routine for task management command.
+ * @phba: The Hba for which this call is being executed.
+ * @cmdiocbq: Pointer to lpfc_iocbq data structure.
+ * @rspiocbq: Pointer to lpfc_iocbq data structure.
+ *
+ * This routine is IOCB completion routine for device reset and target reset
+ * routine. This routine release scsi buffer associated with lpfc_cmd.
+ **/
static void
lpfc_tskmgmt_def_cmpl(struct lpfc_hba *phba,
struct lpfc_iocbq *cmdiocbq,
@@ -1213,6 +2130,20 @@ lpfc_tskmgmt_def_cmpl(struct lpfc_hba *phba,
return;
}
+/**
+ * lpfc_scsi_tgt_reset: Target reset handler.
+ * @lpfc_cmd: Pointer to lpfc_scsi_buf data structure
+ * @vport: The virtual port for which this call is being executed.
+ * @tgt_id: Target ID.
+ * @lun: Lun number.
+ * @rdata: Pointer to lpfc_rport_data.
+ *
+ * This routine issues a TARGET RESET iocb to reset a target with @tgt_id ID.
+ *
+ * Return Code:
+ * 0x2003 - Error
+ * 0x2002 - Success.
+ **/
static int
lpfc_scsi_tgt_reset(struct lpfc_scsi_buf *lpfc_cmd, struct lpfc_vport *vport,
unsigned tgt_id, unsigned int lun,
@@ -1266,6 +2197,15 @@ lpfc_scsi_tgt_reset(struct lpfc_scsi_buf *lpfc_cmd, struct lpfc_vport *vport,
return ret;
}
+/**
+ * lpfc_info: Info entry point of scsi_host_template data structure.
+ * @host: The scsi host for which this call is being executed.
+ *
+ * This routine provides module information about hba.
+ *
+ * Reutrn code:
+ * Pointer to char - Success.
+ **/
const char *
lpfc_info(struct Scsi_Host *host)
{
@@ -1295,6 +2235,13 @@ lpfc_info(struct Scsi_Host *host)
return lpfcinfobuf;
}
+/**
+ * lpfc_poll_rearm_time: Routine to modify fcp_poll timer of hba.
+ * @phba: The Hba for which this call is being executed.
+ *
+ * This routine modifies fcp_poll_timer field of @phba by cfg_poll_tmo.
+ * The default value of cfg_poll_tmo is 10 milliseconds.
+ **/
static __inline__ void lpfc_poll_rearm_timer(struct lpfc_hba * phba)
{
unsigned long poll_tmo_expires =
@@ -1305,11 +2252,25 @@ static __inline__ void lpfc_poll_rearm_timer(struct lpfc_hba * phba)
poll_tmo_expires);
}
+/**
+ * lpfc_poll_start_timer: Routine to start fcp_poll_timer of HBA.
+ * @phba: The Hba for which this call is being executed.
+ *
+ * This routine starts the fcp_poll_timer of @phba.
+ **/
void lpfc_poll_start_timer(struct lpfc_hba * phba)
{
lpfc_poll_rearm_timer(phba);
}
+/**
+ * lpfc_poll_timeout: Restart polling timer.
+ * @ptr: Map to lpfc_hba data structure pointer.
+ *
+ * This routine restarts fcp_poll timer, when FCP ring polling is enable
+ * and FCP Ring interrupt is disable.
+ **/
+
void lpfc_poll_timeout(unsigned long ptr)
{
struct lpfc_hba *phba = (struct lpfc_hba *) ptr;
@@ -1321,6 +2282,20 @@ void lpfc_poll_timeout(unsigned long ptr)
}
}
+/**
+ * lpfc_queuecommand: Queuecommand entry point of Scsi Host Templater data
+ * structure.
+ * @cmnd: Pointer to scsi_cmnd data structure.
+ * @done: Pointer to done routine.
+ *
+ * Driver registers this routine to scsi midlayer to submit a @cmd to process.
+ * This routine prepares an IOCB from scsi command and provides to firmware.
+ * The @done callback is invoked after driver finished processing the command.
+ *
+ * Return value :
+ * 0 - Success
+ * SCSI_MLQUEUE_HOST_BUSY - Block all devices served by this host temporarily.
+ **/
static int
lpfc_queuecommand(struct scsi_cmnd *cmnd, void (*done) (struct scsi_cmnd *))
{
@@ -1340,6 +2315,17 @@ lpfc_queuecommand(struct scsi_cmnd *cmnd, void (*done) (struct scsi_cmnd *))
goto out_fail_command;
}
+ 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",
+ cmnd->cmnd[0], scsi_get_prot_op(cmnd),
+ dif_op_str[scsi_get_prot_op(cmnd)]);
+ goto out_fail_command;
+ }
+
/*
* Catch race where our node has transitioned, but the
* transport is still transitioning.
@@ -1348,12 +2334,13 @@ lpfc_queuecommand(struct scsi_cmnd *cmnd, void (*done) (struct scsi_cmnd *))
cmnd->result = ScsiResult(DID_TRANSPORT_DISRUPTED, 0);
goto out_fail_command;
}
- if (atomic_read(&ndlp->cmd_pending) >= ndlp->cmd_qdepth)
+ if (vport->cfg_max_scsicmpl_time &&
+ (atomic_read(&ndlp->cmd_pending) >= ndlp->cmd_qdepth))
goto out_host_busy;
lpfc_cmd = lpfc_get_scsi_buf(phba);
if (lpfc_cmd == NULL) {
- lpfc_adjust_queue_depth(phba);
+ lpfc_rampdown_queue_depth(phba);
lpfc_printf_vlog(vport, KERN_INFO, LOG_FCP,
"0707 driver's buffer pool is empty, "
@@ -1361,7 +2348,6 @@ lpfc_queuecommand(struct scsi_cmnd *cmnd, void (*done) (struct scsi_cmnd *))
goto out_host_busy;
}
- lpfc_cmd->start_time = jiffies;
/*
* Store the midlayer's command structure for the completion phase
* and complete the command initialization.
@@ -1373,7 +2359,65 @@ lpfc_queuecommand(struct scsi_cmnd *cmnd, void (*done) (struct scsi_cmnd *))
cmnd->host_scribble = (unsigned char *)lpfc_cmd;
cmnd->scsi_done = done;
- err = lpfc_scsi_prep_dma_buf(phba, lpfc_cmd);
+ if (scsi_get_prot_op(cmnd) != SCSI_PROT_NORMAL) {
+ 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,
+ "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,
+ "9035 BLKGRD: READ @ sector %llu, "
+ "count %lu\n",
+ (unsigned long long)scsi_get_lba(cmnd),
+ cmnd->request->nr_sectors);
+ else if (cmnd->cmnd[0] == WRITE_10)
+ lpfc_printf_vlog(vport, KERN_WARNING, LOG_BG,
+ "9036 BLKGRD: WRITE @ sector %llu, "
+ "count %lu cmd=%p\n",
+ (unsigned long long)scsi_get_lba(cmnd),
+ cmnd->request->nr_sectors,
+ 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)
+ lpfc_printf_vlog(vport, KERN_WARNING, LOG_BG,
+ "9040 dbg: READ @ sector %llu, "
+ "count %lu\n",
+ (unsigned long long)scsi_get_lba(cmnd),
+ cmnd->request->nr_sectors);
+ else if (cmnd->cmnd[0] == WRITE_10)
+ lpfc_printf_vlog(vport, KERN_WARNING, LOG_BG,
+ "9041 dbg: WRITE @ sector %llu, "
+ "count %lu cmd=%p\n",
+ (unsigned long long)scsi_get_lba(cmnd),
+ cmnd->request->nr_sectors, cmnd);
+ else
+ lpfc_printf_vlog(vport, KERN_WARNING, LOG_BG,
+ "9042 dbg: parser not implemented\n");
+ err = lpfc_scsi_prep_dma_buf(phba, lpfc_cmd);
+ }
+
if (err)
goto out_host_busy_free_buf;
@@ -1382,9 +2426,10 @@ lpfc_queuecommand(struct scsi_cmnd *cmnd, void (*done) (struct scsi_cmnd *))
atomic_inc(&ndlp->cmd_pending);
err = lpfc_sli_issue_iocb(phba, &phba->sli.ring[psli->fcp_ring],
&lpfc_cmd->cur_iocbq, SLI_IOCB_RET_IOCB);
- if (err)
+ if (err) {
+ atomic_dec(&ndlp->cmd_pending);
goto out_host_busy_free_buf;
-
+ }
if (phba->cfg_poll & ENABLE_FCP_RING_POLLING) {
lpfc_sli_poll_fcp_ring(phba);
if (phba->cfg_poll & DISABLE_FCP_RING_INT)
@@ -1394,7 +2439,6 @@ lpfc_queuecommand(struct scsi_cmnd *cmnd, void (*done) (struct scsi_cmnd *))
return 0;
out_host_busy_free_buf:
- atomic_dec(&ndlp->cmd_pending);
lpfc_scsi_unprep_dma_buf(phba, lpfc_cmd);
lpfc_release_scsi_buf(phba, lpfc_cmd);
out_host_busy:
@@ -1405,6 +2449,12 @@ lpfc_queuecommand(struct scsi_cmnd *cmnd, void (*done) (struct scsi_cmnd *))
return 0;
}
+/**
+ * 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)
{
@@ -1421,6 +2471,17 @@ lpfc_block_error_handler(struct scsi_cmnd *cmnd)
return;
}
+/**
+ * lpfc_abort_handler: Eh_abort_handler entry point of Scsi Host Template data
+ *structure.
+ * @cmnd: Pointer to scsi_cmnd data structure.
+ *
+ * This routine aborts @cmnd pending in base driver.
+ *
+ * Return code :
+ * 0x2003 - Error
+ * 0x2002 - Success
+ **/
static int
lpfc_abort_handler(struct scsi_cmnd *cmnd)
{
@@ -1516,6 +2577,18 @@ lpfc_abort_handler(struct scsi_cmnd *cmnd)
return ret;
}
+/**
+ * lpfc_device_reset_handler: eh_device_reset entry point of Scsi Host Template
+ *data structure.
+ * @cmnd: Pointer to scsi_cmnd data structure.
+ *
+ * This routine does a device reset by sending a TARGET_RESET task management
+ * command.
+ *
+ * Return code :
+ * 0x2003 - Error
+ * 0ex2002 - Success
+ **/
static int
lpfc_device_reset_handler(struct scsi_cmnd *cmnd)
{
@@ -1560,7 +2633,7 @@ lpfc_device_reset_handler(struct scsi_cmnd *cmnd)
fc_get_event_number(),
sizeof(scsi_event),
(char *)&scsi_event,
- SCSI_NL_VID_TYPE_PCI | PCI_VENDOR_ID_EMULEX);
+ LPFC_NL_VENDOR_ID);
if (!rdata || pnode->nlp_state != NLP_STE_MAPPED_NODE) {
lpfc_printf_vlog(vport, KERN_ERR, LOG_FCP,
@@ -1633,6 +2706,17 @@ lpfc_device_reset_handler(struct scsi_cmnd *cmnd)
return ret;
}
+/**
+ * lpfc_bus_reset_handler: eh_bus_reset_handler entry point of Scsi Host
+ * Template data structure.
+ * @cmnd: Pointer to scsi_cmnd data structure.
+ *
+ * This routine does target reset to all target on @cmnd->device->host.
+ *
+ * Return Code:
+ * 0x2003 - Error
+ * 0x2002 - Success
+ **/
static int
lpfc_bus_reset_handler(struct scsi_cmnd *cmnd)
{
@@ -1657,7 +2741,7 @@ lpfc_bus_reset_handler(struct scsi_cmnd *cmnd)
fc_get_event_number(),
sizeof(scsi_event),
(char *)&scsi_event,
- SCSI_NL_VID_TYPE_PCI | PCI_VENDOR_ID_EMULEX);
+ LPFC_NL_VENDOR_ID);
lpfc_block_error_handler(cmnd);
/*
@@ -1723,6 +2807,20 @@ lpfc_bus_reset_handler(struct scsi_cmnd *cmnd)
return ret;
}
+/**
+ * lpfc_slave_alloc: slave_alloc entry point of Scsi Host Template data
+ * structure.
+ * @sdev: Pointer to scsi_device.
+ *
+ * This routine populates the cmds_per_lun count + 2 scsi_bufs into this host's
+ * globally available list of scsi buffers. This routine also makes sure scsi
+ * buffer is not allocated more than HBA limit conveyed to midlayer. This list
+ * of scsi buffer exists for the lifetime of the driver.
+ *
+ * Return codes:
+ * non-0 - Error
+ * 0 - Success
+ **/
static int
lpfc_slave_alloc(struct scsi_device *sdev)
{
@@ -1784,6 +2882,19 @@ lpfc_slave_alloc(struct scsi_device *sdev)
return 0;
}
+/**
+ * lpfc_slave_configure: slave_configure entry point of Scsi Host Templater data
+ * structure.
+ * @sdev: Pointer to scsi_device.
+ *
+ * This routine configures following items
+ * - Tag command queuing support for @sdev if supported.
+ * - Dev loss time out value of fc_rport.
+ * - Enable SLI polling for fcp ring if ENABLE_FCP_RING_POLLING flag is set.
+ *
+ * Return codes:
+ * 0 - Success
+ **/
static int
lpfc_slave_configure(struct scsi_device *sdev)
{
@@ -1813,6 +2924,12 @@ lpfc_slave_configure(struct scsi_device *sdev)
return 0;
}
+/**
+ * lpfc_slave_destroy: slave_destroy entry point of SHT data structure.
+ * @sdev: Pointer to scsi_device.
+ *
+ * This routine sets @sdev hostatdata filed to null.
+ **/
static void
lpfc_slave_destroy(struct scsi_device *sdev)
{
diff --git a/drivers/scsi/lpfc/lpfc_scsi.h b/drivers/scsi/lpfc/lpfc_scsi.h
index 437f182e2322..c7c440d5fa29 100644
--- a/drivers/scsi/lpfc/lpfc_scsi.h
+++ b/drivers/scsi/lpfc/lpfc_scsi.h
@@ -124,6 +124,8 @@ struct lpfc_scsi_buf {
uint32_t seg_cnt; /* Number of scatter-gather segments returned by
* dma_map_sg. The driver needs this for calls
* to dma_unmap_sg. */
+ uint32_t prot_seg_cnt; /* seg_cnt's counterpart for protection data */
+
dma_addr_t nonsg_phys; /* Non scatter-gather physical address. */
/*
diff --git a/drivers/scsi/lpfc/lpfc_sli.c b/drivers/scsi/lpfc/lpfc_sli.c
index 8ab5babdeebc..01dfdc8696f8 100644
--- a/drivers/scsi/lpfc/lpfc_sli.c
+++ b/drivers/scsi/lpfc/lpfc_sli.c
@@ -542,6 +542,7 @@ lpfc_sli_submit_iocb(struct lpfc_hba *phba, struct lpfc_sli_ring *pring,
*/
nextiocb->iocb.ulpIoTag = (nextiocb->iocb_cmpl) ? nextiocb->iotag : 0;
+
if (pring->ringno == LPFC_ELS_RING) {
lpfc_debugfs_slow_ring_trc(phba,
"IOCB cmd ring: wd4:x%08x wd6:x%08x wd7:x%08x",
@@ -1259,68 +1260,6 @@ lpfc_sli_handle_mb_event(struct lpfc_hba *phba)
}
/**
- * lpfc_sli_replace_hbqbuff: Replace the HBQ buffer with a new buffer.
- * @phba: Pointer to HBA context object.
- * @tag: Tag for the HBQ buffer.
- *
- * This function is called from unsolicited event handler code path to get the
- * HBQ buffer associated with an unsolicited iocb. This function is called with
- * no lock held. It returns the buffer associated with the given tag and posts
- * another buffer to the firmware. Note that the new buffer must be allocated
- * before taking the hbalock and that the hba lock must be held until it is
- * finished with the hbq entry swap.
- **/
-static struct lpfc_dmabuf *
-lpfc_sli_replace_hbqbuff(struct lpfc_hba *phba, uint32_t tag)
-{
- struct hbq_dmabuf *hbq_entry, *new_hbq_entry;
- uint32_t hbqno;
- void *virt; /* virtual address ptr */
- dma_addr_t phys; /* mapped address */
- unsigned long flags;
-
- hbqno = tag >> 16;
- new_hbq_entry = (phba->hbqs[hbqno].hbq_alloc_buffer)(phba);
- /* Check whether HBQ is still in use */
- spin_lock_irqsave(&phba->hbalock, flags);
- if (!phba->hbq_in_use) {
- if (new_hbq_entry)
- (phba->hbqs[hbqno].hbq_free_buffer)(phba,
- new_hbq_entry);
- spin_unlock_irqrestore(&phba->hbalock, flags);
- return NULL;
- }
-
- hbq_entry = lpfc_sli_hbqbuf_find(phba, tag);
- if (hbq_entry == NULL) {
- if (new_hbq_entry)
- (phba->hbqs[hbqno].hbq_free_buffer)(phba,
- new_hbq_entry);
- spin_unlock_irqrestore(&phba->hbalock, flags);
- return NULL;
- }
- list_del(&hbq_entry->dbuf.list);
-
- if (new_hbq_entry == NULL) {
- list_add_tail(&hbq_entry->dbuf.list, &phba->hbqbuf_in_list);
- spin_unlock_irqrestore(&phba->hbalock, flags);
- return &hbq_entry->dbuf;
- }
- new_hbq_entry->tag = -1;
- phys = new_hbq_entry->dbuf.phys;
- virt = new_hbq_entry->dbuf.virt;
- new_hbq_entry->dbuf.phys = hbq_entry->dbuf.phys;
- new_hbq_entry->dbuf.virt = hbq_entry->dbuf.virt;
- hbq_entry->dbuf.phys = phys;
- hbq_entry->dbuf.virt = virt;
- lpfc_sli_free_hbq(phba, hbq_entry);
- list_add_tail(&new_hbq_entry->dbuf.list, &phba->hbqbuf_in_list);
- spin_unlock_irqrestore(&phba->hbalock, flags);
-
- return &new_hbq_entry->dbuf;
-}
-
-/**
* lpfc_sli_get_buff: Get the buffer associated with the buffer tag.
* @phba: Pointer to HBA context object.
* @pring: Pointer to driver SLI ring object.
@@ -1334,13 +1273,17 @@ lpfc_sli_replace_hbqbuff(struct lpfc_hba *phba, uint32_t tag)
**/
static struct lpfc_dmabuf *
lpfc_sli_get_buff(struct lpfc_hba *phba,
- struct lpfc_sli_ring *pring,
- uint32_t tag)
+ struct lpfc_sli_ring *pring,
+ uint32_t tag)
{
+ struct hbq_dmabuf *hbq_entry;
+
if (tag & QUE_BUFTAG_BIT)
return lpfc_sli_ring_taggedbuf_get(phba, pring, tag);
- else
- return lpfc_sli_replace_hbqbuff(phba, tag);
+ hbq_entry = lpfc_sli_hbqbuf_find(phba, tag);
+ if (!hbq_entry)
+ return NULL;
+ return &hbq_entry->dbuf;
}
@@ -1372,8 +1315,6 @@ lpfc_sli_process_unsol_iocb(struct lpfc_hba *phba, struct lpfc_sli_ring *pring,
match = 0;
irsp = &(saveq->iocb);
- if (irsp->ulpStatus == IOSTAT_NEED_BUFFER)
- return 1;
if (irsp->ulpCommand == CMD_ASYNC_STATUS) {
if (pring->lpfc_sli_rcv_async_status)
pring->lpfc_sli_rcv_async_status(phba, pring, saveq);
@@ -1982,7 +1923,7 @@ lpfc_sli_handle_fast_ring_event(struct lpfc_hba *phba,
if ((irsp->ulpStatus == IOSTAT_LOCAL_REJECT) &&
(irsp->un.ulpWord[4] == IOERR_NO_RESOURCES)) {
spin_unlock_irqrestore(&phba->hbalock, iflag);
- lpfc_adjust_queue_depth(phba);
+ lpfc_rampdown_queue_depth(phba);
spin_lock_irqsave(&phba->hbalock, iflag);
}
@@ -2225,7 +2166,7 @@ lpfc_sli_handle_slow_ring_event(struct lpfc_hba *phba,
if ((irsp->ulpStatus == IOSTAT_LOCAL_REJECT) &&
(irsp->un.ulpWord[4] == IOERR_NO_RESOURCES)) {
spin_unlock_irqrestore(&phba->hbalock, iflag);
- lpfc_adjust_queue_depth(phba);
+ lpfc_rampdown_queue_depth(phba);
spin_lock_irqsave(&phba->hbalock, iflag);
}
@@ -2790,7 +2731,6 @@ lpfc_sli_brdrestart(struct lpfc_hba *phba)
{
MAILBOX_t *mb;
struct lpfc_sli *psli;
- uint16_t skip_post;
volatile uint32_t word0;
void __iomem *to_slim;
@@ -2815,13 +2755,10 @@ lpfc_sli_brdrestart(struct lpfc_hba *phba)
readl(to_slim); /* flush */
/* Only skip post after fc_ffinit is completed */
- if (phba->pport->port_state) {
- skip_post = 1;
+ if (phba->pport->port_state)
word0 = 1; /* This is really setting up word1 */
- } else {
- skip_post = 0;
+ else
word0 = 0; /* This is really setting up word1 */
- }
to_slim = phba->MBslimaddr + sizeof (uint32_t);
writel(*(uint32_t *) mb, to_slim);
readl(to_slim); /* flush */
@@ -2835,10 +2772,8 @@ lpfc_sli_brdrestart(struct lpfc_hba *phba)
memset(&psli->lnk_stat_offsets, 0, sizeof(psli->lnk_stat_offsets));
psli->stats_start = get_seconds();
- if (skip_post)
- mdelay(100);
- else
- mdelay(2000);
+ /* Give the INITFF and Post time to settle. */
+ mdelay(100);
lpfc_hba_down_post(phba);
@@ -3084,7 +3019,6 @@ lpfc_sli_config_port(struct lpfc_hba *phba, int sli_mode)
spin_unlock_irq(&phba->hbalock);
phba->pport->port_state = LPFC_VPORT_UNKNOWN;
lpfc_sli_brdrestart(phba);
- msleep(2500);
rc = lpfc_sli_chipset_init(phba);
if (rc)
break;
@@ -3111,7 +3045,8 @@ lpfc_sli_config_port(struct lpfc_hba *phba, int sli_mode)
phba->sli3_options &= ~(LPFC_SLI3_NPIV_ENABLED |
LPFC_SLI3_HBQ_ENABLED |
LPFC_SLI3_CRP_ENABLED |
- LPFC_SLI3_INB_ENABLED);
+ LPFC_SLI3_INB_ENABLED |
+ LPFC_SLI3_BG_ENABLED);
if (rc != MBX_SUCCESS) {
lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
"0442 Adapter failed to init, mbxCmd x%x "
@@ -3144,17 +3079,29 @@ lpfc_sli_config_port(struct lpfc_hba *phba, int sli_mode)
phba->sli3_options |= LPFC_SLI3_CRP_ENABLED;
if (pmb->mb.un.varCfgPort.ginb) {
phba->sli3_options |= LPFC_SLI3_INB_ENABLED;
+ phba->hbq_get = phba->mbox->us.s3_inb_pgp.hbq_get;
phba->port_gp = phba->mbox->us.s3_inb_pgp.port;
phba->inb_ha_copy = &phba->mbox->us.s3_inb_pgp.ha_copy;
phba->inb_counter = &phba->mbox->us.s3_inb_pgp.counter;
phba->inb_last_counter =
phba->mbox->us.s3_inb_pgp.counter;
} else {
+ phba->hbq_get = phba->mbox->us.s3_pgp.hbq_get;
phba->port_gp = phba->mbox->us.s3_pgp.port;
phba->inb_ha_copy = NULL;
phba->inb_counter = NULL;
}
+
+ if (phba->cfg_enable_bg) {
+ if (pmb->mb.un.varCfgPort.gbg)
+ phba->sli3_options |= LPFC_SLI3_BG_ENABLED;
+ else
+ lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+ "0443 Adapter did not grant "
+ "BlockGuard\n");
+ }
} else {
+ phba->hbq_get = NULL;
phba->port_gp = phba->mbox->us.s2.port;
phba->inb_ha_copy = NULL;
phba->inb_counter = NULL;
@@ -3305,10 +3252,6 @@ lpfc_mbox_timeout_handler(struct lpfc_hba *phba)
struct lpfc_sli *psli = &phba->sli;
struct lpfc_sli_ring *pring;
- if (!(phba->pport->work_port_events & WORKER_MBOX_TMO)) {
- return;
- }
-
/* Mbox cmd <mbxCommand> timeout */
lpfc_printf_log(phba, KERN_ERR, LOG_MBOX | LOG_SLI,
"0310 Mailbox command x%x timeout Data: x%x x%x x%p\n",
@@ -4005,7 +3948,7 @@ lpfc_sli_async_event_handler(struct lpfc_hba * phba,
shost = lpfc_shost_from_vport(phba->pport);
fc_host_post_vendor_event(shost, fc_get_event_number(),
sizeof(temp_event_data), (char *) &temp_event_data,
- SCSI_NL_VID_TYPE_PCI | PCI_VENDOR_ID_EMULEX);
+ LPFC_NL_VENDOR_ID);
}
@@ -5184,6 +5127,10 @@ lpfc_sli_check_eratt(struct lpfc_hba *phba)
{
uint32_t ha_copy;
+ /* If PCI channel is offline, don't process it */
+ if (unlikely(pci_channel_offline(phba->pcidev)))
+ return 0;
+
/* If somebody is waiting to handle an eratt, don't process it
* here. The brdkill function will do this.
*/
@@ -5242,6 +5189,7 @@ lpfc_sp_intr_handler(int irq, void *dev_id)
uint32_t ha_copy;
uint32_t work_ha_copy;
unsigned long status;
+ unsigned long iflag;
uint32_t control;
MAILBOX_t *mbox, *pmbox;
@@ -5274,7 +5222,7 @@ lpfc_sp_intr_handler(int irq, void *dev_id)
if (unlikely(phba->link_state < LPFC_LINK_DOWN))
return IRQ_NONE;
/* Need to read HA REG for slow-path events */
- spin_lock(&phba->hbalock);
+ spin_lock_irqsave(&phba->hbalock, iflag);
ha_copy = readl(phba->HAregaddr);
/* If somebody is waiting to handle an eratt don't process it
* here. The brdkill function will do this.
@@ -5294,7 +5242,7 @@ lpfc_sp_intr_handler(int irq, void *dev_id)
writel((ha_copy & (HA_MBATT | HA_R2_CLR_MSK)),
phba->HAregaddr);
readl(phba->HAregaddr); /* flush */
- spin_unlock(&phba->hbalock);
+ spin_unlock_irqrestore(&phba->hbalock, iflag);
} else
ha_copy = phba->ha_copy;
@@ -5307,13 +5255,13 @@ lpfc_sp_intr_handler(int irq, void *dev_id)
* Turn off Link Attention interrupts
* until CLEAR_LA done
*/
- spin_lock(&phba->hbalock);
+ spin_lock_irqsave(&phba->hbalock, iflag);
phba->sli.sli_flag &= ~LPFC_PROCESS_LA;
control = readl(phba->HCregaddr);
control &= ~HC_LAINT_ENA;
writel(control, phba->HCregaddr);
readl(phba->HCregaddr); /* flush */
- spin_unlock(&phba->hbalock);
+ spin_unlock_irqrestore(&phba->hbalock, iflag);
}
else
work_ha_copy &= ~HA_LATT;
@@ -5328,7 +5276,7 @@ lpfc_sp_intr_handler(int irq, void *dev_id)
(HA_RXMASK << (4*LPFC_ELS_RING)));
status >>= (4*LPFC_ELS_RING);
if (status & HA_RXMASK) {
- spin_lock(&phba->hbalock);
+ spin_lock_irqsave(&phba->hbalock, iflag);
control = readl(phba->HCregaddr);
lpfc_debugfs_slow_ring_trc(phba,
@@ -5357,10 +5305,10 @@ lpfc_sp_intr_handler(int irq, void *dev_id)
(uint32_t)((unsigned long)
&phba->work_waitq));
}
- spin_unlock(&phba->hbalock);
+ spin_unlock_irqrestore(&phba->hbalock, iflag);
}
}
- spin_lock(&phba->hbalock);
+ spin_lock_irqsave(&phba->hbalock, iflag);
if (work_ha_copy & HA_ERATT)
lpfc_sli_read_hs(phba);
if ((work_ha_copy & HA_MBATT) && (phba->sli.mbox_active)) {
@@ -5372,7 +5320,7 @@ lpfc_sp_intr_handler(int irq, void *dev_id)
/* First check out the status word */
lpfc_sli_pcimem_bcopy(mbox, pmbox, sizeof(uint32_t));
if (pmbox->mbxOwner != OWN_HOST) {
- spin_unlock(&phba->hbalock);
+ spin_unlock_irqrestore(&phba->hbalock, iflag);
/*
* Stray Mailbox Interrupt, mbxCommand <cmd>
* mbxStatus <status>
@@ -5389,7 +5337,7 @@ lpfc_sp_intr_handler(int irq, void *dev_id)
work_ha_copy &= ~HA_MBATT;
} else {
phba->sli.mbox_active = NULL;
- spin_unlock(&phba->hbalock);
+ spin_unlock_irqrestore(&phba->hbalock, iflag);
phba->last_completion_time = jiffies;
del_timer(&phba->sli.mbox_tmo);
if (pmb->mbox_cmpl) {
@@ -5438,14 +5386,18 @@ lpfc_sp_intr_handler(int irq, void *dev_id)
goto send_current_mbox;
}
}
- spin_lock(&phba->pport->work_port_lock);
+ spin_lock_irqsave(
+ &phba->pport->work_port_lock,
+ iflag);
phba->pport->work_port_events &=
~WORKER_MBOX_TMO;
- spin_unlock(&phba->pport->work_port_lock);
+ spin_unlock_irqrestore(
+ &phba->pport->work_port_lock,
+ iflag);
lpfc_mbox_cmpl_put(phba, pmb);
}
} else
- spin_unlock(&phba->hbalock);
+ spin_unlock_irqrestore(&phba->hbalock, iflag);
if ((work_ha_copy & HA_MBATT) &&
(phba->sli.mbox_active == NULL)) {
@@ -5461,9 +5413,9 @@ send_current_mbox:
"MBX_SUCCESS");
}
- spin_lock(&phba->hbalock);
+ spin_lock_irqsave(&phba->hbalock, iflag);
phba->work_ha |= work_ha_copy;
- spin_unlock(&phba->hbalock);
+ spin_unlock_irqrestore(&phba->hbalock, iflag);
lpfc_worker_wake_up(phba);
}
return IRQ_HANDLED;
@@ -5495,6 +5447,7 @@ lpfc_fp_intr_handler(int irq, void *dev_id)
struct lpfc_hba *phba;
uint32_t ha_copy;
unsigned long status;
+ unsigned long iflag;
/* Get the driver's phba structure from the dev_id and
* assume the HBA is not interrupting.
@@ -5520,11 +5473,11 @@ lpfc_fp_intr_handler(int irq, void *dev_id)
/* Need to read HA REG for FCP ring and other ring events */
ha_copy = readl(phba->HAregaddr);
/* Clear up only attention source related to fast-path */
- spin_lock(&phba->hbalock);
+ spin_lock_irqsave(&phba->hbalock, iflag);
writel((ha_copy & (HA_R0_CLR_MSK | HA_R1_CLR_MSK)),
phba->HAregaddr);
readl(phba->HAregaddr); /* flush */
- spin_unlock(&phba->hbalock);
+ spin_unlock_irqrestore(&phba->hbalock, iflag);
} else
ha_copy = phba->ha_copy;
diff --git a/drivers/scsi/lpfc/lpfc_version.h b/drivers/scsi/lpfc/lpfc_version.h
index cc43e9de22cc..7e32e95c5392 100644
--- a/drivers/scsi/lpfc/lpfc_version.h
+++ b/drivers/scsi/lpfc/lpfc_version.h
@@ -18,7 +18,7 @@
* included with this package. *
*******************************************************************/
-#define LPFC_DRIVER_VERSION "8.2.8"
+#define LPFC_DRIVER_VERSION "8.3.0"
#define LPFC_DRIVER_NAME "lpfc"
#define LPFC_SP_DRIVER_HANDLER_NAME "lpfc:sp"
diff --git a/drivers/scsi/lpfc/lpfc_vport.c b/drivers/scsi/lpfc/lpfc_vport.c
index a7de1cc02b40..63b54c66756c 100644
--- a/drivers/scsi/lpfc/lpfc_vport.c
+++ b/drivers/scsi/lpfc/lpfc_vport.c
@@ -288,10 +288,8 @@ lpfc_vport_create(struct fc_vport *fc_vport, bool disable)
int vpi;
int rc = VPORT_ERROR;
int status;
- int size;
- if ((phba->sli_rev < 3) ||
- !(phba->sli3_options & LPFC_SLI3_NPIV_ENABLED)) {
+ if ((phba->sli_rev < 3) || !(phba->cfg_enable_npiv)) {
lpfc_printf_log(phba, KERN_ERR, LOG_VPORT,
"1808 Create VPORT failed: "
"NPIV is not enabled: SLImode:%d\n",
@@ -351,20 +349,6 @@ lpfc_vport_create(struct fc_vport *fc_vport, bool disable)
memcpy(vport->fc_portname.u.wwn, vport->fc_sparam.portName.u.wwn, 8);
memcpy(vport->fc_nodename.u.wwn, vport->fc_sparam.nodeName.u.wwn, 8);
- size = strnlen(fc_vport->symbolic_name, LPFC_VNAME_LEN);
- if (size) {
- vport->vname = kzalloc(size+1, GFP_KERNEL);
- if (!vport->vname) {
- lpfc_printf_vlog(vport, KERN_ERR, LOG_VPORT,
- "1814 Create VPORT failed. "
- "vname allocation failed.\n");
- rc = VPORT_ERROR;
- lpfc_free_vpi(phba, vpi);
- destroy_port(vport);
- goto error_out;
- }
- memcpy(vport->vname, fc_vport->symbolic_name, size+1);
- }
if (fc_vport->node_name != 0)
u64_to_wwn(fc_vport->node_name, vport->fc_nodename.u.wwn);
if (fc_vport->port_name != 0)
@@ -394,6 +378,9 @@ lpfc_vport_create(struct fc_vport *fc_vport, bool disable)
goto error_out;
}
+ /* Create binary sysfs attribute for vport */
+ lpfc_alloc_sysfs_attr(vport);
+
*(struct lpfc_vport **)fc_vport->dd_data = vport;
vport->fc_vport = fc_vport;
@@ -405,6 +392,7 @@ lpfc_vport_create(struct fc_vport *fc_vport, bool disable)
}
if (disable) {
+ lpfc_vport_set_state(vport, FC_VPORT_DISABLED);
rc = VPORT_OK;
goto out;
}
@@ -587,8 +575,12 @@ lpfc_vport_delete(struct fc_vport *fc_vport)
spin_lock_irq(&phba->hbalock);
vport->load_flag |= FC_UNLOADING;
spin_unlock_irq(&phba->hbalock);
- kfree(vport->vname);
+
+ lpfc_free_sysfs_attr(vport);
+
lpfc_debugfs_terminate(vport);
+
+ /* Remove FC host and then SCSI host with the vport */
fc_remove_host(lpfc_shost_from_vport(vport));
scsi_remove_host(lpfc_shost_from_vport(vport));