From 30a78b2881189da89cbbb79f35da4bd20c932163 Mon Sep 17 00:00:00 2001 From: Vladislav Bolkhovitin Date: Wed, 27 Apr 2011 20:46:17 +0000 Subject: [PATCH] Patch from Grigory Eykalis with some changes implementing pass-through functionality git-svn-id: http://svn.code.sf.net/p/scst/svn/trunk@3422 d57e44dd-8a1f-0410-8b47-8ef2f437770f --- qla2x00t/qla_attr.c | 193 ++++++++++++++++++++++++++++++++++++++++++++ qla2x00t/qla_def.h | 73 +++++++++++++++++ qla2x00t/qla_gbl.h | 1 + qla2x00t/qla_gs.c | 2 +- qla2x00t/qla_isr.c | 124 ++++++++++++++++++++++++++++ qla2x00t/qla_os.c | 20 +++++ 6 files changed, 412 insertions(+), 1 deletion(-) diff --git a/qla2x00t/qla_attr.c b/qla2x00t/qla_attr.c index 62a70b1de..c09494651 100644 --- a/qla2x00t/qla_attr.c +++ b/qla2x00t/qla_attr.c @@ -1048,6 +1048,198 @@ static struct bin_attribute sysfs_sfp_attr = { .read = qla2x00_sysfs_read_sfp, }; +static void +qla2x00_wait_for_passthru_completion(struct scsi_qla_host *ha) +{ + unsigned long timeout; + + if (unlikely(pci_channel_offline(ha->pdev))) + return; + + timeout = ((ha->r_a_tov / 10 * 2) + 5) * HZ; + if (!wait_for_completion_timeout(&ha->pass_thru_intr_comp, timeout)) { + DEBUG2(qla_printk(KERN_WARNING, ha, + "Passthru request timed out.\n")); + if (IS_QLA82XX(ha)) + set_bit(FCOE_CTX_RESET_NEEDED, &ha->dpc_flags); + else { + ha->isp_ops->fw_dump(ha, 0); + set_bit(ISP_ABORT_NEEDED, &ha->dpc_flags); + } + qla2xxx_wake_dpc(ha); + ha->pass_thru_cmd_result = 0; + ha->pass_thru_cmd_in_process = 0; + } +} + +static ssize_t +qla2x00_sysfs_read_ct(struct kobject *kobj, + struct bin_attribute *bin_attr, + char *buf, + loff_t off, size_t count) +{ + struct scsi_qla_host *ha = shost_priv(dev_to_shost(container_of(kobj, + struct device, kobj))); + + if (!ha->pass_thru_cmd_in_process || !ha->pass_thru_cmd_result) { + DEBUG3(qla_printk(KERN_WARNING, ha, + "Passthru CT response is not available.\n")); + return 0; + } + + memcpy(buf, ha->pass_thru, count); + + ha->pass_thru_cmd_result = 0; + ha->pass_thru_cmd_in_process = 0; + + return count; +} + +static ssize_t +qla2x00_sysfs_write_ct(struct kobject *kobj, + struct bin_attribute *bin_attr, + char *buf, + loff_t off, size_t count) +{ + struct scsi_qla_host *ha = shost_priv(dev_to_shost(container_of(kobj, + struct device, kobj))); + fc_ct_request_t *request = (void *)buf; + struct ct_entry_24xx *ct_iocb = NULL; + ms_iocb_entry_t *ct_iocb_2G = NULL; + unsigned long flags; + + if (test_bit(ISP_ABORT_NEEDED, &ha->dpc_flags) || + test_bit(ABORT_ISP_ACTIVE, &ha->dpc_flags) || + test_bit(ISP_ABORT_RETRY, &ha->dpc_flags)) { + DEBUG2_3_11(qla_printk(KERN_INFO, ha, + "%s(%ld): isp reset in progress.\n", + __func__, ha->host_no)); + goto ct_error0; + } + if (atomic_read(&ha->loop_state) != LOOP_READY) + goto ct_error0; + if (count < sizeof(request->ct_iu)) { + DEBUG2(qla_printk(KERN_WARNING, ha, + "Passthru CT buffer insufficient size %zu...\n", count)); + goto ct_error0; + } + if (ha->pass_thru_cmd_in_process || ha->pass_thru_cmd_result) { + DEBUG2(qla_printk(KERN_WARNING, ha, + "Passthru CT request is already progress\n")); + goto ct_error0; + } + if (qla2x00_mgmt_svr_login(ha)) { + DEBUG2(qla_printk(KERN_WARNING, ha, + "Passthru CT request failed to login management server\n")); + goto ct_error0; + } + + ha->pass_thru_cmd_in_process = 1; + spin_lock_irqsave(&ha->hardware_lock, flags); + + if (count > PAGE_SIZE) { + DEBUG2(qla_printk(KERN_INFO, ha, + "Passthru CT request excessive size %d...\n", + (int)count)); + count = PAGE_SIZE; + } + + memset(ha->pass_thru, 0, PAGE_SIZE); + memcpy(ha->pass_thru, &request->ct_iu, count); + + if (IS_FWI2_CAPABLE(ha)) { + ct_iocb = (void *)qla2x00_req_pkt(ha); + + if (ct_iocb == NULL) { + DEBUG2(qla_printk(KERN_WARNING, ha, + "Passthru CT request failed to get request " + "packet\n")); + goto ct_error1; + } + + ct_iocb->entry_type = CT_IOCB_TYPE; + ct_iocb->entry_count = 1; + ct_iocb->entry_status = 0; + ct_iocb->comp_status = __constant_cpu_to_le16(0); + if (*(buf+4) & 0xfc) + ct_iocb->nport_handle = __constant_cpu_to_le16(NPH_SNS); + else + ct_iocb->nport_handle = cpu_to_le16(ha->mgmt_svr_loop_id); + ct_iocb->cmd_dsd_count = __constant_cpu_to_le16(1); + ct_iocb->vp_index = ha->vp_idx; + ct_iocb->timeout = (cpu_to_le16(ha->r_a_tov / 10 * 2) + 2); + ct_iocb->rsp_dsd_count = __constant_cpu_to_le16(1); + ct_iocb->rsp_byte_count = cpu_to_le32(PAGE_SIZE); + ct_iocb->cmd_byte_count = cpu_to_le32(count); + + ct_iocb->dseg_0_address[0] = cpu_to_le32(LSD(ha->pass_thru_dma)); + ct_iocb->dseg_0_address[1] = cpu_to_le32(MSD(ha->pass_thru_dma)); + ct_iocb->dseg_0_len = ct_iocb->cmd_byte_count; + + ct_iocb->dseg_1_address[0] = cpu_to_le32(LSD(ha->pass_thru_dma)); + ct_iocb->dseg_1_address[1] = cpu_to_le32(MSD(ha->pass_thru_dma)); + ct_iocb->dseg_1_len = ct_iocb->rsp_byte_count; + } else { + ct_iocb_2G = (void *)qla2x00_req_pkt(ha); + + if (ct_iocb_2G == NULL) { + DEBUG2(qla_printk(KERN_WARNING, ha, + "Passthru CT request failed to get request " + "packet\n")); + goto ct_error1; + } + + ct_iocb_2G->entry_type = CT_IOCB_TYPE; + ct_iocb_2G->entry_count = 1; + ct_iocb_2G->entry_status = 0; + SET_TARGET_ID(ha, ct_iocb_2G->loop_id, ha->mgmt_svr_loop_id); + ct_iocb_2G->status = __constant_cpu_to_le16(0); + ct_iocb_2G->control_flags = __constant_cpu_to_le16(0); + ct_iocb_2G->timeout = (cpu_to_le16(ha->r_a_tov / 10 * 2) + 2); + ct_iocb_2G->cmd_dsd_count = __constant_cpu_to_le16(1); + ct_iocb_2G->total_dsd_count = __constant_cpu_to_le16(2); + ct_iocb_2G->rsp_bytecount = cpu_to_le32(PAGE_SIZE); + ct_iocb_2G->req_bytecount = cpu_to_le32(count); + + ct_iocb_2G->dseg_req_address[0] = cpu_to_le32(LSD(ha->pass_thru_dma)); + ct_iocb_2G->dseg_req_address[1] = cpu_to_le32(MSD(ha->pass_thru_dma)); + ct_iocb_2G->dseg_req_length = ct_iocb_2G->req_bytecount; + + ct_iocb_2G->dseg_rsp_address[0] = cpu_to_le32(LSD(ha->pass_thru_dma)); + ct_iocb_2G->dseg_rsp_address[1] = cpu_to_le32(MSD(ha->pass_thru_dma)); + ct_iocb_2G->dseg_rsp_length = ct_iocb_2G->rsp_bytecount; + } + + wmb(); + qla2x00_isp_cmd(ha); + + spin_unlock_irqrestore(&ha->hardware_lock, flags); + + qla2x00_wait_for_passthru_completion(ha); + + return count; + +ct_error1: + ha->pass_thru_cmd_in_process = 0; + spin_unlock_irqrestore(&ha->hardware_lock, flags); + +ct_error0: + DEBUG3(qla_printk(KERN_WARNING, ha, + "Passthru CT failed on scsi(%ld)\n", ha->host_no)); + return 0; +} + +static struct bin_attribute sysfs_ct_attr = { + .attr = { + .name = "ct", + .mode = S_IRUSR | S_IWUSR, + }, + .size = 0, + .read = qla2x00_sysfs_read_ct, + .write = qla2x00_sysfs_write_ct, +}; + + static struct sysfs_entry { char *name; struct bin_attribute *attr; @@ -1059,6 +1251,7 @@ static struct sysfs_entry { { "optrom_ctl", &sysfs_optrom_ctl_attr, }, { "vpd", &sysfs_vpd_attr, 1 }, { "sfp", &sysfs_sfp_attr, 1 }, + { "ct", &sysfs_ct_attr, }, { NULL }, }; diff --git a/qla2x00t/qla_def.h b/qla2x00t/qla_def.h index ea5d578b2..c7ccd6faf 100644 --- a/qla2x00t/qla_def.h +++ b/qla2x00t/qla_def.h @@ -94,6 +94,68 @@ #define LSD(x) ((uint32_t)((uint64_t)(x))) #define MSD(x) ((uint32_t)((((uint64_t)(x)) >> 16) >> 16)) +/* CT IU */ +typedef struct{ + uint8_t revision; + uint8_t in_id[3]; + uint8_t gs_type; + uint8_t gs_subtype; + uint8_t options; + uint8_t reserved0; + uint16_t command; + uint16_t max_rsp_size; + uint8_t fragment_id; + uint8_t reserved1[3]; +} ct_iu_t; + +/* CT request format */ +typedef struct { + ct_iu_t ct_iu; + union { + struct { + uint8_t reserved; + uint8_t port_id[3]; + } port_id; + + struct { + uint8_t port_type; + uint8_t domain; + uint8_t area; + uint8_t reserved; + } gid_pt; + + struct { + uint8_t reserved; + uint8_t port_id[3]; + uint8_t fc4_types[32]; + } rft_id; + + struct { + uint8_t reserved; + uint8_t port_id[3]; + uint16_t reserved2; + uint8_t fc4_feature; + uint8_t fc4_type; + } rff_id; + + struct { + uint8_t reserved; + uint8_t port_id[3]; + uint8_t node_name[8]; + } rnn_id; + + struct { + uint8_t node_name[8]; + uint8_t name_len; + uint8_t sym_node_name[255]; + } rsnn_nn; + + struct { + uint8_t hba_indentifier[8]; + } ghat; + } extended; +} fc_ct_request_t; + /* * I/O register */ @@ -2251,6 +2313,7 @@ typedef struct scsi_qla_host { #define REGISTER_FDMI_NEEDED 26 #define FCPORT_UPDATE_NEEDED 27 #define VP_DPC_NEEDED 28 /* wake up for VP dpc handling */ +#define FCOE_CTX_RESET_NEEDED 18 /* Initiate FCoE context reset */ uint32_t device_flags; #define DFLG_LOCAL_DEVICES BIT_0 @@ -2275,6 +2338,7 @@ typedef struct scsi_qla_host { #define DT_ISP5432 BIT_10 #define DT_ISP2532 BIT_11 #define DT_ISP8432 BIT_12 +#define DT_ISP8021 BIT_14 #define DT_ISP_LAST (DT_ISP8432 << 1) #define DT_IIDMA BIT_26 @@ -2298,6 +2362,7 @@ typedef struct scsi_qla_host { #define IS_QLA5432(ha) (DT_MASK(ha) & DT_ISP5432) #define IS_QLA2532(ha) (DT_MASK(ha) & DT_ISP2532) #define IS_QLA8432(ha) (DT_MASK(ha) & DT_ISP8432) +#define IS_QLA82XX(ha) (DT_MASK(ha) & DT_ISP8021) #define IS_QLA23XX(ha) (IS_QLA2300(ha) || IS_QLA2312(ha) || IS_QLA2322(ha) || \ IS_QLA6312(ha) || IS_QLA6322(ha)) @@ -2473,6 +2538,9 @@ typedef struct scsi_qla_host { struct sns_cmd_pkt *sns_cmd; dma_addr_t sns_cmd_dma; + char *pass_thru; + dma_addr_t pass_thru_dma; + #define SFP_DEV_SIZE 256 #define SFP_BLOCK_SIZE 64 void *sfp_data; @@ -2514,6 +2582,7 @@ typedef struct scsi_qla_host { struct mutex vport_lock; /* Virtual port synchronization */ struct completion mbx_cmd_comp; /* Serialize mbx access */ struct completion mbx_intr_comp; /* Used for completion notification */ + struct completion pass_thru_intr_comp; /* For pass thru notification */ uint32_t mbx_flags; #define MBX_IN_PROGRESS BIT_0 @@ -2672,6 +2741,10 @@ typedef struct scsi_qla_host { uint16_t max_npiv_vports; /* 63 or 125 per topoloty */ int cur_vport_count; + /* Pass through support */ + int pass_thru_cmd_result; + int pass_thru_cmd_in_process; + struct qla_chip_state_84xx *cs84xx; } scsi_qla_host_t; diff --git a/qla2x00t/qla_gbl.h b/qla2x00t/qla_gbl.h index 25d86495c..a8a67be21 100644 --- a/qla2x00t/qla_gbl.h +++ b/qla2x00t/qla_gbl.h @@ -361,6 +361,7 @@ extern int qla2x00_fdmi_register(scsi_qla_host_t *); extern int qla2x00_gfpn_id(scsi_qla_host_t *, sw_info_t *); extern int qla2x00_gpsc(scsi_qla_host_t *, sw_info_t *); extern void qla2x00_get_sym_node_name(scsi_qla_host_t *, uint8_t *); +extern int qla2x00_mgmt_svr_login(scsi_qla_host_t *); /* * Global Function Prototypes in qla_attr.c source file. diff --git a/qla2x00t/qla_gs.c b/qla2x00t/qla_gs.c index c1b0dea61..f8a8eaa83 100644 --- a/qla2x00t/qla_gs.c +++ b/qla2x00t/qla_gs.c @@ -1106,7 +1106,7 @@ qla2x00_sns_rnn_id(scsi_qla_host_t *ha) * * Returns 0 on success. */ -static int +int qla2x00_mgmt_svr_login(scsi_qla_host_t *ha) { int ret; diff --git a/qla2x00t/qla_isr.c b/qla2x00t/qla_isr.c index cdec96451..a8c17db8f 100644 --- a/qla2x00t/qla_isr.c +++ b/qla2x00t/qla_isr.c @@ -16,6 +16,9 @@ static void qla2x00_process_completed_request(struct scsi_qla_host *, uint32_t); static void qla2x00_status_entry(scsi_qla_host_t *, void *); static void qla2x00_status_cont_entry(scsi_qla_host_t *, sts_cont_entry_t *); static void qla2x00_error_entry(scsi_qla_host_t *, sts_entry_t *); +static void qla2x00_ms_entry(scsi_qla_host_t *, ms_iocb_entry_t *); +static void qla24xx_ms_entry(scsi_qla_host_t *, struct ct_entry_24xx *); + /** * qla2100_intr_handler() - Process interrupts for the ISP2100 and ISP2200. @@ -964,6 +967,21 @@ qla2x00_process_response_queue(struct scsi_qla_host *ha) break; case MARKER_TYPE: break; + case MS_IOCB_TYPE: + if (ha->outstanding_cmds[pkt->handle]) + qla2x00_ms_entry(ha, (ms_iocb_entry_t *)pkt); + else { + if (ha->pass_thru_cmd_result) + DEBUG2(qla_printk(KERN_INFO, ha, + "Passthru cmd result on.\n")); + if (!ha->pass_thru_cmd_in_process) + DEBUG2(qla_printk(KERN_INFO, ha, + "Passthru in process off.\n")); + + ha->pass_thru_cmd_result = 1; + complete(&ha->pass_thru_intr_comp); + } + break; default: /* Type Not Supported. */ DEBUG4(printk(KERN_WARNING @@ -1499,6 +1517,43 @@ qla2x00_error_entry(scsi_qla_host_t *ha, sts_entry_t *pkt) } } +/** + * qla2x00_ms_entry() - Process a Management Server entry. + * @ha: SCSI driver HA context + * @index: Response queue out pointer + */ +static void +qla2x00_ms_entry(scsi_qla_host_t *ha, ms_iocb_entry_t *pkt) +{ + srb_t *sp; + + DEBUG3(printk("%s(%ld): pkt=%p pkthandle=%d.\n", + __func__, ha->host_no, pkt, pkt->handle1)); + + /* Validate handle. */ + if (pkt->handle1 < MAX_OUTSTANDING_COMMANDS) + sp = ha->outstanding_cmds[pkt->handle1]; + else + sp = NULL; + + if (sp == NULL) { + DEBUG2(printk("scsi(%ld): MS entry - invalid handle\n", + ha->host_no)); + qla_printk(KERN_WARNING, ha, "MS entry - invalid handle\n"); + + set_bit(ISP_ABORT_NEEDED, &ha->dpc_flags); + return; + } + + CMD_COMPL_STATUS(sp->cmd) = le16_to_cpu(pkt->status); + CMD_ENTRY_STATUS(sp->cmd) = pkt->entry_status; + + /* Free outstanding command slot. */ + ha->outstanding_cmds[pkt->handle1] = NULL; + + qla2x00_sp_compl(ha, sp); +} + /** * qla24xx_mbx_completion() - Process mailbox command completions. * @ha: SCSI driver HA context @@ -1668,6 +1723,32 @@ qla24xx_process_response_queue(struct scsi_qla_host *ha) #endif /* CONFIG_SCSI_QLA2XXX_TARGET */ case MARKER_TYPE: break; + case MS_IOCB_TYPE: + if (ha->outstanding_cmds[pkt->handle]) + qla24xx_ms_entry(ha, (void *)pkt); + else { + if (ha->pass_thru_cmd_result) + DEBUG2(qla_printk(KERN_INFO, ha, + "Passthru cmd result on.\n")); + if (!ha->pass_thru_cmd_in_process) + DEBUG2(qla_printk(KERN_INFO, ha, + "Passthru in process off.\n")); + + ha->pass_thru_cmd_result = 1; + complete(&ha->pass_thru_intr_comp); + } + break; + case ELS_IOCB_TYPE: + if (ha->pass_thru_cmd_result) + DEBUG2(qla_printk(KERN_INFO, ha, + "Passthru cmd result on.\n")); + if (!ha->pass_thru_cmd_in_process) + DEBUG2(qla_printk(KERN_INFO, ha, + "Passthru in process off.\n")); + + ha->pass_thru_cmd_result = 1; + complete(&ha->pass_thru_intr_comp); + break; default: /* Type Not Supported. */ DEBUG4(printk(KERN_WARNING @@ -1834,6 +1915,49 @@ qla24xx_intr_handler(int irq, void *dev_id) return IRQ_HANDLED; } +/** + * qla24xx_ms_entry() - Process a Management Server entry. + * @ha: SCSI driver HA context + * @index: Response queue out pointer + */ +static void +qla24xx_ms_entry(scsi_qla_host_t *ha, struct ct_entry_24xx *pkt) +{ + srb_t *sp; + + DEBUG3(printk("%s(%ld): pkt=%p pkthandle=%d.\n", + __func__, ha->host_no, pkt, pkt->handle)); + + DEBUG9(printk("%s: ct pkt dump:\n", __func__)); + DEBUG9(qla2x00_dump_buffer((void *)pkt, sizeof(struct ct_entry_24xx))); + + /* Validate handle. */ + if (pkt->handle < MAX_OUTSTANDING_COMMANDS) + sp = ha->outstanding_cmds[pkt->handle]; + else + sp = NULL; + + if (sp == NULL) { + DEBUG2(printk("scsi(%ld): MS entry - invalid handle\n", + ha->host_no)); + DEBUG10(printk("scsi(%ld): MS entry - invalid handle\n", + ha->host_no)); + qla_printk(KERN_WARNING, ha, "MS entry - invalid handle %d\n", + pkt->handle); + + set_bit(ISP_ABORT_NEEDED, &ha->dpc_flags); + return; + } + + CMD_COMPL_STATUS(sp->cmd) = le16_to_cpu(pkt->comp_status); + CMD_ENTRY_STATUS(sp->cmd) = pkt->entry_status; + + /* Free outstanding command slot. */ + ha->outstanding_cmds[pkt->handle] = NULL; + + qla2x00_sp_compl(ha, sp); +} + static irqreturn_t qla24xx_msix_rsp_q(int irq, void *dev_id) { diff --git a/qla2x00t/qla_os.c b/qla2x00t/qla_os.c index 9cfe44e2c..e1b117baa 100644 --- a/qla2x00t/qla_os.c +++ b/qla2x00t/qla_os.c @@ -1933,6 +1933,7 @@ qla2x00_probe_one(struct pci_dev *pdev, const struct pci_device_id *id) init_completion(&ha->mbx_cmd_comp); complete(&ha->mbx_cmd_comp); init_completion(&ha->mbx_intr_comp); + init_completion(&ha->pass_thru_intr_comp); INIT_LIST_HEAD(&ha->list); INIT_LIST_HEAD(&ha->fcports); @@ -2347,10 +2348,23 @@ qla2x00_mem_alloc(scsi_qla_host_t *ha) sizeof(struct ct_sns_pkt), &ha->ct_sns_dma, GFP_KERNEL); if (!ha->ct_sns) goto fail_free_ms_iocb; + + /* Get consistent memory allocated for pass-thru commands */ + ha->pass_thru = dma_alloc_coherent(&ha->pdev->dev, + PAGE_SIZE, &ha->pass_thru_dma, GFP_KERNEL); + if (!ha->pass_thru) { + qla_printk(KERN_WARNING, ha, + "Memory Allocation failed - pass_thru\n"); + goto fail_free_pass_thru; + } } return 0; +fail_free_pass_thru: + dma_free_coherent(&ha->pdev->dev, + PAGE_SIZE, ha->pass_thru, ha->pass_thru_dma); + ha->pass_thru = NULL; fail_free_ms_iocb: dma_pool_free(ha->s_dma_pool, ha->ms_iocb, ha->ms_iocb_dma); ha->ms_iocb = NULL; @@ -2468,6 +2482,10 @@ qla2x00_mem_free(scsi_qla_host_t *ha) (ha->request_q_length + 1) * sizeof(request_t), ha->request_ring, ha->request_dma); + if (ha->pass_thru) + dma_free_coherent(&ha->pdev->dev, + PAGE_SIZE, ha->pass_thru, ha->pass_thru_dma); + ha->srb_mempool = NULL; ha->eft = NULL; ha->eft_dma = 0; @@ -2485,6 +2503,8 @@ qla2x00_mem_free(scsi_qla_host_t *ha) ha->gid_list = NULL; ha->gid_list_dma = 0; + ha->pass_thru = NULL; + ha->response_ring = NULL; ha->response_dma = 0; ha->request_ring = NULL;