From: Adheer Chandravanshi <[email protected]> This patch allows iscsiadm to manage iSCSI target information stored on qla4xxx adapter flash on per host basis.
Signed-off-by: Adheer Chandravanshi <[email protected]> Signed-off-by: Vikas Chaudhary <[email protected]> --- drivers/scsi/qla4xxx/ql4_def.h | 9 + drivers/scsi/qla4xxx/ql4_fw.h | 43 ++- drivers/scsi/qla4xxx/ql4_glbl.h | 4 + drivers/scsi/qla4xxx/ql4_mbx.c | 53 ++- drivers/scsi/qla4xxx/ql4_os.c | 1320 ++++++++++++++++++++++++++++++++++++++- 5 files changed, 1425 insertions(+), 4 deletions(-) diff --git a/drivers/scsi/qla4xxx/ql4_def.h b/drivers/scsi/qla4xxx/ql4_def.h index c71a371..d2a32cc 100644 --- a/drivers/scsi/qla4xxx/ql4_def.h +++ b/drivers/scsi/qla4xxx/ql4_def.h @@ -280,6 +280,14 @@ struct ddb_entry { uint16_t chap_tbl_idx; }; +struct ddb_sysfs_entry { + struct scsi_qla_host *ha; + struct dev_db_entry fw_ddb; + uint16_t tgt_flash_idx; + uint16_t tgt_sess_idx; + uint8_t is_dirty; +}; + struct qla_ddb_index { struct list_head list; uint16_t fw_ddb_idx; @@ -498,6 +506,7 @@ struct scsi_qla_host { #define AF_INIT_DONE 1 /* 0x00000002 */ #define AF_MBOX_COMMAND 2 /* 0x00000004 */ #define AF_MBOX_COMMAND_DONE 3 /* 0x00000008 */ +#define AF_ST_DISCOVERY_IN_PROGRESS 4 /* 0x00000010 */ #define AF_INTERRUPTS_ON 6 /* 0x00000040 */ #define AF_GET_CRASH_RECORD 7 /* 0x00000080 */ #define AF_LINK_UP 8 /* 0x00000100 */ diff --git a/drivers/scsi/qla4xxx/ql4_fw.h b/drivers/scsi/qla4xxx/ql4_fw.h index 1c47950..95f39a5 100644 --- a/drivers/scsi/qla4xxx/ql4_fw.h +++ b/drivers/scsi/qla4xxx/ql4_fw.h @@ -766,6 +766,46 @@ struct ql4_chap_table { uint16_t cookie; }; +/* Options bits */ +#define OPT_IS_FW_ASSIGNED_IPV6 (1 << 11) +#define OPT_DEV_TYPE (1 << 8) +#define OPT_DIF_EN (1 << 7) +#define OPT_AUTO_SEND_TARGET_DISABLE (1 << 6) +#define OPT_DISCOVERY_SESSION (1 << 4) +#define OPT_DDB_ENTRY_STATE (1 << 3) +#define OPT_DEV_ASSOC_TARGET (1 << 1) +#define OPT_DEV_ASSOC_INITIATOR (1 << 0) + +/* iSCSI Options bits */ +#define ISCSIOPT_HEADER_DIGEST_EN (1 << 13) +#define ISCSIOPT_DATA_DIGEST_EN (1 << 12) +#define ISCSIOPT_IMMEDIATE_DATA_EN (1 << 11) +#define ISCSIOPT_INITIAL_R2T_EN (1 << 10) +#define ISCSIOPT_DATA_SEQ_IN_ORDER (1 << 9) +#define ISCSIOPT_DATA_PDU_IN_ORDER (1 << 8) +#define ISCSIOPT_CHAP_AUTH_EN (1 << 7) +#define ISCSIOPT_SNACK_REQ_EN (1 << 6) +#define ISCSIOPT_DISCOVERY_LOGOUT_EN (1 << 5) +#define ISCSIOPT_BIDI_CHAP_EN (1 << 4) +#define ISCSIOPT_DISCOVERY_AUTH_OPTIONAL (1 << 3) +#define ISCSIOPT_ERL1 (1 << 1) +#define ISCSIOPT_ERL0 (1 << 0) + +/* TCP Options bits */ +#define TCPOPT_TIMESTAMP_STAT (1 << 6) +#define TCPOPT_NAGLE_DISABLE (1 << 5) +#define TCPOPT_WSF_DISABLE (1 << 4) +#define TCPOPT_TIMER_SCALE3 (1 << 3) +#define TCPOPT_TIMER_SCALE2 (1 << 2) +#define TCPOPT_TIMER_SCALE1 (1 << 1) +#define TCPOPT_TIMESTAMP_EN (1 << 0) + +/* IP Options bits */ +#define IPOPT_FRAGMENTATION_DISABLE (1 << 4) + +#define DEV_TYPE_IPV4 "ipv4" +#define DEV_TYPE_IPV6 "ipv6" + struct dev_db_entry { uint16_t options; /* 00-01 */ #define DDB_OPT_DISC_SESSION 0x10 @@ -777,7 +817,8 @@ struct dev_db_entry { uint16_t exec_throttle; /* 02-03 */ uint16_t exec_count; /* 04-05 */ - uint16_t res0; /* 06-07 */ + uint8_t retry_count; /* 06 */ + uint8_t retry_delay; /* 07 */ uint16_t iscsi_options; /* 08-09 */ uint16_t tcp_options; /* 0A-0B */ uint16_t ip_options; /* 0C-0D */ diff --git a/drivers/scsi/qla4xxx/ql4_glbl.h b/drivers/scsi/qla4xxx/ql4_glbl.h index 7a2a35a..cbb5c4e 100644 --- a/drivers/scsi/qla4xxx/ql4_glbl.h +++ b/drivers/scsi/qla4xxx/ql4_glbl.h @@ -260,6 +260,10 @@ int qla4_8xxx_update_idc_reg(struct scsi_qla_host *ha); int qla4_83xx_post_idc_ack(struct scsi_qla_host *ha); void qla4_83xx_disable_pause(struct scsi_qla_host *ha); void qla4_83xx_enable_mbox_intrs(struct scsi_qla_host *ha); +int qla4xxx_get_default_ddb(struct scsi_qla_host *ha, uint32_t options, + dma_addr_t dma_addr); +int qla4xxx_get_uni_chap_at_index(struct scsi_qla_host *ha, char *username, + char *password, uint16_t chap_index); extern int ql4xextended_error_logging; extern int ql4xdontresethba; diff --git a/drivers/scsi/qla4xxx/ql4_mbx.c b/drivers/scsi/qla4xxx/ql4_mbx.c index 1c57c22..8d4200a 100644 --- a/drivers/scsi/qla4xxx/ql4_mbx.c +++ b/drivers/scsi/qla4xxx/ql4_mbx.c @@ -1265,8 +1265,8 @@ exit_about_fw: return status; } -static int qla4xxx_get_default_ddb(struct scsi_qla_host *ha, uint32_t options, - dma_addr_t dma_addr) +int qla4xxx_get_default_ddb(struct scsi_qla_host *ha, uint32_t options, + dma_addr_t dma_addr) { uint32_t mbox_cmd[MBOX_REG_COUNT]; uint32_t mbox_sts[MBOX_REG_COUNT]; @@ -1489,6 +1489,55 @@ exit_set_chap: return ret; } + +int qla4xxx_get_uni_chap_at_index(struct scsi_qla_host *ha, char *username, + char *password, uint16_t chap_index) +{ + int rval = QLA_ERROR; + struct ql4_chap_table *chap_table = NULL; + + if (!ha->chap_list) { + ql4_printk(KERN_ERR, ha, "Do not have CHAP table cache\n"); + rval = QLA_ERROR; + goto exit_uni_chap; + } + + if (!username || !password) { + ql4_printk(KERN_ERR, ha, "No memory for username & secret\n"); + rval = QLA_ERROR; + goto exit_uni_chap; + } + + if (chap_index > MAX_RESRV_CHAP_IDX) { + ql4_printk(KERN_ERR, ha, "Invalid Chap index\n"); + rval = QLA_ERROR; + goto exit_uni_chap; + } + + mutex_lock(&ha->chap_sem); + chap_table = (struct ql4_chap_table *)ha->chap_list + chap_index; + if (chap_table->cookie != __constant_cpu_to_le16(CHAP_VALID_COOKIE)) { + rval = QLA_ERROR; + goto exit_unlock_uni_chap; + } + + if (!(chap_table->flags & BIT_6)) { + ql4_printk(KERN_ERR, ha, "Unidirectional entry not set\n"); + rval = QLA_ERROR; + goto exit_unlock_uni_chap; + } + + strncpy(password, chap_table->secret, MAX_CHAP_SECRET_LEN); + strncpy(username, chap_table->name, MAX_CHAP_NAME_LEN); + + rval = QLA_SUCCESS; + +exit_unlock_uni_chap: + mutex_unlock(&ha->chap_sem); +exit_uni_chap: + return rval; +} + /** * qla4xxx_get_chap_index - Get chap index given username and secret * @ha: pointer to adapter structure diff --git a/drivers/scsi/qla4xxx/ql4_os.c b/drivers/scsi/qla4xxx/ql4_os.c index 3a96816..d3a9200 100644 --- a/drivers/scsi/qla4xxx/ql4_os.c +++ b/drivers/scsi/qla4xxx/ql4_os.c @@ -166,6 +166,20 @@ static int qla4xxx_host_reset(struct Scsi_Host *shost, int reset_type); static int qla4xxx_change_queue_depth(struct scsi_device *sdev, int qdepth, int reason); +/* + * iSCSI Flash DDB sysfs entry points + */ +static int qla4xxx_sysfs_ddb_set_param(struct iscsi_flash_tgt *tgt, int param, + const char *buf, int len); +static int qla4xxx_sysfs_ddb_get_param(struct iscsi_flash_tgt *tgt, int param, + char *buf); +static struct iscsi_flash_tgt *qla4xxx_sysfs_ddb_add(struct Scsi_Host *shost, + const char *buf, int len); +static void qla4xxx_sysfs_ddb_delete(struct iscsi_flash_tgt *tgt); +static int qla4xxx_sysfs_ddb_apply(struct iscsi_flash_tgt *tgt); +static int qla4xxx_sysfs_ddb_login(struct iscsi_flash_tgt *tgt); +static int qla4xxx_sysfs_ddb_logout(struct iscsi_cls_session *cls_sess); + static struct qla4_8xxx_legacy_intr_set legacy_intr[] = QLA82XX_LEGACY_INTR_CONFIG; @@ -232,6 +246,13 @@ static struct iscsi_transport qla4xxx_iscsi_transport = { .send_ping = qla4xxx_send_ping, .get_chap = qla4xxx_get_chap_list, .delete_chap = qla4xxx_delete_chap, + .get_flash_tgt_param = qla4xxx_sysfs_ddb_get_param, + .set_flash_tgt_param = qla4xxx_sysfs_ddb_set_param, + .new_flash_tgt = qla4xxx_sysfs_ddb_add, + .del_flash_tgt = qla4xxx_sysfs_ddb_delete, + .apply_flash_tgt = qla4xxx_sysfs_ddb_apply, + .login_flash_tgt = qla4xxx_sysfs_ddb_login, + .logout_flash_tgt = qla4xxx_sysfs_ddb_logout, }; static struct scsi_transport_template *qla4xxx_scsi_transport; @@ -376,6 +397,71 @@ static umode_t qla4_attr_is_visible(int param_type, int param) default: return 0; } + case ISCSI_FLASHTGT_PARAM: + switch (param) { + case ISCSI_FLASHTGT_IS_FW_ASSIGNED_IPV6: + case ISCSI_FLASHTGT_DEV_TYPE: + case ISCSI_FLASHTGT_DIF_EN: + case ISCSI_FLASHTGT_AUTO_SND_TGT_DISABLE: + case ISCSI_FLASHTGT_DISCOVERY_SESS: + case ISCSI_FLASHTGT_ENTRY_EN: + case ISCSI_FLASHTGT_DEV_ASSOC_TARGET: + case ISCSI_FLASHTGT_DEV_ASSOC_INITIATOR: + case ISCSI_FLASHTGT_EXEC_THROTTLE: + case ISCSI_FLASHTGT_EXEC_COUNT: + case ISCSI_FLASHTGT_RETRY_COUNT: + case ISCSI_FLASHTGT_RETRY_DELAY: + case ISCSI_FLASHTGT_HDR_DGST_EN: + case ISCSI_FLASHTGT_DATA_DGST_EN: + case ISCSI_FLASHTGT_IMM_DATA_EN: + case ISCSI_FLASHTGT_INITIAL_R2T_EN: + case ISCSI_FLASHTGT_DATASEQ_INORDER: + case ISCSI_FLASHTGT_PDU_INORDER: + case ISCSI_FLASHTGT_CHAP_AUTH_EN: + case ISCSI_FLASHTGT_SNACK_REQ_EN: + case ISCSI_FLASHTGT_DISCOVERY_LOGOUT_EN: + case ISCSI_FLASHTGT_BIDI_CHAP_EN: + case ISCSI_FLASHTGT_DISCOVERY_AUTH_OPTIONAL: + case ISCSI_FLASHTGT_ERL: + case ISCSI_FLASHTGT_TCP_TIMESTAMP_STAT: + case ISCSI_FLASHTGT_TCP_NAGLE_DISABLE: + case ISCSI_FLASHTGT_TCP_WSF_DISABLE: + case ISCSI_FLASHTGT_TCP_TIMER_SCALE: + case ISCSI_FLASHTGT_TCP_TIMESTAMP_EN: + case ISCSI_FLASHTGT_IP_FRAG_DISABLE: + case ISCSI_FLASHTGT_MAX_RECV_DLENGTH: + case ISCSI_FLASHTGT_MAX_XMIT_DLENGTH: + case ISCSI_FLASHTGT_FIRST_BURST: + case ISCSI_FLASHTGT_DEF_TIME2WAIT: + case ISCSI_FLASHTGT_DEF_TIME2RETAIN: + case ISCSI_FLASHTGT_MAX_R2T: + case ISCSI_FLASHTGT_KEEPALIVE_TMO: + case ISCSI_FLASHTGT_ISID: + case ISCSI_FLASHTGT_TSID: + case ISCSI_FLASHTGT_PORT: + case ISCSI_FLASHTGT_MAX_BURST: + case ISCSI_FLASHTGT_DEF_TMF_TMO: + case ISCSI_FLASHTGT_IPADDR: + case ISCSI_FLASHTGT_ALIAS: + case ISCSI_FLASHTGT_REDIRECT_IPADDR: + case ISCSI_FLASHTGT_MAX_SEGMENT_SIZE: + case ISCSI_FLASHTGT_LOCAL_PORT: + case ISCSI_FLASHTGT_IPV4_TOS: + case ISCSI_FLASHTGT_IPV6_FLOW_LABEL: + case ISCSI_FLASHTGT_NAME: + case ISCSI_FLASHTGT_TPGT: + case ISCSI_FLASHTGT_LINK_LOCAL_IPV6: + case ISCSI_FLASHTGT_DISCOVERY_PARENT: + case ISCSI_FLASHTGT_TCP_XMIT_WSF: + case ISCSI_FLASHTGT_TCP_RECV_WSF: + case ISCSI_FLASHTGT_USERNAME: + case ISCSI_FLASHTGT_PASSWORD: + case ISCSI_FLASHTGT_STATSN: + case ISCSI_FLASHTGT_EXP_STATSN: + return S_IRUGO; + default: + return 0; + } } return 0; @@ -5051,6 +5137,1233 @@ exit_nt_list: dma_pool_free(ha->fw_ddb_dma_pool, fw_ddb_entry, fw_ddb_dma); } +static void qla4xxx_build_new_nt_list(struct scsi_qla_host *ha, + struct list_head *list_nt) +{ + struct dev_db_entry *fw_ddb_entry; + dma_addr_t fw_ddb_dma; + int max_ddbs; + int fw_idx_size; + int ret; + uint32_t idx = 0, next_idx = 0; + uint32_t state = 0, conn_err = 0; + uint16_t conn_id = 0; + struct qla_ddb_index *nt_ddb_idx; + + fw_ddb_entry = dma_pool_alloc(ha->fw_ddb_dma_pool, GFP_KERNEL, + &fw_ddb_dma); + if (fw_ddb_entry == NULL) { + DEBUG2(ql4_printk(KERN_ERR, ha, "Out of memory\n")); + goto exit_new_nt_list; + } + max_ddbs = is_qla40XX(ha) ? MAX_DEV_DB_ENTRIES_40XX : + MAX_DEV_DB_ENTRIES; + fw_idx_size = sizeof(struct qla_ddb_index); + + for (idx = 0; idx < max_ddbs; idx = next_idx) { + ret = qla4xxx_get_fwddb_entry(ha, idx, fw_ddb_entry, fw_ddb_dma, + NULL, &next_idx, &state, + &conn_err, NULL, &conn_id); + if (ret == QLA_ERROR) + break; + + /* Check if NT, then add to list it */ + if (strlen((char *)fw_ddb_entry->iscsi_name) == 0) + goto continue_next_new_nt; + + if (!(state == DDB_DS_NO_CONNECTION_ACTIVE)) + goto continue_next_new_nt; + + DEBUG2(ql4_printk(KERN_INFO, ha, + "Adding DDB to session = 0x%x\n", idx)); + + nt_ddb_idx = vmalloc(fw_idx_size); + if (!nt_ddb_idx) + break; + + nt_ddb_idx->fw_ddb_idx = idx; + + if (qla4xxx_is_session_exists(ha, fw_ddb_entry) == + QLA_SUCCESS) { + /* free nt_ddb_idx and do not add to list_nt */ + vfree(nt_ddb_idx); + goto continue_next_new_nt; + } + + list_add_tail(&nt_ddb_idx->list, list_nt); + + ret = qla4xxx_sess_conn_setup(ha, fw_ddb_entry, RESET_ADAPTER); + if (ret == QLA_ERROR) + goto exit_new_nt_list; + +continue_next_new_nt: + if (next_idx == 0) + break; + } + +exit_new_nt_list: + if (fw_ddb_entry) + dma_pool_free(ha->fw_ddb_dma_pool, fw_ddb_entry, fw_ddb_dma); +} + +/** + * qla4xxx_sysfs_ddb_is_non_persistent - check for non-persistence of ddb entry + * @dev: dev associated with the sysfs entry + * @data: pointer containing value of valid cookie + * + * Returns: + * 0: if cookie is valid (persistent) + * 1: if cookie is invalid (non-persistent) + **/ +static int qla4xxx_sysfs_ddb_is_non_persistent(struct device *dev, void *data) +{ + struct iscsi_flash_tgt *tgt; + struct ddb_sysfs_entry *ddb_sysfs; + int *valid_cookie = data; + + if (!iscsi_is_flash_tgt_dev(dev)) + return 0; + + tgt = iscsi_dev_to_flash_tgt(dev); + ddb_sysfs = tgt->dd_data; + + return !(ddb_sysfs->fw_ddb.cookie == *valid_cookie); +} + +/** + * qla4xxx_sysfs_ddb_tgt_create - Create sysfs entry for target + * @ha: pointer to host + * @fw_ddb_entry: flash ddb data + * @t_list: pointer list of targets maintained in iscsi_host + * @idx: target index + * @user: if set then this call is made from userland else from kernel + * + * Returns: + * On sucess: QLA_SUCCESS + * On failure: QLA_ERROR + * + * This create sysfs entry for the target with given index. + * If it is a call from userspace then the entry is made dirty, + * using is_dirty flag, at the time of creation. + **/ +static struct iscsi_flash_tgt * +qla4xxx_sysfs_ddb_tgt_create(struct scsi_qla_host *ha, + struct dev_db_entry *fw_ddb_entry, + uint16_t *idx, int user) +{ + struct ddb_sysfs_entry *ddb_sysfs; + struct iscsi_flash_tgt *tgt = NULL; + + ddb_sysfs = kzalloc(sizeof(*ddb_sysfs), GFP_KERNEL); + if (!ddb_sysfs) { + DEBUG2(ql4_printk(KERN_ERR, ha, + "%s: Unable to allocate ddb buffer to create sysfs entry\n", + __func__)); + goto leave_tgt_create; + } + + ddb_sysfs->ha = ha; + memcpy(&ddb_sysfs->fw_ddb, fw_ddb_entry, sizeof(*fw_ddb_entry)); + ddb_sysfs->tgt_flash_idx = *idx; + + tgt = iscsi_create_flash_tgt(ha->host, *idx, &qla4xxx_iscsi_transport, + sizeof(*ddb_sysfs)); + if (!tgt) { + ql4_printk(KERN_ERR, ha, + "%s: Unable to create sysfs entry for tgt %d of host%lu\n", + __func__, *idx, ha->host_no); + goto leave_tgt_create; + } + + if (user) { + ddb_sysfs->tgt_sess_idx = INVALID_ENTRY; + ddb_sysfs->is_dirty = 1; + } + + memcpy(tgt->dd_data, ddb_sysfs, sizeof(*ddb_sysfs)); + + ql4_printk(KERN_INFO, ha, "%s: sysfs entry %s created\n", + __func__, tgt->dev.kobj.name); + +leave_tgt_create: + kfree(ddb_sysfs); + return tgt; +} + +/** + * qla4xxx_sysfs_ddb_add - Add new ddb entry in flash + * @shost: pointer to host + * @buf: type of ddb entry (ipv4/ipv6) + * @len: length of buf + * + * This creates new ddb entry in the flash by finding first free index and + * storing default ddb there. And then create sysfs entry for the new ddb entry. + **/ +static struct iscsi_flash_tgt *qla4xxx_sysfs_ddb_add(struct Scsi_Host *shost, + const char *buf, int len) +{ + struct scsi_qla_host *ha = to_qla_host(shost); + uint16_t idx = 0; + uint16_t max_ddbs = 0; + uint32_t options = 0; + uint32_t rval = QLA_SUCCESS; + struct iscsi_flash_tgt *tgt = NULL; + struct dev_db_entry *fw_ddb_entry = NULL; + int valid_cookie; + dma_addr_t fw_ddb_entry_dma; + struct device *dev; + + if (strncasecmp(DEV_TYPE_IPV4, buf, 4) && + strncasecmp(DEV_TYPE_IPV6, buf, 4)) + goto leave_ddb_add; + + max_ddbs = is_qla40XX(ha) ? MAX_DEV_DB_ENTRIES_40XX : + MAX_DEV_DB_ENTRIES; + + fw_ddb_entry = dma_alloc_coherent(&ha->pdev->dev, sizeof(*fw_ddb_entry), + &fw_ddb_entry_dma, GFP_KERNEL); + if (!fw_ddb_entry) { + DEBUG2(ql4_printk(KERN_ERR, ha, + "%s: Unable to allocate dma buffer\n", + __func__)); + goto leave_ddb_add; + } + + for (idx = 0; idx < max_ddbs; idx++) { + if (qla4xxx_bootdb_by_index(ha, fw_ddb_entry, + fw_ddb_entry_dma, idx)) + break; + } + + valid_cookie = DDB_VALID_COOKIE; + dev = iscsi_find_flash_tgt(ha->host, &valid_cookie, + qla4xxx_sysfs_ddb_is_non_persistent); + if (dev) { + ql4_printk(KERN_ERR, ha, + "%s: A non-persistent target %s found\n", + __func__, dev->kobj.name); + goto leave_ddb_add; + } + + if (!strncasecmp("IPv6", buf, 4)) + options |= IPV6_DEFAULT_DDB_ENTRY; + + rval = qla4xxx_get_default_ddb(ha, options, fw_ddb_entry_dma); + if (rval == QLA_ERROR) + goto leave_ddb_add; + + tgt = qla4xxx_sysfs_ddb_tgt_create(ha, fw_ddb_entry, &idx, 1); + +leave_ddb_add: + if (fw_ddb_entry) + dma_free_coherent(&ha->pdev->dev, sizeof(*fw_ddb_entry), + fw_ddb_entry, fw_ddb_entry_dma); + + return tgt; +} + +/** + * qla4xxx_sysfs_ddb_apply - write the target ddb contents to Flash + * @tgt: pointer to flash ddb entry + * + * This writes the contents of target ddb buffer to Flash with a valid cookie + * value in order to make the ddb entry persistent. + * If the cookie is already valid then nothing to be done. + **/ +static int qla4xxx_sysfs_ddb_apply(struct iscsi_flash_tgt *tgt) +{ + struct ddb_sysfs_entry *ddb_sysfs = tgt->dd_data; + struct scsi_qla_host *ha = ddb_sysfs->ha; + uint32_t dev_db_start_offset = FLASH_OFFSET_DB_INFO; + struct dev_db_entry *fw_ddb_entry = NULL; + dma_addr_t fw_ddb_entry_dma; + int rval = 0; + + if (!ddb_sysfs->is_dirty) { + DEBUG2(ql4_printk(KERN_ERR, ha, + "%s: ddb entry is up to date\n", + __func__)); + goto exit_ddb_apply; + } + + fw_ddb_entry = dma_alloc_coherent(&ha->pdev->dev, sizeof(*fw_ddb_entry), + &fw_ddb_entry_dma, GFP_KERNEL); + if (!fw_ddb_entry) { + DEBUG2(ql4_printk(KERN_ERR, ha, + "%s: Unable to allocate dma buffer\n", + __func__)); + rval = -ENOMEM; + goto exit_ddb_apply; + } + + dev_db_start_offset += (ddb_sysfs->tgt_flash_idx * + sizeof(*fw_ddb_entry)); + + ddb_sysfs->fw_ddb.cookie = DDB_VALID_COOKIE; + memcpy(fw_ddb_entry, &ddb_sysfs->fw_ddb, sizeof(*fw_ddb_entry)); + + rval = qla4xxx_set_flash(ha, fw_ddb_entry_dma, dev_db_start_offset, + sizeof(*fw_ddb_entry), FLASH_OPT_RMW_COMMIT); + + if (rval == QLA_SUCCESS) { + ddb_sysfs->is_dirty = 0; + ql4_printk(KERN_INFO, ha, + "%s: flash tgt entry tgt-%lu-%u applied to flash\n", + __func__, ha->host_no, ddb_sysfs->tgt_flash_idx); + } else { + ddb_sysfs->fw_ddb.cookie = 0xFFEE; + ddb_sysfs->is_dirty = 1; + rval = -EIO; + DEBUG2(ql4_printk(KERN_ERR, ha, + "%s: Error while applying tgt-%lu-%u to flash\n", + __func__, ha->host_no, + ddb_sysfs->tgt_flash_idx)); + } + +exit_ddb_apply: + if (fw_ddb_entry) + dma_free_coherent(&ha->pdev->dev, sizeof(*fw_ddb_entry), + fw_ddb_entry, fw_ddb_entry_dma); + return rval; +} + +static ssize_t qla4xxx_sysfs_ddb_conn_open(struct scsi_qla_host *ha, + struct dev_db_entry *fw_ddb_entry, + uint16_t idx) +{ + struct dev_db_entry *ddb_entry = NULL; + dma_addr_t ddb_entry_dma; + unsigned long wtime; + uint32_t mbx_sts = 0; + uint32_t state = 0, conn_err = 0; + uint16_t tmo = 0; + int ret = 0; + + ddb_entry = dma_alloc_coherent(&ha->pdev->dev, sizeof(*ddb_entry), + &ddb_entry_dma, GFP_KERNEL); + if (!ddb_entry) { + DEBUG2(ql4_printk(KERN_ERR, ha, + "%s: Unable to allocate dma buffer\n", + __func__)); + return QLA_ERROR; + } + + memcpy(ddb_entry, fw_ddb_entry, sizeof(*ddb_entry)); + + ret = qla4xxx_set_ddb_entry(ha, idx, ddb_entry_dma, &mbx_sts); + if (ret != QLA_SUCCESS) { + DEBUG2(ql4_printk(KERN_ERR, ha, + "%s: Unable to set ddb entry for index %d\n", + __func__, idx)); + goto exit_ddb_conn_open; + } + + qla4xxx_conn_open(ha, idx); + + /* To ensure that sendtargets is done, wait for at least 12 secs */ + tmo = ((ha->def_timeout > LOGIN_TOV) && + (ha->def_timeout < LOGIN_TOV * 10) ? + ha->def_timeout : LOGIN_TOV); + + DEBUG2(ql4_printk(KERN_INFO, ha, + "Default time to wait for login to ddb %d\n", tmo)); + + wtime = jiffies + (HZ * tmo); + do { + ret = qla4xxx_get_fwddb_entry(ha, idx, NULL, 0, NULL, + NULL, &state, &conn_err, NULL, + NULL); + if (ret == QLA_ERROR) + continue; + + if (state == DDB_DS_NO_CONNECTION_ACTIVE || + state == DDB_DS_SESSION_FAILED) + break; + + schedule_timeout_uninterruptible(HZ / 10); + } while (time_after(wtime, jiffies)); + +exit_ddb_conn_open: + if (ddb_entry) + dma_free_coherent(&ha->pdev->dev, sizeof(*ddb_entry), + ddb_entry, ddb_entry_dma); + return ret; +} + +static int qla4xxx_ddb_login_st(struct scsi_qla_host *ha, + struct dev_db_entry *fw_ddb_entry) +{ + struct qla_ddb_index *ddb_idx, *ddb_idx_tmp; + struct list_head list_nt; + uint16_t ddb_index; + int ret = 0; + + if (test_bit(AF_ST_DISCOVERY_IN_PROGRESS, &ha->flags)) { + ql4_printk(KERN_WARNING, ha, + "%s: A discovery already in progress!\n", __func__); + return QLA_ERROR; + } + + INIT_LIST_HEAD(&list_nt); + + set_bit(AF_ST_DISCOVERY_IN_PROGRESS, &ha->flags); + + ret = qla4xxx_get_ddb_index(ha, &ddb_index); + if (ret == QLA_ERROR) + goto exit_login_st_clr_bit; + + ret = qla4xxx_sysfs_ddb_conn_open(ha, fw_ddb_entry, ddb_index); + if (ret == QLA_ERROR) + goto exit_login_st; + + qla4xxx_build_new_nt_list(ha, &list_nt); + + list_for_each_entry_safe(ddb_idx, ddb_idx_tmp, &list_nt, list) { + list_del_init(&ddb_idx->list); + qla4xxx_clear_ddb_entry(ha, ddb_idx->fw_ddb_idx); + vfree(ddb_idx); + } + +exit_login_st: + if (qla4xxx_clear_ddb_entry(ha, ddb_index) == QLA_ERROR) { + ql4_printk(KERN_ERR, ha, + "Unable to clear DDB index = 0x%x\n", ddb_index); + } + +exit_login_st_clr_bit: + clear_bit(AF_ST_DISCOVERY_IN_PROGRESS, &ha->flags); + return ret; +} + +static int qla4xxx_ddb_login_nt(struct scsi_qla_host *ha, + struct dev_db_entry *fw_ddb_entry) +{ + int ret = QLA_ERROR; + + ret = qla4xxx_is_session_exists(ha, fw_ddb_entry); + if (ret != QLA_SUCCESS) + ret = qla4xxx_sess_conn_setup(ha, fw_ddb_entry, RESET_ADAPTER); + + return ret; +} + +/** + * qla4xxx_sysfs_ddb_login - Login to the specified target + * @tgt: pointer to flash ddb entry + * + * This logs in to the specified target + **/ +static int qla4xxx_sysfs_ddb_login(struct iscsi_flash_tgt *tgt) +{ + struct ddb_sysfs_entry *ddb_sysfs = tgt->dd_data; + struct dev_db_entry *fw_ddb_entry = &ddb_sysfs->fw_ddb; + struct scsi_qla_host *ha = ddb_sysfs->ha; + int ret = 0; + + if (fw_ddb_entry->cookie != DDB_VALID_COOKIE) { + ql4_printk(KERN_ERR, ha, + "%s: Target info is not persistent\n", __func__); + return -EIO; + } + + if (strlen((char *)fw_ddb_entry->iscsi_name) == 0) + ret = qla4xxx_ddb_login_st(ha, fw_ddb_entry); + else + ret = qla4xxx_ddb_login_nt(ha, fw_ddb_entry); + + if (ret != QLA_SUCCESS) + ret = -EIO; + + return ret; +} + +/** + * qla4xxx_sysfs_ddb_logout - Logout from the specified target + * @cls_sess: pointer to session to be logged out + * + * This performs log out from the specified target + **/ +static int qla4xxx_sysfs_ddb_logout(struct iscsi_cls_session *cls_sess) +{ + struct iscsi_session *sess; + struct ddb_entry *ddb_entry = NULL; + struct scsi_qla_host *ha; + unsigned long flags; + int options; + int ret = 0; + + sess = cls_sess->dd_data; + ddb_entry = sess->dd_data; + ha = ddb_entry->ha; + + if (ddb_entry->ddb_type != FLASH_DDB) { + ql4_printk(KERN_ERR, ha, "%s: Not a flash tgt session\n", + __func__); + return -EINVAL; + } + + options = LOGOUT_OPTION_CLOSE_SESSION; + if (qla4xxx_session_logout_ddb(ha, ddb_entry, options) + == QLA_ERROR) + ql4_printk(KERN_ERR, ha, "%s: Logout failed\n", + __func__); + + qla4xxx_clear_ddb_entry(ha, ddb_entry->fw_ddb_index); + /* + * we have decremented the reference count of the driver + * when we setup the session to have the driver unload + * to be seamless without actually destroying the + * session + **/ + try_module_get(qla4xxx_iscsi_transport.owner); + iscsi_destroy_endpoint(ddb_entry->conn->ep); + + spin_lock_irqsave(&ha->hardware_lock, flags); + qla4xxx_free_ddb(ha, ddb_entry); + spin_unlock_irqrestore(&ha->hardware_lock, flags); + + iscsi_session_teardown(ddb_entry->sess); + + return ret; +} + +#define GET_BITVAL(o) (o ? 1 : 0) + +static int qla4xxx_sysfs_ddb_get_param(struct iscsi_flash_tgt *tgt, int param, + char *buf) +{ + struct ddb_sysfs_entry *ddb_sysfs = tgt->dd_data; + struct dev_db_entry *fw_ddb_entry = &ddb_sysfs->fw_ddb; + struct ql4_chap_table chap_tbl; + int rc = 0; + + switch (param) { + case ISCSI_FLASHTGT_IS_FW_ASSIGNED_IPV6: + rc = sprintf(buf, "%u\n", + GET_BITVAL(fw_ddb_entry->options & + OPT_IS_FW_ASSIGNED_IPV6)); + break; + case ISCSI_FLASHTGT_DEV_TYPE: + if (fw_ddb_entry->options & OPT_DEV_TYPE) + rc = sprintf(buf, "%s\n", DEV_TYPE_IPV6); + else + rc = sprintf(buf, "%s\n", DEV_TYPE_IPV4); + break; + case ISCSI_FLASHTGT_DIF_EN: + rc = sprintf(buf, "%u\n", + GET_BITVAL(fw_ddb_entry->options & + OPT_DIF_EN)); + break; + case ISCSI_FLASHTGT_AUTO_SND_TGT_DISABLE: + rc = sprintf(buf, "%u\n", + GET_BITVAL(fw_ddb_entry->options & + OPT_AUTO_SEND_TARGET_DISABLE)); + break; + case ISCSI_FLASHTGT_DISCOVERY_SESS: + rc = sprintf(buf, "%u\n", + GET_BITVAL(fw_ddb_entry->options & + OPT_DISCOVERY_SESSION)); + break; + case ISCSI_FLASHTGT_ENTRY_EN: + rc = sprintf(buf, "%u\n", + GET_BITVAL(fw_ddb_entry->options & + OPT_DDB_ENTRY_STATE)); + break; + case ISCSI_FLASHTGT_DEV_ASSOC_TARGET: + rc = sprintf(buf, "%u\n", + GET_BITVAL(fw_ddb_entry->options & + OPT_DEV_ASSOC_TARGET)); + break; + case ISCSI_FLASHTGT_DEV_ASSOC_INITIATOR: + rc = sprintf(buf, "%u\n", + GET_BITVAL(fw_ddb_entry->options & + OPT_DEV_ASSOC_INITIATOR)); + break; + case ISCSI_FLASHTGT_EXEC_THROTTLE: + rc = sprintf(buf, "%u\n", fw_ddb_entry->exec_throttle); + break; + case ISCSI_FLASHTGT_EXEC_COUNT: + rc = sprintf(buf, "%u\n", fw_ddb_entry->exec_count); + break; + case ISCSI_FLASHTGT_RETRY_COUNT: + rc = sprintf(buf, "%u\n", fw_ddb_entry->retry_count); + break; + case ISCSI_FLASHTGT_RETRY_DELAY: + rc = sprintf(buf, "%u\n", fw_ddb_entry->retry_delay); + break; + case ISCSI_FLASHTGT_HDR_DGST_EN: + rc = sprintf(buf, "%u\n", + GET_BITVAL(fw_ddb_entry->iscsi_options & + ISCSIOPT_HEADER_DIGEST_EN)); + break; + case ISCSI_FLASHTGT_DATA_DGST_EN: + rc = sprintf(buf, "%u\n", + GET_BITVAL(fw_ddb_entry->iscsi_options & + ISCSIOPT_DATA_DIGEST_EN)); + break; + case ISCSI_FLASHTGT_IMM_DATA_EN: + rc = sprintf(buf, "%u\n", + GET_BITVAL(fw_ddb_entry->iscsi_options & + ISCSIOPT_IMMEDIATE_DATA_EN)); + break; + case ISCSI_FLASHTGT_INITIAL_R2T_EN: + rc = sprintf(buf, "%u\n", + GET_BITVAL(fw_ddb_entry->iscsi_options & + ISCSIOPT_INITIAL_R2T_EN)); + break; + case ISCSI_FLASHTGT_DATASEQ_INORDER: + rc = sprintf(buf, "%u\n", + GET_BITVAL(fw_ddb_entry->iscsi_options & + ISCSIOPT_DATA_SEQ_IN_ORDER)); + break; + case ISCSI_FLASHTGT_PDU_INORDER: + rc = sprintf(buf, "%u\n", + GET_BITVAL(fw_ddb_entry->iscsi_options & + ISCSIOPT_DATA_PDU_IN_ORDER)); + break; + case ISCSI_FLASHTGT_CHAP_AUTH_EN: + rc = sprintf(buf, "%u\n", + GET_BITVAL(fw_ddb_entry->iscsi_options & + ISCSIOPT_CHAP_AUTH_EN)); + break; + case ISCSI_FLASHTGT_SNACK_REQ_EN: + rc = sprintf(buf, "%u\n", + GET_BITVAL(fw_ddb_entry->iscsi_options & + ISCSIOPT_SNACK_REQ_EN)); + break; + case ISCSI_FLASHTGT_DISCOVERY_LOGOUT_EN: + rc = sprintf(buf, "%u\n", + GET_BITVAL(fw_ddb_entry->iscsi_options & + ISCSIOPT_DISCOVERY_LOGOUT_EN)); + break; + case ISCSI_FLASHTGT_BIDI_CHAP_EN: + rc = sprintf(buf, "%u\n", + GET_BITVAL(fw_ddb_entry->iscsi_options & + ISCSIOPT_BIDI_CHAP_EN)); + break; + case ISCSI_FLASHTGT_DISCOVERY_AUTH_OPTIONAL: + rc = sprintf(buf, "%u\n", + GET_BITVAL(fw_ddb_entry->iscsi_options & + ISCSIOPT_DISCOVERY_AUTH_OPTIONAL)); + break; + case ISCSI_FLASHTGT_ERL: + rc = sprintf(buf, "0x%X\n", + GET_BITVAL(fw_ddb_entry->iscsi_options & + (ISCSIOPT_ERL1 | ISCSIOPT_ERL0))); + break; + case ISCSI_FLASHTGT_TCP_TIMESTAMP_STAT: + rc = sprintf(buf, "%u\n", + GET_BITVAL(fw_ddb_entry->tcp_options & + TCPOPT_TIMESTAMP_STAT)); + break; + case ISCSI_FLASHTGT_TCP_NAGLE_DISABLE: + rc = sprintf(buf, "%u\n", + GET_BITVAL(fw_ddb_entry->tcp_options & + TCPOPT_NAGLE_DISABLE)); + break; + case ISCSI_FLASHTGT_TCP_WSF_DISABLE: + rc = sprintf(buf, "%u\n", + GET_BITVAL(fw_ddb_entry->tcp_options & + TCPOPT_WSF_DISABLE)); + break; + case ISCSI_FLASHTGT_TCP_TIMER_SCALE: + rc = sprintf(buf, "0x%X\n", + (fw_ddb_entry->tcp_options >> 1 & + (TCPOPT_TIMER_SCALE3 | TCPOPT_TIMER_SCALE2 | + TCPOPT_TIMER_SCALE1))); + break; + case ISCSI_FLASHTGT_TCP_TIMESTAMP_EN: + rc = sprintf(buf, "%u\n", + GET_BITVAL(fw_ddb_entry->tcp_options & + TCPOPT_TIMESTAMP_EN)); + break; + case ISCSI_FLASHTGT_IP_FRAG_DISABLE: + rc = sprintf(buf, "%u\n", + GET_BITVAL(fw_ddb_entry->ip_options & + IPOPT_FRAGMENTATION_DISABLE)); + break; + case ISCSI_FLASHTGT_MAX_RECV_DLENGTH: + rc = sprintf(buf, "%u\n", + fw_ddb_entry->iscsi_max_rcv_data_seg_len); + break; + case ISCSI_FLASHTGT_MAX_XMIT_DLENGTH: + rc = sprintf(buf, "%u\n", + fw_ddb_entry->iscsi_max_snd_data_seg_len); + break; + case ISCSI_FLASHTGT_FIRST_BURST: + rc = sprintf(buf, "%u\n", fw_ddb_entry->iscsi_first_burst_len); + break; + case ISCSI_FLASHTGT_DEF_TIME2WAIT: + rc = sprintf(buf, "%u\n", fw_ddb_entry->iscsi_def_time2wait); + break; + case ISCSI_FLASHTGT_DEF_TIME2RETAIN: + rc = sprintf(buf, "%u\n", fw_ddb_entry->iscsi_def_time2retain); + break; + case ISCSI_FLASHTGT_MAX_R2T: + rc = sprintf(buf, "%u\n", fw_ddb_entry->iscsi_max_outsnd_r2t); + break; + case ISCSI_FLASHTGT_KEEPALIVE_TMO: + rc = sprintf(buf, "%u\n", fw_ddb_entry->ka_timeout); + break; + case ISCSI_FLASHTGT_ISID: + memcpy(buf, &fw_ddb_entry->isid[0], sizeof(fw_ddb_entry->isid)); + break; + case ISCSI_FLASHTGT_TSID: + rc = sprintf(buf, "%u\n", fw_ddb_entry->tsid); + break; + case ISCSI_FLASHTGT_PORT: + rc = sprintf(buf, "%d\n", fw_ddb_entry->port); + break; + case ISCSI_FLASHTGT_MAX_BURST: + rc = sprintf(buf, "%u\n", fw_ddb_entry->iscsi_max_burst_len); + break; + case ISCSI_FLASHTGT_DEF_TMF_TMO: + rc = sprintf(buf, "%u\n", fw_ddb_entry->def_timeout); + break; + case ISCSI_FLASHTGT_IPADDR: + if (fw_ddb_entry->options & DDB_OPT_IPV6_DEVICE) + rc = sprintf(buf, "%pI6\n", + &fw_ddb_entry->ip_addr); + else + rc = sprintf(buf, "%pI4\n", + &fw_ddb_entry->ip_addr); + break; + case ISCSI_FLASHTGT_ALIAS: + rc = sprintf(buf, "%s\n", (char *)&fw_ddb_entry->iscsi_alias); + break; + case ISCSI_FLASHTGT_REDIRECT_IPADDR: + if (fw_ddb_entry->options & DDB_OPT_IPV6_DEVICE) + rc = sprintf(buf, "%pI6\n", + &fw_ddb_entry->tgt_addr); + else + rc = sprintf(buf, "%pI4\n", + &fw_ddb_entry->tgt_addr); + break; + case ISCSI_FLASHTGT_MAX_SEGMENT_SIZE: + rc = sprintf(buf, "%u\n", fw_ddb_entry->mss); + break; + case ISCSI_FLASHTGT_LOCAL_PORT: + rc = sprintf(buf, "%u\n", fw_ddb_entry->lcl_port); + break; + case ISCSI_FLASHTGT_IPV4_TOS: + rc = sprintf(buf, "%u\n", fw_ddb_entry->ipv4_tos); + break; + case ISCSI_FLASHTGT_IPV6_FLOW_LABEL: + rc = sprintf(buf, "%u\n", fw_ddb_entry->ipv6_flow_lbl); + break; + case ISCSI_FLASHTGT_LINK_LOCAL_IPV6: + if (fw_ddb_entry->options & DDB_OPT_IPV6_DEVICE) + rc = sprintf(buf, "%pI6\n", + &fw_ddb_entry->link_local_ipv6_addr); + else + rc = sprintf(buf, "\n"); + break; + case ISCSI_FLASHTGT_DISCOVERY_PARENT: + rc = sprintf(buf, "%u\n", fw_ddb_entry->ddb_link); + break; + case ISCSI_FLASHTGT_NAME: + rc = sprintf(buf, "%s\n", (char *)&fw_ddb_entry->iscsi_name); + break; + case ISCSI_FLASHTGT_TPGT: + rc = sprintf(buf, "%u\n", fw_ddb_entry->tgt_portal_grp); + break; + case ISCSI_FLASHTGT_TCP_XMIT_WSF: + rc = sprintf(buf, "%u\n", fw_ddb_entry->tcp_xmt_wsf); + break; + case ISCSI_FLASHTGT_TCP_RECV_WSF: + rc = sprintf(buf, "%u\n", fw_ddb_entry->tcp_rcv_wsf); + break; + case ISCSI_FLASHTGT_USERNAME: + if (fw_ddb_entry->iscsi_options & ISCSIOPT_CHAP_AUTH_EN) { + qla4xxx_get_uni_chap_at_index(ddb_sysfs->ha, + chap_tbl.name, + chap_tbl.secret, + fw_ddb_entry->chap_tbl_idx); + rc = sprintf(buf, "%s\n", chap_tbl.name); + } else { + rc = sprintf(buf, "\n"); + } + break; + case ISCSI_FLASHTGT_PASSWORD: + if (fw_ddb_entry->iscsi_options & ISCSIOPT_CHAP_AUTH_EN) { + qla4xxx_get_uni_chap_at_index(ddb_sysfs->ha, + chap_tbl.name, + chap_tbl.secret, + fw_ddb_entry->chap_tbl_idx); + rc = sprintf(buf, "%s\n", chap_tbl.secret); + } else { + rc = sprintf(buf, "\n"); + } + break; + case ISCSI_FLASHTGT_STATSN: + rc = sprintf(buf, "%u\n", fw_ddb_entry->stat_sn); + break; + case ISCSI_FLASHTGT_EXP_STATSN: + rc = sprintf(buf, "%u\n", fw_ddb_entry->exp_stat_sn); + break; + default: + rc = -ENOSYS; + break; + } + return rc; +} + +#define GET_OPTVAL(f, v, l, u) { \ + rc = kstrtou16(f, 10, &v); \ + if (!rc && (v < l || v > u)) { \ + rc = -EINVAL; \ + goto chk_error; \ + } \ +} + +#define SET_OPTVAL(o, n, v) { \ + if (o) \ + n |= v; \ + else \ + n &= ~v; \ +} + +/** + * qla4xxx_sysfs_ddb_set_param - Set parameter for firmware DDB entry + * @tgt: pointer to flash ddb data + * @param: Parameter to update + * @buf: value of parameter + * @len: len of buf + * + * This sets the parameter of flash ddb entry. + **/ +static int qla4xxx_sysfs_ddb_set_param(struct iscsi_flash_tgt *tgt, int param, + const char *buf, int len) +{ + struct ddb_sysfs_entry *ddb_sysfs = tgt->dd_data; + struct dev_db_entry *fw_ddb_entry = &ddb_sysfs->fw_ddb; + uint16_t options; + int bitval; + int rc = QLA_SUCCESS; + + switch (param) { + case ISCSI_FLASHTGT_IS_FW_ASSIGNED_IPV6: + GET_OPTVAL(buf, options, 0, 1); + if (!rc) + SET_OPTVAL(options, fw_ddb_entry->options, + OPT_IS_FW_ASSIGNED_IPV6); + break; + case ISCSI_FLASHTGT_DEV_TYPE: + if (!strncasecmp(buf, DEV_TYPE_IPV6, strlen(DEV_TYPE_IPV6))) + fw_ddb_entry->options |= OPT_DEV_TYPE; + else + fw_ddb_entry->options &= ~OPT_DEV_TYPE; + break; + case ISCSI_FLASHTGT_DIF_EN: + GET_OPTVAL(buf, options, 0, 1); + if (!rc) + SET_OPTVAL(options, fw_ddb_entry->options, + OPT_DIF_EN); + break; + case ISCSI_FLASHTGT_AUTO_SND_TGT_DISABLE: + GET_OPTVAL(buf, options, 0, 1); + if (!rc) + SET_OPTVAL(options, fw_ddb_entry->options, + OPT_AUTO_SEND_TARGET_DISABLE); + break; + case ISCSI_FLASHTGT_DISCOVERY_SESS: + GET_OPTVAL(buf, options, 0, 1); + if (!rc) + SET_OPTVAL(options, fw_ddb_entry->options, + OPT_DISCOVERY_SESSION); + break; + case ISCSI_FLASHTGT_ENTRY_EN: + GET_OPTVAL(buf, options, 0, 1); + if (!rc) + SET_OPTVAL(options, fw_ddb_entry->options, + OPT_DDB_ENTRY_STATE); + break; + case ISCSI_FLASHTGT_DEV_ASSOC_TARGET: + GET_OPTVAL(buf, options, 0, 1); + if (!rc) + SET_OPTVAL(options, fw_ddb_entry->options, + OPT_DEV_ASSOC_TARGET); + break; + case ISCSI_FLASHTGT_DEV_ASSOC_INITIATOR: + GET_OPTVAL(buf, options, 0, 1); + if (!rc) + SET_OPTVAL(options, fw_ddb_entry->options, + OPT_DEV_ASSOC_INITIATOR); + break; + case ISCSI_FLASHTGT_EXEC_THROTTLE: + rc = kstrtou16(buf, 10, &fw_ddb_entry->exec_throttle); + break; + case ISCSI_FLASHTGT_EXEC_COUNT: + rc = kstrtou16(buf, 10, &fw_ddb_entry->exec_count); + break; + case ISCSI_FLASHTGT_RETRY_COUNT: + rc = kstrtou8(buf, 10, &fw_ddb_entry->retry_count); + break; + case ISCSI_FLASHTGT_RETRY_DELAY: + rc = kstrtou8(buf, 10, &fw_ddb_entry->retry_delay); + break; + case ISCSI_FLASHTGT_HDR_DGST_EN: + GET_OPTVAL(buf, options, 0, 1); + if (!rc) + SET_OPTVAL(options, fw_ddb_entry->iscsi_options, + ISCSIOPT_HEADER_DIGEST_EN); + break; + case ISCSI_FLASHTGT_DATA_DGST_EN: + GET_OPTVAL(buf, options, 0, 1); + if (!rc) + SET_OPTVAL(options, fw_ddb_entry->iscsi_options, + ISCSIOPT_DATA_DIGEST_EN); + break; + case ISCSI_FLASHTGT_IMM_DATA_EN: + GET_OPTVAL(buf, options, 0, 1); + if (!rc) + SET_OPTVAL(options, fw_ddb_entry->iscsi_options, + ISCSIOPT_IMMEDIATE_DATA_EN); + break; + case ISCSI_FLASHTGT_INITIAL_R2T_EN: + GET_OPTVAL(buf, options, 0, 1); + if (!rc) + SET_OPTVAL(options, fw_ddb_entry->iscsi_options, + ISCSIOPT_INITIAL_R2T_EN); + break; + case ISCSI_FLASHTGT_DATASEQ_INORDER: + GET_OPTVAL(buf, options, 0, 1); + if (!rc) + SET_OPTVAL(options, fw_ddb_entry->iscsi_options, + ISCSIOPT_DATA_SEQ_IN_ORDER); + break; + case ISCSI_FLASHTGT_PDU_INORDER: + GET_OPTVAL(buf, options, 0, 1); + if (!rc) + SET_OPTVAL(options, fw_ddb_entry->iscsi_options, + ISCSIOPT_DATA_PDU_IN_ORDER); + break; + case ISCSI_FLASHTGT_CHAP_AUTH_EN: + GET_OPTVAL(buf, options, 0, 1); + if (!rc) + SET_OPTVAL(options, fw_ddb_entry->iscsi_options, + ISCSIOPT_CHAP_AUTH_EN); + break; + case ISCSI_FLASHTGT_SNACK_REQ_EN: + GET_OPTVAL(buf, options, 0, 1); + if (!rc) + SET_OPTVAL(options, fw_ddb_entry->iscsi_options, + ISCSIOPT_SNACK_REQ_EN); + break; + case ISCSI_FLASHTGT_DISCOVERY_LOGOUT_EN: + GET_OPTVAL(buf, options, 0, 1); + if (!rc) + SET_OPTVAL(options, fw_ddb_entry->iscsi_options, + ISCSIOPT_DISCOVERY_LOGOUT_EN); + break; + case ISCSI_FLASHTGT_BIDI_CHAP_EN: + GET_OPTVAL(buf, options, 0, 1); + if (!rc) + SET_OPTVAL(options, fw_ddb_entry->iscsi_options, + ISCSIOPT_BIDI_CHAP_EN); + break; + case ISCSI_FLASHTGT_DISCOVERY_AUTH_OPTIONAL: + GET_OPTVAL(buf, options, 0, 1); + if (!rc) + SET_OPTVAL(options, fw_ddb_entry->iscsi_options, + ISCSIOPT_DISCOVERY_AUTH_OPTIONAL); + break; + case ISCSI_FLASHTGT_ERL: + GET_OPTVAL(buf, options, 0, 3); + if (!rc) { + bitval = options & 1; + SET_OPTVAL(bitval, fw_ddb_entry->iscsi_options, + ISCSIOPT_ERL0); + bitval = (options >> 1) & 1; + SET_OPTVAL(bitval, fw_ddb_entry->iscsi_options, + ISCSIOPT_ERL1); + } + break; + case ISCSI_FLASHTGT_TCP_TIMESTAMP_STAT: + GET_OPTVAL(buf, options, 0, 1); + if (!rc) + SET_OPTVAL(options, fw_ddb_entry->tcp_options, + TCPOPT_TIMESTAMP_STAT); + break; + case ISCSI_FLASHTGT_TCP_NAGLE_DISABLE: + GET_OPTVAL(buf, options, 0, 1); + if (!rc) + SET_OPTVAL(options, fw_ddb_entry->tcp_options, + TCPOPT_NAGLE_DISABLE); + break; + case ISCSI_FLASHTGT_TCP_WSF_DISABLE: + GET_OPTVAL(buf, options, 0, 1); + if (!rc) + SET_OPTVAL(options, fw_ddb_entry->tcp_options, + TCPOPT_WSF_DISABLE); + break; + case ISCSI_FLASHTGT_TCP_TIMER_SCALE: + GET_OPTVAL(buf, options, 0, 7); + if (!rc) { + bitval = options & 1; + SET_OPTVAL(bitval, fw_ddb_entry->tcp_options, + TCPOPT_TIMER_SCALE1); + bitval = (options >> 1) & 1; + SET_OPTVAL(bitval, fw_ddb_entry->tcp_options, + TCPOPT_TIMER_SCALE2); + bitval = (options >> 2) & 1; + SET_OPTVAL(bitval, fw_ddb_entry->tcp_options, + TCPOPT_TIMER_SCALE3); + } + break; + case ISCSI_FLASHTGT_TCP_TIMESTAMP_EN: + GET_OPTVAL(buf, options, 0, 1); + if (!rc) + SET_OPTVAL(options, fw_ddb_entry->tcp_options, + TCPOPT_TIMESTAMP_EN); + break; + case ISCSI_FLASHTGT_IP_FRAG_DISABLE: + GET_OPTVAL(buf, options, 0, 1); + if (!rc) + SET_OPTVAL(options, fw_ddb_entry->ip_options, + IPOPT_FRAGMENTATION_DISABLE); + break; + case ISCSI_FLASHTGT_MAX_RECV_DLENGTH: + rc = kstrtou16(buf, 10, + &fw_ddb_entry->iscsi_max_rcv_data_seg_len); + break; + case ISCSI_FLASHTGT_MAX_XMIT_DLENGTH: + rc = kstrtou16(buf, 10, + &fw_ddb_entry->iscsi_max_snd_data_seg_len); + break; + case ISCSI_FLASHTGT_FIRST_BURST: + rc = kstrtou16(buf, 10, &fw_ddb_entry->iscsi_first_burst_len); + break; + case ISCSI_FLASHTGT_DEF_TIME2WAIT: + rc = kstrtou16(buf, 10, &fw_ddb_entry->iscsi_def_time2wait); + break; + case ISCSI_FLASHTGT_DEF_TIME2RETAIN: + rc = kstrtou16(buf, 10, + &fw_ddb_entry->iscsi_def_time2retain); + break; + case ISCSI_FLASHTGT_MAX_R2T: + rc = kstrtou16(buf, 10, + &fw_ddb_entry->iscsi_max_outsnd_r2t); + break; + case ISCSI_FLASHTGT_KEEPALIVE_TMO: + rc = kstrtou16(buf, 10, &fw_ddb_entry->ka_timeout); + break; + case ISCSI_FLASHTGT_ISID: + memcpy(&fw_ddb_entry->isid[0], buf, sizeof(fw_ddb_entry->isid)); + rc = QLA_SUCCESS; + goto chk_error; + case ISCSI_FLASHTGT_TSID: + rc = kstrtou16(buf, 10, &fw_ddb_entry->tsid); + break; + case ISCSI_FLASHTGT_PORT: + rc = kstrtou16(buf, 10, &fw_ddb_entry->port); + break; + case ISCSI_FLASHTGT_MAX_BURST: + rc = kstrtou16(buf, 10, + &fw_ddb_entry->iscsi_max_burst_len); + break; + case ISCSI_FLASHTGT_DEF_TMF_TMO: + rc = kstrtou16(buf, 10, &fw_ddb_entry->def_timeout); + break; + case ISCSI_FLASHTGT_IPADDR: + if (fw_ddb_entry->options & DDB_OPT_IPV6_DEVICE) + rc = in6_pton(buf, strlen(buf), fw_ddb_entry->ip_addr, + '\0', NULL); + else + rc = in4_pton(buf, strlen(buf), fw_ddb_entry->ip_addr, + '\0', NULL); + + if (rc) + rc = QLA_SUCCESS; + else + rc = -EINVAL; + goto chk_error; + case ISCSI_FLASHTGT_ALIAS: + rc = sprintf(&fw_ddb_entry->iscsi_alias[0], "%s", buf); + if (rc > 0) + rc = QLA_SUCCESS; + else + rc = -EINVAL; + goto chk_error; + case ISCSI_FLASHTGT_REDIRECT_IPADDR: + if (fw_ddb_entry->options & DDB_OPT_IPV6_DEVICE) + rc = in6_pton(buf, strlen(buf), fw_ddb_entry->tgt_addr, + '\0', NULL); + else + rc = in4_pton(buf, strlen(buf), fw_ddb_entry->tgt_addr, + '\0', NULL); + if (rc) + rc = QLA_SUCCESS; + else + rc = -EINVAL; + goto chk_error; + case ISCSI_FLASHTGT_MAX_SEGMENT_SIZE: + rc = kstrtou16(buf, 10, &fw_ddb_entry->mss); + break; + case ISCSI_FLASHTGT_LOCAL_PORT: + rc = kstrtou16(buf, 10, &fw_ddb_entry->lcl_port); + break; + case ISCSI_FLASHTGT_IPV4_TOS: + rc = kstrtou8(buf, 10, &fw_ddb_entry->ipv4_tos); + break; + case ISCSI_FLASHTGT_IPV6_FLOW_LABEL: + rc = kstrtou16(buf, 10, &fw_ddb_entry->ipv6_flow_lbl); + break; + case ISCSI_FLASHTGT_NAME: + rc = sprintf(&fw_ddb_entry->iscsi_name[0], "%s", buf); + goto chk_error; + case ISCSI_FLASHTGT_TPGT: + rc = kstrtou16(buf, 10, &fw_ddb_entry->tgt_portal_grp); + break; + case ISCSI_FLASHTGT_LINK_LOCAL_IPV6: + if (fw_ddb_entry->options & DDB_OPT_IPV6_DEVICE) + rc = in6_pton(buf, strlen(buf), + fw_ddb_entry->link_local_ipv6_addr, + '\0', NULL); + break; + case ISCSI_FLASHTGT_DISCOVERY_PARENT: + rc = kstrtou16(buf, 10, &fw_ddb_entry->ddb_link); + break; + case ISCSI_FLASHTGT_TCP_XMIT_WSF: + rc = kstrtou8(buf, 10, &fw_ddb_entry->tcp_xmt_wsf); + break; + case ISCSI_FLASHTGT_TCP_RECV_WSF: + rc = kstrtou8(buf, 10, &fw_ddb_entry->tcp_rcv_wsf); + break; + case ISCSI_FLASHTGT_USERNAME: + case ISCSI_FLASHTGT_PASSWORD: + /* TODO - Set the chap auth user and passwd according to + * chap table index in flash ddb */ + rc = QLA_SUCCESS; + goto exit_set_param; + case ISCSI_FLASHTGT_STATSN: + rc = kstrtou32(buf, 10, &fw_ddb_entry->stat_sn); + break; + case ISCSI_FLASHTGT_EXP_STATSN: + rc = kstrtou32(buf, 10, &fw_ddb_entry->exp_stat_sn); + break; + default: + ql4_printk(KERN_ERR, ddb_sysfs->ha, + "%s: No such sysfs attribute\n", __func__); + rc = -ENOSYS; + goto exit_set_param; + } + +chk_error: + if (rc == QLA_SUCCESS) { + ddb_sysfs->is_dirty = 1; + } else { + ql4_printk(KERN_ERR, ddb_sysfs->ha, + "%s: Error while setting value, rc=%d\n", + __func__, rc); + } + +exit_set_param: + return rc; +} + +/** + * qla4xxx_sysfs_ddb_delete - Delete firmware DDB entry + * @tgt: pointer to flash ddb data + * + * This invalidates the flash ddb entry at the given index + **/ +static void qla4xxx_sysfs_ddb_delete(struct iscsi_flash_tgt *tgt) +{ + struct ddb_sysfs_entry *ddb_sysfs = tgt->dd_data; + struct scsi_qla_host *ha = ddb_sysfs->ha; + uint32_t dev_db_start_offset = FLASH_OFFSET_DB_INFO; + struct dev_db_entry *fw_ddb_entry = NULL; + dma_addr_t fw_ddb_entry_dma; + + if (!ddb_sysfs) + goto exit_ddb_delete; + + if (ddb_sysfs->fw_ddb.cookie != DDB_VALID_COOKIE) + goto exit_ddb_delete; + + fw_ddb_entry = dma_alloc_coherent(&ha->pdev->dev, sizeof(*fw_ddb_entry), + &fw_ddb_entry_dma, GFP_KERNEL); + if (!fw_ddb_entry) { + DEBUG2(ql4_printk(KERN_ERR, ha, + "%s: Unable to allocate dma buffer\n", + __func__)); + goto exit_ddb_delete; + } + + dev_db_start_offset += (ddb_sysfs->tgt_flash_idx * + sizeof(*fw_ddb_entry)); + + ddb_sysfs->fw_ddb.cookie = 0xFFEE; + memcpy(fw_ddb_entry, &ddb_sysfs->fw_ddb, sizeof(*fw_ddb_entry)); + + qla4xxx_set_flash(ha, fw_ddb_entry_dma, dev_db_start_offset, + sizeof(*fw_ddb_entry), FLASH_OPT_RMW_COMMIT); + + dma_free_coherent(&ha->pdev->dev, sizeof(*fw_ddb_entry), fw_ddb_entry, + fw_ddb_entry_dma); + +exit_ddb_delete: + iscsi_destroy_flash_tgt(tgt); + ql4_printk(KERN_INFO, ha, "%s: flash tgt entry tgt-%lu-%u deleted\n", + __func__, ha->host_no, tgt->tgt_idx); +} + +/** + * qla4xxx_sysfs_ddb_export - Create sysfs entries for firmware DDBs + * @ha: pointer to adapter structure + * + * Export the firmware DDB for all send targets and normal targets to sysfs. + **/ +static int qla4xxx_sysfs_ddb_export(struct scsi_qla_host *ha) +{ + struct dev_db_entry *fw_ddb_entry = NULL; + struct iscsi_flash_tgt *tgt = NULL; + dma_addr_t fw_ddb_entry_dma; + uint16_t max_ddbs; + uint16_t idx = 0; + int ret = QLA_SUCCESS; + + fw_ddb_entry = dma_alloc_coherent(&ha->pdev->dev, + sizeof(*fw_ddb_entry), + &fw_ddb_entry_dma, GFP_KERNEL); + if (!fw_ddb_entry) { + DEBUG2(ql4_printk(KERN_ERR, ha, + "%s: Unable to allocate dma buffer\n", + __func__)); + return -ENOMEM; + } + + max_ddbs = is_qla40XX(ha) ? MAX_DEV_DB_ENTRIES_40XX : + MAX_DEV_DB_ENTRIES; + + for (idx = 0; idx < max_ddbs; idx++) { + if (qla4xxx_bootdb_by_index(ha, fw_ddb_entry, + fw_ddb_entry_dma, idx)) { + DEBUG2(ql4_printk(KERN_INFO, ha, + "%s: No Flash DDB found at index [%d]\n", + __func__, idx)); + continue; + } + + tgt = qla4xxx_sysfs_ddb_tgt_create(ha, fw_ddb_entry, &idx, 0); + if (!tgt) { + ret = -EIO; + break; + } + } + + dma_free_coherent(&ha->pdev->dev, sizeof(*fw_ddb_entry), fw_ddb_entry, + fw_ddb_entry_dma); + + return ret; +} + +static void qla4xxx_sysfs_ddb_remove(struct scsi_qla_host *ha) +{ + iscsi_destroy_all_flash_tgt(ha->host); +} + /** * qla4xxx_build_ddb_list - Build ddb list and setup sessions * @ha: pointer to adapter structure @@ -5366,7 +6679,11 @@ skip_retry_init: ql4_printk(KERN_ERR, ha, "%s: No iSCSI boot target configured\n", __func__); - /* Perform the build ddb list and login to each */ + if (qla4xxx_sysfs_ddb_export(ha)) + ql4_printk(KERN_ERR, ha, + "%s: Error exporting ddb to sysfs\n", __func__); + + /* Perform the build ddb list and login to each */ qla4xxx_build_ddb_list(ha, INIT_ADAPTER); iscsi_host_for_each_session(ha->host, qla4xxx_login_flash_ddb); @@ -5490,6 +6807,7 @@ static void qla4xxx_remove_adapter(struct pci_dev *pdev) qla4xxx_destroy_fw_ddb_session(ha); qla4_8xxx_free_sysfs_attr(ha); + qla4xxx_sysfs_ddb_remove(ha); scsi_remove_host(ha->host); qla4xxx_free_adapter(ha); -- 1.7.8.GIT -- You received this message because you are subscribed to the Google Groups "open-iscsi" group. To post to this group, send email to [email protected]. To unsubscribe from this group, send email to [email protected]. For more options, visit this group at http://groups.google.com/group/open-iscsi?hl=en.
