From: Adheer Chandravanshi <adheer.chandravan...@qlogic.com> This patch allows iscsiadm to manage iSCSI target information stored on qla4xxx adapter flash on per host basis.
Signed-off-by: Adheer Chandravanshi <adheer.chandravan...@qlogic.com> Signed-off-by: Vikas Chaudhary <vikas.chaudh...@qlogic.com> --- drivers/scsi/qla4xxx/Kconfig | 1 + drivers/scsi/qla4xxx/ql4_def.h | 12 + drivers/scsi/qla4xxx/ql4_fw.h | 28 + drivers/scsi/qla4xxx/ql4_glbl.h | 2 + drivers/scsi/qla4xxx/ql4_mbx.c | 4 +- drivers/scsi/qla4xxx/ql4_os.c | 1154 ++++++++++++++++++++++++++++++++++++++- 6 files changed, 1196 insertions(+), 5 deletions(-) diff --git a/drivers/scsi/qla4xxx/Kconfig b/drivers/scsi/qla4xxx/Kconfig index e4dc7c7..16f2b58 100644 --- a/drivers/scsi/qla4xxx/Kconfig +++ b/drivers/scsi/qla4xxx/Kconfig @@ -3,6 +3,7 @@ config SCSI_QLA_ISCSI depends on PCI && SCSI && NET select SCSI_ISCSI_ATTRS select ISCSI_BOOT_SYSFS + select ISCSI_FLASH_SYSFS ---help--- This driver supports the QLogic 40xx (ISP4XXX), 8022 (ISP82XX) and 8032 (ISP83XX) iSCSI host adapter family. diff --git a/drivers/scsi/qla4xxx/ql4_def.h b/drivers/scsi/qla4xxx/ql4_def.h index 8e061ea..15aee02 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 */ @@ -658,6 +667,9 @@ struct scsi_qla_host { /* FW ddb index map */ struct ddb_entry *fw_ddb_index_map[MAX_DDB_ENTRIES]; + /* kobj of host sysfs entry holding flash ddbs */ + struct iscsi_host_kobj *host_kobj; + /* Saved srb for status continuation entry processing */ struct srb *status_srb; diff --git a/drivers/scsi/qla4xxx/ql4_fw.h b/drivers/scsi/qla4xxx/ql4_fw.h index 1ef954f..f41419c 100644 --- a/drivers/scsi/qla4xxx/ql4_fw.h +++ b/drivers/scsi/qla4xxx/ql4_fw.h @@ -757,6 +757,34 @@ struct ql4_chap_table { uint16_t cookie; }; +/* Options bits */ +#define OPT_AUTO_SEND_TARGET_DISABLE (1 << 6) +#define OPT_DISCOVERY_SESSION (1 << 4) +#define OPT_DDB_ENTRY_STATE (1 << 3) + +/* iSCSI Options bits */ +#define ISCSIOPT_ENABLE_HEADER_DIGEST (1 << 13) +#define ISCSIOPT_ENABLE_DATA_DIGEST (1 << 12) +#define ISCSIOPT_ENABLE_IMMEDIATE_DATA (1 << 11) +#define ISCSIOPT_ENABLE_INITIAL_R2T (1 << 10) +#define ISCSIOPT_DATA_SEQ_IN_ORDER (1 << 9) +#define ISCSIOPT_DATA_PDU_IN_ORDER (1 << 8) +#define ISCSIOPT_CHAP_AUTH_ENABLE (1 << 7) +#define ISCSIOPT_SNACK_REQ_ENABLE (1 << 6) +#define ISCSIOPT_DISCOVERY_LOGOUT_ENABLE (1 << 5) +#define ISCSIOPT_BI_CHAP_CHALLENGE_ENABLE (1 << 4) +#define ISCSIOPT_DISCOVERY_AUTH_OPT (1 << 3) + +/* TCP Options bits */ +#define TCPOPT_NAGLE_DISABLE (1 << 5) +#define TCPOPT_TCP_TIMER_SCALE3 (1 << 3) +#define TCPOPT_TCP_TIMER_SCALE2 (1 << 2) +#define TCPOPT_TCP_TIMER_SCALE1 (1 << 1) +#define TCPOPT_TIMESTAMP_ENABLE (1 << 0) + +/* IP Options bits */ +#define IPOPT_FRAGMENTATION_DISABLE (1 << 4) + struct dev_db_entry { uint16_t options; /* 00-01 */ #define DDB_OPT_DISC_SESSION 0x10 diff --git a/drivers/scsi/qla4xxx/ql4_glbl.h b/drivers/scsi/qla4xxx/ql4_glbl.h index b5655ac..e884efe 100644 --- a/drivers/scsi/qla4xxx/ql4_glbl.h +++ b/drivers/scsi/qla4xxx/ql4_glbl.h @@ -255,6 +255,8 @@ int qla4_8xxx_device_bootstrap(struct scsi_qla_host *ha); void qla4_8xxx_get_minidump(struct scsi_qla_host *ha); int qla4_8xxx_mbx_intr_disable(struct scsi_qla_host *ha); int qla4_8xxx_mbx_intr_enable(struct scsi_qla_host *ha); +int qla4xxx_get_default_ddb(struct scsi_qla_host *ha, uint32_t options, + dma_addr_t dma_addr); 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 0375132..2789d5f 100644 --- a/drivers/scsi/qla4xxx/ql4_mbx.c +++ b/drivers/scsi/qla4xxx/ql4_mbx.c @@ -1247,8 +1247,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]; diff --git a/drivers/scsi/qla4xxx/ql4_os.c b/drivers/scsi/qla4xxx/ql4_os.c index b0b11c5..974640c 100644 --- a/drivers/scsi/qla4xxx/ql4_os.c +++ b/drivers/scsi/qla4xxx/ql4_os.c @@ -12,6 +12,7 @@ #include <scsi/scsi_tcq.h> #include <scsi/scsicam.h> +#include <scsi/iscsi_flash_sysfs.h> #include "ql4_def.h" #include "ql4_version.h" @@ -166,6 +167,17 @@ 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 ssize_t qla4xxx_sysfs_ddb_set_param(void *data, int type, + const char *buf, size_t size); +static ssize_t qla4xxx_sysfs_ddb_get_param(void *data, int type, char *buf); +static umode_t qla4xxx_sysfs_ddb_attr_visibility(void *data, int type); +static ssize_t qla4xxx_sysfs_ddb_add(void *data, const char *buf, size_t len); +static void qla4xxx_sysfs_ddb_delete(void *data); +static void qla4xxx_sysfs_ddb_release(void *data); + static struct qla4_8xxx_legacy_intr_set legacy_intr[] = QLA82XX_LEGACY_INTR_CONFIG; @@ -4448,7 +4460,8 @@ static int qla4xxx_compare_tuple_ddb(struct scsi_qla_host *ha, } static int qla4xxx_is_session_exists(struct scsi_qla_host *ha, - struct dev_db_entry *fw_ddb_entry) + struct dev_db_entry *fw_ddb_entry, + struct ddb_entry *get_ddb_entry) { struct ddb_entry *ddb_entry; struct ql4_tuple_ddb *fw_tddb = NULL; @@ -4482,6 +4495,9 @@ static int qla4xxx_is_session_exists(struct scsi_qla_host *ha, qla4xxx_get_param_ddb(ddb_entry, tmp_tddb); if (!qla4xxx_compare_tuple_ddb(ha, fw_tddb, tmp_tddb, false)) { ret = QLA_SUCCESS; /* found */ + if (get_ddb_entry) + memcpy(get_ddb_entry, ddb_entry, + sizeof(*get_ddb_entry)); goto exit_check; } } @@ -5009,7 +5025,7 @@ static void qla4xxx_build_nt_list(struct scsi_qla_host *ha, list_add_tail(&nt_ddb_idx->list, list_nt); } else if (is_reset == RESET_ADAPTER) { - if (qla4xxx_is_session_exists(ha, fw_ddb_entry) == + if (qla4xxx_is_session_exists(ha, fw_ddb_entry, NULL) == QLA_SUCCESS) goto continue_next_nt; } @@ -5028,6 +5044,1133 @@ 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, NULL) == + 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_tgt_create - Create sysfs entry for target + * @ha: pointer to host + * @fw_ddb_entry: flash ddb data + * @idx: target index + * + * 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 int 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 = NULL; + struct iscsi_flash_tgt_kobj *tgt_kobj = NULL; + int ret = QLA_SUCCESS; + + 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__)); + ret = QLA_ERROR; + 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_kobj = iscsi_flash_tgt_kobj_create(ha->host_kobj, *idx, ddb_sysfs, + qla4xxx_sysfs_ddb_get_param, + qla4xxx_sysfs_ddb_set_param, + qla4xxx_sysfs_ddb_attr_visibility, + qla4xxx_sysfs_ddb_release); + if (!tgt_kobj) { + ql4_printk(KERN_ERR, ha, + "%s: Unable to create sysfs entry for tgt%d of host%lu\n", + __func__, *idx, ha->host_no); + kfree(ddb_sysfs); + ret = QLA_ERROR; + goto leave_tgt_create; + } + + if (user) { + ddb_sysfs->tgt_sess_idx = INVALID_ENTRY; + ddb_sysfs->is_dirty = 1; + } + + ql4_printk(KERN_INFO, ha, "%s: sysfs entry created for tgt%d\n", + __func__, *idx); + +leave_tgt_create: + return ret; +} + +/** + * qla4xxx_sysfs_ddb_add - Add new ddb entry in flash + * @data: 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 ssize_t qla4xxx_sysfs_ddb_add(void *data, const char *buf, size_t len) +{ + struct scsi_qla_host *ha = data; + uint16_t idx = 0; + uint16_t max_ddbs = 0; + uint32_t options = 0; + uint32_t rval = QLA_SUCCESS; + struct iscsi_flash_tgt_kobj *tgt_kobj = NULL; + struct dev_db_entry *fw_ddb_entry = NULL; + dma_addr_t fw_ddb_entry_dma; + + if (strncasecmp("IPv4", buf, 4) && strncasecmp("IPv6", buf, 4)) + return -EINVAL; + + 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__)); + return -ENOMEM; + } + + for (idx = 0; idx < max_ddbs; idx++) { + if (qla4xxx_bootdb_by_index(ha, fw_ddb_entry, + fw_ddb_entry_dma, idx)) + break; + } + + rval = iscsi_flash_find_tgt_kobj_by_index(&(ha->host_kobj->tgt_list), + &tgt_kobj, idx); + if (rval) { + ql4_printk(KERN_ERR, ha, + "%s: A non-persistent target %s found\n", + __func__, tgt_kobj->kobj.name); + rval = -EIO; + 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) { + rval = -EIO; + goto leave_ddb_add; + } + + rval = qla4xxx_sysfs_ddb_tgt_create(ha, fw_ddb_entry, &idx, 1); + if (rval == QLA_ERROR) { + rval = -EIO; + goto leave_ddb_add; + } + +leave_ddb_add: + dma_free_coherent(&ha->pdev->dev, sizeof(*fw_ddb_entry), fw_ddb_entry, + fw_ddb_entry_dma); + + if (rval == QLA_SUCCESS) + rval = len; + + 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, + uint16_t *ddb_index) +{ + struct qla_ddb_index *ddb_idx, *ddb_idx_tmp; + struct list_head list_nt; + 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, + uint16_t *ddb_index) +{ + int ret = QLA_ERROR; + + ret = qla4xxx_is_session_exists(ha, fw_ddb_entry, NULL); + 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 + * @data: pointer to ddb_sysfs_entry + * @buf: trigger to login to the target + * @len: length of buf + * + * This logs in to the specified target + **/ +static ssize_t qla4xxx_sysfs_ddb_login(void *data, const char *buf, size_t len) +{ + struct ddb_sysfs_entry *ddb_sysfs = data; + struct dev_db_entry *fw_ddb_entry = &ddb_sysfs->fw_ddb; + struct scsi_qla_host *ha = ddb_sysfs->ha; + uint16_t ddb_index; + 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, &ddb_index); + else + ret = qla4xxx_ddb_login_nt(ha, fw_ddb_entry, &ddb_index); + + if (ret == QLA_SUCCESS) { + ddb_sysfs->tgt_sess_idx = ddb_index; + ret = len; + } + + return ret; +} + +/** + * qla4xxx_sysfs_ddb_logout - Logout from the specified target + * @data: pointer to ddb_sysfs_entry + * @buf: trigger to logout from the target + * @len: length of buf + * + * This performs log out from the specified target + **/ +static ssize_t +qla4xxx_sysfs_ddb_logout(void *data, const char *buf, size_t len) +{ + struct ddb_sysfs_entry *ddb_sysfs = data; + struct scsi_qla_host *ha = ddb_sysfs->ha; + struct dev_db_entry *fw_ddb_entry = &ddb_sysfs->fw_ddb; + struct ddb_entry *ddb_entry = NULL; + unsigned long flags; + int options; + int ret = len; + + if (strlen((char *)ddb_sysfs->fw_ddb.iscsi_name) == 0) { + ql4_printk(KERN_ERR, ha, + "%s: Cannot perform logout on a SendTarget entry\n", + __func__); + ret = -EPERM; + goto exit_ddb_logout; + } + + ddb_entry = vzalloc(sizeof(*ddb_entry)); + if (!ddb_entry) { + DEBUG2(ql4_printk(KERN_WARNING, ha, + "Memory Allocation failed.\n")); + ret = -ENOMEM; + goto exit_ddb_logout; + } + + if (qla4xxx_is_session_exists(ha, fw_ddb_entry, ddb_entry) != + QLA_SUCCESS) { + ret = -EINVAL; + goto exit_ddb_logout; + } + + + if (ddb_entry->ddb_type != FLASH_DDB) { + ql4_printk(KERN_ERR, ha, + "%s: Cannot logout as it is not Flash DDB entry\n", + __func__); + ret = -EINVAL; + goto exit_ddb_logout; + } + + spin_lock_irqsave(&ha->hardware_lock, flags); + qla4xxx_free_ddb(ha, ddb_entry); + spin_unlock_irqrestore(&ha->hardware_lock, flags); + + options = LOGOUT_OPTION_CLOSE_SESSION | LOGOUT_OPTION_FREE_DDB; + if (qla4xxx_session_logout_ddb(ha, ddb_entry, options) == + QLA_ERROR) + ql4_printk(KERN_ERR, ha, "%s: Logout failed\n", + __func__); + + /* + * 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); + iscsi_session_teardown(ddb_entry->sess); + + ret = len; + +exit_ddb_logout: + if (ddb_entry) + vfree(ddb_entry); + + return ret; +} + +/** + * qla4xxx_sysfs_ddb_apply - write the target ddb contents to Flash + * @data: pointer to ddb_sysfs_entry + * @buf: trigger to apply the target contents to Flash + * @len: length of buf + * + * 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 ssize_t qla4xxx_sysfs_ddb_apply(void *data, const char *buf, size_t len) +{ + struct ddb_sysfs_entry *ddb_sysfs = 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 = len; + + 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; + rval = len; + } else { + ddb_sysfs->fw_ddb.cookie = 0xFFEE; + ddb_sysfs->is_dirty = 1; + rval = -EIO; + } +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; +} + +#define GET_BITVAL(o) (o ? 1 : 0) + +static ssize_t qla4xxx_sysfs_ddb_get_param(void *data, int type, char *buf) +{ + struct ddb_sysfs_entry *ddb_sysfs = data; + struct dev_db_entry *fw_ddb_entry = &ddb_sysfs->fw_ddb; + char *str = buf; + int rc = 0; + + switch (type) { + case ISCSI_FLASH_TGT_IP_ADDR: + 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_FLASH_TGT_PORT: + rc = sprintf(str, "%d\n", fw_ddb_entry->port); + break; + case ISCSI_FLASH_TGT_NAME: + rc = sprintf(buf, "%s\n", (char *)&fw_ddb_entry->iscsi_name); + break; + case ISCSI_FLASH_TGT_OPT_AUTO_SND_TGT_DISABLE: + rc = sprintf(str, "%u\n", + GET_BITVAL(fw_ddb_entry->options & + OPT_AUTO_SEND_TARGET_DISABLE)); + break; + case ISCSI_FLASH_TGT_OPT_DISCOVERY_SESS: + rc = sprintf(str, "%u\n", + GET_BITVAL(fw_ddb_entry->options & + OPT_DISCOVERY_SESSION)); + break; + case ISCSI_FLASH_TGT_OPT_ENTRY_ENABLE: + rc = sprintf(str, "%u\n", + GET_BITVAL(fw_ddb_entry->options & + OPT_DDB_ENTRY_STATE)); + break; + case ISCSI_FLASH_TGT_ISCSIOPT_ENABLE_HDR_DIGEST: + rc = sprintf(str, "%u\n", + GET_BITVAL(fw_ddb_entry->iscsi_options & + ISCSIOPT_ENABLE_HEADER_DIGEST)); + break; + case ISCSI_FLASH_TGT_ISCSIOPT_ENABLE_DATA_DIGEST: + rc = sprintf(str, "%u\n", + GET_BITVAL(fw_ddb_entry->iscsi_options & + ISCSIOPT_ENABLE_DATA_DIGEST)); + break; + case ISCSI_FLASH_TGT_ISCSIOPT_ENABLE_IMMEDIATE_DATA: + rc = sprintf(str, "%u\n", + GET_BITVAL(fw_ddb_entry->iscsi_options & + ISCSIOPT_ENABLE_IMMEDIATE_DATA)); + break; + case ISCSI_FLASH_TGT_ISCSIOPT_ENABLE_INITIAL_R2T: + rc = sprintf(str, "%u\n", + GET_BITVAL(fw_ddb_entry->iscsi_options & + ISCSIOPT_ENABLE_INITIAL_R2T)); + break; + case ISCSI_FLASH_TGT_ISCSIOPT_DATA_SEQ_IN_ORDER: + rc = sprintf(str, "%u\n", + GET_BITVAL(fw_ddb_entry->iscsi_options & + ISCSIOPT_DATA_SEQ_IN_ORDER)); + break; + case ISCSI_FLASH_TGT_ISCSIOPT_DATA_PDU_IN_ORDER: + rc = sprintf(str, "%u\n", + GET_BITVAL(fw_ddb_entry->iscsi_options & + ISCSIOPT_DATA_PDU_IN_ORDER)); + break; + case ISCSI_FLASH_TGT_ISCSIOPT_CHAP_AUTH_ENABLE: + rc = sprintf(str, "%u\n", + GET_BITVAL(fw_ddb_entry->iscsi_options & + ISCSIOPT_CHAP_AUTH_ENABLE)); + break; + case ISCSI_FLASH_TGT_ISCSIOPT_SNACK_REQ_ENABLE: + rc = sprintf(str, "%u\n", + GET_BITVAL(fw_ddb_entry->iscsi_options & + ISCSIOPT_SNACK_REQ_ENABLE)); + break; + case ISCSI_FLASH_TGT_ISCSIOPT_DISCOVERY_LOGOUT_ENABLE: + rc = sprintf(str, "%u\n", + GET_BITVAL(fw_ddb_entry->iscsi_options & + ISCSIOPT_DISCOVERY_LOGOUT_ENABLE)); + break; + case ISCSI_FLASH_TGT_ISCSIOPT_BI_CHAP_CHALLENGE_ENABLE: + rc = sprintf(str, "%u\n", + GET_BITVAL(fw_ddb_entry->iscsi_options & + ISCSIOPT_BI_CHAP_CHALLENGE_ENABLE)); + break; + case ISCSI_FLASH_TGT_ISCSIOPT_DISCOVERY_AUTH_OPT: + rc = sprintf(str, "%u\n", + GET_BITVAL(fw_ddb_entry->iscsi_options & + ISCSIOPT_DISCOVERY_AUTH_OPT)); + break; + case ISCSI_FLASH_TGT_TCPOPT_NAGLE_DISABLE: + rc = sprintf(str, "%u\n", + GET_BITVAL(fw_ddb_entry->tcp_options & + TCPOPT_NAGLE_DISABLE)); + break; + case ISCSI_FLASH_TGT_TCPOPT_TIMER_SCALE: + rc = sprintf(str, "0x%X\n", + (fw_ddb_entry->tcp_options >> 1) & 0x7); + break; + case ISCSI_FLASH_TGT_TCPOPT_TIMESTAMP_ENABLE: + rc = sprintf(str, "%u\n", + GET_BITVAL(fw_ddb_entry->tcp_options & + TCPOPT_TIMESTAMP_ENABLE)); + break; + case ISCSI_FLASH_TGT_IPOPT_FRAG_DISABLE: + rc = sprintf(str, "%u\n", + GET_BITVAL(fw_ddb_entry->ip_options & + IPOPT_FRAGMENTATION_DISABLE)); + break; + case ISCSI_FLASH_TGT_MAX_RECV_DS_LEN: + rc = sprintf(str, "%u\n", + fw_ddb_entry->iscsi_max_rcv_data_seg_len); + break; + case ISCSI_FLASH_TGT_MAX_XMIT_DS_LEN: + rc = sprintf(str, "%u\n", + fw_ddb_entry->iscsi_max_snd_data_seg_len); + break; + case ISCSI_FLASH_TGT_FIRST_BURST_LEN: + rc = sprintf(str, "%u\n", fw_ddb_entry->iscsi_first_burst_len); + break; + case ISCSI_FLASH_TGT_DEF_TIME2WAIT: + rc = sprintf(str, "%u\n", fw_ddb_entry->iscsi_def_time2wait); + break; + case ISCSI_FLASH_TGT_DEF_TIME2RETAIN: + rc = sprintf(str, "%u\n", fw_ddb_entry->iscsi_def_time2retain); + break; + case ISCSI_FLASH_TGT_MAX_OUTSTANDING_R2T: + rc = sprintf(str, "%u\n", fw_ddb_entry->iscsi_max_outsnd_r2t); + break; + case ISCSI_FLASH_TGT_NOOP_OUT_INTERVAL: + rc = sprintf(str, "%u\n", fw_ddb_entry->ka_timeout); + break; + case ISCSI_FLASH_TGT_ISID: + memcpy(str, &fw_ddb_entry->isid[0], sizeof(fw_ddb_entry->isid)); + break; + case ISCSI_FLASH_TGT_TSID: + rc = sprintf(str, "%u\n", fw_ddb_entry->tsid); + break; + case ISCSI_FLASH_TGT_MAX_BURST_LEN: + rc = sprintf(str, "%u\n", fw_ddb_entry->iscsi_max_burst_len); + break; + case ISCSI_FLASH_TGT_FW_CMD_TIMEOUT: + rc = sprintf(str, "%u\n", fw_ddb_entry->def_timeout); + break; + case ISCSI_FLASH_TGT_ISCSI_ALIAS: + rc = sprintf(str, "%s\n", (char *)&fw_ddb_entry->iscsi_alias); + break; + case ISCSI_FLASH_TGT_ADDRESS: + 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_FLASH_TGT_TPGT: + rc = sprintf(str, "%u\n", fw_ddb_entry->tgt_portal_grp); + break; + case ISCSI_FLASH_TGT_CHAP_TBL_IDX: + rc = sprintf(str, "%u\n", fw_ddb_entry->chap_tbl_idx); + break; + case ISCSI_FLASH_TGT_IS_PERSISTENT: + if (fw_ddb_entry->cookie == DDB_VALID_COOKIE) + rc = sprintf(str, "1\n"); + else + rc = sprintf(str, "0\n"); + break; + case ISCSI_FLASH_TGT_IS_DIRTY: + rc = sprintf(str, "%u\n", ddb_sysfs->is_dirty); + 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; \ +} + +static ssize_t +qla4xxx_sysfs_ddb_set_param(void *data, int type, const char *buf, size_t size) +{ + struct ddb_sysfs_entry *ddb_sysfs = data; + struct dev_db_entry *fw_ddb_entry = &ddb_sysfs->fw_ddb; + uint16_t options; + int bitval; + int rc; + + switch (type) { + case ISCSI_FLASH_TGT_NAME: + rc = sprintf(&fw_ddb_entry->iscsi_name[0], "%s", buf); + goto chk_error; + case ISCSI_FLASH_TGT_IP_ADDR: + 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 = size; + else + rc = -EINVAL; + goto chk_error; + case ISCSI_FLASH_TGT_PORT: + rc = kstrtou16(buf, 10, &fw_ddb_entry->port); + break; + case ISCSI_FLASH_TGT_OPT_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_FLASH_TGT_OPT_DISCOVERY_SESS: + GET_OPTVAL(buf, options, 0, 1); + if (!rc) + SET_OPTVAL(options, fw_ddb_entry->options, + OPT_DISCOVERY_SESSION); + break; + case ISCSI_FLASH_TGT_OPT_ENTRY_ENABLE: + GET_OPTVAL(buf, options, 0, 1); + if (!rc) + SET_OPTVAL(options, fw_ddb_entry->options, + OPT_DDB_ENTRY_STATE); + break; + case ISCSI_FLASH_TGT_ISCSIOPT_ENABLE_HDR_DIGEST: + GET_OPTVAL(buf, options, 0, 1); + if (!rc) + SET_OPTVAL(options, fw_ddb_entry->iscsi_options, + ISCSIOPT_ENABLE_HEADER_DIGEST); + break; + case ISCSI_FLASH_TGT_ISCSIOPT_ENABLE_DATA_DIGEST: + GET_OPTVAL(buf, options, 0, 1); + if (!rc) + SET_OPTVAL(options, fw_ddb_entry->iscsi_options, + ISCSIOPT_ENABLE_DATA_DIGEST); + break; + case ISCSI_FLASH_TGT_ISCSIOPT_ENABLE_IMMEDIATE_DATA: + GET_OPTVAL(buf, options, 0, 1); + if (!rc) + SET_OPTVAL(options, fw_ddb_entry->iscsi_options, + ISCSIOPT_ENABLE_IMMEDIATE_DATA); + break; + case ISCSI_FLASH_TGT_ISCSIOPT_ENABLE_INITIAL_R2T: + GET_OPTVAL(buf, options, 0, 1); + if (!rc) + SET_OPTVAL(options, fw_ddb_entry->iscsi_options, + ISCSIOPT_ENABLE_INITIAL_R2T); + break; + case ISCSI_FLASH_TGT_ISCSIOPT_DATA_SEQ_IN_ORDER: + GET_OPTVAL(buf, options, 0, 1); + if (!rc) + SET_OPTVAL(options, fw_ddb_entry->iscsi_options, + ISCSIOPT_DATA_SEQ_IN_ORDER); + break; + case ISCSI_FLASH_TGT_ISCSIOPT_DATA_PDU_IN_ORDER: + GET_OPTVAL(buf, options, 0, 1); + if (!rc) + SET_OPTVAL(options, fw_ddb_entry->iscsi_options, + ISCSIOPT_DATA_PDU_IN_ORDER); + break; + case ISCSI_FLASH_TGT_ISCSIOPT_CHAP_AUTH_ENABLE: + GET_OPTVAL(buf, options, 0, 1); + if (!rc) + SET_OPTVAL(options, fw_ddb_entry->iscsi_options, + ISCSIOPT_CHAP_AUTH_ENABLE); + break; + case ISCSI_FLASH_TGT_ISCSIOPT_SNACK_REQ_ENABLE: + GET_OPTVAL(buf, options, 0, 1); + if (!rc) + SET_OPTVAL(options, fw_ddb_entry->iscsi_options, + ISCSIOPT_SNACK_REQ_ENABLE); + break; + case ISCSI_FLASH_TGT_ISCSIOPT_DISCOVERY_LOGOUT_ENABLE: + GET_OPTVAL(buf, options, 0, 1); + if (!rc) + SET_OPTVAL(options, fw_ddb_entry->iscsi_options, + ISCSIOPT_DISCOVERY_LOGOUT_ENABLE); + break; + case ISCSI_FLASH_TGT_ISCSIOPT_BI_CHAP_CHALLENGE_ENABLE: + GET_OPTVAL(buf, options, 0, 1); + if (!rc) + SET_OPTVAL(options, fw_ddb_entry->iscsi_options, + ISCSIOPT_BI_CHAP_CHALLENGE_ENABLE); + break; + case ISCSI_FLASH_TGT_ISCSIOPT_DISCOVERY_AUTH_OPT: + GET_OPTVAL(buf, options, 0, 1); + if (!rc) + SET_OPTVAL(options, fw_ddb_entry->iscsi_options, + ISCSIOPT_DISCOVERY_AUTH_OPT); + break; + case ISCSI_FLASH_TGT_TCPOPT_NAGLE_DISABLE: + GET_OPTVAL(buf, options, 0, 1); + if (!rc) + SET_OPTVAL(options, fw_ddb_entry->tcp_options, + TCPOPT_NAGLE_DISABLE); + break; + case ISCSI_FLASH_TGT_TCPOPT_TIMER_SCALE: + GET_OPTVAL(buf, options, 0, 7); + if (!rc) { + bitval = options & 1; + SET_OPTVAL(bitval, fw_ddb_entry->tcp_options, + TCPOPT_TCP_TIMER_SCALE1); + bitval = (options >> 1) & 1; + SET_OPTVAL(bitval, fw_ddb_entry->tcp_options, + TCPOPT_TCP_TIMER_SCALE2); + bitval = (options >> 2) & 1; + SET_OPTVAL(bitval, fw_ddb_entry->tcp_options, + TCPOPT_TCP_TIMER_SCALE3); + } + break; + case ISCSI_FLASH_TGT_TCPOPT_TIMESTAMP_ENABLE: + GET_OPTVAL(buf, options, 0, 1); + if (!rc) + SET_OPTVAL(options, fw_ddb_entry->tcp_options, + TCPOPT_TIMESTAMP_ENABLE); + break; + case ISCSI_FLASH_TGT_IPOPT_FRAG_DISABLE: + GET_OPTVAL(buf, options, 0, 1); + if (!rc) + SET_OPTVAL(options, fw_ddb_entry->ip_options, + IPOPT_FRAGMENTATION_DISABLE); + break; + case ISCSI_FLASH_TGT_MAX_RECV_DS_LEN: + rc = kstrtou16(buf, 10, + &fw_ddb_entry->iscsi_max_rcv_data_seg_len); + break; + case ISCSI_FLASH_TGT_MAX_XMIT_DS_LEN: + rc = kstrtou16(buf, 10, + &fw_ddb_entry->iscsi_max_snd_data_seg_len); + break; + case ISCSI_FLASH_TGT_FIRST_BURST_LEN: + rc = kstrtou16(buf, 10, &fw_ddb_entry->iscsi_first_burst_len); + break; + case ISCSI_FLASH_TGT_DEF_TIME2WAIT: + rc = kstrtou16(buf, 10, &fw_ddb_entry->iscsi_def_time2wait); + break; + case ISCSI_FLASH_TGT_DEF_TIME2RETAIN: + rc = kstrtou16(buf, 10, + &fw_ddb_entry->iscsi_def_time2retain); + break; + case ISCSI_FLASH_TGT_MAX_OUTSTANDING_R2T: + rc = kstrtou16(buf, 10, + &fw_ddb_entry->iscsi_max_outsnd_r2t); + break; + case ISCSI_FLASH_TGT_NOOP_OUT_INTERVAL: + rc = kstrtou16(buf, 10, &fw_ddb_entry->ka_timeout); + break; + case ISCSI_FLASH_TGT_ISID: + memcpy(&fw_ddb_entry->isid[0], buf, sizeof(fw_ddb_entry->isid)); + rc = size; + goto chk_error; + case ISCSI_FLASH_TGT_TSID: + rc = kstrtou16(buf, 10, &fw_ddb_entry->tsid); + break; + case ISCSI_FLASH_TGT_MAX_BURST_LEN: + rc = kstrtou16(buf, 10, + &fw_ddb_entry->iscsi_max_burst_len); + break; + case ISCSI_FLASH_TGT_FW_CMD_TIMEOUT: + rc = kstrtou16(buf, 10, &fw_ddb_entry->def_timeout); + break; + case ISCSI_FLASH_TGT_ISCSI_ALIAS: + rc = sprintf(&fw_ddb_entry->iscsi_alias[0], "%s", buf); + goto chk_error; + case ISCSI_FLASH_TGT_ADDRESS: + 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 = size; + else + rc = -EINVAL; + goto chk_error; + case ISCSI_FLASH_TGT_TPGT: + rc = kstrtou16(buf, 10, &fw_ddb_entry->tgt_portal_grp); + break; + case ISCSI_FLASH_TGT_CHAP_TBL_IDX: + rc = kstrtou16(buf, 10, &fw_ddb_entry->chap_tbl_idx); + break; + case ISCSI_FLASH_TGT_LOGIN: + rc = qla4xxx_sysfs_ddb_login(data, buf, size); + goto exit_set_param; + case ISCSI_FLASH_TGT_LOGOUT: + rc = qla4xxx_sysfs_ddb_logout(data, buf, size); + goto exit_set_param; + case ISCSI_FLASH_TGT_APPLY: + rc = qla4xxx_sysfs_ddb_apply(data, buf, size); + goto exit_set_param; + default: + ql4_printk(KERN_ERR, ddb_sysfs->ha, + "%s: No such sysfs attribute\n", __func__); + rc = -ENOSYS; + goto exit_set_param; + } + + /* Only for cases having str to num conversion */ + if (!rc) + rc = size; + +chk_error: + if (rc == size) { + 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_attr_visibility - Get access permission for attr + * @data: target info + * @type: type of attribute + * + * Returns: + * On success: access permission for attribute + * On failure: 0 + **/ +static umode_t qla4xxx_sysfs_ddb_attr_visibility(void *data, int type) +{ + int rc; + + switch (type) { + case ISCSI_FLASH_TGT_IP_ADDR: + case ISCSI_FLASH_TGT_PORT: + case ISCSI_FLASH_TGT_NAME: + case ISCSI_FLASH_TGT_OPT_AUTO_SND_TGT_DISABLE: + case ISCSI_FLASH_TGT_OPT_DISCOVERY_SESS: + case ISCSI_FLASH_TGT_OPT_ENTRY_ENABLE: + case ISCSI_FLASH_TGT_ISCSIOPT_ENABLE_HDR_DIGEST: + case ISCSI_FLASH_TGT_ISCSIOPT_ENABLE_DATA_DIGEST: + case ISCSI_FLASH_TGT_ISCSIOPT_ENABLE_IMMEDIATE_DATA: + case ISCSI_FLASH_TGT_ISCSIOPT_ENABLE_INITIAL_R2T: + case ISCSI_FLASH_TGT_ISCSIOPT_DATA_SEQ_IN_ORDER: + case ISCSI_FLASH_TGT_ISCSIOPT_DATA_PDU_IN_ORDER: + case ISCSI_FLASH_TGT_ISCSIOPT_CHAP_AUTH_ENABLE: + case ISCSI_FLASH_TGT_ISCSIOPT_SNACK_REQ_ENABLE: + case ISCSI_FLASH_TGT_ISCSIOPT_DISCOVERY_LOGOUT_ENABLE: + case ISCSI_FLASH_TGT_ISCSIOPT_BI_CHAP_CHALLENGE_ENABLE: + case ISCSI_FLASH_TGT_ISCSIOPT_DISCOVERY_AUTH_OPT: + case ISCSI_FLASH_TGT_TCPOPT_NAGLE_DISABLE: + case ISCSI_FLASH_TGT_TCPOPT_TIMER_SCALE: + case ISCSI_FLASH_TGT_TCPOPT_TIMESTAMP_ENABLE: + case ISCSI_FLASH_TGT_IPOPT_FRAG_DISABLE: + case ISCSI_FLASH_TGT_MAX_RECV_DS_LEN: + case ISCSI_FLASH_TGT_MAX_XMIT_DS_LEN: + case ISCSI_FLASH_TGT_FIRST_BURST_LEN: + case ISCSI_FLASH_TGT_DEF_TIME2WAIT: + case ISCSI_FLASH_TGT_DEF_TIME2RETAIN: + case ISCSI_FLASH_TGT_MAX_OUTSTANDING_R2T: + case ISCSI_FLASH_TGT_NOOP_OUT_INTERVAL: + case ISCSI_FLASH_TGT_ISID: + case ISCSI_FLASH_TGT_TSID: + case ISCSI_FLASH_TGT_MAX_BURST_LEN: + case ISCSI_FLASH_TGT_FW_CMD_TIMEOUT: + case ISCSI_FLASH_TGT_ISCSI_ALIAS: + case ISCSI_FLASH_TGT_ADDRESS: + case ISCSI_FLASH_TGT_TPGT: + case ISCSI_FLASH_TGT_CHAP_TBL_IDX: + rc = S_IRUGO | S_IWUSR; + break; + case ISCSI_FLASH_TGT_IS_PERSISTENT: + case ISCSI_FLASH_TGT_IS_DIRTY: + rc = S_IRUGO; + break; + case ISCSI_FLASH_TGT_LOGIN: + case ISCSI_FLASH_TGT_LOGOUT: + case ISCSI_FLASH_TGT_APPLY: + rc = S_IWUSR; + break; + default: + rc = 0; + break; + } + return rc; +} + +/** + * qla4xxx_sysfs_ddb_delete - Delete firmware DDB entry + * @data: pointer to flash ddb data + * + * This invalidates the flash ddb entry at the given index + **/ +static void qla4xxx_sysfs_ddb_delete(void *data) +{ + struct ddb_sysfs_entry *ddb_sysfs = 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) + return; + + if (ddb_sysfs->fw_ddb.cookie != DDB_VALID_COOKIE) + return; + + 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; + } + + 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); +} + +/** + * qla4xxx_sysfs_ddb_release - Free firmware DDB entry in sysfs + * @data: pointer to flash ddb data + * + * This releases the ddb data buffer that holds data exported in sysfs + **/ +static void qla4xxx_sysfs_ddb_release(void *data) +{ + struct ddb_sysfs_entry *ddb_sysfs = data; + kfree(ddb_sysfs); +} + +/** + * 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 iscsi_host_kobj *host_kobj = NULL; + struct dev_db_entry *fw_ddb_entry = NULL; + dma_addr_t fw_ddb_entry_dma; + uint16_t max_ddbs; + uint16_t idx = 0; + int ret = QLA_SUCCESS; + + ha->host_kobj = NULL; + 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; + + host_kobj = iscsi_host_kobj_create(ha->host_no, ha, + qla4xxx_sysfs_ddb_add, + qla4xxx_sysfs_ddb_delete); + if (!host_kobj) { + DEBUG2(ql4_printk(KERN_ERR, ha, + "%s: Unable to create sysfs interface for host%lu\n", + __func__, ha->host_no)); + ret = -EIO; + goto leave_ddb_export; + } + + ha->host_kobj = host_kobj; + + 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; + } + + ret = qla4xxx_sysfs_ddb_tgt_create(ha, fw_ddb_entry, &idx, 0); + if (ret == QLA_ERROR) { + ret = -EIO; + break; + } + } + +leave_ddb_export: + 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) +{ + if (ha->host_kobj) + iscsi_host_kobj_delete(ha->host_kobj); +} + /** * qla4xxx_build_ddb_list - Build ddb list and setup sessions * @ha: pointer to adapter structure @@ -5341,7 +6484,11 @@ initialize_success: 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); @@ -5458,6 +6605,7 @@ static void __devexit 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.1 -- You received this message because you are subscribed to the Google Groups "open-iscsi" group. To post to this group, send email to open-iscsi@googlegroups.com. To unsubscribe from this group, send email to open-iscsi+unsubscr...@googlegroups.com. For more options, visit this group at http://groups.google.com/group/open-iscsi?hl=en.