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  |   13 +
 drivers/scsi/qla4xxx/ql4_glbl.h |    2 +
 drivers/scsi/qla4xxx/ql4_mbx.c  |    4 +-
 drivers/scsi/qla4xxx/ql4_os.c   |  939 ++++++++++++++++++++++++++++++++++++++-
 5 files changed, 956 insertions(+), 3 deletions(-)

diff --git a/drivers/scsi/qla4xxx/Kconfig b/drivers/scsi/qla4xxx/Kconfig
index e4dc7c7..16f2b58 100644
--- a/drivers/scsi/qla4xxx/Kconfig
+++ b/drivers/scsi/qla4xxx/Kconfig
@@ -3,6 +3,7 @@ config SCSI_QLA_ISCSI
        depends on PCI && SCSI && NET
        select SCSI_ISCSI_ATTRS
        select ISCSI_BOOT_SYSFS
+       select ISCSI_FLASH_SYSFS
        ---help---
        This driver supports the QLogic 40xx (ISP4XXX), 8022 (ISP82XX)
        and 8032 (ISP83XX) iSCSI host adapter family.
diff --git a/drivers/scsi/qla4xxx/ql4_def.h b/drivers/scsi/qla4xxx/ql4_def.h
index 8e061ea..9c996c3 100644
--- a/drivers/scsi/qla4xxx/ql4_def.h
+++ b/drivers/scsi/qla4xxx/ql4_def.h
@@ -280,6 +280,13 @@ struct ddb_entry {
        uint16_t chap_tbl_idx;
 };
 
+struct ddb_sysfs_entry {
+       struct scsi_qla_host *ha;
+       struct dev_db_entry fw_ddb;
+       uint16_t tgt_flash_idx;
+       uint16_t tgt_sess_idx;
+};
+
 struct qla_ddb_index {
        struct list_head list;
        uint16_t fw_ddb_idx;
@@ -658,6 +665,9 @@ struct scsi_qla_host {
        /* FW ddb index map */
        struct ddb_entry *fw_ddb_index_map[MAX_DDB_ENTRIES];
 
+       /* kobj of host sysfs entry holding flash ddbs */
+       struct iscsi_host_kobj *host_kobj;
+
        /* Saved srb for status continuation entry processing */
        struct srb *status_srb;
 
@@ -1003,4 +1013,7 @@ static inline void qla4_8xxx_wr_direct(struct 
scsi_qla_host *ha,
 /* Defines for udev events */
 #define QL4_UEVENT_CODE_FW_DUMP                0
 
+/* Flag to denote ST discovery in progress */
+#define DISCOVERY_IN_PROGRESS  0
+
 #endif /*_QLA4XXX_H */
diff --git a/drivers/scsi/qla4xxx/ql4_glbl.h b/drivers/scsi/qla4xxx/ql4_glbl.h
index b5655ac..e884efe 100644
--- a/drivers/scsi/qla4xxx/ql4_glbl.h
+++ b/drivers/scsi/qla4xxx/ql4_glbl.h
@@ -255,6 +255,8 @@ int qla4_8xxx_device_bootstrap(struct scsi_qla_host *ha);
 void qla4_8xxx_get_minidump(struct scsi_qla_host *ha);
 int qla4_8xxx_mbx_intr_disable(struct scsi_qla_host *ha);
 int qla4_8xxx_mbx_intr_enable(struct scsi_qla_host *ha);
+int qla4xxx_get_default_ddb(struct scsi_qla_host *ha, uint32_t options,
+                           dma_addr_t dma_addr);
 
 extern int ql4xextended_error_logging;
 extern int ql4xdontresethba;
diff --git a/drivers/scsi/qla4xxx/ql4_mbx.c b/drivers/scsi/qla4xxx/ql4_mbx.c
index 0375132..2789d5f 100644
--- a/drivers/scsi/qla4xxx/ql4_mbx.c
+++ b/drivers/scsi/qla4xxx/ql4_mbx.c
@@ -1247,8 +1247,8 @@ exit_about_fw:
        return status;
 }
 
-static int qla4xxx_get_default_ddb(struct scsi_qla_host *ha, uint32_t options,
-                                  dma_addr_t dma_addr)
+int qla4xxx_get_default_ddb(struct scsi_qla_host *ha, uint32_t options,
+                           dma_addr_t dma_addr)
 {
        uint32_t mbox_cmd[MBOX_REG_COUNT];
        uint32_t mbox_sts[MBOX_REG_COUNT];
diff --git a/drivers/scsi/qla4xxx/ql4_os.c b/drivers/scsi/qla4xxx/ql4_os.c
index b0b11c5..a7ab53e 100644
--- a/drivers/scsi/qla4xxx/ql4_os.c
+++ b/drivers/scsi/qla4xxx/ql4_os.c
@@ -12,6 +12,7 @@
 
 #include <scsi/scsi_tcq.h>
 #include <scsi/scsicam.h>
+#include <scsi/iscsi_flash_sysfs.h>
 
 #include "ql4_def.h"
 #include "ql4_version.h"
@@ -166,6 +167,16 @@ static int qla4xxx_host_reset(struct Scsi_Host *shost, int 
reset_type);
 static int qla4xxx_change_queue_depth(struct scsi_device *sdev, int qdepth,
                                      int reason);
 
+/*
+ * iSCSI Flash DDB sysfs entry points
+ */
+static ssize_t qla4xxx_sysfs_ddb_set_param(void *data, int type,
+                                          const char *buf, size_t size);
+static ssize_t qla4xxx_sysfs_ddb_get_param(void *data, int type, char *buf);
+static umode_t qla4xxx_sysfs_ddb_attr_visibility(void *data, int type);
+static ssize_t qla4xxx_sysfs_ddb_add(void *data, const char *buf, size_t len);
+static void qla4xxx_sysfs_ddb_release(void *data);
+
 static struct qla4_8xxx_legacy_intr_set legacy_intr[] =
     QLA82XX_LEGACY_INTR_CONFIG;
 
@@ -5028,6 +5039,927 @@ exit_nt_list:
                dma_pool_free(ha->fw_ddb_dma_pool, fw_ddb_entry, fw_ddb_dma);
 }
 
+static void qla4xxx_build_new_nt_list(struct scsi_qla_host *ha,
+                                     struct list_head *list_nt)
+{
+       struct dev_db_entry *fw_ddb_entry;
+       dma_addr_t fw_ddb_dma;
+       int max_ddbs;
+       int fw_idx_size;
+       int ret;
+       uint32_t idx = 0, next_idx = 0;
+       uint32_t state = 0, conn_err = 0;
+       uint16_t conn_id = 0;
+       struct qla_ddb_index  *nt_ddb_idx;
+
+       fw_ddb_entry = dma_pool_alloc(ha->fw_ddb_dma_pool, GFP_KERNEL,
+                                     &fw_ddb_dma);
+       if (fw_ddb_entry == NULL) {
+               DEBUG2(ql4_printk(KERN_ERR, ha, "Out of memory\n"));
+               goto exit_new_nt_list;
+       }
+       max_ddbs =  is_qla40XX(ha) ? MAX_DEV_DB_ENTRIES_40XX :
+                                    MAX_DEV_DB_ENTRIES;
+       fw_idx_size = sizeof(struct qla_ddb_index);
+
+       for (idx = 0; idx < max_ddbs; idx = next_idx) {
+               ret = qla4xxx_get_fwddb_entry(ha, idx, fw_ddb_entry, fw_ddb_dma,
+                                             NULL, &next_idx, &state,
+                                             &conn_err, NULL, &conn_id);
+               if (ret == QLA_ERROR)
+                       break;
+
+               /* Check if NT, then add to list it */
+               if (strlen((char *)fw_ddb_entry->iscsi_name) == 0)
+                       goto continue_next_new_nt;
+
+               if (!(state == DDB_DS_NO_CONNECTION_ACTIVE))
+                       goto continue_next_new_nt;
+
+               DEBUG2(ql4_printk(KERN_INFO, ha,
+                                 "Adding  DDB to session = 0x%x\n", idx));
+
+               nt_ddb_idx = vmalloc(fw_idx_size);
+               if (!nt_ddb_idx)
+                       break;
+
+               nt_ddb_idx->fw_ddb_idx = idx;
+
+               if (qla4xxx_is_session_exists(ha, fw_ddb_entry) ==
+                   QLA_SUCCESS) {
+                       /* free nt_ddb_idx and do not add to list_nt */
+                       vfree(nt_ddb_idx);
+                       goto continue_next_new_nt;
+               }
+
+               list_add_tail(&nt_ddb_idx->list, list_nt);
+
+               ret = qla4xxx_sess_conn_setup(ha, fw_ddb_entry, RESET_ADAPTER);
+               if (ret == QLA_ERROR)
+                       goto exit_new_nt_list;
+
+continue_next_new_nt:
+               if (next_idx == 0)
+                       break;
+       }
+
+exit_new_nt_list:
+       if (fw_ddb_entry)
+               dma_pool_free(ha->fw_ddb_dma_pool, fw_ddb_entry, fw_ddb_dma);
+}
+
+/**
+ * qla4xxx_sysfs_ddb_tgt_create - Create sysfs entry for target
+ * @ha: pointer to host
+ * @fw_ddb_entry: flash ddb data
+ * @idx: target index
+ *
+ * Returns:
+ * On sucess: QLA_SUCCESS
+ * On failure: QLA_ERROR
+ *
+ * This create sysfs entry for the target with given index.
+ **/
+static int qla4xxx_sysfs_ddb_tgt_create(struct scsi_qla_host *ha,
+                                       struct dev_db_entry *fw_ddb_entry,
+                                       uint16_t *idx)
+{
+       struct ddb_sysfs_entry *ddb_sysfs = NULL;
+       struct iscsi_flash_tgt_kobj *tgt_kobj = NULL;
+       int ret = QLA_SUCCESS;
+
+       ddb_sysfs = kzalloc(sizeof(*ddb_sysfs), GFP_KERNEL);
+       if (!ddb_sysfs) {
+               DEBUG2(ql4_printk(KERN_ERR, ha,
+                                 "%s: Unable to allocate ddb buffer to create 
sysfs entry\n",
+                                 __func__));
+               ret = QLA_ERROR;
+               goto leave_tgt_create;
+       }
+
+       ddb_sysfs->ha = ha;
+       memcpy(&ddb_sysfs->fw_ddb, fw_ddb_entry, sizeof(*fw_ddb_entry));
+       ddb_sysfs->tgt_flash_idx = *idx;
+
+       tgt_kobj = iscsi_flash_tgt_kobj_create(ha->host_kobj, *idx, ddb_sysfs,
+                                             qla4xxx_sysfs_ddb_get_param,
+                                             qla4xxx_sysfs_ddb_set_param,
+                                             qla4xxx_sysfs_ddb_attr_visibility,
+                                             qla4xxx_sysfs_ddb_release);
+       if (!tgt_kobj) {
+               ql4_printk(KERN_ERR, ha,
+                          "%s: Unable to create sysfs entry for tgt%d of 
host%lu\n",
+                          __func__, *idx, ha->host_no);
+               kfree(ddb_sysfs);
+               ret = QLA_ERROR;
+               goto leave_tgt_create;
+       }
+
+       ql4_printk(KERN_INFO, ha, "%s: sysfs entry created for tgt%d\n",
+                  __func__, *idx);
+
+leave_tgt_create:
+       return ret;
+}
+
+/**
+ * qla4xxx_sysfs_ddb_add - Add new ddb entry in flash
+ * @data: pointer to host
+ * @buf: type of ddb entry (ipv4/ipv6)
+ * @len: length of buf
+ *
+ * This creates new ddb entry in the flash by finding first free index
+ * and storing default ddb there. And then create sysfs entry for the new
+ * ddb entry.
+ **/
+static ssize_t  qla4xxx_sysfs_ddb_add(void *data, const char *buf, size_t len)
+{
+       struct scsi_qla_host *ha = data;
+       uint16_t idx = 0;
+       uint16_t max_ddbs = 0;
+       uint32_t options = 0;
+       uint32_t rval = QLA_SUCCESS;
+       struct iscsi_flash_tgt_kobj *tgt_kobj = NULL;
+       struct dev_db_entry *fw_ddb_entry = NULL;
+       dma_addr_t fw_ddb_entry_dma;
+
+       if (strncasecmp("IPv4", buf, 4) && strncasecmp("IPv6", buf, 4))
+               return -EINVAL;
+
+       max_ddbs =  is_qla40XX(ha) ? MAX_DEV_DB_ENTRIES_40XX :
+                                    MAX_DEV_DB_ENTRIES;
+
+       fw_ddb_entry = dma_alloc_coherent(&ha->pdev->dev, sizeof(*fw_ddb_entry),
+                                         &fw_ddb_entry_dma, GFP_KERNEL);
+       if (!fw_ddb_entry) {
+               DEBUG2(ql4_printk(KERN_ERR, ha,
+                                 "%s: Unable to allocate dma buffer\n",
+                                 __func__));
+               return -ENOMEM;
+       }
+
+       for (idx = 0; idx < max_ddbs; idx++) {
+               if (qla4xxx_bootdb_by_index(ha, fw_ddb_entry,
+                                           fw_ddb_entry_dma, idx))
+                       break;
+       }
+
+       rval = iscsi_flash_find_tgt_kobj_by_index(&(ha->host_kobj->tgt_list),
+                                                 &tgt_kobj, idx);
+       if (rval) {
+               ql4_printk(KERN_ERR, ha,
+                          "%s: A non-persistent target %s found\n",
+                          __func__, tgt_kobj->kobj.name);
+               rval = -EIO;
+               goto leave_ddb_add;
+       }
+
+       if (!strncasecmp("IPv6", buf, 4))
+               options |= IPV6_DEFAULT_DDB_ENTRY;
+
+       rval = qla4xxx_get_default_ddb(ha, options, fw_ddb_entry_dma);
+       if (rval == QLA_ERROR) {
+               rval = -EIO;
+               goto leave_ddb_add;
+       }
+
+       rval = qla4xxx_sysfs_ddb_tgt_create(ha, fw_ddb_entry, &idx);
+
+leave_ddb_add:
+       dma_free_coherent(&ha->pdev->dev, sizeof(*fw_ddb_entry), fw_ddb_entry,
+                         fw_ddb_entry_dma);
+
+       if (rval == QLA_SUCCESS)
+               rval = len;
+
+       return rval;
+}
+
+static ssize_t qla4xxx_sysfs_ddb_conn_open(struct scsi_qla_host *ha,
+                                          struct dev_db_entry *fw_ddb_entry,
+                                          uint16_t idx)
+{
+       struct dev_db_entry *ddb_entry = NULL;
+       dma_addr_t ddb_entry_dma;
+       unsigned long wtime;
+       uint32_t mbx_sts = 0;
+       uint32_t state = 0, conn_err = 0;
+       uint16_t tmo = 0;
+       int ret = 0;
+
+       ddb_entry = dma_alloc_coherent(&ha->pdev->dev, sizeof(*ddb_entry),
+                                      &ddb_entry_dma, GFP_KERNEL);
+       if (!ddb_entry) {
+               DEBUG2(ql4_printk(KERN_ERR, ha,
+                                 "%s: Unable to allocate dma buffer\n",
+                                 __func__));
+               return QLA_ERROR;
+       }
+
+       memcpy(ddb_entry, fw_ddb_entry, sizeof(*ddb_entry));
+
+       ret = qla4xxx_set_ddb_entry(ha, idx, ddb_entry_dma, &mbx_sts);
+       if (ret != QLA_SUCCESS) {
+               DEBUG2(ql4_printk(KERN_ERR, ha,
+                                 "%s: Unable to set ddb entry for index %d\n",
+                                 __func__, idx));
+               goto exit_ddb_conn_open;
+       }
+
+       qla4xxx_conn_open(ha, idx);
+
+       /* To ensure that sendtargets is done, wait for at least 12 secs */
+       tmo = ((ha->def_timeout > LOGIN_TOV) &&
+              (ha->def_timeout < LOGIN_TOV * 10) ?
+              ha->def_timeout : LOGIN_TOV);
+
+       DEBUG2(ql4_printk(KERN_INFO, ha,
+                         "Default time to wait for login to ddb %d\n", tmo));
+
+       wtime = jiffies + (HZ * tmo);
+       do {
+               ret = qla4xxx_get_fwddb_entry(ha, idx, NULL, 0, NULL,
+                                             NULL, &state, &conn_err, NULL,
+                                             NULL);
+               if (ret == QLA_ERROR)
+                       continue;
+
+               if (state == DDB_DS_NO_CONNECTION_ACTIVE ||
+                   state == DDB_DS_SESSION_FAILED)
+                       break;
+
+               schedule_timeout_uninterruptible(HZ / 10);
+       } while (time_after(wtime, jiffies));
+
+exit_ddb_conn_open:
+       if (ddb_entry)
+               dma_free_coherent(&ha->pdev->dev, sizeof(*ddb_entry),
+                                 ddb_entry, ddb_entry_dma);
+       return ret;
+}
+
+static int qla4xxx_ddb_login_st(struct scsi_qla_host *ha,
+                               struct dev_db_entry *fw_ddb_entry,
+                               uint16_t *ddb_index)
+{
+       struct qla_ddb_index *ddb_idx, *ddb_idx_tmp;
+       struct list_head list_nt;
+       static unsigned long st_discovery;
+       int ret = 0;
+
+       if (test_bit(DISCOVERY_IN_PROGRESS, &st_discovery)) {
+               ql4_printk(KERN_WARNING, ha,
+                          "%s: A discovery already in progress!\n", __func__);
+               return QLA_ERROR;
+       }
+
+       set_bit(DISCOVERY_IN_PROGRESS, &st_discovery);
+
+       ret = qla4xxx_get_ddb_index(ha, ddb_index);
+       if (ret == QLA_ERROR)
+               goto exit_login_st_clr_bit;
+
+       ret = qla4xxx_sysfs_ddb_conn_open(ha, fw_ddb_entry, *ddb_index);
+       if (ret == QLA_ERROR)
+               goto exit_login_st;
+
+       qla4xxx_build_new_nt_list(ha, &list_nt);
+
+       list_for_each_entry_safe(ddb_idx, ddb_idx_tmp, &list_nt, list) {
+               list_del_init(&ddb_idx->list);
+               qla4xxx_clear_ddb_entry(ha, ddb_idx->fw_ddb_idx);
+               vfree(ddb_idx);
+       }
+
+exit_login_st:
+       if (qla4xxx_clear_ddb_entry(ha, *ddb_index) == QLA_ERROR) {
+               ql4_printk(KERN_ERR, ha,
+                          "Unable to clear DDB index = 0x%x\n", *ddb_index);
+       }
+
+exit_login_st_clr_bit:
+       clear_bit(DISCOVERY_IN_PROGRESS, &st_discovery);
+       return ret;
+}
+
+static int qla4xxx_ddb_login_nt(struct scsi_qla_host *ha,
+                               struct dev_db_entry *fw_ddb_entry,
+                               uint16_t *ddb_index)
+{
+       struct dev_db_entry *ddb_entry = NULL;
+       dma_addr_t ddb_entry_dma;
+       uint32_t state = 0, conn_err = 0;
+       int ret = 0;
+
+       ret = qla4xxx_get_ddb_index(ha, ddb_index);
+       if (ret == QLA_ERROR)
+               return ret;
+
+       ret = qla4xxx_sysfs_ddb_conn_open(ha, fw_ddb_entry, *ddb_index);
+       if (ret == QLA_ERROR)
+               goto exit_login_nt;
+
+       ddb_entry = dma_alloc_coherent(&ha->pdev->dev, sizeof(*ddb_entry),
+                                      &ddb_entry_dma, GFP_KERNEL);
+       if (!ddb_entry) {
+               DEBUG2(ql4_printk(KERN_ERR, ha,
+                                 "%s: Unable to allocate dma buffer\n",
+                                 __func__));
+               goto exit_login_nt;
+       }
+
+       ret = qla4xxx_get_fwddb_entry(ha, *ddb_index, ddb_entry, ddb_entry_dma,
+                                     NULL, NULL, &state,
+                                     &conn_err, NULL, NULL);
+       if (ret == QLA_ERROR)
+               goto exit_login_nt;
+
+       ret = qla4xxx_verify_boot_idx(ha, *ddb_index);
+       if (ret == QLA_ERROR)
+               goto exit_login_nt;
+
+       if (state == DDB_DS_NO_CONNECTION_ACTIVE ||
+           state == DDB_DS_SESSION_FAILED)
+               goto exit_login_nt;
+
+       ret = qla4xxx_is_session_exists(ha, fw_ddb_entry);
+       if (ret == QLA_SUCCESS)
+               goto exit_login_nt;
+
+       ret = qla4xxx_sess_conn_setup(ha, fw_ddb_entry, RESET_ADAPTER);
+
+exit_login_nt:
+       if (qla4xxx_clear_ddb_entry(ha, *ddb_index) == QLA_ERROR)
+               ql4_printk(KERN_ERR, ha,
+                          "Unable to clear DDB index = 0x%x\n", *ddb_index);
+
+       if (ddb_entry)
+               dma_free_coherent(&ha->pdev->dev, sizeof(*ddb_entry),
+                                 ddb_entry, ddb_entry_dma);
+       return ret;
+}
+
+/**
+ * qla4xxx_sysfs_ddb_login - Login to the specified target
+ * @data: pointer to ddb_sysfs_entry
+ * @buf: trigger to login to the target
+ * @len: length of buf
+ *
+ * This logs in to the specified target
+ **/
+static ssize_t  qla4xxx_sysfs_ddb_login(void *data, const char *buf, size_t 
len)
+{
+       struct ddb_sysfs_entry *ddb_sysfs = data;
+       struct dev_db_entry *fw_ddb_entry = &ddb_sysfs->fw_ddb;
+       struct scsi_qla_host *ha = ddb_sysfs->ha;
+       uint16_t ddb_index;
+       int ret = 0;
+
+       if (fw_ddb_entry->cookie != DDB_VALID_COOKIE) {
+               ql4_printk(KERN_ERR, ha,
+                          "%s: Target info is not persistent\n", __func__);
+               return -EIO;
+       }
+
+       if (strlen((char *)fw_ddb_entry->iscsi_name) == 0)
+               ret = qla4xxx_ddb_login_st(ha, fw_ddb_entry, &ddb_index);
+       else
+               ret = qla4xxx_ddb_login_nt(ha, fw_ddb_entry, &ddb_index);
+
+       if (ret == QLA_SUCCESS) {
+               ddb_sysfs->tgt_sess_idx = ddb_index;
+               ret = len;
+       }
+
+       return ret;
+}
+
+static void qla4xxx_ddb_logout(struct scsi_qla_host *ha, uint16_t *idx)
+{
+       struct ddb_entry *ddb_entry;
+       int options;
+
+       ddb_entry = qla4xxx_lookup_ddb_by_fw_index(ha, *idx);
+       if ((ddb_entry != NULL) &&
+           (ddb_entry->ddb_type == FLASH_DDB)) {
+
+               options = LOGOUT_OPTION_CLOSE_SESSION;
+               if (qla4xxx_session_logout_ddb(ha, ddb_entry, options) ==
+                   QLA_ERROR)
+                       ql4_printk(KERN_ERR, ha, "%s: Logout failed\n",
+                                  __func__);
+
+               qla4xxx_clear_ddb_entry(ha, ddb_entry->fw_ddb_index);
+               /*
+                * we have decremented the reference count of the driver
+                * when we setup the session to have the driver unload
+                * to be seamless without actually destroying the
+                * session
+                **/
+               try_module_get(qla4xxx_iscsi_transport.owner);
+               iscsi_destroy_endpoint(ddb_entry->conn->ep);
+               qla4xxx_free_ddb(ha, ddb_entry);
+               iscsi_session_teardown(ddb_entry->sess);
+       }
+}
+
+/**
+ * qla4xxx_sysfs_ddb_logout - Logout from the specified target
+ * @data: pointer to ddb_sysfs_entry
+ * @buf: trigger to logout from the target
+ * @len: length of buf
+ *
+ * This performs log out from the specified target
+ **/
+static ssize_t
+qla4xxx_sysfs_ddb_logout(void *data, const char *buf, size_t len)
+{
+       struct ddb_sysfs_entry *ddb_sysfs = data;
+       struct scsi_qla_host *ha = ddb_sysfs->ha;
+       uint16_t ddb_index;
+
+       ddb_index = ddb_sysfs->tgt_sess_idx;
+       qla4xxx_ddb_logout(ha, &ddb_index);
+       return len;
+}
+
+/**
+ * qla4xxx_sysfs_ddb_apply - write the target ddb contents to Flash
+ * @data: pointer to ddb_sysfs_entry
+ * @buf: trigger to apply the target contents to Flash
+ * @len: length of buf
+ *
+ * This writes the contents of target ddb buffer to Flash with a valid cookie
+ * value in order to make the ddb entry persistent.
+ * If the cookie is already valid then nothing to be done.
+ **/
+static ssize_t  qla4xxx_sysfs_ddb_apply(void *data, const char *buf, size_t 
len)
+{
+       struct ddb_sysfs_entry *ddb_sysfs = data;
+       struct scsi_qla_host *ha = ddb_sysfs->ha;
+       uint32_t dev_db_start_offset = FLASH_OFFSET_DB_INFO;
+       struct dev_db_entry *fw_ddb_entry = NULL;
+       dma_addr_t fw_ddb_entry_dma;
+       int rval = QLA_SUCCESS;
+
+       if (ddb_sysfs->fw_ddb.cookie == DDB_VALID_COOKIE) {
+               DEBUG2(ql4_printk(KERN_ERR, ha,
+                                 "%s: ddb entry is already persistent\n",
+                                 __func__));
+               return QLA_SUCCESS;
+       }
+
+       fw_ddb_entry = dma_alloc_coherent(&ha->pdev->dev, sizeof(*fw_ddb_entry),
+                                         &fw_ddb_entry_dma, GFP_KERNEL);
+       if (!fw_ddb_entry) {
+               DEBUG2(ql4_printk(KERN_ERR, ha,
+                                 "%s: Unable to allocate dma buffer\n",
+                                 __func__));
+               return -ENOMEM;
+       }
+
+       dev_db_start_offset += (ddb_sysfs->tgt_flash_idx *
+                               sizeof(*fw_ddb_entry));
+
+       ddb_sysfs->fw_ddb.cookie = DDB_VALID_COOKIE;
+       memcpy(fw_ddb_entry, &ddb_sysfs->fw_ddb, sizeof(*fw_ddb_entry));
+
+       rval = qla4xxx_set_flash(ha, fw_ddb_entry_dma, dev_db_start_offset,
+                               sizeof(*fw_ddb_entry), FLASH_OPT_RMW_COMMIT);
+
+       if (rval == QLA_SUCCESS)
+               rval = len;
+       else
+               rval = -EIO;
+
+       dma_free_coherent(&ha->pdev->dev, sizeof(*fw_ddb_entry), fw_ddb_entry,
+                         fw_ddb_entry_dma);
+       return rval;
+}
+
+static ssize_t qla4xxx_sysfs_ddb_get_param(void *data, int type, char *buf)
+{
+       struct ddb_sysfs_entry *ddb_sysfs = data;
+       struct dev_db_entry *fw_ddb_entry = &ddb_sysfs->fw_ddb;
+       char *str = buf;
+       int rc = 0;
+
+       switch (type) {
+       case FLASH_TGT_IP_ADDR:
+               if (fw_ddb_entry->options & DDB_OPT_IPV6_DEVICE)
+                       rc = sprintf(buf, "%pI6\n",
+                                    &fw_ddb_entry->ip_addr);
+               else
+                       rc = sprintf(buf, "%pI4\n",
+                                    &fw_ddb_entry->ip_addr);
+               break;
+       case FLASH_TGT_PORT:
+               rc = sprintf(str, "%d\n", fw_ddb_entry->port);
+               break;
+       case FLASH_TGT_NAME:
+               rc = sprintf(buf, "%s\n", (char *)&fw_ddb_entry->iscsi_name);
+               break;
+       case FLASH_TGT_OPTIONS:
+               rc = sprintf(str, "0x%X\n", fw_ddb_entry->options);
+               break;
+       case FLASH_TGT_ISCSI_OPTIONS:
+               rc = sprintf(str, "0x%X\n", fw_ddb_entry->iscsi_options);
+               break;
+       case FLASH_TGT_TCP_OPTIONS:
+               rc = sprintf(str, "0x%x\n", fw_ddb_entry->tcp_options);
+               break;
+       case FLASH_TGT_IP_OPTIONS:
+               rc = sprintf(str, "0x%X\n", fw_ddb_entry->ip_options);
+               break;
+       case FLASH_TGT_MAX_RECV_DS_LEN:
+               rc = sprintf(str, "%u\n",
+                            fw_ddb_entry->iscsi_max_rcv_data_seg_len);
+               break;
+       case FLASH_TGT_MAX_XMIT_DS_LEN:
+               rc = sprintf(str, "%u\n",
+                            fw_ddb_entry->iscsi_max_snd_data_seg_len);
+               break;
+       case FLASH_TGT_FIRST_BURST_LEN:
+               rc = sprintf(str, "%u\n", fw_ddb_entry->iscsi_first_burst_len);
+               break;
+       case FLASH_TGT_DEF_TIME2WAIT:
+               rc = sprintf(str, "%u\n", fw_ddb_entry->iscsi_def_time2wait);
+               break;
+       case FLASH_TGT_DEF_TIME2RETAIN:
+               rc = sprintf(str, "%u\n", fw_ddb_entry->iscsi_def_time2retain);
+               break;
+       case FLASH_TGT_MAX_OUTSTANDING_R2T:
+               rc = sprintf(str, "%u\n", fw_ddb_entry->iscsi_max_outsnd_r2t);
+               break;
+       case FLASH_TGT_NOOP_OUT_INTERVAL:
+               rc = sprintf(str, "%u\n", fw_ddb_entry->ka_timeout);
+               break;
+       case FLASH_TGT_ISID:
+               memcpy(str, &fw_ddb_entry->isid[0], sizeof(fw_ddb_entry->isid));
+               break;
+       case FLASH_TGT_TSID:
+               rc = sprintf(str, "%u\n", fw_ddb_entry->tsid);
+               break;
+       case FLASH_TGT_MAX_BURST_LEN:
+               rc = sprintf(str, "%u\n", fw_ddb_entry->iscsi_max_burst_len);
+               break;
+       case FLASH_TGT_FW_CMD_TIMEOUT:
+               rc = sprintf(str, "%u\n", fw_ddb_entry->def_timeout);
+               break;
+       case FLASH_TGT_ISCSI_ALIAS:
+               rc = sprintf(str, "%s\n", (char *)&fw_ddb_entry->iscsi_alias);
+               break;
+       case FLASH_TGT_ADDRESS:
+               if (fw_ddb_entry->options & DDB_OPT_IPV6_DEVICE)
+                       rc = sprintf(buf, "%pI6\n",
+                                    &fw_ddb_entry->tgt_addr);
+               else
+                       rc = sprintf(buf, "%pI4\n",
+                                    &fw_ddb_entry->tgt_addr);
+               break;
+       case FLASH_TGT_TPGT:
+               rc = sprintf(str, "%u\n", fw_ddb_entry->tgt_portal_grp);
+               break;
+       case FLASH_TGT_CHAP_TBL_IDX:
+               rc = sprintf(str, "%u\n", fw_ddb_entry->chap_tbl_idx);
+               break;
+       case FLASH_TGT_IS_PERSISTENT:
+               if (fw_ddb_entry->cookie == DDB_VALID_COOKIE)
+                       rc = sprintf(str, "1\n");
+               else
+                       rc = sprintf(str, "0\n");
+               break;
+       default:
+               rc = -ENOSYS;
+               break;
+       }
+       return rc;
+}
+
+static ssize_t
+qla4xxx_sysfs_ddb_set_param(void *data, int type, const char *buf, size_t size)
+{
+       struct ddb_sysfs_entry *ddb_sysfs = data;
+       struct dev_db_entry *fw_ddb_entry = &ddb_sysfs->fw_ddb;
+       int rc;
+
+       switch (type) {
+       case FLASH_TGT_NAME:
+               rc = sprintf(&fw_ddb_entry->iscsi_name[0], "%s", buf);
+               break;
+       case FLASH_TGT_IP_ADDR:
+               if (fw_ddb_entry->options & DDB_OPT_IPV6_DEVICE)
+                       rc = in6_pton(buf, strlen(buf), fw_ddb_entry->ip_addr,
+                                     '\0', NULL);
+                else
+                       rc = in4_pton(buf, strlen(buf), fw_ddb_entry->ip_addr,
+                                     '\0', NULL);
+               if (rc)
+                       rc = size;
+               break;
+       case FLASH_TGT_PORT:
+               rc = kstrtou16(buf, 10, &fw_ddb_entry->port);
+               if (!rc)
+                       rc = size;
+               break;
+       case FLASH_TGT_OPTIONS:
+               rc = kstrtou16(buf, 10, &fw_ddb_entry->options);
+               if (!rc)
+                       rc = size;
+               break;
+       case FLASH_TGT_ISCSI_OPTIONS:
+               rc = kstrtou16(buf, 10, &fw_ddb_entry->iscsi_options);
+               if (!rc)
+                       rc = size;
+               break;
+       case FLASH_TGT_TCP_OPTIONS:
+               rc = kstrtou16(buf, 10, &fw_ddb_entry->tcp_options);
+               if (!rc)
+                       rc = size;
+               break;
+       case FLASH_TGT_IP_OPTIONS:
+               rc = kstrtou16(buf, 10, &fw_ddb_entry->ip_options);
+               if (!rc)
+                       rc = size;
+               break;
+       case FLASH_TGT_MAX_RECV_DS_LEN:
+               rc = kstrtou16(buf, 10,
+                             &fw_ddb_entry->iscsi_max_rcv_data_seg_len);
+               if (!rc)
+                       rc = size;
+               break;
+       case FLASH_TGT_MAX_XMIT_DS_LEN:
+               rc = kstrtou16(buf, 10,
+                             &fw_ddb_entry->iscsi_max_snd_data_seg_len);
+               if (!rc)
+                       rc = size;
+               break;
+       case FLASH_TGT_FIRST_BURST_LEN:
+               rc = kstrtou16(buf, 10, &fw_ddb_entry->iscsi_first_burst_len);
+               if (!rc)
+                       rc = size;
+               break;
+       case FLASH_TGT_DEF_TIME2WAIT:
+               rc = kstrtou16(buf, 10, &fw_ddb_entry->iscsi_def_time2wait);
+               if (!rc)
+                       rc = size;
+               break;
+       case FLASH_TGT_DEF_TIME2RETAIN:
+               rc = kstrtou16(buf, 10,
+                             &fw_ddb_entry->iscsi_def_time2retain);
+               if (!rc)
+                       rc = size;
+               break;
+       case FLASH_TGT_MAX_OUTSTANDING_R2T:
+               rc = kstrtou16(buf, 10,
+                             &fw_ddb_entry->iscsi_max_outsnd_r2t);
+               if (!rc)
+                       rc = size;
+               break;
+       case FLASH_TGT_NOOP_OUT_INTERVAL:
+               rc = kstrtou16(buf, 10, &fw_ddb_entry->ka_timeout);
+               if (!rc)
+                       rc = size;
+               break;
+       case FLASH_TGT_ISID:
+               memcpy(&fw_ddb_entry->isid[0], buf, sizeof(fw_ddb_entry->isid));
+               rc = size;
+               break;
+       case FLASH_TGT_TSID:
+               rc = kstrtou16(buf, 10, &fw_ddb_entry->tsid);
+               if (!rc)
+                       rc = size;
+               break;
+       case FLASH_TGT_MAX_BURST_LEN:
+               rc = kstrtou16(buf, 10,
+                             &fw_ddb_entry->iscsi_max_burst_len);
+               if (!rc)
+                       rc = size;
+               break;
+       case FLASH_TGT_FW_CMD_TIMEOUT:
+               rc = kstrtou16(buf, 10, &fw_ddb_entry->def_timeout);
+               if (!rc)
+                       rc = size;
+               break;
+       case FLASH_TGT_ISCSI_ALIAS:
+               rc = sprintf(&fw_ddb_entry->iscsi_alias[0], "%s", buf);
+               break;
+       case FLASH_TGT_ADDRESS:
+               if (fw_ddb_entry->options & DDB_OPT_IPV6_DEVICE)
+                       rc = in6_pton(buf, strlen(buf), fw_ddb_entry->tgt_addr,
+                                     '\0', NULL);
+                else
+                       rc = in4_pton(buf, strlen(buf), fw_ddb_entry->tgt_addr,
+                                     '\0', NULL);
+               if (rc)
+                       rc = size;
+               break;
+       case FLASH_TGT_TPGT:
+               rc = kstrtou16(buf, 10, &fw_ddb_entry->tgt_portal_grp);
+               if (!rc)
+                       rc = size;
+               break;
+       case FLASH_TGT_CHAP_TBL_IDX:
+               rc = kstrtou16(buf, 10, &fw_ddb_entry->chap_tbl_idx);
+               if (!rc)
+                       rc = size;
+               break;
+       case FLASH_TGT_LOGIN:
+               rc = qla4xxx_sysfs_ddb_login(data, buf, size);
+               if (!rc)
+                       rc = size;
+               break;
+       case FLASH_TGT_LOGOUT:
+               rc = qla4xxx_sysfs_ddb_logout(data, buf, size);
+               break;
+       case FLASH_TGT_APPLY:
+               rc = qla4xxx_sysfs_ddb_apply(data, buf, size);
+               break;
+       default:
+               ql4_printk(KERN_ERR, ddb_sysfs->ha,
+                          "%s: No such sysfs attribute\n", __func__);
+               return -ENOSYS;
+       }
+
+       if (rc == size) {
+               fw_ddb_entry->cookie = 0xFFEE;
+       } else {
+               rc = -EINVAL;
+               ql4_printk(KERN_ERR, ddb_sysfs->ha,
+                          "%s: Invalid input\n", __func__);
+       }
+       return rc;
+}
+
+/**
+ * qla4xxx_sysfs_ddb_attr_visibility - Get access permission for attr
+ * @data: target info
+ * @type: type of attribute
+ *
+ * Returns:
+ * On success: access permission for attribute
+ * On failure: 0
+ **/
+static umode_t qla4xxx_sysfs_ddb_attr_visibility(void *data, int type)
+{
+       int rc;
+
+       switch (type) {
+       case FLASH_TGT_IP_ADDR:
+       case FLASH_TGT_PORT:
+       case FLASH_TGT_NAME:
+       case FLASH_TGT_OPTIONS:
+       case FLASH_TGT_ISCSI_OPTIONS:
+       case FLASH_TGT_TCP_OPTIONS:
+       case FLASH_TGT_IP_OPTIONS:
+       case FLASH_TGT_MAX_RECV_DS_LEN:
+       case FLASH_TGT_MAX_XMIT_DS_LEN:
+       case FLASH_TGT_FIRST_BURST_LEN:
+       case FLASH_TGT_DEF_TIME2WAIT:
+       case FLASH_TGT_DEF_TIME2RETAIN:
+       case FLASH_TGT_MAX_OUTSTANDING_R2T:
+       case FLASH_TGT_NOOP_OUT_INTERVAL:
+       case FLASH_TGT_ISID:
+       case FLASH_TGT_TSID:
+       case FLASH_TGT_MAX_BURST_LEN:
+       case FLASH_TGT_FW_CMD_TIMEOUT:
+       case FLASH_TGT_ISCSI_ALIAS:
+       case FLASH_TGT_ADDRESS:
+       case FLASH_TGT_TPGT:
+               rc = S_IRUGO | S_IWUSR;
+               break;
+       case FLASH_TGT_CHAP_TBL_IDX:
+               rc = S_IRUGO | S_IWUSR;
+               break;
+       case FLASH_TGT_IS_PERSISTENT:
+               rc = S_IRUGO;
+               break;
+       case FLASH_TGT_LOGIN:
+       case FLASH_TGT_LOGOUT:
+       case FLASH_TGT_APPLY:
+               rc = S_IWUSR;
+               break;
+       default:
+               rc = 0;
+               break;
+       }
+       return rc;
+}
+
+/**
+ * qla4xxx_sysfs_ddb_release - Free firmware DDB entry
+ * @data: pointer to flash ddb data
+ *
+ * This releases the ddb data buffer and
+ * invalidates the corresponding flash ddb entry
+ **/
+static void qla4xxx_sysfs_ddb_release(void *data)
+{
+       struct ddb_sysfs_entry *ddb_sysfs = data;
+       struct scsi_qla_host *ha = ddb_sysfs->ha;
+       uint32_t dev_db_start_offset = FLASH_OFFSET_DB_INFO;
+       struct dev_db_entry *fw_ddb_entry = NULL;
+       dma_addr_t fw_ddb_entry_dma;
+
+       if (!ddb_sysfs)
+               return;
+
+       fw_ddb_entry = dma_alloc_coherent(&ha->pdev->dev, sizeof(*fw_ddb_entry),
+                                         &fw_ddb_entry_dma, GFP_KERNEL);
+       if (!fw_ddb_entry) {
+               DEBUG2(ql4_printk(KERN_ERR, ha,
+                                 "%s: Unable to allocate dma buffer\n",
+                                 __func__));
+               return;
+       }
+
+       dev_db_start_offset += (ddb_sysfs->tgt_flash_idx *
+                               sizeof(*fw_ddb_entry));
+
+       ddb_sysfs->fw_ddb.cookie = 0xFFEE;
+       memcpy(fw_ddb_entry, &ddb_sysfs->fw_ddb, sizeof(*fw_ddb_entry));
+
+       qla4xxx_set_flash(ha, fw_ddb_entry_dma, dev_db_start_offset,
+                         sizeof(*fw_ddb_entry), FLASH_OPT_RMW_COMMIT);
+
+       dma_free_coherent(&ha->pdev->dev, sizeof(*fw_ddb_entry), fw_ddb_entry,
+                         fw_ddb_entry_dma);
+
+       /* Release the ddb data buffer */
+       kfree(ddb_sysfs);
+       return;
+}
+
+/**
+ * qla4xxx_sysfs_ddb_export - Create sysfs entries for firmware DDBs
+ * @ha: pointer to adapter structure
+ *
+ * Export the firmware DDB for all send targets and normal targets to sysfs.
+ **/
+static int qla4xxx_sysfs_ddb_export(struct scsi_qla_host *ha)
+{
+       struct iscsi_host_kobj *host_kobj = NULL;
+       struct dev_db_entry *fw_ddb_entry = NULL;
+       dma_addr_t fw_ddb_entry_dma;
+       uint16_t max_ddbs;
+       uint16_t idx = 0;
+       int ret = QLA_SUCCESS;
+
+       ha->host_kobj = NULL;
+       fw_ddb_entry = dma_alloc_coherent(&ha->pdev->dev,
+                                         sizeof(*fw_ddb_entry),
+                                         &fw_ddb_entry_dma, GFP_KERNEL);
+       if (!fw_ddb_entry) {
+               DEBUG2(ql4_printk(KERN_ERR, ha,
+                                 "%s: Unable to allocate dma buffer\n",
+                                 __func__));
+               return -ENOMEM;
+       }
+
+       max_ddbs =  is_qla40XX(ha) ? MAX_DEV_DB_ENTRIES_40XX :
+                                    MAX_DEV_DB_ENTRIES;
+
+       host_kobj = iscsi_host_kobj_create(ha->host_no, ha,
+                                          qla4xxx_sysfs_ddb_add);
+       if (!host_kobj) {
+               DEBUG2(ql4_printk(KERN_ERR, ha,
+                                 "%s: Unable to create sysfs interface for 
host%lu\n",
+                                 __func__, ha->host_no));
+               ret = -EIO;
+               goto leave_ddb_export;
+       }
+
+       ha->host_kobj = host_kobj;
+
+       for (idx = 0; idx < max_ddbs; idx++) {
+               if (qla4xxx_bootdb_by_index(ha, fw_ddb_entry,
+                                           fw_ddb_entry_dma, idx)) {
+                       DEBUG2(ql4_printk(KERN_INFO, ha,
+                                         "%s: No Flash DDB found at index 
[%d]\n",
+                                         __func__, idx));
+                       continue;
+               }
+
+               ret = qla4xxx_sysfs_ddb_tgt_create(ha, fw_ddb_entry, &idx);
+               if (ret == QLA_ERROR) {
+                       ret = -EIO;
+                       break;
+               }
+       }
+
+leave_ddb_export:
+       dma_free_coherent(&ha->pdev->dev, sizeof(*fw_ddb_entry), fw_ddb_entry,
+                         fw_ddb_entry_dma);
+
+       return ret;
+}
+
+static void qla4xxx_sysfs_ddb_remove(struct scsi_qla_host *ha)
+{
+       if (ha->host_kobj)
+               iscsi_host_kobj_delete(ha->host_kobj);
+}
+
 /**
  * qla4xxx_build_ddb_list - Build ddb list and setup sessions
  * @ha: pointer to adapter structure
@@ -5341,7 +6273,11 @@ initialize_success:
                ql4_printk(KERN_ERR, ha,
                           "%s: No iSCSI boot target configured\n", __func__);
 
-               /* Perform the build ddb list and login to each */
+       if (qla4xxx_sysfs_ddb_export(ha))
+               ql4_printk(KERN_ERR, ha,
+                          "%s: Error exporting ddb to sysfs\n", __func__);
+
+       /* Perform the build ddb list and login to each */
        qla4xxx_build_ddb_list(ha, INIT_ADAPTER);
        iscsi_host_for_each_session(ha->host, qla4xxx_login_flash_ddb);
 
@@ -5458,6 +6394,7 @@ static void __devexit qla4xxx_remove_adapter(struct 
pci_dev *pdev)
        qla4xxx_destroy_fw_ddb_session(ha);
        qla4_8xxx_free_sysfs_attr(ha);
 
+       qla4xxx_sysfs_ddb_remove(ha);
        scsi_remove_host(ha->host);
 
        qla4xxx_free_adapter(ha);
-- 
1.7.1

-- 
You received this message because you are subscribed to the Google Groups 
"open-iscsi" group.
To post to this group, send email to 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