[SCSI] lpfc 8.3.7: Fix NPIV operation errors
[safe/jmp/linux-2.6] / drivers / scsi / qla2xxx / qla_attr.c
index 15573f6..3a9f5b2 100644 (file)
@@ -97,7 +97,7 @@ qla2x00_sysfs_read_nvram(struct kobject *kobj,
                return 0;
 
        if (IS_NOCACHE_VPD_TYPE(ha))
-               ha->isp_ops->read_optrom(vha, ha->vpd, ha->flt_region_nvram << 2,
+               ha->isp_ops->read_optrom(vha, ha->nvram, ha->flt_region_nvram << 2,
                    ha->nvram_size);
        return memory_read_from_buffer(buf, count, &off, ha->nvram,
                                        ha->nvram_size);
@@ -232,6 +232,9 @@ qla2x00_sysfs_write_optrom_ctl(struct kobject *kobj,
        if (off)
                return 0;
 
+       if (unlikely(pci_channel_offline(ha->pdev)))
+               return 0;
+
        if (sscanf(buf, "%d:%x:%x", &val, &start, &size) < 1)
                return -EINVAL;
        if (start > ha->optrom_size)
@@ -379,6 +382,9 @@ qla2x00_sysfs_read_vpd(struct kobject *kobj,
            struct device, kobj)));
        struct qla_hw_data *ha = vha->hw;
 
+       if (unlikely(pci_channel_offline(ha->pdev)))
+               return 0;
+
        if (!capable(CAP_SYS_ADMIN))
                return 0;
 
@@ -398,6 +404,9 @@ qla2x00_sysfs_write_vpd(struct kobject *kobj,
        struct qla_hw_data *ha = vha->hw;
        uint8_t *tmp_data;
 
+       if (unlikely(pci_channel_offline(ha->pdev)))
+               return 0;
+
        if (!capable(CAP_SYS_ADMIN) || off != 0 || count != ha->vpd_size ||
            !ha->isp_ops->write_nvram)
                return 0;
@@ -692,6 +701,109 @@ static struct bin_attribute sysfs_edc_status_attr = {
        .read = qla2x00_sysfs_read_edc_status,
 };
 
+static ssize_t
+qla2x00_sysfs_read_xgmac_stats(struct kobject *kobj,
+                      struct bin_attribute *bin_attr,
+                      char *buf, loff_t off, size_t count)
+{
+       struct scsi_qla_host *vha = shost_priv(dev_to_shost(container_of(kobj,
+           struct device, kobj)));
+       struct qla_hw_data *ha = vha->hw;
+       int rval;
+       uint16_t actual_size;
+
+       if (!capable(CAP_SYS_ADMIN) || off != 0 || count > XGMAC_DATA_SIZE)
+               return 0;
+
+       if (ha->xgmac_data)
+               goto do_read;
+
+       ha->xgmac_data = dma_alloc_coherent(&ha->pdev->dev, XGMAC_DATA_SIZE,
+           &ha->xgmac_data_dma, GFP_KERNEL);
+       if (!ha->xgmac_data) {
+               qla_printk(KERN_WARNING, ha,
+                   "Unable to allocate memory for XGMAC read-data.\n");
+               return 0;
+       }
+
+do_read:
+       actual_size = 0;
+       memset(ha->xgmac_data, 0, XGMAC_DATA_SIZE);
+
+       rval = qla2x00_get_xgmac_stats(vha, ha->xgmac_data_dma,
+           XGMAC_DATA_SIZE, &actual_size);
+       if (rval != QLA_SUCCESS) {
+               qla_printk(KERN_WARNING, ha,
+                   "Unable to read XGMAC data (%x).\n", rval);
+               count = 0;
+       }
+
+       count = actual_size > count ? count: actual_size;
+       memcpy(buf, ha->xgmac_data, count);
+
+       return count;
+}
+
+static struct bin_attribute sysfs_xgmac_stats_attr = {
+       .attr = {
+               .name = "xgmac_stats",
+               .mode = S_IRUSR,
+       },
+       .size = 0,
+       .read = qla2x00_sysfs_read_xgmac_stats,
+};
+
+static ssize_t
+qla2x00_sysfs_read_dcbx_tlv(struct kobject *kobj,
+                      struct bin_attribute *bin_attr,
+                      char *buf, loff_t off, size_t count)
+{
+       struct scsi_qla_host *vha = shost_priv(dev_to_shost(container_of(kobj,
+           struct device, kobj)));
+       struct qla_hw_data *ha = vha->hw;
+       int rval;
+       uint16_t actual_size;
+
+       if (!capable(CAP_SYS_ADMIN) || off != 0 || count > DCBX_TLV_DATA_SIZE)
+               return 0;
+
+       if (ha->dcbx_tlv)
+               goto do_read;
+
+       ha->dcbx_tlv = dma_alloc_coherent(&ha->pdev->dev, DCBX_TLV_DATA_SIZE,
+           &ha->dcbx_tlv_dma, GFP_KERNEL);
+       if (!ha->dcbx_tlv) {
+               qla_printk(KERN_WARNING, ha,
+                   "Unable to allocate memory for DCBX TLV read-data.\n");
+               return 0;
+       }
+
+do_read:
+       actual_size = 0;
+       memset(ha->dcbx_tlv, 0, DCBX_TLV_DATA_SIZE);
+
+       rval = qla2x00_get_dcbx_params(vha, ha->dcbx_tlv_dma,
+           DCBX_TLV_DATA_SIZE);
+       if (rval != QLA_SUCCESS) {
+               qla_printk(KERN_WARNING, ha,
+                   "Unable to read DCBX TLV data (%x).\n", rval);
+               count = 0;
+       }
+
+       memcpy(buf, ha->dcbx_tlv, count);
+
+       return count;
+}
+
+static struct bin_attribute sysfs_dcbx_tlv_attr = {
+       .attr = {
+               .name = "dcbx_tlv",
+               .mode = S_IRUSR,
+       },
+       .size = 0,
+       .read = qla2x00_sysfs_read_dcbx_tlv,
+};
+
 static struct sysfs_entry {
        char *name;
        struct bin_attribute *attr;
@@ -706,6 +818,8 @@ static struct sysfs_entry {
        { "reset", &sysfs_reset_attr, },
        { "edc", &sysfs_edc_attr, 2 },
        { "edc_status", &sysfs_edc_status_attr, 2 },
+       { "xgmac_stats", &sysfs_xgmac_stats_attr, 3 },
+       { "dcbx_tlv", &sysfs_dcbx_tlv_attr, 3 },
        { NULL },
 };
 
@@ -721,6 +835,8 @@ qla2x00_alloc_sysfs_attr(scsi_qla_host_t *vha)
                        continue;
                if (iter->is4GBp_only == 2 && !IS_QLA25XX(vha->hw))
                        continue;
+               if (iter->is4GBp_only == 3 && !IS_QLA81XX(vha->hw))
+                       continue;
 
                ret = sysfs_create_bin_file(&host->shost_gendev.kobj,
                    iter->attr);
@@ -743,6 +859,8 @@ qla2x00_free_sysfs_attr(scsi_qla_host_t *vha)
                        continue;
                if (iter->is4GBp_only == 2 && !IS_QLA25XX(ha))
                        continue;
+               if (iter->is4GBp_only == 3 && !IS_QLA81XX(ha))
+                       continue;
 
                sysfs_remove_bin_file(&host->shost_gendev.kobj,
                    iter->attr);
@@ -1088,6 +1206,59 @@ qla2x00_flash_block_size_show(struct device *dev,
        return snprintf(buf, PAGE_SIZE, "0x%x\n", ha->fdt_block_size);
 }
 
+static ssize_t
+qla2x00_vlan_id_show(struct device *dev, struct device_attribute *attr,
+    char *buf)
+{
+       scsi_qla_host_t *vha = shost_priv(class_to_shost(dev));
+
+       if (!IS_QLA81XX(vha->hw))
+               return snprintf(buf, PAGE_SIZE, "\n");
+
+       return snprintf(buf, PAGE_SIZE, "%d\n", vha->fcoe_vlan_id);
+}
+
+static ssize_t
+qla2x00_vn_port_mac_address_show(struct device *dev,
+    struct device_attribute *attr, char *buf)
+{
+       scsi_qla_host_t *vha = shost_priv(class_to_shost(dev));
+
+       if (!IS_QLA81XX(vha->hw))
+               return snprintf(buf, PAGE_SIZE, "\n");
+
+       return snprintf(buf, PAGE_SIZE, "%02x:%02x:%02x:%02x:%02x:%02x\n",
+           vha->fcoe_vn_port_mac[5], vha->fcoe_vn_port_mac[4],
+           vha->fcoe_vn_port_mac[3], vha->fcoe_vn_port_mac[2],
+           vha->fcoe_vn_port_mac[1], vha->fcoe_vn_port_mac[0]);
+}
+
+static ssize_t
+qla2x00_fabric_param_show(struct device *dev, struct device_attribute *attr,
+    char *buf)
+{
+       scsi_qla_host_t *vha = shost_priv(class_to_shost(dev));
+
+       return snprintf(buf, PAGE_SIZE, "%d\n", vha->hw->switch_cap);
+}
+
+static ssize_t
+qla2x00_fw_state_show(struct device *dev, struct device_attribute *attr,
+    char *buf)
+{
+       scsi_qla_host_t *vha = shost_priv(class_to_shost(dev));
+       int rval = QLA_FUNCTION_FAILED;
+       uint16_t state[5];
+
+       if (!vha->hw->flags.eeh_busy)
+               rval = qla2x00_get_firmware_state(vha, state);
+       if (rval != QLA_SUCCESS)
+               memset(state, -1, sizeof(state));
+
+       return snprintf(buf, PAGE_SIZE, "0x%x 0x%x 0x%x 0x%x 0x%x\n", state[0],
+           state[1], state[2], state[3], state[4]);
+}
+
 static DEVICE_ATTR(driver_version, S_IRUGO, qla2x00_drvr_version_show, NULL);
 static DEVICE_ATTR(fw_version, S_IRUGO, qla2x00_fw_version_show, NULL);
 static DEVICE_ATTR(serial_num, S_IRUGO, qla2x00_serial_num_show, NULL);
@@ -1116,6 +1287,11 @@ static DEVICE_ATTR(mpi_version, S_IRUGO, qla2x00_mpi_version_show, NULL);
 static DEVICE_ATTR(phy_version, S_IRUGO, qla2x00_phy_version_show, NULL);
 static DEVICE_ATTR(flash_block_size, S_IRUGO, qla2x00_flash_block_size_show,
                   NULL);
+static DEVICE_ATTR(vlan_id, S_IRUGO, qla2x00_vlan_id_show, NULL);
+static DEVICE_ATTR(vn_port_mac_address, S_IRUGO,
+                  qla2x00_vn_port_mac_address_show, NULL);
+static DEVICE_ATTR(fabric_param, S_IRUGO, qla2x00_fabric_param_show, NULL);
+static DEVICE_ATTR(fw_state, S_IRUGO, qla2x00_fw_state_show, NULL);
 
 struct device_attribute *qla2x00_host_attrs[] = {
        &dev_attr_driver_version,
@@ -1138,6 +1314,10 @@ struct device_attribute *qla2x00_host_attrs[] = {
        &dev_attr_mpi_version,
        &dev_attr_phy_version,
        &dev_attr_flash_block_size,
+       &dev_attr_vlan_id,
+       &dev_attr_vn_port_mac_address,
+       &dev_attr_fabric_param,
+       &dev_attr_fw_state,
        NULL,
 };
 
@@ -1282,7 +1462,13 @@ qla2x00_dev_loss_tmo_callbk(struct fc_rport *rport)
        if (!fcport)
                return;
 
-       qla2x00_abort_fcport_cmds(fcport);
+       if (test_bit(ABORT_ISP_ACTIVE, &fcport->vha->dpc_flags))
+               return;
+
+       if (unlikely(pci_channel_offline(fcport->vha->hw->pdev))) {
+               qla2x00_abort_all_cmds(fcport->vha, DID_NO_CONNECT << 16);
+               return;
+       }
 
        /*
         * Transport has effectively 'deleted' the rport, clear
@@ -1302,11 +1488,19 @@ qla2x00_terminate_rport_io(struct fc_rport *rport)
        if (!fcport)
                return;
 
+       if (test_bit(ABORT_ISP_ACTIVE, &fcport->vha->dpc_flags))
+               return;
+
+       if (unlikely(pci_channel_offline(fcport->vha->hw->pdev))) {
+               qla2x00_abort_all_cmds(fcport->vha, DID_NO_CONNECT << 16);
+               return;
+       }
        /*
         * At this point all fcport's software-states are cleared.  Perform any
         * final cleanup of firmware resources (PCBs and XCBs).
         */
-       if (fcport->loop_id != FC_NO_LOOP_ID)
+       if (fcport->loop_id != FC_NO_LOOP_ID &&
+           !test_bit(UNLOADING, &fcport->vha->dpc_flags))
                fcport->vha->hw->isp_ops->fabric_logout(fcport->vha,
                        fcport->loop_id, fcport->d_id.b.domain,
                        fcport->d_id.b.area, fcport->d_id.b.al_pa);
@@ -1337,6 +1531,12 @@ qla2x00_get_fc_host_stats(struct Scsi_Host *shost)
        pfc_host_stat = &ha->fc_host_stat;
        memset(pfc_host_stat, -1, sizeof(struct fc_host_statistics));
 
+       if (test_bit(UNLOADING, &vha->dpc_flags))
+               goto done;
+
+       if (unlikely(pci_channel_offline(ha->pdev)))
+               goto done;
+
        stats = dma_pool_alloc(ha->s_dma_pool, GFP_KERNEL, &stats_dma);
        if (stats == NULL) {
                DEBUG2_3_11(printk("%s(%ld): Failed to allocate memory.\n",
@@ -1430,11 +1630,13 @@ static int
 qla24xx_vport_create(struct fc_vport *fc_vport, bool disable)
 {
        int     ret = 0;
-       int     cnt = 0;
-       uint8_t qos = QLA_DEFAULT_QUE_QOS;
+       uint8_t qos = 0;
        scsi_qla_host_t *base_vha = shost_priv(fc_vport->shost);
        scsi_qla_host_t *vha = NULL;
        struct qla_hw_data *ha = base_vha->hw;
+       uint16_t options = 0;
+       int     cnt;
+       struct req_que *req = ha->req_q_map[0];
 
        ret = qla24xx_vport_create_req_sanity_check(fc_vport);
        if (ret) {
@@ -1474,7 +1676,8 @@ qla24xx_vport_create(struct fc_vport *fc_vport, bool disable)
                        fc_vport_set_state(fc_vport, FC_VPORT_LINKDOWN);
        }
 
-       if (scsi_add_host(vha->host, &fc_vport->dev)) {
+       if (scsi_add_host_with_dma(vha->host, &fc_vport->dev,
+                                  &ha->pdev->dev)) {
                DEBUG15(printk("scsi(%ld): scsi_add_host failure for VP[%d].\n",
                        vha->host_no, vha->vp_idx));
                goto vport_create_failed_2;
@@ -1490,23 +1693,39 @@ qla24xx_vport_create(struct fc_vport *fc_vport, bool disable)
 
        qla24xx_vport_disable(fc_vport, disable);
 
-       /* Create a queue pair for the vport */
-       if (ha->mqenable) {
-               if (ha->npiv_info) {
-                       for (; cnt < ha->nvram_npiv_size; cnt++) {
-                               if (ha->npiv_info[cnt].port_name ==
-                                       vha->port_name &&
-                                       ha->npiv_info[cnt].node_name ==
-                                       vha->node_name) {
-                                       qos = ha->npiv_info[cnt].q_qos;
-                                       break;
-                               }
-                       }
+       if (ha->flags.cpu_affinity_enabled) {
+               req = ha->req_q_map[1];
+               goto vport_queue;
+       } else if (ql2xmaxqueues == 1 || !ha->npiv_info)
+               goto vport_queue;
+       /* Create a request queue in QoS mode for the vport */
+       for (cnt = 0; cnt < ha->nvram_npiv_size; cnt++) {
+               if (memcmp(ha->npiv_info[cnt].port_name, vha->port_name, 8) == 0
+                       && memcmp(ha->npiv_info[cnt].node_name, vha->node_name,
+                                       8) == 0) {
+                       qos = ha->npiv_info[cnt].q_qos;
+                       break;
+               }
+       }
+       if (qos) {
+               ret = qla25xx_create_req_que(ha, options, vha->vp_idx, 0, 0,
+                       qos);
+               if (!ret)
+                       qla_printk(KERN_WARNING, ha,
+                       "Can't create request queue for vp_idx:%d\n",
+                       vha->vp_idx);
+               else {
+                       DEBUG2(qla_printk(KERN_INFO, ha,
+                       "Request Que:%d (QoS: %d) created for vp_idx:%d\n",
+                       ret, qos, vha->vp_idx));
+                       req = ha->req_q_map[ret];
                }
-               qla25xx_create_queues(vha, qos);
        }
 
+vport_queue:
+       vha->req = req;
        return 0;
+
 vport_create_failed_2:
        qla24xx_disable_vp(vha);
        qla24xx_deallocate_vp_id(vha);
@@ -1540,6 +1759,11 @@ qla24xx_vport_delete(struct fc_vport *fc_vport)
 
        qla24xx_deallocate_vp_id(vha);
 
+       mutex_lock(&ha->vport_lock);
+       ha->cur_vport_count--;
+       clear_bit(vha->vp_idx, ha->vp_idx_map);
+       mutex_unlock(&ha->vport_lock);
+
        if (vha->timer_active) {
                qla2x00_vp_stop_timer(vha);
                DEBUG15(printk ("scsi(%ld): timer for the vport[%d] = %p "
@@ -1547,8 +1771,8 @@ qla24xx_vport_delete(struct fc_vport *fc_vport)
                    vha->host_no, vha->vp_idx, vha));
         }
 
-       if (ha->mqenable) {
-               if (qla25xx_delete_queues(vha, 0) != QLA_SUCCESS)
+       if (vha->req->id && !ha->flags.cpu_affinity_enabled) {
+               if (qla25xx_delete_req_que(vha, vha->req) != QLA_SUCCESS)
                        qla_printk(KERN_WARNING, ha,
                                "Queue delete failed.\n");
        }