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/Kconfig | 1 + drivers/scsi/qla4xxx/ql4_def.h | 13 + drivers/scsi/qla4xxx/ql4_glbl.h | 2 + drivers/scsi/qla4xxx/ql4_mbx.c | 4 +- drivers/scsi/qla4xxx/ql4_os.c | 939 ++++++++++++++++++++++++++++++++++++++- 5 files changed, 956 insertions(+), 3 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..9c996c3 100644 --- a/drivers/scsi/qla4xxx/ql4_def.h +++ b/drivers/scsi/qla4xxx/ql4_def.h @@ -280,6 +280,13 @@ 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; +}; + struct qla_ddb_index { struct list_head list; uint16_t fw_ddb_idx; @@ -658,6 +665,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; @@ -1003,4 +1013,7 @@ static inline void qla4_8xxx_wr_direct(struct scsi_qla_host *ha, /* Defines for udev events */ #define QL4_UEVENT_CODE_FW_DUMP 0 +/* Flag to denote ST discovery in progress */ +#define DISCOVERY_IN_PROGRESS 0 + #endif /*_QLA4XXX_H */ 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..a7ab53e 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,16 @@ 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_release(void *data); + static struct qla4_8xxx_legacy_intr_set legacy_intr[] = QLA82XX_LEGACY_INTR_CONFIG; @@ -5028,6 +5039,927 @@ 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_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. + **/ +static int qla4xxx_sysfs_ddb_tgt_create(struct scsi_qla_host *ha, + struct dev_db_entry *fw_ddb_entry, + uint16_t *idx) +{ + 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; + } + + 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); + +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; + static unsigned long st_discovery; + int ret = 0; + + if (test_bit(DISCOVERY_IN_PROGRESS, &st_discovery)) { + ql4_printk(KERN_WARNING, ha, + "%s: A discovery already in progress!\n", __func__); + return QLA_ERROR; + } + + set_bit(DISCOVERY_IN_PROGRESS, &st_discovery); + + 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(DISCOVERY_IN_PROGRESS, &st_discovery); + return ret; +} + +static int qla4xxx_ddb_login_nt(struct scsi_qla_host *ha, + struct dev_db_entry *fw_ddb_entry, + uint16_t *ddb_index) +{ + struct dev_db_entry *ddb_entry = NULL; + dma_addr_t ddb_entry_dma; + uint32_t state = 0, conn_err = 0; + int ret = 0; + + ret = qla4xxx_get_ddb_index(ha, ddb_index); + if (ret == QLA_ERROR) + return ret; + + ret = qla4xxx_sysfs_ddb_conn_open(ha, fw_ddb_entry, *ddb_index); + if (ret == QLA_ERROR) + goto exit_login_nt; + + 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__)); + goto exit_login_nt; + } + + ret = qla4xxx_get_fwddb_entry(ha, *ddb_index, ddb_entry, ddb_entry_dma, + NULL, NULL, &state, + &conn_err, NULL, NULL); + if (ret == QLA_ERROR) + goto exit_login_nt; + + ret = qla4xxx_verify_boot_idx(ha, *ddb_index); + if (ret == QLA_ERROR) + goto exit_login_nt; + + if (state == DDB_DS_NO_CONNECTION_ACTIVE || + state == DDB_DS_SESSION_FAILED) + goto exit_login_nt; + + ret = qla4xxx_is_session_exists(ha, fw_ddb_entry); + if (ret == QLA_SUCCESS) + goto exit_login_nt; + + ret = qla4xxx_sess_conn_setup(ha, fw_ddb_entry, RESET_ADAPTER); + +exit_login_nt: + 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); + + if (ddb_entry) + dma_free_coherent(&ha->pdev->dev, sizeof(*ddb_entry), + ddb_entry, ddb_entry_dma); + 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; +} + +static void qla4xxx_ddb_logout(struct scsi_qla_host *ha, uint16_t *idx) +{ + struct ddb_entry *ddb_entry; + int options; + + ddb_entry = qla4xxx_lookup_ddb_by_fw_index(ha, *idx); + if ((ddb_entry != NULL) && + (ddb_entry->ddb_type == FLASH_DDB)) { + + 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); + qla4xxx_free_ddb(ha, ddb_entry); + iscsi_session_teardown(ddb_entry->sess); + } +} + +/** + * 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; + uint16_t ddb_index; + + ddb_index = ddb_sysfs->tgt_sess_idx; + qla4xxx_ddb_logout(ha, &ddb_index); + return len; +} + +/** + * 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 = QLA_SUCCESS; + + if (ddb_sysfs->fw_ddb.cookie == DDB_VALID_COOKIE) { + DEBUG2(ql4_printk(KERN_ERR, ha, + "%s: ddb entry is already persistent\n", + __func__)); + return 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; + } + + 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) + rval = len; + else + rval = -EIO; + + 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_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 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 FLASH_TGT_PORT: + rc = sprintf(str, "%d\n", fw_ddb_entry->port); + break; + case FLASH_TGT_NAME: + rc = sprintf(buf, "%s\n", (char *)&fw_ddb_entry->iscsi_name); + break; + case FLASH_TGT_OPTIONS: + rc = sprintf(str, "0x%X\n", fw_ddb_entry->options); + break; + case FLASH_TGT_ISCSI_OPTIONS: + rc = sprintf(str, "0x%X\n", fw_ddb_entry->iscsi_options); + break; + case FLASH_TGT_TCP_OPTIONS: + rc = sprintf(str, "0x%x\n", fw_ddb_entry->tcp_options); + break; + case FLASH_TGT_IP_OPTIONS: + rc = sprintf(str, "0x%X\n", fw_ddb_entry->ip_options); + break; + case FLASH_TGT_MAX_RECV_DS_LEN: + rc = sprintf(str, "%u\n", + fw_ddb_entry->iscsi_max_rcv_data_seg_len); + break; + case FLASH_TGT_MAX_XMIT_DS_LEN: + rc = sprintf(str, "%u\n", + fw_ddb_entry->iscsi_max_snd_data_seg_len); + break; + case FLASH_TGT_FIRST_BURST_LEN: + rc = sprintf(str, "%u\n", fw_ddb_entry->iscsi_first_burst_len); + break; + case FLASH_TGT_DEF_TIME2WAIT: + rc = sprintf(str, "%u\n", fw_ddb_entry->iscsi_def_time2wait); + break; + case FLASH_TGT_DEF_TIME2RETAIN: + rc = sprintf(str, "%u\n", fw_ddb_entry->iscsi_def_time2retain); + break; + case FLASH_TGT_MAX_OUTSTANDING_R2T: + rc = sprintf(str, "%u\n", fw_ddb_entry->iscsi_max_outsnd_r2t); + break; + case FLASH_TGT_NOOP_OUT_INTERVAL: + rc = sprintf(str, "%u\n", fw_ddb_entry->ka_timeout); + break; + case FLASH_TGT_ISID: + memcpy(str, &fw_ddb_entry->isid[0], sizeof(fw_ddb_entry->isid)); + break; + case FLASH_TGT_TSID: + rc = sprintf(str, "%u\n", fw_ddb_entry->tsid); + break; + case FLASH_TGT_MAX_BURST_LEN: + rc = sprintf(str, "%u\n", fw_ddb_entry->iscsi_max_burst_len); + break; + case FLASH_TGT_FW_CMD_TIMEOUT: + rc = sprintf(str, "%u\n", fw_ddb_entry->def_timeout); + break; + case FLASH_TGT_ISCSI_ALIAS: + rc = sprintf(str, "%s\n", (char *)&fw_ddb_entry->iscsi_alias); + break; + case 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 FLASH_TGT_TPGT: + rc = sprintf(str, "%u\n", fw_ddb_entry->tgt_portal_grp); + break; + case FLASH_TGT_CHAP_TBL_IDX: + rc = sprintf(str, "%u\n", fw_ddb_entry->chap_tbl_idx); + break; + case FLASH_TGT_IS_PERSISTENT: + if (fw_ddb_entry->cookie == DDB_VALID_COOKIE) + rc = sprintf(str, "1\n"); + else + rc = sprintf(str, "0\n"); + break; + default: + rc = -ENOSYS; + break; + } + return rc; +} + +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; + int rc; + + switch (type) { + case FLASH_TGT_NAME: + rc = sprintf(&fw_ddb_entry->iscsi_name[0], "%s", buf); + break; + case 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; + break; + case FLASH_TGT_PORT: + rc = kstrtou16(buf, 10, &fw_ddb_entry->port); + if (!rc) + rc = size; + break; + case FLASH_TGT_OPTIONS: + rc = kstrtou16(buf, 10, &fw_ddb_entry->options); + if (!rc) + rc = size; + break; + case FLASH_TGT_ISCSI_OPTIONS: + rc = kstrtou16(buf, 10, &fw_ddb_entry->iscsi_options); + if (!rc) + rc = size; + break; + case FLASH_TGT_TCP_OPTIONS: + rc = kstrtou16(buf, 10, &fw_ddb_entry->tcp_options); + if (!rc) + rc = size; + break; + case FLASH_TGT_IP_OPTIONS: + rc = kstrtou16(buf, 10, &fw_ddb_entry->ip_options); + if (!rc) + rc = size; + break; + case FLASH_TGT_MAX_RECV_DS_LEN: + rc = kstrtou16(buf, 10, + &fw_ddb_entry->iscsi_max_rcv_data_seg_len); + if (!rc) + rc = size; + break; + case FLASH_TGT_MAX_XMIT_DS_LEN: + rc = kstrtou16(buf, 10, + &fw_ddb_entry->iscsi_max_snd_data_seg_len); + if (!rc) + rc = size; + break; + case FLASH_TGT_FIRST_BURST_LEN: + rc = kstrtou16(buf, 10, &fw_ddb_entry->iscsi_first_burst_len); + if (!rc) + rc = size; + break; + case FLASH_TGT_DEF_TIME2WAIT: + rc = kstrtou16(buf, 10, &fw_ddb_entry->iscsi_def_time2wait); + if (!rc) + rc = size; + break; + case FLASH_TGT_DEF_TIME2RETAIN: + rc = kstrtou16(buf, 10, + &fw_ddb_entry->iscsi_def_time2retain); + if (!rc) + rc = size; + break; + case FLASH_TGT_MAX_OUTSTANDING_R2T: + rc = kstrtou16(buf, 10, + &fw_ddb_entry->iscsi_max_outsnd_r2t); + if (!rc) + rc = size; + break; + case FLASH_TGT_NOOP_OUT_INTERVAL: + rc = kstrtou16(buf, 10, &fw_ddb_entry->ka_timeout); + if (!rc) + rc = size; + break; + case FLASH_TGT_ISID: + memcpy(&fw_ddb_entry->isid[0], buf, sizeof(fw_ddb_entry->isid)); + rc = size; + break; + case FLASH_TGT_TSID: + rc = kstrtou16(buf, 10, &fw_ddb_entry->tsid); + if (!rc) + rc = size; + break; + case FLASH_TGT_MAX_BURST_LEN: + rc = kstrtou16(buf, 10, + &fw_ddb_entry->iscsi_max_burst_len); + if (!rc) + rc = size; + break; + case FLASH_TGT_FW_CMD_TIMEOUT: + rc = kstrtou16(buf, 10, &fw_ddb_entry->def_timeout); + if (!rc) + rc = size; + break; + case FLASH_TGT_ISCSI_ALIAS: + rc = sprintf(&fw_ddb_entry->iscsi_alias[0], "%s", buf); + break; + case 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; + break; + case FLASH_TGT_TPGT: + rc = kstrtou16(buf, 10, &fw_ddb_entry->tgt_portal_grp); + if (!rc) + rc = size; + break; + case FLASH_TGT_CHAP_TBL_IDX: + rc = kstrtou16(buf, 10, &fw_ddb_entry->chap_tbl_idx); + if (!rc) + rc = size; + break; + case FLASH_TGT_LOGIN: + rc = qla4xxx_sysfs_ddb_login(data, buf, size); + if (!rc) + rc = size; + break; + case FLASH_TGT_LOGOUT: + rc = qla4xxx_sysfs_ddb_logout(data, buf, size); + break; + case FLASH_TGT_APPLY: + rc = qla4xxx_sysfs_ddb_apply(data, buf, size); + break; + default: + ql4_printk(KERN_ERR, ddb_sysfs->ha, + "%s: No such sysfs attribute\n", __func__); + return -ENOSYS; + } + + if (rc == size) { + fw_ddb_entry->cookie = 0xFFEE; + } else { + rc = -EINVAL; + ql4_printk(KERN_ERR, ddb_sysfs->ha, + "%s: Invalid input\n", __func__); + } + 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 FLASH_TGT_IP_ADDR: + case FLASH_TGT_PORT: + case FLASH_TGT_NAME: + case FLASH_TGT_OPTIONS: + case FLASH_TGT_ISCSI_OPTIONS: + case FLASH_TGT_TCP_OPTIONS: + case FLASH_TGT_IP_OPTIONS: + case FLASH_TGT_MAX_RECV_DS_LEN: + case FLASH_TGT_MAX_XMIT_DS_LEN: + case FLASH_TGT_FIRST_BURST_LEN: + case FLASH_TGT_DEF_TIME2WAIT: + case FLASH_TGT_DEF_TIME2RETAIN: + case FLASH_TGT_MAX_OUTSTANDING_R2T: + case FLASH_TGT_NOOP_OUT_INTERVAL: + case FLASH_TGT_ISID: + case FLASH_TGT_TSID: + case FLASH_TGT_MAX_BURST_LEN: + case FLASH_TGT_FW_CMD_TIMEOUT: + case FLASH_TGT_ISCSI_ALIAS: + case FLASH_TGT_ADDRESS: + case FLASH_TGT_TPGT: + rc = S_IRUGO | S_IWUSR; + break; + case FLASH_TGT_CHAP_TBL_IDX: + rc = S_IRUGO | S_IWUSR; + break; + case FLASH_TGT_IS_PERSISTENT: + rc = S_IRUGO; + break; + case FLASH_TGT_LOGIN: + case FLASH_TGT_LOGOUT: + case FLASH_TGT_APPLY: + rc = S_IWUSR; + break; + default: + rc = 0; + break; + } + return rc; +} + +/** + * qla4xxx_sysfs_ddb_release - Free firmware DDB entry + * @data: pointer to flash ddb data + * + * This releases the ddb data buffer and + * invalidates the corresponding flash ddb entry + **/ +static void qla4xxx_sysfs_ddb_release(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; + + 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); + + /* Release the ddb data buffer */ + kfree(ddb_sysfs); + return; +} + +/** + * 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); + 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); + 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 +6273,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 +6394,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 [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.
