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/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 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.

Reply via email to