diff --git a/drivers/scsi/qla4xxx/ql4_def.h b/drivers/scsi/qla4xxx/ql4_def.h index a80adfc6d78..fcb1dff9bc1 100644 --- a/drivers/scsi/qla4xxx/ql4_def.h +++ b/drivers/scsi/qla4xxx/ql4_def.h @@ -279,7 +279,8 @@ struct ql82xx_hw_data { uint32_t flt_region_fw; uint32_t flt_iscsi_param; - uint32_t reserved; + uint32_t flt_region_chap; + uint32_t flt_chap_size; }; struct qla4_8xxx_legacy_intr_set { @@ -609,6 +610,8 @@ struct scsi_qla_host { #define QLFLASH_READING 1 #define QLFLASH_WRITING 2 struct dma_pool *chap_dma_pool; + uint8_t *chap_list; /* CHAP table cache */ + struct mutex chap_sem; #define CHAP_DMA_BLOCK_SIZE 512 struct workqueue_struct *task_wq; unsigned long ddb_idx_map[MAX_DDB_ENTRIES / BITS_PER_LONG]; @@ -671,6 +674,11 @@ static inline int is_qla4032(struct scsi_qla_host *ha) return ha->pdev->device == PCI_DEVICE_ID_QLOGIC_ISP4032; } +static inline int is_qla40XX(struct scsi_qla_host *ha) +{ + return is_qla4032(ha) || is_qla4022(ha) || is_qla4010(ha); +} + static inline int is_qla8022(struct scsi_qla_host *ha) { return ha->pdev->device == PCI_DEVICE_ID_QLOGIC_ISP8022; diff --git a/drivers/scsi/qla4xxx/ql4_fw.h b/drivers/scsi/qla4xxx/ql4_fw.h index 597875fbb24..cbd5a20dbbd 100644 --- a/drivers/scsi/qla4xxx/ql4_fw.h +++ b/drivers/scsi/qla4xxx/ql4_fw.h @@ -251,6 +251,8 @@ union external_hw_config_reg { #define FA_BOOT_CODE_ADDR_82 0x20000 #define FA_RISC_CODE_ADDR_82 0x40000 #define FA_GOLD_RISC_CODE_ADDR_82 0x80000 +#define FA_FLASH_ISCSI_CHAP 0x540000 +#define FA_FLASH_CHAP_SIZE 0xC0000 /* Flash Description Table */ struct qla_fdt_layout { @@ -310,6 +312,7 @@ struct qla_flt_header { #define FLT_REG_GOLD_FW_82 0x75 #define FLT_REG_BOOT_CODE_82 0x78 #define FLT_REG_ISCSI_PARAM 0x65 +#define FLT_REG_ISCSI_CHAP 0x63 struct qla_flt_region { uint32_t code; @@ -681,6 +684,8 @@ struct addr_ctrl_blk_def { #define MAX_CHAP_ENTRIES_40XX 128 #define MAX_CHAP_ENTRIES_82XX 1024 +#define MAX_RESRV_CHAP_IDX 3 +#define FLASH_CHAP_OFFSET 0x06000000 struct ql4_chap_table { uint16_t link; diff --git a/drivers/scsi/qla4xxx/ql4_mbx.c b/drivers/scsi/qla4xxx/ql4_mbx.c index b60b90301a8..4c2b8487039 100644 --- a/drivers/scsi/qla4xxx/ql4_mbx.c +++ b/drivers/scsi/qla4xxx/ql4_mbx.c @@ -1304,7 +1304,7 @@ int qla4xxx_get_chap(struct scsi_qla_host *ha, char *username, char *password, { int ret = 0; int rval = QLA_ERROR; - uint32_t offset = 0; + uint32_t offset = 0, chap_size; struct ql4_chap_table *chap_table; dma_addr_t chap_dma; @@ -1314,12 +1314,22 @@ int qla4xxx_get_chap(struct scsi_qla_host *ha, char *username, char *password, goto exit_get_chap; } - memset(chap_table, 0, sizeof(struct ql4_chap_table)); + chap_size = sizeof(struct ql4_chap_table); + memset(chap_table, 0, chap_size); - offset = 0x06000000 | (idx * sizeof(struct ql4_chap_table)); + if (is_qla40XX(ha)) + offset = FLASH_CHAP_OFFSET | (idx * chap_size); + else { + offset = FLASH_RAW_ACCESS_ADDR + (ha->hw.flt_region_chap << 2); + /* flt_chap_size is CHAP table size for both ports + * so divide it by 2 to calculate the offset for second port + */ + if (ha->port_num == 1) + offset += (ha->hw.flt_chap_size / 2); + offset += (idx * chap_size); + } - rval = qla4xxx_get_flash(ha, chap_dma, offset, - sizeof(struct ql4_chap_table)); + rval = qla4xxx_get_flash(ha, chap_dma, offset, chap_size); if (rval != QLA_SUCCESS) { ret = -EINVAL; goto exit_get_chap; @@ -1366,10 +1376,16 @@ static int qla4xxx_set_chap(struct scsi_qla_host *ha, char *username, strncpy(chap_table->secret, password, MAX_CHAP_SECRET_LEN); strncpy(chap_table->name, username, MAX_CHAP_NAME_LEN); chap_table->cookie = __constant_cpu_to_le16(CHAP_VALID_COOKIE); - offset = 0x06000000 | (idx * sizeof(struct ql4_chap_table)); + offset = FLASH_CHAP_OFFSET | (idx * sizeof(struct ql4_chap_table)); rval = qla4xxx_set_flash(ha, chap_dma, offset, sizeof(struct ql4_chap_table), FLASH_OPT_RMW_COMMIT); + + if (rval == QLA_SUCCESS && ha->chap_list) { + /* Update ha chap_list cache */ + memcpy((struct ql4_chap_table *)ha->chap_list + idx, + chap_table, sizeof(struct ql4_chap_table)); + } dma_pool_free(ha->chap_dma_pool, chap_table, chap_dma); if (rval != QLA_SUCCESS) ret = -EINVAL; @@ -1378,6 +1394,83 @@ exit_set_chap: return ret; } +/** + * qla4xxx_get_chap_index - Get chap index given username and secret + * @ha: pointer to adapter structure + * @username: CHAP username to be searched + * @password: CHAP password to be searched + * @bidi: Is this a BIDI CHAP + * @chap_index: CHAP index to be returned + * + * Match the username and password in the chap_list, return the index if a + * match is found. If a match is not found then add the entry in FLASH and + * return the index at which entry is written in the FLASH. + **/ +static int qla4xxx_get_chap_index(struct scsi_qla_host *ha, char *username, + char *password, int bidi, uint16_t *chap_index) +{ + int i, rval; + int free_index = -1; + int found_index = 0; + int max_chap_entries = 0; + struct ql4_chap_table *chap_table; + + if (is_qla8022(ha)) + max_chap_entries = (ha->hw.flt_chap_size / 2) / + sizeof(struct ql4_chap_table); + else + max_chap_entries = MAX_CHAP_ENTRIES_40XX; + + if (!ha->chap_list) { + ql4_printk(KERN_ERR, ha, "Do not have CHAP table cache\n"); + return QLA_ERROR; + } + + mutex_lock(&ha->chap_sem); + for (i = 0; i < max_chap_entries; i++) { + chap_table = (struct ql4_chap_table *)ha->chap_list + i; + if (chap_table->cookie != + __constant_cpu_to_le16(CHAP_VALID_COOKIE)) { + if (i > MAX_RESRV_CHAP_IDX && free_index == -1) + free_index = i; + continue; + } + if (bidi) { + if (chap_table->flags & BIT_7) + continue; + } else { + if (chap_table->flags & BIT_6) + continue; + } + if (!strncmp(chap_table->secret, password, + MAX_CHAP_SECRET_LEN) && + !strncmp(chap_table->name, username, + MAX_CHAP_NAME_LEN)) { + *chap_index = i; + found_index = 1; + break; + } + } + + /* If chap entry is not present and a free index is available then + * write the entry in flash + */ + if (!found_index && free_index != -1) { + rval = qla4xxx_set_chap(ha, username, password, + free_index, bidi); + if (!rval) { + *chap_index = free_index; + found_index = 1; + } + } + + mutex_unlock(&ha->chap_sem); + + if (found_index) + return QLA_SUCCESS; + return QLA_ERROR; +} + int qla4xxx_conn_close_sess_logout(struct scsi_qla_host *ha, uint16_t fw_ddb_index, uint16_t connection_id, @@ -1490,7 +1583,6 @@ int qla4xxx_set_param_ddbentry(struct scsi_qla_host *ha, uint16_t iscsi_opts = 0; uint32_t options = 0; uint16_t idx; - int max_chap_entries = 0; fw_ddb_entry = dma_alloc_coherent(&ha->pdev->dev, sizeof(*fw_ddb_entry), &fw_ddb_entry_dma, GFP_KERNEL); @@ -1559,26 +1651,14 @@ int qla4xxx_set_param_ddbentry(struct scsi_qla_host *ha, goto exit_set_param; } - if (is_qla8022(ha)) - max_chap_entries = MAX_CHAP_ENTRIES_82XX; - else - max_chap_entries = MAX_CHAP_ENTRIES_40XX; /* CHAP */ if (sess->username != NULL && sess->password != NULL) { if (strlen(sess->username) && strlen(sess->password)) { iscsi_opts |= BIT_7; - idx = ddb_entry->fw_ddb_index * 2; - if (idx > max_chap_entries) { - ql4_printk(KERN_ERR, ha, - "%s: Invalid ddb or chap index\n", - __func__); - rval = -EINVAL; - goto exit_set_param; - } - rval = qla4xxx_set_chap(ha, sess->username, - sess->password, idx, - LOCAL_CHAP); + rval = qla4xxx_get_chap_index(ha, sess->username, + sess->password, + LOCAL_CHAP, &idx); if (rval) goto exit_set_param; @@ -1590,17 +1670,10 @@ int qla4xxx_set_param_ddbentry(struct scsi_qla_host *ha, /* Check if BIDI CHAP */ if (strlen(sess->username_in) && strlen(sess->password_in)) { iscsi_opts |= BIT_4; - idx = (ddb_entry->fw_ddb_index * 2) + 1; - if (idx > max_chap_entries) { - ql4_printk(KERN_ERR, ha, - "%s: Invalid ddb or bidi chap " - "index\n", __func__); - rval = -EINVAL; - goto exit_set_param; - } - rval = qla4xxx_set_chap(ha, sess->username_in, - sess->password_in, idx, - BIDI_CHAP); + + rval = qla4xxx_get_chap_index(ha, sess->username_in, + sess->password_in, + BIDI_CHAP, &idx); if (rval) goto exit_set_param; } diff --git a/drivers/scsi/qla4xxx/ql4_nx.c b/drivers/scsi/qla4xxx/ql4_nx.c index 84c254afac0..f484ff43819 100644 --- a/drivers/scsi/qla4xxx/ql4_nx.c +++ b/drivers/scsi/qla4xxx/ql4_nx.c @@ -2024,6 +2024,10 @@ qla4_8xxx_get_flt_info(struct scsi_qla_host *ha, uint32_t flt_addr) case FLT_REG_ISCSI_PARAM: hw->flt_iscsi_param = start; break; + case FLT_REG_ISCSI_CHAP: + hw->flt_region_chap = start; + hw->flt_chap_size = le32_to_cpu(region->size); + break; } } goto done; @@ -2036,6 +2040,9 @@ no_flash_data: hw->flt_region_boot = FA_BOOT_CODE_ADDR_82; hw->flt_region_bootload = FA_BOOT_LOAD_ADDR_82; hw->flt_region_fw = FA_RISC_CODE_ADDR_82; + hw->flt_region_chap = FA_FLASH_ISCSI_CHAP; + hw->flt_chap_size = FA_FLASH_CHAP_SIZE; + done: DEBUG2(ql4_printk(KERN_INFO, ha, "FLT[%s]: flt=0x%x fdt=0x%x " "boot=0x%x bootload=0x%x fw=0x%x\n", loc, hw->flt_region_flt, diff --git a/drivers/scsi/qla4xxx/ql4_os.c b/drivers/scsi/qla4xxx/ql4_os.c index ce391d5511e..874621db4a9 100644 --- a/drivers/scsi/qla4xxx/ql4_os.c +++ b/drivers/scsi/qla4xxx/ql4_os.c @@ -1605,6 +1605,10 @@ static void qla4xxx_mem_free(struct scsi_qla_host *ha) if (ha->chap_dma_pool) dma_pool_destroy(ha->chap_dma_pool); + if (ha->chap_list) + vfree(ha->chap_list); + ha->chap_list = NULL; + /* release io space registers */ if (is_qla8022(ha)) { if (ha->nx_pcibase) @@ -3058,6 +3062,66 @@ kset_free: return -ENOMEM; } + +/** + * qla4xxx_create chap_list - Create CHAP list from FLASH + * @ha: pointer to adapter structure + * + * Read flash and make a list of CHAP entries, during login when a CHAP entry + * is received, it will be checked in this list. If entry exist then the CHAP + * entry index is set in the DDB. If CHAP entry does not exist in this list + * then a new entry is added in FLASH in CHAP table and the index obtained is + * used in the DDB. + **/ +static void qla4xxx_create_chap_list(struct scsi_qla_host *ha) +{ + int rval = 0; + uint8_t *chap_flash_data = NULL; + uint32_t offset; + dma_addr_t chap_dma; + uint32_t chap_size = 0; + + if (is_qla40XX(ha)) + chap_size = MAX_CHAP_ENTRIES_40XX * + sizeof(struct ql4_chap_table); + else /* Single region contains CHAP info for both + * ports which is divided into half for each port. + */ + chap_size = ha->hw.flt_chap_size / 2; + + chap_flash_data = dma_alloc_coherent(&ha->pdev->dev, chap_size, + &chap_dma, GFP_KERNEL); + if (!chap_flash_data) { + ql4_printk(KERN_ERR, ha, "No memory for chap_flash_data\n"); + return; + } + if (is_qla40XX(ha)) + offset = FLASH_CHAP_OFFSET; + else { + offset = FLASH_RAW_ACCESS_ADDR + (ha->hw.flt_region_chap << 2); + if (ha->port_num == 1) + offset += chap_size; + } + + rval = qla4xxx_get_flash(ha, chap_dma, offset, chap_size); + if (rval != QLA_SUCCESS) + goto exit_chap_list; + + if (ha->chap_list == NULL) + ha->chap_list = vmalloc(chap_size); + if (ha->chap_list == NULL) { + ql4_printk(KERN_ERR, ha, "No memory for ha->chap_list\n"); + goto exit_chap_list; + } + + memcpy(ha->chap_list, chap_flash_data, chap_size); + +exit_chap_list: + dma_free_coherent(&ha->pdev->dev, chap_size, + chap_flash_data, chap_dma); + return; +} + /** * qla4xxx_probe_adapter - callback function to probe HBA * @pdev: pointer to pci_dev structure @@ -3135,6 +3199,7 @@ static int __devinit qla4xxx_probe_adapter(struct pci_dev *pdev, INIT_LIST_HEAD(&ha->free_srb_q); mutex_init(&ha->mbox_sem); + mutex_init(&ha->chap_sem); init_completion(&ha->mbx_intr_comp); init_completion(&ha->disable_acb_comp); @@ -3266,6 +3331,8 @@ static int __devinit qla4xxx_probe_adapter(struct pci_dev *pdev, ha->host_no, ha->firmware_version[0], ha->firmware_version[1], ha->patch_number, ha->build_number); + qla4xxx_create_chap_list(ha); + if (qla4xxx_setup_boot_info(ha)) ql4_printk(KERN_ERR, ha, "%s:ISCSI boot info setup failed\n", __func__);