From: Adheer Chandravanshi <adheer.chandravan...@qlogic.com>

This patch allows iscsiadm to manage iSCSI target information stored on
qla4xxx adapter flash on per host basis.

Signed-off-by: Adheer Chandravanshi <adheer.chandravan...@qlogic.com>
Signed-off-by: Vikas Chaudhary <vikas.chaudh...@qlogic.com>
---
 drivers/scsi/qla4xxx/Kconfig    |    1 +
 drivers/scsi/qla4xxx/ql4_def.h  |   12 +
 drivers/scsi/qla4xxx/ql4_fw.h   |   28 +
 drivers/scsi/qla4xxx/ql4_glbl.h |    2 +
 drivers/scsi/qla4xxx/ql4_mbx.c  |    4 +-
 drivers/scsi/qla4xxx/ql4_os.c   | 1154 ++++++++++++++++++++++++++++++++++++++-
 6 files changed, 1196 insertions(+), 5 deletions(-)

diff --git a/drivers/scsi/qla4xxx/Kconfig b/drivers/scsi/qla4xxx/Kconfig
index e4dc7c7..16f2b58 100644
--- a/drivers/scsi/qla4xxx/Kconfig
+++ b/drivers/scsi/qla4xxx/Kconfig
@@ -3,6 +3,7 @@ config SCSI_QLA_ISCSI
        depends on PCI && SCSI && NET
        select SCSI_ISCSI_ATTRS
        select ISCSI_BOOT_SYSFS
+       select ISCSI_FLASH_SYSFS
        ---help---
        This driver supports the QLogic 40xx (ISP4XXX), 8022 (ISP82XX)
        and 8032 (ISP83XX) iSCSI host adapter family.
diff --git a/drivers/scsi/qla4xxx/ql4_def.h b/drivers/scsi/qla4xxx/ql4_def.h
index 8e061ea..15aee02 100644
--- a/drivers/scsi/qla4xxx/ql4_def.h
+++ b/drivers/scsi/qla4xxx/ql4_def.h
@@ -280,6 +280,14 @@ struct ddb_entry {
        uint16_t chap_tbl_idx;
 };
 
+struct ddb_sysfs_entry {
+       struct scsi_qla_host *ha;
+       struct dev_db_entry fw_ddb;
+       uint16_t tgt_flash_idx;
+       uint16_t tgt_sess_idx;
+       uint8_t is_dirty;
+};
+
 struct qla_ddb_index {
        struct list_head list;
        uint16_t fw_ddb_idx;
@@ -498,6 +506,7 @@ struct scsi_qla_host {
 #define AF_INIT_DONE                   1 /* 0x00000002 */
 #define AF_MBOX_COMMAND                        2 /* 0x00000004 */
 #define AF_MBOX_COMMAND_DONE           3 /* 0x00000008 */
+#define AF_ST_DISCOVERY_IN_PROGRESS    4 /* 0x00000010 */
 #define AF_INTERRUPTS_ON               6 /* 0x00000040 */
 #define AF_GET_CRASH_RECORD            7 /* 0x00000080 */
 #define AF_LINK_UP                     8 /* 0x00000100 */
@@ -658,6 +667,9 @@ struct scsi_qla_host {
        /* FW ddb index map */
        struct ddb_entry *fw_ddb_index_map[MAX_DDB_ENTRIES];
 
+       /* kobj of host sysfs entry holding flash ddbs */
+       struct iscsi_host_kobj *host_kobj;
+
        /* Saved srb for status continuation entry processing */
        struct srb *status_srb;
 
diff --git a/drivers/scsi/qla4xxx/ql4_fw.h b/drivers/scsi/qla4xxx/ql4_fw.h
index 1ef954f..f41419c 100644
--- a/drivers/scsi/qla4xxx/ql4_fw.h
+++ b/drivers/scsi/qla4xxx/ql4_fw.h
@@ -757,6 +757,34 @@ struct ql4_chap_table {
        uint16_t cookie;
 };
 
+/* Options bits */
+#define OPT_AUTO_SEND_TARGET_DISABLE   (1 << 6)
+#define OPT_DISCOVERY_SESSION          (1 << 4)
+#define OPT_DDB_ENTRY_STATE            (1 << 3)
+
+/* iSCSI Options bits */
+#define ISCSIOPT_ENABLE_HEADER_DIGEST          (1 << 13)
+#define ISCSIOPT_ENABLE_DATA_DIGEST            (1 << 12)
+#define ISCSIOPT_ENABLE_IMMEDIATE_DATA         (1 << 11)
+#define ISCSIOPT_ENABLE_INITIAL_R2T            (1 << 10)
+#define ISCSIOPT_DATA_SEQ_IN_ORDER             (1 << 9)
+#define ISCSIOPT_DATA_PDU_IN_ORDER             (1 << 8)
+#define ISCSIOPT_CHAP_AUTH_ENABLE              (1 << 7)
+#define ISCSIOPT_SNACK_REQ_ENABLE              (1 << 6)
+#define ISCSIOPT_DISCOVERY_LOGOUT_ENABLE       (1 << 5)
+#define ISCSIOPT_BI_CHAP_CHALLENGE_ENABLE      (1 << 4)
+#define ISCSIOPT_DISCOVERY_AUTH_OPT            (1 << 3)
+
+/* TCP Options bits */
+#define TCPOPT_NAGLE_DISABLE   (1 << 5)
+#define TCPOPT_TCP_TIMER_SCALE3        (1 << 3)
+#define TCPOPT_TCP_TIMER_SCALE2        (1 << 2)
+#define TCPOPT_TCP_TIMER_SCALE1        (1 << 1)
+#define TCPOPT_TIMESTAMP_ENABLE        (1 << 0)
+
+/* IP Options bits */
+#define IPOPT_FRAGMENTATION_DISABLE    (1 << 4)
+
 struct dev_db_entry {
        uint16_t options;       /* 00-01 */
 #define DDB_OPT_DISC_SESSION  0x10
diff --git a/drivers/scsi/qla4xxx/ql4_glbl.h b/drivers/scsi/qla4xxx/ql4_glbl.h
index b5655ac..e884efe 100644
--- a/drivers/scsi/qla4xxx/ql4_glbl.h
+++ b/drivers/scsi/qla4xxx/ql4_glbl.h
@@ -255,6 +255,8 @@ int qla4_8xxx_device_bootstrap(struct scsi_qla_host *ha);
 void qla4_8xxx_get_minidump(struct scsi_qla_host *ha);
 int qla4_8xxx_mbx_intr_disable(struct scsi_qla_host *ha);
 int qla4_8xxx_mbx_intr_enable(struct scsi_qla_host *ha);
+int qla4xxx_get_default_ddb(struct scsi_qla_host *ha, uint32_t options,
+                           dma_addr_t dma_addr);
 
 extern int ql4xextended_error_logging;
 extern int ql4xdontresethba;
diff --git a/drivers/scsi/qla4xxx/ql4_mbx.c b/drivers/scsi/qla4xxx/ql4_mbx.c
index 0375132..2789d5f 100644
--- a/drivers/scsi/qla4xxx/ql4_mbx.c
+++ b/drivers/scsi/qla4xxx/ql4_mbx.c
@@ -1247,8 +1247,8 @@ exit_about_fw:
        return status;
 }
 
-static int qla4xxx_get_default_ddb(struct scsi_qla_host *ha, uint32_t options,
-                                  dma_addr_t dma_addr)
+int qla4xxx_get_default_ddb(struct scsi_qla_host *ha, uint32_t options,
+                           dma_addr_t dma_addr)
 {
        uint32_t mbox_cmd[MBOX_REG_COUNT];
        uint32_t mbox_sts[MBOX_REG_COUNT];
diff --git a/drivers/scsi/qla4xxx/ql4_os.c b/drivers/scsi/qla4xxx/ql4_os.c
index b0b11c5..974640c 100644
--- a/drivers/scsi/qla4xxx/ql4_os.c
+++ b/drivers/scsi/qla4xxx/ql4_os.c
@@ -12,6 +12,7 @@
 
 #include <scsi/scsi_tcq.h>
 #include <scsi/scsicam.h>
+#include <scsi/iscsi_flash_sysfs.h>
 
 #include "ql4_def.h"
 #include "ql4_version.h"
@@ -166,6 +167,17 @@ static int qla4xxx_host_reset(struct Scsi_Host *shost, int 
reset_type);
 static int qla4xxx_change_queue_depth(struct scsi_device *sdev, int qdepth,
                                      int reason);
 
+/*
+ * iSCSI Flash DDB sysfs entry points
+ */
+static ssize_t qla4xxx_sysfs_ddb_set_param(void *data, int type,
+                                          const char *buf, size_t size);
+static ssize_t qla4xxx_sysfs_ddb_get_param(void *data, int type, char *buf);
+static umode_t qla4xxx_sysfs_ddb_attr_visibility(void *data, int type);
+static ssize_t qla4xxx_sysfs_ddb_add(void *data, const char *buf, size_t len);
+static void qla4xxx_sysfs_ddb_delete(void *data);
+static void qla4xxx_sysfs_ddb_release(void *data);
+
 static struct qla4_8xxx_legacy_intr_set legacy_intr[] =
     QLA82XX_LEGACY_INTR_CONFIG;
 
@@ -4448,7 +4460,8 @@ static int qla4xxx_compare_tuple_ddb(struct scsi_qla_host 
*ha,
 }
 
 static int qla4xxx_is_session_exists(struct scsi_qla_host *ha,
-                                    struct dev_db_entry *fw_ddb_entry)
+                                    struct dev_db_entry *fw_ddb_entry,
+                                    struct ddb_entry *get_ddb_entry)
 {
        struct ddb_entry *ddb_entry;
        struct ql4_tuple_ddb *fw_tddb = NULL;
@@ -4482,6 +4495,9 @@ static int qla4xxx_is_session_exists(struct scsi_qla_host 
*ha,
                qla4xxx_get_param_ddb(ddb_entry, tmp_tddb);
                if (!qla4xxx_compare_tuple_ddb(ha, fw_tddb, tmp_tddb, false)) {
                        ret = QLA_SUCCESS; /* found */
+                       if (get_ddb_entry)
+                               memcpy(get_ddb_entry, ddb_entry,
+                                      sizeof(*get_ddb_entry));
                        goto exit_check;
                }
        }
@@ -5009,7 +5025,7 @@ static void qla4xxx_build_nt_list(struct scsi_qla_host 
*ha,
 
                        list_add_tail(&nt_ddb_idx->list, list_nt);
                } else if (is_reset == RESET_ADAPTER) {
-                       if (qla4xxx_is_session_exists(ha, fw_ddb_entry) ==
+                       if (qla4xxx_is_session_exists(ha, fw_ddb_entry, NULL) ==
                                                                QLA_SUCCESS)
                                goto continue_next_nt;
                }
@@ -5028,6 +5044,1133 @@ exit_nt_list:
                dma_pool_free(ha->fw_ddb_dma_pool, fw_ddb_entry, fw_ddb_dma);
 }
 
+static void qla4xxx_build_new_nt_list(struct scsi_qla_host *ha,
+                                     struct list_head *list_nt)
+{
+       struct dev_db_entry *fw_ddb_entry;
+       dma_addr_t fw_ddb_dma;
+       int max_ddbs;
+       int fw_idx_size;
+       int ret;
+       uint32_t idx = 0, next_idx = 0;
+       uint32_t state = 0, conn_err = 0;
+       uint16_t conn_id = 0;
+       struct qla_ddb_index  *nt_ddb_idx;
+
+       fw_ddb_entry = dma_pool_alloc(ha->fw_ddb_dma_pool, GFP_KERNEL,
+                                     &fw_ddb_dma);
+       if (fw_ddb_entry == NULL) {
+               DEBUG2(ql4_printk(KERN_ERR, ha, "Out of memory\n"));
+               goto exit_new_nt_list;
+       }
+       max_ddbs =  is_qla40XX(ha) ? MAX_DEV_DB_ENTRIES_40XX :
+                                    MAX_DEV_DB_ENTRIES;
+       fw_idx_size = sizeof(struct qla_ddb_index);
+
+       for (idx = 0; idx < max_ddbs; idx = next_idx) {
+               ret = qla4xxx_get_fwddb_entry(ha, idx, fw_ddb_entry, fw_ddb_dma,
+                                             NULL, &next_idx, &state,
+                                             &conn_err, NULL, &conn_id);
+               if (ret == QLA_ERROR)
+                       break;
+
+               /* Check if NT, then add to list it */
+               if (strlen((char *)fw_ddb_entry->iscsi_name) == 0)
+                       goto continue_next_new_nt;
+
+               if (!(state == DDB_DS_NO_CONNECTION_ACTIVE))
+                       goto continue_next_new_nt;
+
+               DEBUG2(ql4_printk(KERN_INFO, ha,
+                                 "Adding  DDB to session = 0x%x\n", idx));
+
+               nt_ddb_idx = vmalloc(fw_idx_size);
+               if (!nt_ddb_idx)
+                       break;
+
+               nt_ddb_idx->fw_ddb_idx = idx;
+
+               if (qla4xxx_is_session_exists(ha, fw_ddb_entry, NULL) ==
+                   QLA_SUCCESS) {
+                       /* free nt_ddb_idx and do not add to list_nt */
+                       vfree(nt_ddb_idx);
+                       goto continue_next_new_nt;
+               }
+
+               list_add_tail(&nt_ddb_idx->list, list_nt);
+
+               ret = qla4xxx_sess_conn_setup(ha, fw_ddb_entry, RESET_ADAPTER);
+               if (ret == QLA_ERROR)
+                       goto exit_new_nt_list;
+
+continue_next_new_nt:
+               if (next_idx == 0)
+                       break;
+       }
+
+exit_new_nt_list:
+       if (fw_ddb_entry)
+               dma_pool_free(ha->fw_ddb_dma_pool, fw_ddb_entry, fw_ddb_dma);
+}
+
+/**
+ * qla4xxx_sysfs_ddb_tgt_create - Create sysfs entry for target
+ * @ha: pointer to host
+ * @fw_ddb_entry: flash ddb data
+ * @idx: target index
+ *
+ * Returns:
+ * On sucess: QLA_SUCCESS
+ * On failure: QLA_ERROR
+ *
+ * This create sysfs entry for the target with given index.
+ * If it is a call from userspace then the entry is made dirty,
+ * using is_dirty flag, at the time of creation.
+ **/
+static int qla4xxx_sysfs_ddb_tgt_create(struct scsi_qla_host *ha,
+                                       struct dev_db_entry *fw_ddb_entry,
+                                       uint16_t *idx, int user)
+{
+       struct ddb_sysfs_entry *ddb_sysfs = NULL;
+       struct iscsi_flash_tgt_kobj *tgt_kobj = NULL;
+       int ret = QLA_SUCCESS;
+
+       ddb_sysfs = kzalloc(sizeof(*ddb_sysfs), GFP_KERNEL);
+       if (!ddb_sysfs) {
+               DEBUG2(ql4_printk(KERN_ERR, ha,
+                                 "%s: Unable to allocate ddb buffer to create 
sysfs entry\n",
+                                 __func__));
+               ret = QLA_ERROR;
+               goto leave_tgt_create;
+       }
+
+       ddb_sysfs->ha = ha;
+       memcpy(&ddb_sysfs->fw_ddb, fw_ddb_entry, sizeof(*fw_ddb_entry));
+       ddb_sysfs->tgt_flash_idx = *idx;
+
+       tgt_kobj = iscsi_flash_tgt_kobj_create(ha->host_kobj, *idx, ddb_sysfs,
+                                             qla4xxx_sysfs_ddb_get_param,
+                                             qla4xxx_sysfs_ddb_set_param,
+                                             qla4xxx_sysfs_ddb_attr_visibility,
+                                             qla4xxx_sysfs_ddb_release);
+       if (!tgt_kobj) {
+               ql4_printk(KERN_ERR, ha,
+                          "%s: Unable to create sysfs entry for tgt%d of 
host%lu\n",
+                          __func__, *idx, ha->host_no);
+               kfree(ddb_sysfs);
+               ret = QLA_ERROR;
+               goto leave_tgt_create;
+       }
+
+       if (user) {
+               ddb_sysfs->tgt_sess_idx = INVALID_ENTRY;
+               ddb_sysfs->is_dirty = 1;
+       }
+
+       ql4_printk(KERN_INFO, ha, "%s: sysfs entry created for tgt%d\n",
+                  __func__, *idx);
+
+leave_tgt_create:
+       return ret;
+}
+
+/**
+ * qla4xxx_sysfs_ddb_add - Add new ddb entry in flash
+ * @data: pointer to host
+ * @buf: type of ddb entry (ipv4/ipv6)
+ * @len: length of buf
+ *
+ * This creates new ddb entry in the flash by finding first free index and
+ * storing default ddb there. And then create sysfs entry for the new ddb 
entry.
+ **/
+static ssize_t  qla4xxx_sysfs_ddb_add(void *data, const char *buf, size_t len)
+{
+       struct scsi_qla_host *ha = data;
+       uint16_t idx = 0;
+       uint16_t max_ddbs = 0;
+       uint32_t options = 0;
+       uint32_t rval = QLA_SUCCESS;
+       struct iscsi_flash_tgt_kobj *tgt_kobj = NULL;
+       struct dev_db_entry *fw_ddb_entry = NULL;
+       dma_addr_t fw_ddb_entry_dma;
+
+       if (strncasecmp("IPv4", buf, 4) && strncasecmp("IPv6", buf, 4))
+               return -EINVAL;
+
+       max_ddbs =  is_qla40XX(ha) ? MAX_DEV_DB_ENTRIES_40XX :
+                                    MAX_DEV_DB_ENTRIES;
+
+       fw_ddb_entry = dma_alloc_coherent(&ha->pdev->dev, sizeof(*fw_ddb_entry),
+                                         &fw_ddb_entry_dma, GFP_KERNEL);
+       if (!fw_ddb_entry) {
+               DEBUG2(ql4_printk(KERN_ERR, ha,
+                                 "%s: Unable to allocate dma buffer\n",
+                                 __func__));
+               return -ENOMEM;
+       }
+
+       for (idx = 0; idx < max_ddbs; idx++) {
+               if (qla4xxx_bootdb_by_index(ha, fw_ddb_entry,
+                                           fw_ddb_entry_dma, idx))
+                       break;
+       }
+
+       rval = iscsi_flash_find_tgt_kobj_by_index(&(ha->host_kobj->tgt_list),
+                                                 &tgt_kobj, idx);
+       if (rval) {
+               ql4_printk(KERN_ERR, ha,
+                          "%s: A non-persistent target %s found\n",
+                          __func__, tgt_kobj->kobj.name);
+               rval = -EIO;
+               goto leave_ddb_add;
+       }
+
+       if (!strncasecmp("IPv6", buf, 4))
+               options |= IPV6_DEFAULT_DDB_ENTRY;
+
+       rval = qla4xxx_get_default_ddb(ha, options, fw_ddb_entry_dma);
+       if (rval == QLA_ERROR) {
+               rval = -EIO;
+               goto leave_ddb_add;
+       }
+
+       rval = qla4xxx_sysfs_ddb_tgt_create(ha, fw_ddb_entry, &idx, 1);
+       if (rval == QLA_ERROR) {
+               rval = -EIO;
+               goto leave_ddb_add;
+       }
+
+leave_ddb_add:
+       dma_free_coherent(&ha->pdev->dev, sizeof(*fw_ddb_entry), fw_ddb_entry,
+                         fw_ddb_entry_dma);
+
+       if (rval == QLA_SUCCESS)
+               rval = len;
+
+       return rval;
+}
+
+static ssize_t qla4xxx_sysfs_ddb_conn_open(struct scsi_qla_host *ha,
+                                          struct dev_db_entry *fw_ddb_entry,
+                                          uint16_t idx)
+{
+       struct dev_db_entry *ddb_entry = NULL;
+       dma_addr_t ddb_entry_dma;
+       unsigned long wtime;
+       uint32_t mbx_sts = 0;
+       uint32_t state = 0, conn_err = 0;
+       uint16_t tmo = 0;
+       int ret = 0;
+
+       ddb_entry = dma_alloc_coherent(&ha->pdev->dev, sizeof(*ddb_entry),
+                                      &ddb_entry_dma, GFP_KERNEL);
+       if (!ddb_entry) {
+               DEBUG2(ql4_printk(KERN_ERR, ha,
+                                 "%s: Unable to allocate dma buffer\n",
+                                 __func__));
+               return QLA_ERROR;
+       }
+
+       memcpy(ddb_entry, fw_ddb_entry, sizeof(*ddb_entry));
+
+       ret = qla4xxx_set_ddb_entry(ha, idx, ddb_entry_dma, &mbx_sts);
+       if (ret != QLA_SUCCESS) {
+               DEBUG2(ql4_printk(KERN_ERR, ha,
+                                 "%s: Unable to set ddb entry for index %d\n",
+                                 __func__, idx));
+               goto exit_ddb_conn_open;
+       }
+
+       qla4xxx_conn_open(ha, idx);
+
+       /* To ensure that sendtargets is done, wait for at least 12 secs */
+       tmo = ((ha->def_timeout > LOGIN_TOV) &&
+              (ha->def_timeout < LOGIN_TOV * 10) ?
+              ha->def_timeout : LOGIN_TOV);
+
+       DEBUG2(ql4_printk(KERN_INFO, ha,
+                         "Default time to wait for login to ddb %d\n", tmo));
+
+       wtime = jiffies + (HZ * tmo);
+       do {
+               ret = qla4xxx_get_fwddb_entry(ha, idx, NULL, 0, NULL,
+                                             NULL, &state, &conn_err, NULL,
+                                             NULL);
+               if (ret == QLA_ERROR)
+                       continue;
+
+               if (state == DDB_DS_NO_CONNECTION_ACTIVE ||
+                   state == DDB_DS_SESSION_FAILED)
+                       break;
+
+               schedule_timeout_uninterruptible(HZ / 10);
+       } while (time_after(wtime, jiffies));
+
+exit_ddb_conn_open:
+       if (ddb_entry)
+               dma_free_coherent(&ha->pdev->dev, sizeof(*ddb_entry),
+                                 ddb_entry, ddb_entry_dma);
+       return ret;
+}
+
+static int qla4xxx_ddb_login_st(struct scsi_qla_host *ha,
+                               struct dev_db_entry *fw_ddb_entry,
+                               uint16_t *ddb_index)
+{
+       struct qla_ddb_index *ddb_idx, *ddb_idx_tmp;
+       struct list_head list_nt;
+       int ret = 0;
+
+       if (test_bit(AF_ST_DISCOVERY_IN_PROGRESS, &ha->flags)) {
+               ql4_printk(KERN_WARNING, ha,
+                          "%s: A discovery already in progress!\n", __func__);
+               return QLA_ERROR;
+       }
+
+       INIT_LIST_HEAD(&list_nt);
+
+       set_bit(AF_ST_DISCOVERY_IN_PROGRESS, &ha->flags);
+
+       ret = qla4xxx_get_ddb_index(ha, ddb_index);
+       if (ret == QLA_ERROR)
+               goto exit_login_st_clr_bit;
+
+       ret = qla4xxx_sysfs_ddb_conn_open(ha, fw_ddb_entry, *ddb_index);
+       if (ret == QLA_ERROR)
+               goto exit_login_st;
+
+       qla4xxx_build_new_nt_list(ha, &list_nt);
+
+       list_for_each_entry_safe(ddb_idx, ddb_idx_tmp, &list_nt, list) {
+               list_del_init(&ddb_idx->list);
+               qla4xxx_clear_ddb_entry(ha, ddb_idx->fw_ddb_idx);
+               vfree(ddb_idx);
+       }
+
+exit_login_st:
+       if (qla4xxx_clear_ddb_entry(ha, *ddb_index) == QLA_ERROR) {
+               ql4_printk(KERN_ERR, ha,
+                          "Unable to clear DDB index = 0x%x\n", *ddb_index);
+       }
+
+exit_login_st_clr_bit:
+       clear_bit(AF_ST_DISCOVERY_IN_PROGRESS, &ha->flags);
+       return ret;
+}
+
+static int qla4xxx_ddb_login_nt(struct scsi_qla_host *ha,
+                               struct dev_db_entry *fw_ddb_entry,
+                               uint16_t *ddb_index)
+{
+       int ret = QLA_ERROR;
+
+       ret = qla4xxx_is_session_exists(ha, fw_ddb_entry, NULL);
+       if (ret != QLA_SUCCESS)
+               ret = qla4xxx_sess_conn_setup(ha, fw_ddb_entry, RESET_ADAPTER);
+
+       return ret;
+}
+
+/**
+ * qla4xxx_sysfs_ddb_login - Login to the specified target
+ * @data: pointer to ddb_sysfs_entry
+ * @buf: trigger to login to the target
+ * @len: length of buf
+ *
+ * This logs in to the specified target
+ **/
+static ssize_t  qla4xxx_sysfs_ddb_login(void *data, const char *buf, size_t 
len)
+{
+       struct ddb_sysfs_entry *ddb_sysfs = data;
+       struct dev_db_entry *fw_ddb_entry = &ddb_sysfs->fw_ddb;
+       struct scsi_qla_host *ha = ddb_sysfs->ha;
+       uint16_t ddb_index;
+       int ret = 0;
+
+       if (fw_ddb_entry->cookie != DDB_VALID_COOKIE) {
+               ql4_printk(KERN_ERR, ha,
+                          "%s: Target info is not persistent\n", __func__);
+               return -EIO;
+       }
+
+       if (strlen((char *)fw_ddb_entry->iscsi_name) == 0)
+               ret = qla4xxx_ddb_login_st(ha, fw_ddb_entry, &ddb_index);
+       else
+               ret = qla4xxx_ddb_login_nt(ha, fw_ddb_entry, &ddb_index);
+
+       if (ret == QLA_SUCCESS) {
+               ddb_sysfs->tgt_sess_idx = ddb_index;
+               ret = len;
+       }
+
+       return ret;
+}
+
+/**
+ * qla4xxx_sysfs_ddb_logout - Logout from the specified target
+ * @data: pointer to ddb_sysfs_entry
+ * @buf: trigger to logout from the target
+ * @len: length of buf
+ *
+ * This performs log out from the specified target
+ **/
+static ssize_t
+qla4xxx_sysfs_ddb_logout(void *data, const char *buf, size_t len)
+{
+       struct ddb_sysfs_entry *ddb_sysfs = data;
+       struct scsi_qla_host *ha = ddb_sysfs->ha;
+       struct dev_db_entry *fw_ddb_entry = &ddb_sysfs->fw_ddb;
+       struct ddb_entry *ddb_entry = NULL;
+       unsigned long flags;
+       int options;
+       int ret = len;
+
+       if (strlen((char *)ddb_sysfs->fw_ddb.iscsi_name) == 0) {
+               ql4_printk(KERN_ERR, ha,
+                          "%s: Cannot perform logout on a SendTarget entry\n",
+                          __func__);
+               ret = -EPERM;
+               goto exit_ddb_logout;
+       }
+
+       ddb_entry = vzalloc(sizeof(*ddb_entry));
+       if (!ddb_entry) {
+               DEBUG2(ql4_printk(KERN_WARNING, ha,
+                                 "Memory Allocation failed.\n"));
+               ret = -ENOMEM;
+               goto exit_ddb_logout;
+       }
+
+       if (qla4xxx_is_session_exists(ha, fw_ddb_entry, ddb_entry) !=
+           QLA_SUCCESS) {
+               ret = -EINVAL;
+               goto exit_ddb_logout;
+       }
+
+
+       if (ddb_entry->ddb_type != FLASH_DDB) {
+               ql4_printk(KERN_ERR, ha,
+                          "%s: Cannot logout as it is not Flash DDB entry\n",
+                          __func__);
+               ret = -EINVAL;
+               goto exit_ddb_logout;
+       }
+
+       spin_lock_irqsave(&ha->hardware_lock, flags);
+       qla4xxx_free_ddb(ha, ddb_entry);
+       spin_unlock_irqrestore(&ha->hardware_lock, flags);
+
+       options = LOGOUT_OPTION_CLOSE_SESSION | LOGOUT_OPTION_FREE_DDB;
+       if (qla4xxx_session_logout_ddb(ha, ddb_entry, options) ==
+           QLA_ERROR)
+               ql4_printk(KERN_ERR, ha, "%s: Logout failed\n",
+                          __func__);
+
+       /*
+        * we have decremented the reference count of the driver
+        * when we setup the session to have the driver unload
+        * to be seamless without actually destroying the
+        * session
+        **/
+       try_module_get(qla4xxx_iscsi_transport.owner);
+       iscsi_destroy_endpoint(ddb_entry->conn->ep);
+       iscsi_session_teardown(ddb_entry->sess);
+
+       ret = len;
+
+exit_ddb_logout:
+       if (ddb_entry)
+               vfree(ddb_entry);
+
+       return ret;
+}
+
+/**
+ * qla4xxx_sysfs_ddb_apply - write the target ddb contents to Flash
+ * @data: pointer to ddb_sysfs_entry
+ * @buf: trigger to apply the target contents to Flash
+ * @len: length of buf
+ *
+ * This writes the contents of target ddb buffer to Flash with a valid cookie
+ * value in order to make the ddb entry persistent.
+ * If the cookie is already valid then nothing to be done.
+ **/
+static ssize_t  qla4xxx_sysfs_ddb_apply(void *data, const char *buf, size_t 
len)
+{
+       struct ddb_sysfs_entry *ddb_sysfs = data;
+       struct scsi_qla_host *ha = ddb_sysfs->ha;
+       uint32_t dev_db_start_offset = FLASH_OFFSET_DB_INFO;
+       struct dev_db_entry *fw_ddb_entry = NULL;
+       dma_addr_t fw_ddb_entry_dma;
+       int rval = len;
+
+       if (!ddb_sysfs->is_dirty) {
+               DEBUG2(ql4_printk(KERN_ERR, ha,
+                                 "%s: ddb entry is up to date\n",
+                                 __func__));
+               goto exit_ddb_apply;
+       }
+
+       fw_ddb_entry = dma_alloc_coherent(&ha->pdev->dev, sizeof(*fw_ddb_entry),
+                                         &fw_ddb_entry_dma, GFP_KERNEL);
+       if (!fw_ddb_entry) {
+               DEBUG2(ql4_printk(KERN_ERR, ha,
+                                 "%s: Unable to allocate dma buffer\n",
+                                 __func__));
+               rval = -ENOMEM;
+               goto exit_ddb_apply;
+       }
+
+       dev_db_start_offset += (ddb_sysfs->tgt_flash_idx *
+                               sizeof(*fw_ddb_entry));
+
+       ddb_sysfs->fw_ddb.cookie = DDB_VALID_COOKIE;
+       memcpy(fw_ddb_entry, &ddb_sysfs->fw_ddb, sizeof(*fw_ddb_entry));
+
+       rval = qla4xxx_set_flash(ha, fw_ddb_entry_dma, dev_db_start_offset,
+                               sizeof(*fw_ddb_entry), FLASH_OPT_RMW_COMMIT);
+
+       if (rval == QLA_SUCCESS) {
+               ddb_sysfs->is_dirty = 0;
+               rval = len;
+       } else {
+               ddb_sysfs->fw_ddb.cookie = 0xFFEE;
+               ddb_sysfs->is_dirty = 1;
+               rval = -EIO;
+       }
+exit_ddb_apply:
+       if (fw_ddb_entry)
+               dma_free_coherent(&ha->pdev->dev, sizeof(*fw_ddb_entry),
+                                 fw_ddb_entry, fw_ddb_entry_dma);
+       return rval;
+}
+
+#define GET_BITVAL(o) (o ? 1 : 0)
+
+static ssize_t qla4xxx_sysfs_ddb_get_param(void *data, int type, char *buf)
+{
+       struct ddb_sysfs_entry *ddb_sysfs = data;
+       struct dev_db_entry *fw_ddb_entry = &ddb_sysfs->fw_ddb;
+       char *str = buf;
+       int rc = 0;
+
+       switch (type) {
+       case ISCSI_FLASH_TGT_IP_ADDR:
+               if (fw_ddb_entry->options & DDB_OPT_IPV6_DEVICE)
+                       rc = sprintf(buf, "%pI6\n",
+                                    &fw_ddb_entry->ip_addr);
+               else
+                       rc = sprintf(buf, "%pI4\n",
+                                    &fw_ddb_entry->ip_addr);
+               break;
+       case ISCSI_FLASH_TGT_PORT:
+               rc = sprintf(str, "%d\n", fw_ddb_entry->port);
+               break;
+       case ISCSI_FLASH_TGT_NAME:
+               rc = sprintf(buf, "%s\n", (char *)&fw_ddb_entry->iscsi_name);
+               break;
+       case ISCSI_FLASH_TGT_OPT_AUTO_SND_TGT_DISABLE:
+               rc = sprintf(str, "%u\n",
+                            GET_BITVAL(fw_ddb_entry->options &
+                                       OPT_AUTO_SEND_TARGET_DISABLE));
+               break;
+       case ISCSI_FLASH_TGT_OPT_DISCOVERY_SESS:
+               rc = sprintf(str, "%u\n",
+                            GET_BITVAL(fw_ddb_entry->options &
+                                       OPT_DISCOVERY_SESSION));
+               break;
+       case ISCSI_FLASH_TGT_OPT_ENTRY_ENABLE:
+               rc = sprintf(str, "%u\n",
+                            GET_BITVAL(fw_ddb_entry->options &
+                                       OPT_DDB_ENTRY_STATE));
+               break;
+       case ISCSI_FLASH_TGT_ISCSIOPT_ENABLE_HDR_DIGEST:
+               rc = sprintf(str, "%u\n",
+                            GET_BITVAL(fw_ddb_entry->iscsi_options &
+                                       ISCSIOPT_ENABLE_HEADER_DIGEST));
+               break;
+       case ISCSI_FLASH_TGT_ISCSIOPT_ENABLE_DATA_DIGEST:
+               rc = sprintf(str, "%u\n",
+                            GET_BITVAL(fw_ddb_entry->iscsi_options &
+                                       ISCSIOPT_ENABLE_DATA_DIGEST));
+               break;
+       case ISCSI_FLASH_TGT_ISCSIOPT_ENABLE_IMMEDIATE_DATA:
+               rc = sprintf(str, "%u\n",
+                            GET_BITVAL(fw_ddb_entry->iscsi_options &
+                                       ISCSIOPT_ENABLE_IMMEDIATE_DATA));
+               break;
+       case ISCSI_FLASH_TGT_ISCSIOPT_ENABLE_INITIAL_R2T:
+               rc = sprintf(str, "%u\n",
+                            GET_BITVAL(fw_ddb_entry->iscsi_options &
+                                       ISCSIOPT_ENABLE_INITIAL_R2T));
+               break;
+       case ISCSI_FLASH_TGT_ISCSIOPT_DATA_SEQ_IN_ORDER:
+               rc = sprintf(str, "%u\n",
+                            GET_BITVAL(fw_ddb_entry->iscsi_options &
+                                       ISCSIOPT_DATA_SEQ_IN_ORDER));
+               break;
+       case ISCSI_FLASH_TGT_ISCSIOPT_DATA_PDU_IN_ORDER:
+               rc = sprintf(str, "%u\n",
+                            GET_BITVAL(fw_ddb_entry->iscsi_options &
+                                       ISCSIOPT_DATA_PDU_IN_ORDER));
+               break;
+       case ISCSI_FLASH_TGT_ISCSIOPT_CHAP_AUTH_ENABLE:
+               rc = sprintf(str, "%u\n",
+                            GET_BITVAL(fw_ddb_entry->iscsi_options &
+                                       ISCSIOPT_CHAP_AUTH_ENABLE));
+               break;
+       case ISCSI_FLASH_TGT_ISCSIOPT_SNACK_REQ_ENABLE:
+               rc = sprintf(str, "%u\n",
+                            GET_BITVAL(fw_ddb_entry->iscsi_options &
+                                       ISCSIOPT_SNACK_REQ_ENABLE));
+               break;
+       case ISCSI_FLASH_TGT_ISCSIOPT_DISCOVERY_LOGOUT_ENABLE:
+               rc = sprintf(str, "%u\n",
+                            GET_BITVAL(fw_ddb_entry->iscsi_options &
+                                       ISCSIOPT_DISCOVERY_LOGOUT_ENABLE));
+               break;
+       case ISCSI_FLASH_TGT_ISCSIOPT_BI_CHAP_CHALLENGE_ENABLE:
+               rc = sprintf(str, "%u\n",
+                            GET_BITVAL(fw_ddb_entry->iscsi_options &
+                                       ISCSIOPT_BI_CHAP_CHALLENGE_ENABLE));
+               break;
+       case ISCSI_FLASH_TGT_ISCSIOPT_DISCOVERY_AUTH_OPT:
+               rc = sprintf(str, "%u\n",
+                            GET_BITVAL(fw_ddb_entry->iscsi_options &
+                                       ISCSIOPT_DISCOVERY_AUTH_OPT));
+               break;
+       case ISCSI_FLASH_TGT_TCPOPT_NAGLE_DISABLE:
+               rc = sprintf(str, "%u\n",
+                            GET_BITVAL(fw_ddb_entry->tcp_options &
+                                       TCPOPT_NAGLE_DISABLE));
+               break;
+       case ISCSI_FLASH_TGT_TCPOPT_TIMER_SCALE:
+               rc = sprintf(str, "0x%X\n",
+                            (fw_ddb_entry->tcp_options >> 1) & 0x7);
+               break;
+       case ISCSI_FLASH_TGT_TCPOPT_TIMESTAMP_ENABLE:
+               rc = sprintf(str, "%u\n",
+                            GET_BITVAL(fw_ddb_entry->tcp_options &
+                                       TCPOPT_TIMESTAMP_ENABLE));
+               break;
+       case ISCSI_FLASH_TGT_IPOPT_FRAG_DISABLE:
+               rc = sprintf(str, "%u\n",
+                            GET_BITVAL(fw_ddb_entry->ip_options &
+                                       IPOPT_FRAGMENTATION_DISABLE));
+               break;
+       case ISCSI_FLASH_TGT_MAX_RECV_DS_LEN:
+               rc = sprintf(str, "%u\n",
+                            fw_ddb_entry->iscsi_max_rcv_data_seg_len);
+               break;
+       case ISCSI_FLASH_TGT_MAX_XMIT_DS_LEN:
+               rc = sprintf(str, "%u\n",
+                            fw_ddb_entry->iscsi_max_snd_data_seg_len);
+               break;
+       case ISCSI_FLASH_TGT_FIRST_BURST_LEN:
+               rc = sprintf(str, "%u\n", fw_ddb_entry->iscsi_first_burst_len);
+               break;
+       case ISCSI_FLASH_TGT_DEF_TIME2WAIT:
+               rc = sprintf(str, "%u\n", fw_ddb_entry->iscsi_def_time2wait);
+               break;
+       case ISCSI_FLASH_TGT_DEF_TIME2RETAIN:
+               rc = sprintf(str, "%u\n", fw_ddb_entry->iscsi_def_time2retain);
+               break;
+       case ISCSI_FLASH_TGT_MAX_OUTSTANDING_R2T:
+               rc = sprintf(str, "%u\n", fw_ddb_entry->iscsi_max_outsnd_r2t);
+               break;
+       case ISCSI_FLASH_TGT_NOOP_OUT_INTERVAL:
+               rc = sprintf(str, "%u\n", fw_ddb_entry->ka_timeout);
+               break;
+       case ISCSI_FLASH_TGT_ISID:
+               memcpy(str, &fw_ddb_entry->isid[0], sizeof(fw_ddb_entry->isid));
+               break;
+       case ISCSI_FLASH_TGT_TSID:
+               rc = sprintf(str, "%u\n", fw_ddb_entry->tsid);
+               break;
+       case ISCSI_FLASH_TGT_MAX_BURST_LEN:
+               rc = sprintf(str, "%u\n", fw_ddb_entry->iscsi_max_burst_len);
+               break;
+       case ISCSI_FLASH_TGT_FW_CMD_TIMEOUT:
+               rc = sprintf(str, "%u\n", fw_ddb_entry->def_timeout);
+               break;
+       case ISCSI_FLASH_TGT_ISCSI_ALIAS:
+               rc = sprintf(str, "%s\n", (char *)&fw_ddb_entry->iscsi_alias);
+               break;
+       case ISCSI_FLASH_TGT_ADDRESS:
+               if (fw_ddb_entry->options & DDB_OPT_IPV6_DEVICE)
+                       rc = sprintf(buf, "%pI6\n",
+                                    &fw_ddb_entry->tgt_addr);
+               else
+                       rc = sprintf(buf, "%pI4\n",
+                                    &fw_ddb_entry->tgt_addr);
+               break;
+       case ISCSI_FLASH_TGT_TPGT:
+               rc = sprintf(str, "%u\n", fw_ddb_entry->tgt_portal_grp);
+               break;
+       case ISCSI_FLASH_TGT_CHAP_TBL_IDX:
+               rc = sprintf(str, "%u\n", fw_ddb_entry->chap_tbl_idx);
+               break;
+       case ISCSI_FLASH_TGT_IS_PERSISTENT:
+               if (fw_ddb_entry->cookie == DDB_VALID_COOKIE)
+                       rc = sprintf(str, "1\n");
+               else
+                       rc = sprintf(str, "0\n");
+               break;
+       case ISCSI_FLASH_TGT_IS_DIRTY:
+               rc = sprintf(str, "%u\n", ddb_sysfs->is_dirty);
+               break;
+       default:
+               rc = -ENOSYS;
+               break;
+       }
+       return rc;
+}
+
+#define GET_OPTVAL(f, v, l, u) {       \
+       rc = kstrtou16(f, 10, &v);      \
+       if (!rc && (v < l || v > u)) {  \
+               rc = -EINVAL;           \
+               goto chk_error;         \
+       }                               \
+}
+
+#define SET_OPTVAL(o, n, v) {  \
+       if (o)                  \
+               n |= v;         \
+       else                    \
+               n &= ~v;        \
+}
+
+static ssize_t
+qla4xxx_sysfs_ddb_set_param(void *data, int type, const char *buf, size_t size)
+{
+       struct ddb_sysfs_entry *ddb_sysfs = data;
+       struct dev_db_entry *fw_ddb_entry = &ddb_sysfs->fw_ddb;
+       uint16_t options;
+       int bitval;
+       int rc;
+
+       switch (type) {
+       case ISCSI_FLASH_TGT_NAME:
+               rc = sprintf(&fw_ddb_entry->iscsi_name[0], "%s", buf);
+               goto chk_error;
+       case ISCSI_FLASH_TGT_IP_ADDR:
+               if (fw_ddb_entry->options & DDB_OPT_IPV6_DEVICE)
+                       rc = in6_pton(buf, strlen(buf), fw_ddb_entry->ip_addr,
+                                     '\0', NULL);
+                else
+                       rc = in4_pton(buf, strlen(buf), fw_ddb_entry->ip_addr,
+                                     '\0', NULL);
+               if (rc)
+                       rc = size;
+               else
+                       rc = -EINVAL;
+               goto chk_error;
+       case ISCSI_FLASH_TGT_PORT:
+               rc = kstrtou16(buf, 10, &fw_ddb_entry->port);
+               break;
+       case ISCSI_FLASH_TGT_OPT_AUTO_SND_TGT_DISABLE:
+               GET_OPTVAL(buf, options, 0, 1);
+               if (!rc)
+                       SET_OPTVAL(options, fw_ddb_entry->options,
+                                  OPT_AUTO_SEND_TARGET_DISABLE);
+               break;
+       case ISCSI_FLASH_TGT_OPT_DISCOVERY_SESS:
+               GET_OPTVAL(buf, options, 0, 1);
+               if (!rc)
+                       SET_OPTVAL(options, fw_ddb_entry->options,
+                                  OPT_DISCOVERY_SESSION);
+               break;
+       case ISCSI_FLASH_TGT_OPT_ENTRY_ENABLE:
+               GET_OPTVAL(buf, options, 0, 1);
+               if (!rc)
+                       SET_OPTVAL(options, fw_ddb_entry->options,
+                                  OPT_DDB_ENTRY_STATE);
+               break;
+       case ISCSI_FLASH_TGT_ISCSIOPT_ENABLE_HDR_DIGEST:
+               GET_OPTVAL(buf, options, 0, 1);
+               if (!rc)
+                       SET_OPTVAL(options, fw_ddb_entry->iscsi_options,
+                                  ISCSIOPT_ENABLE_HEADER_DIGEST);
+               break;
+       case ISCSI_FLASH_TGT_ISCSIOPT_ENABLE_DATA_DIGEST:
+               GET_OPTVAL(buf, options, 0, 1);
+               if (!rc)
+                       SET_OPTVAL(options, fw_ddb_entry->iscsi_options,
+                                  ISCSIOPT_ENABLE_DATA_DIGEST);
+               break;
+       case ISCSI_FLASH_TGT_ISCSIOPT_ENABLE_IMMEDIATE_DATA:
+               GET_OPTVAL(buf, options, 0, 1);
+               if (!rc)
+                       SET_OPTVAL(options, fw_ddb_entry->iscsi_options,
+                                  ISCSIOPT_ENABLE_IMMEDIATE_DATA);
+               break;
+       case ISCSI_FLASH_TGT_ISCSIOPT_ENABLE_INITIAL_R2T:
+               GET_OPTVAL(buf, options, 0, 1);
+               if (!rc)
+                       SET_OPTVAL(options, fw_ddb_entry->iscsi_options,
+                                  ISCSIOPT_ENABLE_INITIAL_R2T);
+               break;
+       case ISCSI_FLASH_TGT_ISCSIOPT_DATA_SEQ_IN_ORDER:
+               GET_OPTVAL(buf, options, 0, 1);
+               if (!rc)
+                       SET_OPTVAL(options, fw_ddb_entry->iscsi_options,
+                                  ISCSIOPT_DATA_SEQ_IN_ORDER);
+               break;
+       case ISCSI_FLASH_TGT_ISCSIOPT_DATA_PDU_IN_ORDER:
+               GET_OPTVAL(buf, options, 0, 1);
+               if (!rc)
+                       SET_OPTVAL(options, fw_ddb_entry->iscsi_options,
+                                  ISCSIOPT_DATA_PDU_IN_ORDER);
+               break;
+       case ISCSI_FLASH_TGT_ISCSIOPT_CHAP_AUTH_ENABLE:
+               GET_OPTVAL(buf, options, 0, 1);
+               if (!rc)
+                       SET_OPTVAL(options, fw_ddb_entry->iscsi_options,
+                                  ISCSIOPT_CHAP_AUTH_ENABLE);
+               break;
+       case ISCSI_FLASH_TGT_ISCSIOPT_SNACK_REQ_ENABLE:
+               GET_OPTVAL(buf, options, 0, 1);
+               if (!rc)
+                       SET_OPTVAL(options, fw_ddb_entry->iscsi_options,
+                                  ISCSIOPT_SNACK_REQ_ENABLE);
+               break;
+       case ISCSI_FLASH_TGT_ISCSIOPT_DISCOVERY_LOGOUT_ENABLE:
+               GET_OPTVAL(buf, options, 0, 1);
+               if (!rc)
+                       SET_OPTVAL(options, fw_ddb_entry->iscsi_options,
+                                  ISCSIOPT_DISCOVERY_LOGOUT_ENABLE);
+               break;
+       case ISCSI_FLASH_TGT_ISCSIOPT_BI_CHAP_CHALLENGE_ENABLE:
+               GET_OPTVAL(buf, options, 0, 1);
+               if (!rc)
+                       SET_OPTVAL(options, fw_ddb_entry->iscsi_options,
+                                  ISCSIOPT_BI_CHAP_CHALLENGE_ENABLE);
+               break;
+       case ISCSI_FLASH_TGT_ISCSIOPT_DISCOVERY_AUTH_OPT:
+               GET_OPTVAL(buf, options, 0, 1);
+               if (!rc)
+                       SET_OPTVAL(options, fw_ddb_entry->iscsi_options,
+                                  ISCSIOPT_DISCOVERY_AUTH_OPT);
+               break;
+       case ISCSI_FLASH_TGT_TCPOPT_NAGLE_DISABLE:
+               GET_OPTVAL(buf, options, 0, 1);
+               if (!rc)
+                       SET_OPTVAL(options, fw_ddb_entry->tcp_options,
+                                  TCPOPT_NAGLE_DISABLE);
+               break;
+       case ISCSI_FLASH_TGT_TCPOPT_TIMER_SCALE:
+               GET_OPTVAL(buf, options, 0, 7);
+               if (!rc) {
+                       bitval = options & 1;
+                       SET_OPTVAL(bitval, fw_ddb_entry->tcp_options,
+                                  TCPOPT_TCP_TIMER_SCALE1);
+                       bitval = (options >> 1) & 1;
+                       SET_OPTVAL(bitval, fw_ddb_entry->tcp_options,
+                                  TCPOPT_TCP_TIMER_SCALE2);
+                       bitval = (options >> 2) & 1;
+                       SET_OPTVAL(bitval, fw_ddb_entry->tcp_options,
+                                  TCPOPT_TCP_TIMER_SCALE3);
+               }
+               break;
+       case ISCSI_FLASH_TGT_TCPOPT_TIMESTAMP_ENABLE:
+               GET_OPTVAL(buf, options, 0, 1);
+               if (!rc)
+                       SET_OPTVAL(options, fw_ddb_entry->tcp_options,
+                                  TCPOPT_TIMESTAMP_ENABLE);
+               break;
+       case ISCSI_FLASH_TGT_IPOPT_FRAG_DISABLE:
+               GET_OPTVAL(buf, options, 0, 1);
+               if (!rc)
+                       SET_OPTVAL(options, fw_ddb_entry->ip_options,
+                                  IPOPT_FRAGMENTATION_DISABLE);
+               break;
+       case ISCSI_FLASH_TGT_MAX_RECV_DS_LEN:
+               rc = kstrtou16(buf, 10,
+                             &fw_ddb_entry->iscsi_max_rcv_data_seg_len);
+               break;
+       case ISCSI_FLASH_TGT_MAX_XMIT_DS_LEN:
+               rc = kstrtou16(buf, 10,
+                             &fw_ddb_entry->iscsi_max_snd_data_seg_len);
+               break;
+       case ISCSI_FLASH_TGT_FIRST_BURST_LEN:
+               rc = kstrtou16(buf, 10, &fw_ddb_entry->iscsi_first_burst_len);
+               break;
+       case ISCSI_FLASH_TGT_DEF_TIME2WAIT:
+               rc = kstrtou16(buf, 10, &fw_ddb_entry->iscsi_def_time2wait);
+               break;
+       case ISCSI_FLASH_TGT_DEF_TIME2RETAIN:
+               rc = kstrtou16(buf, 10,
+                             &fw_ddb_entry->iscsi_def_time2retain);
+               break;
+       case ISCSI_FLASH_TGT_MAX_OUTSTANDING_R2T:
+               rc = kstrtou16(buf, 10,
+                             &fw_ddb_entry->iscsi_max_outsnd_r2t);
+               break;
+       case ISCSI_FLASH_TGT_NOOP_OUT_INTERVAL:
+               rc = kstrtou16(buf, 10, &fw_ddb_entry->ka_timeout);
+               break;
+       case ISCSI_FLASH_TGT_ISID:
+               memcpy(&fw_ddb_entry->isid[0], buf, sizeof(fw_ddb_entry->isid));
+               rc = size;
+               goto chk_error;
+       case ISCSI_FLASH_TGT_TSID:
+               rc = kstrtou16(buf, 10, &fw_ddb_entry->tsid);
+               break;
+       case ISCSI_FLASH_TGT_MAX_BURST_LEN:
+               rc = kstrtou16(buf, 10,
+                             &fw_ddb_entry->iscsi_max_burst_len);
+               break;
+       case ISCSI_FLASH_TGT_FW_CMD_TIMEOUT:
+               rc = kstrtou16(buf, 10, &fw_ddb_entry->def_timeout);
+               break;
+       case ISCSI_FLASH_TGT_ISCSI_ALIAS:
+               rc = sprintf(&fw_ddb_entry->iscsi_alias[0], "%s", buf);
+               goto chk_error;
+       case ISCSI_FLASH_TGT_ADDRESS:
+               if (fw_ddb_entry->options & DDB_OPT_IPV6_DEVICE)
+                       rc = in6_pton(buf, strlen(buf), fw_ddb_entry->tgt_addr,
+                                     '\0', NULL);
+                else
+                       rc = in4_pton(buf, strlen(buf), fw_ddb_entry->tgt_addr,
+                                     '\0', NULL);
+               if (rc)
+                       rc = size;
+               else
+                       rc = -EINVAL;
+               goto chk_error;
+       case ISCSI_FLASH_TGT_TPGT:
+               rc = kstrtou16(buf, 10, &fw_ddb_entry->tgt_portal_grp);
+               break;
+       case ISCSI_FLASH_TGT_CHAP_TBL_IDX:
+               rc = kstrtou16(buf, 10, &fw_ddb_entry->chap_tbl_idx);
+               break;
+       case ISCSI_FLASH_TGT_LOGIN:
+               rc = qla4xxx_sysfs_ddb_login(data, buf, size);
+               goto exit_set_param;
+       case ISCSI_FLASH_TGT_LOGOUT:
+               rc = qla4xxx_sysfs_ddb_logout(data, buf, size);
+               goto exit_set_param;
+       case ISCSI_FLASH_TGT_APPLY:
+               rc = qla4xxx_sysfs_ddb_apply(data, buf, size);
+               goto exit_set_param;
+       default:
+               ql4_printk(KERN_ERR, ddb_sysfs->ha,
+                          "%s: No such sysfs attribute\n", __func__);
+               rc = -ENOSYS;
+               goto exit_set_param;
+       }
+
+       /* Only for cases having str to num conversion */
+       if (!rc)
+               rc = size;
+
+chk_error:
+       if (rc == size) {
+               ddb_sysfs->is_dirty = 1;
+       } else {
+               ql4_printk(KERN_ERR, ddb_sysfs->ha,
+                          "%s: Error while setting value, rc=%d\n",
+                          __func__, rc);
+       }
+
+exit_set_param:
+       return rc;
+}
+
+/**
+ * qla4xxx_sysfs_ddb_attr_visibility - Get access permission for attr
+ * @data: target info
+ * @type: type of attribute
+ *
+ * Returns:
+ * On success: access permission for attribute
+ * On failure: 0
+ **/
+static umode_t qla4xxx_sysfs_ddb_attr_visibility(void *data, int type)
+{
+       int rc;
+
+       switch (type) {
+       case ISCSI_FLASH_TGT_IP_ADDR:
+       case ISCSI_FLASH_TGT_PORT:
+       case ISCSI_FLASH_TGT_NAME:
+       case ISCSI_FLASH_TGT_OPT_AUTO_SND_TGT_DISABLE:
+       case ISCSI_FLASH_TGT_OPT_DISCOVERY_SESS:
+       case ISCSI_FLASH_TGT_OPT_ENTRY_ENABLE:
+       case ISCSI_FLASH_TGT_ISCSIOPT_ENABLE_HDR_DIGEST:
+       case ISCSI_FLASH_TGT_ISCSIOPT_ENABLE_DATA_DIGEST:
+       case ISCSI_FLASH_TGT_ISCSIOPT_ENABLE_IMMEDIATE_DATA:
+       case ISCSI_FLASH_TGT_ISCSIOPT_ENABLE_INITIAL_R2T:
+       case ISCSI_FLASH_TGT_ISCSIOPT_DATA_SEQ_IN_ORDER:
+       case ISCSI_FLASH_TGT_ISCSIOPT_DATA_PDU_IN_ORDER:
+       case ISCSI_FLASH_TGT_ISCSIOPT_CHAP_AUTH_ENABLE:
+       case ISCSI_FLASH_TGT_ISCSIOPT_SNACK_REQ_ENABLE:
+       case ISCSI_FLASH_TGT_ISCSIOPT_DISCOVERY_LOGOUT_ENABLE:
+       case ISCSI_FLASH_TGT_ISCSIOPT_BI_CHAP_CHALLENGE_ENABLE:
+       case ISCSI_FLASH_TGT_ISCSIOPT_DISCOVERY_AUTH_OPT:
+       case ISCSI_FLASH_TGT_TCPOPT_NAGLE_DISABLE:
+       case ISCSI_FLASH_TGT_TCPOPT_TIMER_SCALE:
+       case ISCSI_FLASH_TGT_TCPOPT_TIMESTAMP_ENABLE:
+       case ISCSI_FLASH_TGT_IPOPT_FRAG_DISABLE:
+       case ISCSI_FLASH_TGT_MAX_RECV_DS_LEN:
+       case ISCSI_FLASH_TGT_MAX_XMIT_DS_LEN:
+       case ISCSI_FLASH_TGT_FIRST_BURST_LEN:
+       case ISCSI_FLASH_TGT_DEF_TIME2WAIT:
+       case ISCSI_FLASH_TGT_DEF_TIME2RETAIN:
+       case ISCSI_FLASH_TGT_MAX_OUTSTANDING_R2T:
+       case ISCSI_FLASH_TGT_NOOP_OUT_INTERVAL:
+       case ISCSI_FLASH_TGT_ISID:
+       case ISCSI_FLASH_TGT_TSID:
+       case ISCSI_FLASH_TGT_MAX_BURST_LEN:
+       case ISCSI_FLASH_TGT_FW_CMD_TIMEOUT:
+       case ISCSI_FLASH_TGT_ISCSI_ALIAS:
+       case ISCSI_FLASH_TGT_ADDRESS:
+       case ISCSI_FLASH_TGT_TPGT:
+       case ISCSI_FLASH_TGT_CHAP_TBL_IDX:
+               rc = S_IRUGO | S_IWUSR;
+               break;
+       case ISCSI_FLASH_TGT_IS_PERSISTENT:
+       case ISCSI_FLASH_TGT_IS_DIRTY:
+               rc = S_IRUGO;
+               break;
+       case ISCSI_FLASH_TGT_LOGIN:
+       case ISCSI_FLASH_TGT_LOGOUT:
+       case ISCSI_FLASH_TGT_APPLY:
+               rc = S_IWUSR;
+               break;
+       default:
+               rc = 0;
+               break;
+       }
+       return rc;
+}
+
+/**
+ * qla4xxx_sysfs_ddb_delete - Delete firmware DDB entry
+ * @data: pointer to flash ddb data
+ *
+ * This invalidates the flash ddb entry at the given index
+ **/
+static void qla4xxx_sysfs_ddb_delete(void *data)
+{
+       struct ddb_sysfs_entry *ddb_sysfs = data;
+       struct scsi_qla_host *ha = ddb_sysfs->ha;
+       uint32_t dev_db_start_offset = FLASH_OFFSET_DB_INFO;
+       struct dev_db_entry *fw_ddb_entry = NULL;
+       dma_addr_t fw_ddb_entry_dma;
+
+       if (!ddb_sysfs)
+               return;
+
+       if (ddb_sysfs->fw_ddb.cookie != DDB_VALID_COOKIE)
+               return;
+
+       fw_ddb_entry = dma_alloc_coherent(&ha->pdev->dev, sizeof(*fw_ddb_entry),
+                                         &fw_ddb_entry_dma, GFP_KERNEL);
+       if (!fw_ddb_entry) {
+               DEBUG2(ql4_printk(KERN_ERR, ha,
+                                 "%s: Unable to allocate dma buffer\n",
+                                 __func__));
+               return;
+       }
+
+       dev_db_start_offset += (ddb_sysfs->tgt_flash_idx *
+                               sizeof(*fw_ddb_entry));
+
+       ddb_sysfs->fw_ddb.cookie = 0xFFEE;
+       memcpy(fw_ddb_entry, &ddb_sysfs->fw_ddb, sizeof(*fw_ddb_entry));
+
+       qla4xxx_set_flash(ha, fw_ddb_entry_dma, dev_db_start_offset,
+                         sizeof(*fw_ddb_entry), FLASH_OPT_RMW_COMMIT);
+
+       dma_free_coherent(&ha->pdev->dev, sizeof(*fw_ddb_entry), fw_ddb_entry,
+                         fw_ddb_entry_dma);
+}
+
+/**
+ * qla4xxx_sysfs_ddb_release - Free firmware DDB entry in sysfs
+ * @data: pointer to flash ddb data
+ *
+ * This releases the ddb data buffer that holds data exported in sysfs
+ **/
+static void qla4xxx_sysfs_ddb_release(void *data)
+{
+       struct ddb_sysfs_entry *ddb_sysfs = data;
+       kfree(ddb_sysfs);
+}
+
+/**
+ * qla4xxx_sysfs_ddb_export - Create sysfs entries for firmware DDBs
+ * @ha: pointer to adapter structure
+ *
+ * Export the firmware DDB for all send targets and normal targets to sysfs.
+ **/
+static int qla4xxx_sysfs_ddb_export(struct scsi_qla_host *ha)
+{
+       struct iscsi_host_kobj *host_kobj = NULL;
+       struct dev_db_entry *fw_ddb_entry = NULL;
+       dma_addr_t fw_ddb_entry_dma;
+       uint16_t max_ddbs;
+       uint16_t idx = 0;
+       int ret = QLA_SUCCESS;
+
+       ha->host_kobj = NULL;
+       fw_ddb_entry = dma_alloc_coherent(&ha->pdev->dev,
+                                         sizeof(*fw_ddb_entry),
+                                         &fw_ddb_entry_dma, GFP_KERNEL);
+       if (!fw_ddb_entry) {
+               DEBUG2(ql4_printk(KERN_ERR, ha,
+                                 "%s: Unable to allocate dma buffer\n",
+                                 __func__));
+               return -ENOMEM;
+       }
+
+       max_ddbs =  is_qla40XX(ha) ? MAX_DEV_DB_ENTRIES_40XX :
+                                    MAX_DEV_DB_ENTRIES;
+
+       host_kobj = iscsi_host_kobj_create(ha->host_no, ha,
+                                          qla4xxx_sysfs_ddb_add,
+                                          qla4xxx_sysfs_ddb_delete);
+       if (!host_kobj) {
+               DEBUG2(ql4_printk(KERN_ERR, ha,
+                                 "%s: Unable to create sysfs interface for 
host%lu\n",
+                                 __func__, ha->host_no));
+               ret = -EIO;
+               goto leave_ddb_export;
+       }
+
+       ha->host_kobj = host_kobj;
+
+       for (idx = 0; idx < max_ddbs; idx++) {
+               if (qla4xxx_bootdb_by_index(ha, fw_ddb_entry,
+                                           fw_ddb_entry_dma, idx)) {
+                       DEBUG2(ql4_printk(KERN_INFO, ha,
+                                         "%s: No Flash DDB found at index 
[%d]\n",
+                                         __func__, idx));
+                       continue;
+               }
+
+               ret = qla4xxx_sysfs_ddb_tgt_create(ha, fw_ddb_entry, &idx, 0);
+               if (ret == QLA_ERROR) {
+                       ret = -EIO;
+                       break;
+               }
+       }
+
+leave_ddb_export:
+       dma_free_coherent(&ha->pdev->dev, sizeof(*fw_ddb_entry), fw_ddb_entry,
+                         fw_ddb_entry_dma);
+
+       return ret;
+}
+
+static void qla4xxx_sysfs_ddb_remove(struct scsi_qla_host *ha)
+{
+       if (ha->host_kobj)
+               iscsi_host_kobj_delete(ha->host_kobj);
+}
+
 /**
  * qla4xxx_build_ddb_list - Build ddb list and setup sessions
  * @ha: pointer to adapter structure
@@ -5341,7 +6484,11 @@ initialize_success:
                ql4_printk(KERN_ERR, ha,
                           "%s: No iSCSI boot target configured\n", __func__);
 
-               /* Perform the build ddb list and login to each */
+       if (qla4xxx_sysfs_ddb_export(ha))
+               ql4_printk(KERN_ERR, ha,
+                          "%s: Error exporting ddb to sysfs\n", __func__);
+
+       /* Perform the build ddb list and login to each */
        qla4xxx_build_ddb_list(ha, INIT_ADAPTER);
        iscsi_host_for_each_session(ha->host, qla4xxx_login_flash_ddb);
 
@@ -5458,6 +6605,7 @@ static void __devexit qla4xxx_remove_adapter(struct 
pci_dev *pdev)
        qla4xxx_destroy_fw_ddb_session(ha);
        qla4_8xxx_free_sysfs_attr(ha);
 
+       qla4xxx_sysfs_ddb_remove(ha);
        scsi_remove_host(ha->host);
 
        qla4xxx_free_adapter(ha);
-- 
1.7.1

-- 
You received this message because you are subscribed to the Google Groups 
"open-iscsi" group.
To post to this group, send email to open-iscsi@googlegroups.com.
To unsubscribe from this group, send email to 
open-iscsi+unsubscr...@googlegroups.com.
For more options, visit this group at 
http://groups.google.com/group/open-iscsi?hl=en.

Reply via email to