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 | 12 + drivers/scsi/qla4xxx/ql4_fw.h | 33 +- drivers/scsi/qla4xxx/ql4_glbl.h | 2 + drivers/scsi/qla4xxx/ql4_mbx.c | 4 +- drivers/scsi/qla4xxx/ql4_os.c | 1324 ++++++++++++++++++++++++++++++++++++++- 5 files changed, 1369 insertions(+), 6 deletions(-) diff --git a/drivers/scsi/qla4xxx/ql4_def.h b/drivers/scsi/qla4xxx/ql4_def.h index 329d553..d099c89 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 1c47950..50f1f93 100644 --- a/drivers/scsi/qla4xxx/ql4_fw.h +++ b/drivers/scsi/qla4xxx/ql4_fw.h @@ -766,6 +766,36 @@ 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) +#define OPT_DEV_ASSOC_TARGET (1 << 1) +#define OPT_DEV_ASSOC_INITIATOR (1 << 0) + +/* 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_BIDI_CHAP_CHALLENGE_ENABLE (1 << 4) +#define ISCSIOPT_DISCOVERY_AUTH_OPTIONAL (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 @@ -777,7 +807,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 57a5a3c..3de6e73 100644 --- a/drivers/scsi/qla4xxx/ql4_glbl.h +++ b/drivers/scsi/qla4xxx/ql4_glbl.h @@ -259,6 +259,8 @@ int qla4_8xxx_set_param(struct scsi_qla_host *ha, int param); 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); +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 3d41034..efb1488 100644 --- a/drivers/scsi/qla4xxx/ql4_mbx.c +++ b/drivers/scsi/qla4xxx/ql4_mbx.c @@ -1246,8 +1246,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 fbc546e..d95a48d 100644 --- a/drivers/scsi/qla4xxx/ql4_os.c +++ b/drivers/scsi/qla4xxx/ql4_os.c @@ -112,6 +112,9 @@ static int qla4xxx_conn_get_param(struct iscsi_cls_conn *conn, enum iscsi_param param, char *buf); static int qla4xxx_host_get_param(struct Scsi_Host *shost, enum iscsi_host_param param, char *buf); +static int qla4xxx_host_flash_tgt_set_param(struct Scsi_Host *shost, + enum iscsi_host_param param, + const char *buf, int buflen); static int qla4xxx_iface_set_param(struct Scsi_Host *shost, void *data, uint32_t len); static int qla4xxx_get_iface_param(struct iscsi_iface *iface, @@ -166,6 +169,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 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 int qla4xxx_sysfs_ddb_match_index(struct device *dev, void *data); +static ssize_t qla4xxx_sysfs_ddb_add(void *data, const char *buf, size_t len); +static void qla4xxx_sysfs_ddb_delete(void *data); + static struct qla4_8xxx_legacy_intr_set legacy_intr[] = QLA82XX_LEGACY_INTR_CONFIG; @@ -232,6 +246,9 @@ 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, + .set_host_flash_tgt_param = qla4xxx_host_flash_tgt_set_param }; static struct scsi_transport_template *qla4xxx_scsi_transport; @@ -327,6 +344,9 @@ static umode_t qla4_attr_is_visible(int param_type, int param) case ISCSI_HOST_PARAM_PORT_STATE: case ISCSI_HOST_PARAM_PORT_SPEED: return S_IRUGO; + case ISCSI_HOST_PARAM_ADD_FLASH_TGT: + case ISCSI_HOST_PARAM_DEL_FLASH_TGT: + return S_IWUSR; default: return 0; } @@ -376,6 +396,69 @@ static umode_t qla4_attr_is_visible(int param_type, int param) default: return 0; } + case ISCSI_FLASH_TGT_PARAM: + switch (param) { + case ISCSI_FLASH_TGT_IP_ADDR: + case ISCSI_FLASH_TGT_PORT: + 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_EXEC_THROTTLE: + 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_CHAP_AUTH_ENABLE: + case ISCSI_FLASH_TGT_ISCSIOPT_SNACK_REQ_ENABLE: + case ISCSI_FLASH_TGT_ISCSIOPT_DISCOVERY_LOGOUT_ENABLE: + case ISCSI_FLASH_TGT_ISCSIOPT_BIDI_CHAP_CHALLENGE_ENABLE: + case ISCSI_FLASH_TGT_ISCSIOPT_DISCOVERY_AUTH_OPTIONAL: + case ISCSI_FLASH_TGT_TCPOPT_NAGLE_DISABLE: + case ISCSI_FLASH_TGT_FIRST_BURST_LEN: + case ISCSI_FLASH_TGT_DEF_TIME2WAIT: + 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_MAX_SEGMENT_SIZE: + case ISCSI_FLASH_TGT_IPV4_TOS: + case ISCSI_FLASH_TGT_IPV6_FLOW_LABEL: + case ISCSI_FLASH_TGT_NAME: + case ISCSI_FLASH_TGT_TCP_XMIT_WIN_SCALE: + case ISCSI_FLASH_TGT_TCP_RECV_WIN_SCALE: + case ISCSI_FLASH_TGT_CHAP_USER: + case ISCSI_FLASH_TGT_CHAP_PASSWD: + case ISCSI_FLASH_TGT_CHAP_USER_IN: + case ISCSI_FLASH_TGT_CHAP_PASSWD_IN: + return S_IRUGO | S_IWUSR; + case ISCSI_FLASH_TGT_OPT_DEV_ASSOC_TARGET: + case ISCSI_FLASH_TGT_OPT_DEV_ASSOC_INITIATOR: + case ISCSI_FLASH_TGT_EXEC_COUNT: + case ISCSI_FLASH_TGT_RETRY_COUNT: + case ISCSI_FLASH_TGT_RETRY_DELAY: + case ISCSI_FLASH_TGT_ISCSIOPT_DATA_SEQ_IN_ORDER: + case ISCSI_FLASH_TGT_ISCSIOPT_DATA_PDU_IN_ORDER: + 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_DATA_SEGMENT_LEN: + case ISCSI_FLASH_TGT_MAX_XMIT_DATA_SEGMENT_LEN: + case ISCSI_FLASH_TGT_DEF_TIME2RETAIN: + case ISCSI_FLASH_TGT_LOCAL_PORT: + case ISCSI_FLASH_TGT_TPGT: + return S_IRUGO; + case ISCSI_FLASH_TGT_LOGIN: + case ISCSI_FLASH_TGT_LOGOUT: + case ISCSI_FLASH_TGT_APPLY: + return S_IWUSR; + default: + return 0; + + } } return 0; @@ -925,6 +1008,46 @@ static int qla4xxx_host_get_param(struct Scsi_Host *shost, return len; } +static int qla4xxx_host_flash_tgt_set_param(struct Scsi_Host *shost, + enum iscsi_host_param param, + const char *buf, int buflen) +{ + struct scsi_qla_host *ha = to_qla_host(shost); + struct iscsi_flash_tgt *tgt = NULL; + struct ddb_sysfs_entry *ddb_sysfs = NULL; + struct device *dev; + int idx; + int ret; + + switch (param) { + case ISCSI_HOST_PARAM_ADD_FLASH_TGT: + ret = qla4xxx_sysfs_ddb_add(ha, buf, buflen); + break; + case ISCSI_HOST_PARAM_DEL_FLASH_TGT: + dev = iscsi_find_flash_tgt(shost, (void *)buf, + qla4xxx_sysfs_ddb_match_index); + if (!dev) { + ret = -EINVAL; + break; + } + + tgt = iscsi_dev_to_flash_tgt(dev); + ddb_sysfs = tgt->dd_data; + idx = ddb_sysfs->tgt_flash_idx; + qla4xxx_sysfs_ddb_delete(ddb_sysfs); + iscsi_destroy_flash_tgt(tgt); + ret = buflen; + ql4_printk(KERN_INFO, ha, "%s: flash tgt entry tgt-%lu-%u deleted\n", + __func__, ha->host_no, idx); + + break; + default: + ret = -ENOSYS; + } + + return ret; +} + static void qla4xxx_create_ipv4_iface(struct scsi_qla_host *ha) { if (ha->iface_ipv4) @@ -4470,7 +4593,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; @@ -4504,6 +4628,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; } } @@ -5031,7 +5158,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; } @@ -5050,6 +5177,1192 @@ 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_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 = iscsi_dev_to_flash_tgt(dev); + struct ddb_sysfs_entry *ddb_sysfs = tgt->dd_data; + 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_match_index - match ddb index + * @dev: dev associated with the sysfs entry + * @data: pointer containing index of flash ddb entry + * + * Returns: + * 0: if index does not match + * 1: if index matches + **/ +static int qla4xxx_sysfs_ddb_match_index(struct device *dev, void *data) +{ + struct iscsi_flash_tgt *tgt; + struct ddb_sysfs_entry *ddb_sysfs; + int ddb_index; + int ret = 0; + + if (!iscsi_is_flash_tgt_dev(dev)) + goto exit_match_index; + + if (kstrtoint((char *)data, 10, &ddb_index)) + goto exit_match_index; + + tgt = iscsi_dev_to_flash_tgt(dev); + ddb_sysfs = tgt->dd_data; + ret = (ddb_sysfs->tgt_flash_idx == ddb_index) ? 1 : 0; + +exit_match_index: + return ret; +} + +/** + * 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 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; + struct iscsi_flash_tgt *tgt; + 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 = 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); + ret = QLA_ERROR; + 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 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 *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("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; + } + + valid_cookie = DDB_VALID_COOKIE; + dev = iscsi_find_flash_tgt(ha->host, &valid_cookie, + qla4xxx_sysfs_ddb_is_non_persistent); + if (dev) { + tgt = iscsi_dev_to_flash_tgt(dev); + ql4_printk(KERN_ERR, ha, + "%s: A non-persistent target %s found\n", + __func__, tgt->dev.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); + if (ret == QLA_SUCCESS) + ret = len; + } 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; + 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; +} + +#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; + char *str = buf; + int rc = 0; + + switch (param) { + 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_OPT_DEV_ASSOC_TARGET: + rc = sprintf(str, "%u\n", + GET_BITVAL(fw_ddb_entry->options & + OPT_DEV_ASSOC_TARGET)); + break; + case ISCSI_FLASH_TGT_OPT_DEV_ASSOC_INITIATOR: + rc = sprintf(str, "%u\n", + GET_BITVAL(fw_ddb_entry->options & + OPT_DEV_ASSOC_INITIATOR)); + break; + case ISCSI_FLASH_TGT_EXEC_THROTTLE: + rc = sprintf(str, "%u\n", fw_ddb_entry->exec_throttle); + break; + case ISCSI_FLASH_TGT_EXEC_COUNT: + rc = sprintf(str, "%u\n", fw_ddb_entry->exec_count); + break; + case ISCSI_FLASH_TGT_RETRY_COUNT: + rc = sprintf(str, "%u\n", fw_ddb_entry->retry_count); + break; + case ISCSI_FLASH_TGT_RETRY_DELAY: + rc = sprintf(str, "%u\n", fw_ddb_entry->retry_delay); + 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_BIDI_CHAP_CHALLENGE_ENABLE: + rc = sprintf(str, "%u\n", + GET_BITVAL(fw_ddb_entry->iscsi_options & + ISCSIOPT_BIDI_CHAP_CHALLENGE_ENABLE)); + break; + case ISCSI_FLASH_TGT_ISCSIOPT_DISCOVERY_AUTH_OPTIONAL: + rc = sprintf(str, "%u\n", + GET_BITVAL(fw_ddb_entry->iscsi_options & + ISCSIOPT_DISCOVERY_AUTH_OPTIONAL)); + 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_DATA_SEGMENT_LEN: + rc = sprintf(str, "%u\n", + fw_ddb_entry->iscsi_max_rcv_data_seg_len); + break; + case ISCSI_FLASH_TGT_MAX_XMIT_DATA_SEGMENT_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_MAX_SEGMENT_SIZE: + rc = sprintf(str, "%u\n", fw_ddb_entry->mss); + break; + case ISCSI_FLASH_TGT_LOCAL_PORT: + rc = sprintf(str, "%u\n", fw_ddb_entry->lcl_port); + break; + case ISCSI_FLASH_TGT_IPV4_TOS: + rc = sprintf(str, "%u\n", fw_ddb_entry->ipv4_tos); + break; + case ISCSI_FLASH_TGT_IPV6_FLOW_LABEL: + rc = sprintf(str, "%u\n", fw_ddb_entry->ipv6_flow_lbl); + break; + case ISCSI_FLASH_TGT_TPGT: + rc = sprintf(str, "%u\n", fw_ddb_entry->tgt_portal_grp); + break; + case ISCSI_FLASH_TGT_TCP_XMIT_WIN_SCALE: + rc = sprintf(str, "%u\n", fw_ddb_entry->tcp_xmt_wsf); + break; + case ISCSI_FLASH_TGT_TCP_RECV_WIN_SCALE: + rc = sprintf(str, "%u\n", fw_ddb_entry->tcp_rcv_wsf); + break; + case ISCSI_FLASH_TGT_CHAP_USER: + case ISCSI_FLASH_TGT_CHAP_PASSWD: + case ISCSI_FLASH_TGT_CHAP_USER_IN: + case ISCSI_FLASH_TGT_CHAP_PASSWD_IN: + /* TODO - Get chap auth details using chap table index + * in flash ddb + */ + rc = sprintf(str, "\n"); + 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 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; + + switch (param) { + 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 = len; + 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_OPT_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_FLASH_TGT_OPT_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_FLASH_TGT_EXEC_THROTTLE: + rc = kstrtou16(buf, 10, &fw_ddb_entry->exec_throttle); + break; + case ISCSI_FLASH_TGT_EXEC_COUNT: + rc = kstrtou16(buf, 10, &fw_ddb_entry->exec_count); + break; + case ISCSI_FLASH_TGT_RETRY_COUNT: + rc = kstrtou8(buf, 10, &fw_ddb_entry->retry_count); + break; + case ISCSI_FLASH_TGT_RETRY_DELAY: + rc = kstrtou8(buf, 10, &fw_ddb_entry->retry_delay); + 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_BIDI_CHAP_CHALLENGE_ENABLE: + GET_OPTVAL(buf, options, 0, 1); + if (!rc) + SET_OPTVAL(options, fw_ddb_entry->iscsi_options, + ISCSIOPT_BIDI_CHAP_CHALLENGE_ENABLE); + break; + case ISCSI_FLASH_TGT_ISCSIOPT_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_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_DATA_SEGMENT_LEN: + rc = kstrtou16(buf, 10, + &fw_ddb_entry->iscsi_max_rcv_data_seg_len); + break; + case ISCSI_FLASH_TGT_MAX_XMIT_DATA_SEGMENT_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 = len; + 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 = len; + else + rc = -EINVAL; + goto chk_error; + case ISCSI_FLASH_TGT_MAX_SEGMENT_SIZE: + rc = kstrtou16(buf, 10, &fw_ddb_entry->mss); + break; + case ISCSI_FLASH_TGT_LOCAL_PORT: + rc = kstrtou16(buf, 10, &fw_ddb_entry->lcl_port); + break; + case ISCSI_FLASH_TGT_IPV4_TOS: + rc = kstrtou8(buf, 10, &fw_ddb_entry->ipv4_tos); + break; + case ISCSI_FLASH_TGT_IPV6_FLOW_LABEL: + rc = kstrtou16(buf, 10, &fw_ddb_entry->ipv6_flow_lbl); + break; + case ISCSI_FLASH_TGT_TPGT: + rc = kstrtou16(buf, 10, &fw_ddb_entry->tgt_portal_grp); + break; + case ISCSI_FLASH_TGT_TCP_XMIT_WIN_SCALE: + rc = kstrtou8(buf, 10, &fw_ddb_entry->tcp_xmt_wsf); + break; + case ISCSI_FLASH_TGT_TCP_RECV_WIN_SCALE: + rc = kstrtou8(buf, 10, &fw_ddb_entry->tcp_rcv_wsf); + break; + case ISCSI_FLASH_TGT_CHAP_USER: + case ISCSI_FLASH_TGT_CHAP_PASSWD: + case ISCSI_FLASH_TGT_CHAP_USER_IN: + case ISCSI_FLASH_TGT_CHAP_PASSWD_IN: + /* TODO - Set the chap auth user and passwd according to + * chap table index in flash ddb */ + rc = len; + goto exit_set_param; + case ISCSI_FLASH_TGT_LOGIN: + rc = qla4xxx_sysfs_ddb_login(ddb_sysfs, buf, len); + goto exit_set_param; + case ISCSI_FLASH_TGT_LOGOUT: + rc = qla4xxx_sysfs_ddb_logout(ddb_sysfs, buf, len); + goto exit_set_param; + case ISCSI_FLASH_TGT_APPLY: + rc = qla4xxx_sysfs_ddb_apply(ddb_sysfs, buf, len); + 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 = len; + +chk_error: + if (rc == len) { + 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 + * @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_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; + 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; + + 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; + } + } + + 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 @@ -5365,7 +6678,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); @@ -5489,6 +6806,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.
