Merge git://www.linux-watchdog.org/linux-watchdog
[pandora-kernel.git] / drivers / scsi / qla4xxx / ql4_os.c
index bfb3d37..30f31b1 100644 (file)
@@ -6,6 +6,8 @@
  */
 #include <linux/moduleparam.h>
 #include <linux/slab.h>
+#include <linux/blkdev.h>
+#include <linux/iscsi_boot_sysfs.h>
 
 #include <scsi/scsi_tcq.h>
 #include <scsi/scsicam.h>
@@ -76,8 +78,8 @@ static int qla4xxx_conn_get_param(struct iscsi_cls_conn *conn,
                                  enum iscsi_param param, char *buf);
 static int qla4xxx_host_get_param(struct Scsi_Host *shost,
                                  enum iscsi_host_param param, char *buf);
-static int qla4xxx_iface_set_param(struct Scsi_Host *shost, char *data,
-                                  int count);
+static int qla4xxx_iface_set_param(struct Scsi_Host *shost, void *data,
+                                  uint32_t len);
 static int qla4xxx_get_iface_param(struct iscsi_iface *iface,
                                   enum iscsi_param_type param_type,
                                   int param, char *buf);
@@ -119,6 +121,7 @@ static int qla4xxx_slave_alloc(struct scsi_device *device);
 static int qla4xxx_slave_configure(struct scsi_device *device);
 static void qla4xxx_slave_destroy(struct scsi_device *sdev);
 static mode_t ql4_attr_is_visible(int param_type, int param);
+static int qla4xxx_host_reset(struct Scsi_Host *shost, int reset_type);
 
 static struct qla4_8xxx_legacy_intr_set legacy_intr[] =
     QLA82XX_LEGACY_INTR_CONFIG;
@@ -146,6 +149,7 @@ static struct scsi_host_template qla4xxx_driver_template = {
 
        .max_sectors            = 0xFFFF,
        .shost_attrs            = qla4xxx_host_attrs,
+       .host_reset             = qla4xxx_host_reset,
        .vendor_id              = SCSI_NL_VID_TYPE_PCI | PCI_VENDOR_ID_QLOGIC,
 };
 
@@ -199,6 +203,8 @@ static mode_t ql4_attr_is_visible(int param_type, int param)
                }
        case ISCSI_PARAM:
                switch (param) {
+               case ISCSI_PARAM_PERSISTENT_ADDRESS:
+               case ISCSI_PARAM_PERSISTENT_PORT:
                case ISCSI_PARAM_CONN_ADDRESS:
                case ISCSI_PARAM_CONN_PORT:
                case ISCSI_PARAM_TARGET_NAME:
@@ -209,6 +215,7 @@ static mode_t ql4_attr_is_visible(int param_type, int param)
                case ISCSI_PARAM_FIRST_BURST:
                case ISCSI_PARAM_MAX_RECV_DLENGTH:
                case ISCSI_PARAM_MAX_XMIT_DLENGTH:
+               case ISCSI_PARAM_IFACE_NAME:
                        return S_IRUGO;
                default:
                        return 0;
@@ -228,6 +235,8 @@ static mode_t ql4_attr_is_visible(int param_type, int param)
                case ISCSI_NET_PARAM_VLAN_ID:
                case ISCSI_NET_PARAM_VLAN_PRIORITY:
                case ISCSI_NET_PARAM_VLAN_ENABLED:
+               case ISCSI_NET_PARAM_MTU:
+               case ISCSI_NET_PARAM_PORT:
                        return S_IRUGO;
                default:
                        return 0;
@@ -333,6 +342,15 @@ static int qla4xxx_get_iface_param(struct iscsi_iface *iface,
                                       IPV6_OPT_VLAN_TAGGING_ENABLE) ?
                                       "enabled" : "disabled");
                break;
+       case ISCSI_NET_PARAM_MTU:
+               len = sprintf(buf, "%d\n", ha->ip_config.eth_mtu_size);
+               break;
+       case ISCSI_NET_PARAM_PORT:
+               if (iface->iface_type == ISCSI_IFACE_TYPE_IPV4)
+                       len = sprintf(buf, "%d\n", ha->ip_config.ipv4_port);
+               else if (iface->iface_type == ISCSI_IFACE_TYPE_IPV6)
+                       len = sprintf(buf, "%d\n", ha->ip_config.ipv6_port);
+               break;
        default:
                len = -ENOSYS;
        }
@@ -702,7 +720,7 @@ static void qla4xxx_set_ipv6(struct scsi_qla_host *ha,
                        qla4xxx_destroy_ipv6_iface(ha);
                }
                break;
-       case ISCSI_NET_PARAM_VLAN_ID:
+       case ISCSI_NET_PARAM_VLAN_TAG:
                if (iface_param->len != sizeof(init_fw_cb->ipv6_vlan_tag))
                        break;
                init_fw_cb->ipv6_vlan_tag =
@@ -716,6 +734,18 @@ static void qla4xxx_set_ipv6(struct scsi_qla_host *ha,
                        init_fw_cb->ipv6_opts &=
                                cpu_to_le16(~IPV6_OPT_VLAN_TAGGING_ENABLE);
                break;
+       case ISCSI_NET_PARAM_MTU:
+               init_fw_cb->eth_mtu_size =
+                               cpu_to_le16(*(uint16_t *)iface_param->value);
+               break;
+       case ISCSI_NET_PARAM_PORT:
+               /* Autocfg applies to even interface */
+               if (iface_param->iface_num & 0x1)
+                       break;
+
+               init_fw_cb->ipv6_port =
+                               cpu_to_le16(*(uint16_t *)iface_param->value);
+               break;
        default:
                ql4_printk(KERN_ERR, ha, "Unknown IPv6 param = %d\n",
                           iface_param->param);
@@ -762,7 +792,7 @@ static void qla4xxx_set_ipv4(struct scsi_qla_host *ha,
                        qla4xxx_destroy_ipv4_iface(ha);
                }
                break;
-       case ISCSI_NET_PARAM_VLAN_ID:
+       case ISCSI_NET_PARAM_VLAN_TAG:
                if (iface_param->len != sizeof(init_fw_cb->ipv4_vlan_tag))
                        break;
                init_fw_cb->ipv4_vlan_tag =
@@ -776,6 +806,14 @@ static void qla4xxx_set_ipv4(struct scsi_qla_host *ha,
                        init_fw_cb->ipv4_ip_opts &=
                                        cpu_to_le16(~IPOPT_VLAN_TAGGING_ENABLE);
                break;
+       case ISCSI_NET_PARAM_MTU:
+               init_fw_cb->eth_mtu_size =
+                               cpu_to_le16(*(uint16_t *)iface_param->value);
+               break;
+       case ISCSI_NET_PARAM_PORT:
+               init_fw_cb->ipv4_port =
+                               cpu_to_le16(*(uint16_t *)iface_param->value);
+               break;
        default:
                ql4_printk(KERN_ERR, ha, "Unknown IPv4 param = %d\n",
                           iface_param->param);
@@ -806,7 +844,7 @@ qla4xxx_initcb_to_acb(struct addr_ctrl_blk *init_fw_cb)
 }
 
 static int
-qla4xxx_iface_set_param(struct Scsi_Host *shost, char *data, int count)
+qla4xxx_iface_set_param(struct Scsi_Host *shost, void *data, uint32_t len)
 {
        struct scsi_qla_host *ha = to_qla_host(shost);
        int rval = 0;
@@ -815,8 +853,8 @@ qla4xxx_iface_set_param(struct Scsi_Host *shost, char *data, int count)
        dma_addr_t init_fw_cb_dma;
        uint32_t mbox_cmd[MBOX_REG_COUNT];
        uint32_t mbox_sts[MBOX_REG_COUNT];
-       uint32_t total_param_count;
-       uint32_t length;
+       uint32_t rem = len;
+       struct nlattr *attr;
 
        init_fw_cb = dma_alloc_coherent(&ha->pdev->dev,
                                        sizeof(struct addr_ctrl_blk),
@@ -837,11 +875,8 @@ qla4xxx_iface_set_param(struct Scsi_Host *shost, char *data, int count)
                goto exit_init_fw_cb;
        }
 
-       total_param_count = count;
-       iface_param = (struct iscsi_iface_param_info *)data;
-
-       for ( ; total_param_count != 0; total_param_count--) {
-               length = iface_param->len;
+       nla_for_each_attr(attr, data, len, rem) {
+               iface_param = nla_data(attr);
 
                if (iface_param->param_type != ISCSI_NET_PARAM)
                        continue;
@@ -878,10 +913,6 @@ qla4xxx_iface_set_param(struct Scsi_Host *shost, char *data, int count)
                        ql4_printk(KERN_ERR, ha, "Invalid iface type\n");
                        break;
                }
-
-               iface_param = (struct iscsi_iface_param_info *)
-                                               ((uint8_t *)iface_param +
-                           sizeof(struct iscsi_iface_param_info) + length);
        }
 
        init_fw_cb->cookie = cpu_to_le32(0x11BEAD5A);
@@ -968,6 +999,7 @@ qla4xxx_session_create(struct iscsi_endpoint *ep,
        qla_ep = ep->dd_data;
        dst_addr = (struct sockaddr *)&qla_ep->dst_addr;
        ha = to_qla_host(qla_ep->host);
+
 get_ddb_index:
        ddb_index = find_first_zero_bit(ha->ddb_idx_map, MAX_DDB_ENTRIES);
 
@@ -1027,6 +1059,8 @@ static void qla4xxx_session_destroy(struct iscsi_cls_session *cls_sess)
        ddb_entry = sess->dd_data;
        ha = ddb_entry->ha;
 
+       qla4xxx_clear_ddb_entry(ha, ddb_entry->fw_ddb_index);
+
        spin_lock_irqsave(&ha->hardware_lock, flags);
        qla4xxx_free_ddb(ha, ddb_entry);
        spin_unlock_irqrestore(&ha->hardware_lock, flags);
@@ -1103,8 +1137,12 @@ static int qla4xxx_conn_start(struct iscsi_cls_conn *cls_conn)
                */
                if (mbx_sts)
                        if (ddb_entry->fw_ddb_device_state ==
-                                                       DDB_DS_SESSION_ACTIVE)
+                                               DDB_DS_SESSION_ACTIVE) {
+                               iscsi_conn_start(ddb_entry->conn);
+                               iscsi_conn_login_event(ddb_entry->conn,
+                                               ISCSI_CONN_STATE_LOGGED_IN);
                                goto exit_set_param;
+                       }
 
                ql4_printk(KERN_ERR, ha, "%s: Failed set param for index[%d]\n",
                           __func__, ddb_entry->fw_ddb_index);
@@ -1119,10 +1157,13 @@ static int qla4xxx_conn_start(struct iscsi_cls_conn *cls_conn)
                goto exit_conn_start;
        }
 
-       ddb_entry->fw_ddb_device_state = DDB_DS_LOGIN_IN_PROCESS;
+       if (ddb_entry->fw_ddb_device_state == DDB_DS_NO_CONNECTION_ACTIVE)
+               ddb_entry->fw_ddb_device_state = DDB_DS_LOGIN_IN_PROCESS;
+
+       DEBUG2(printk(KERN_INFO "%s: DDB state [%d]\n", __func__,
+                     ddb_entry->fw_ddb_device_state));
 
 exit_set_param:
-       iscsi_conn_start(cls_conn);
        ret = 0;
 
 exit_conn_start:
@@ -1147,14 +1188,6 @@ static void qla4xxx_conn_destroy(struct iscsi_cls_conn *cls_conn)
        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__);
-       else
-               qla4xxx_clear_ddb_entry(ha, ddb_entry->fw_ddb_index);
-
-       /*
-        * Clear the DDB bit so that next login can use the bit
-        * if FW is not clearing the DDB entry then set DDB will fail anyways
-        */
-       clear_bit(ddb_entry->fw_ddb_index, ha->ddb_idx_map);
 }
 
 static void qla4xxx_task_work(struct work_struct *wdata)
@@ -1234,7 +1267,7 @@ static int qla4xxx_alloc_pdu(struct iscsi_task *task, uint8_t opcode)
        DEBUG2(ql4_printk(KERN_INFO, ha, "%s: MaxRecvLen %u, iscsi hrd %d\n",
                      __func__, task->conn->max_recv_dlength, hdr_len));
 
-       task_data->resp_len = task->conn->max_recv_dlength;
+       task_data->resp_len = task->conn->max_recv_dlength + hdr_len;
        task_data->resp_buffer = dma_alloc_coherent(&ha->pdev->dev,
                                                    task_data->resp_len,
                                                    &task_data->resp_dma,
@@ -1242,8 +1275,9 @@ static int qla4xxx_alloc_pdu(struct iscsi_task *task, uint8_t opcode)
        if (!task_data->resp_buffer)
                goto exit_alloc_pdu;
 
+       task_data->req_len = task->data_count + hdr_len;
        task_data->req_buffer = dma_alloc_coherent(&ha->pdev->dev,
-                                                  task->data_count + hdr_len,
+                                                  task_data->req_len,
                                                   &task_data->req_dma,
                                                   GFP_ATOMIC);
        if (!task_data->req_buffer)
@@ -1261,7 +1295,7 @@ exit_alloc_pdu:
                                  task_data->resp_buffer, task_data->resp_dma);
 
        if (task_data->req_buffer)
-               dma_free_coherent(&ha->pdev->dev, task->data_count + hdr_len,
+               dma_free_coherent(&ha->pdev->dev, task_data->req_len,
                                  task_data->req_buffer, task_data->req_dma);
        return -ENOMEM;
 }
@@ -1290,7 +1324,7 @@ static void qla4xxx_task_cleanup(struct iscsi_task *task)
 
        dma_free_coherent(&ha->pdev->dev, task_data->resp_len,
                          task_data->resp_buffer, task_data->resp_dma);
-       dma_free_coherent(&ha->pdev->dev, task->data_count + hdr_len,
+       dma_free_coherent(&ha->pdev->dev, task_data->req_len,
                          task_data->req_buffer, task_data->req_dma);
        return;
 }
@@ -1569,6 +1603,10 @@ static void qla4xxx_mem_free(struct scsi_qla_host *ha)
        if (ha->chap_dma_pool)
                dma_pool_destroy(ha->chap_dma_pool);
 
+       if (ha->chap_list)
+               vfree(ha->chap_list);
+       ha->chap_list = NULL;
+
        /* release io space registers  */
        if (is_qla8022(ha)) {
                if (ha->nx_pcibase)
@@ -2551,6 +2589,594 @@ uint16_t qla4_8xxx_rd_shdw_rsp_q_in(struct scsi_qla_host *ha)
        return (uint16_t)le32_to_cpu(readl(&ha->qla4_8xxx_reg->rsp_q_in));
 }
 
+static ssize_t qla4xxx_show_boot_eth_info(void *data, int type, char *buf)
+{
+       struct scsi_qla_host *ha = data;
+       char *str = buf;
+       int rc;
+
+       switch (type) {
+       case ISCSI_BOOT_ETH_FLAGS:
+               rc = sprintf(str, "%d\n", SYSFS_FLAG_FW_SEL_BOOT);
+               break;
+       case ISCSI_BOOT_ETH_INDEX:
+               rc = sprintf(str, "0\n");
+               break;
+       case ISCSI_BOOT_ETH_MAC:
+               rc = sysfs_format_mac(str, ha->my_mac,
+                                     MAC_ADDR_LEN);
+               break;
+       default:
+               rc = -ENOSYS;
+               break;
+       }
+       return rc;
+}
+
+static mode_t qla4xxx_eth_get_attr_visibility(void *data, int type)
+{
+       int rc;
+
+       switch (type) {
+       case ISCSI_BOOT_ETH_FLAGS:
+       case ISCSI_BOOT_ETH_MAC:
+       case ISCSI_BOOT_ETH_INDEX:
+               rc = S_IRUGO;
+               break;
+       default:
+               rc = 0;
+               break;
+       }
+       return rc;
+}
+
+static ssize_t qla4xxx_show_boot_ini_info(void *data, int type, char *buf)
+{
+       struct scsi_qla_host *ha = data;
+       char *str = buf;
+       int rc;
+
+       switch (type) {
+       case ISCSI_BOOT_INI_INITIATOR_NAME:
+               rc = sprintf(str, "%s\n", ha->name_string);
+               break;
+       default:
+               rc = -ENOSYS;
+               break;
+       }
+       return rc;
+}
+
+static mode_t qla4xxx_ini_get_attr_visibility(void *data, int type)
+{
+       int rc;
+
+       switch (type) {
+       case ISCSI_BOOT_INI_INITIATOR_NAME:
+               rc = S_IRUGO;
+               break;
+       default:
+               rc = 0;
+               break;
+       }
+       return rc;
+}
+
+static ssize_t
+qla4xxx_show_boot_tgt_info(struct ql4_boot_session_info *boot_sess, int type,
+                          char *buf)
+{
+       struct ql4_conn_info *boot_conn = &boot_sess->conn_list[0];
+       char *str = buf;
+       int rc;
+
+       switch (type) {
+       case ISCSI_BOOT_TGT_NAME:
+               rc = sprintf(buf, "%s\n", (char *)&boot_sess->target_name);
+               break;
+       case ISCSI_BOOT_TGT_IP_ADDR:
+               if (boot_sess->conn_list[0].dest_ipaddr.ip_type == 0x1)
+                       rc = sprintf(buf, "%pI4\n",
+                                    &boot_conn->dest_ipaddr.ip_address);
+               else
+                       rc = sprintf(str, "%pI6\n",
+                                    &boot_conn->dest_ipaddr.ip_address);
+               break;
+       case ISCSI_BOOT_TGT_PORT:
+                       rc = sprintf(str, "%d\n", boot_conn->dest_port);
+               break;
+       case ISCSI_BOOT_TGT_CHAP_NAME:
+               rc = sprintf(str,  "%.*s\n",
+                            boot_conn->chap.target_chap_name_length,
+                            (char *)&boot_conn->chap.target_chap_name);
+               break;
+       case ISCSI_BOOT_TGT_CHAP_SECRET:
+               rc = sprintf(str,  "%.*s\n",
+                            boot_conn->chap.target_secret_length,
+                            (char *)&boot_conn->chap.target_secret);
+               break;
+       case ISCSI_BOOT_TGT_REV_CHAP_NAME:
+               rc = sprintf(str,  "%.*s\n",
+                            boot_conn->chap.intr_chap_name_length,
+                            (char *)&boot_conn->chap.intr_chap_name);
+               break;
+       case ISCSI_BOOT_TGT_REV_CHAP_SECRET:
+               rc = sprintf(str,  "%.*s\n",
+                            boot_conn->chap.intr_secret_length,
+                            (char *)&boot_conn->chap.intr_secret);
+               break;
+       case ISCSI_BOOT_TGT_FLAGS:
+               rc = sprintf(str, "%d\n", SYSFS_FLAG_FW_SEL_BOOT);
+               break;
+       case ISCSI_BOOT_TGT_NIC_ASSOC:
+               rc = sprintf(str, "0\n");
+               break;
+       default:
+               rc = -ENOSYS;
+               break;
+       }
+       return rc;
+}
+
+static ssize_t qla4xxx_show_boot_tgt_pri_info(void *data, int type, char *buf)
+{
+       struct scsi_qla_host *ha = data;
+       struct ql4_boot_session_info *boot_sess = &(ha->boot_tgt.boot_pri_sess);
+
+       return qla4xxx_show_boot_tgt_info(boot_sess, type, buf);
+}
+
+static ssize_t qla4xxx_show_boot_tgt_sec_info(void *data, int type, char *buf)
+{
+       struct scsi_qla_host *ha = data;
+       struct ql4_boot_session_info *boot_sess = &(ha->boot_tgt.boot_sec_sess);
+
+       return qla4xxx_show_boot_tgt_info(boot_sess, type, buf);
+}
+
+static mode_t qla4xxx_tgt_get_attr_visibility(void *data, int type)
+{
+       int rc;
+
+       switch (type) {
+       case ISCSI_BOOT_TGT_NAME:
+       case ISCSI_BOOT_TGT_IP_ADDR:
+       case ISCSI_BOOT_TGT_PORT:
+       case ISCSI_BOOT_TGT_CHAP_NAME:
+       case ISCSI_BOOT_TGT_CHAP_SECRET:
+       case ISCSI_BOOT_TGT_REV_CHAP_NAME:
+       case ISCSI_BOOT_TGT_REV_CHAP_SECRET:
+       case ISCSI_BOOT_TGT_NIC_ASSOC:
+       case ISCSI_BOOT_TGT_FLAGS:
+               rc = S_IRUGO;
+               break;
+       default:
+               rc = 0;
+               break;
+       }
+       return rc;
+}
+
+static void qla4xxx_boot_release(void *data)
+{
+       struct scsi_qla_host *ha = data;
+
+       scsi_host_put(ha->host);
+}
+
+static int get_fw_boot_info(struct scsi_qla_host *ha, uint16_t ddb_index[])
+{
+       dma_addr_t buf_dma;
+       uint32_t addr, pri_addr, sec_addr;
+       uint32_t offset;
+       uint16_t func_num;
+       uint8_t val;
+       uint8_t *buf = NULL;
+       size_t size = 13 * sizeof(uint8_t);
+       int ret = QLA_SUCCESS;
+
+       func_num = PCI_FUNC(ha->pdev->devfn);
+
+       ql4_printk(KERN_INFO, ha, "%s: Get FW boot info for 0x%x func %d\n",
+                  __func__, ha->pdev->device, func_num);
+
+       if (is_qla40XX(ha)) {
+               if (func_num == 1) {
+                       addr = NVRAM_PORT0_BOOT_MODE;
+                       pri_addr = NVRAM_PORT0_BOOT_PRI_TGT;
+                       sec_addr = NVRAM_PORT0_BOOT_SEC_TGT;
+               } else if (func_num == 3) {
+                       addr = NVRAM_PORT1_BOOT_MODE;
+                       pri_addr = NVRAM_PORT1_BOOT_PRI_TGT;
+                       sec_addr = NVRAM_PORT1_BOOT_SEC_TGT;
+               } else {
+                       ret = QLA_ERROR;
+                       goto exit_boot_info;
+               }
+
+               /* Check Boot Mode */
+               val = rd_nvram_byte(ha, addr);
+               if (!(val & 0x07)) {
+                       DEBUG2(ql4_printk(KERN_ERR, ha,
+                                         "%s: Failed Boot options : 0x%x\n",
+                                         __func__, val));
+                       ret = QLA_ERROR;
+                       goto exit_boot_info;
+               }
+
+               /* get primary valid target index */
+               val = rd_nvram_byte(ha, pri_addr);
+               if (val & BIT_7)
+                       ddb_index[0] = (val & 0x7f);
+
+               /* get secondary valid target index */
+               val = rd_nvram_byte(ha, sec_addr);
+               if (val & BIT_7)
+                       ddb_index[1] = (val & 0x7f);
+
+       } else if (is_qla8022(ha)) {
+               buf = dma_alloc_coherent(&ha->pdev->dev, size,
+                                        &buf_dma, GFP_KERNEL);
+               if (!buf) {
+                       DEBUG2(ql4_printk(KERN_ERR, ha,
+                                         "%s: Unable to allocate dma buffer\n",
+                                          __func__));
+                       ret = QLA_ERROR;
+                       goto exit_boot_info;
+               }
+
+               if (ha->port_num == 0)
+                       offset = BOOT_PARAM_OFFSET_PORT0;
+               else if (ha->port_num == 1)
+                       offset = BOOT_PARAM_OFFSET_PORT1;
+               else {
+                       ret = QLA_ERROR;
+                       goto exit_boot_info_free;
+               }
+               addr = FLASH_RAW_ACCESS_ADDR + (ha->hw.flt_iscsi_param * 4) +
+                      offset;
+               if (qla4xxx_get_flash(ha, buf_dma, addr,
+                                     13 * sizeof(uint8_t)) != QLA_SUCCESS) {
+                       DEBUG2(ql4_printk(KERN_ERR, ha, "scsi%ld: %s: Get Flash"
+                                         "failed\n", ha->host_no, __func__));
+                       ret = QLA_ERROR;
+                       goto exit_boot_info_free;
+               }
+               /* Check Boot Mode */
+               if (!(buf[1] & 0x07)) {
+                       DEBUG2(ql4_printk(KERN_INFO, ha,
+                                         "Failed: Boot options : 0x%x\n",
+                                         buf[1]));
+                       ret = QLA_ERROR;
+                       goto exit_boot_info_free;
+               }
+
+               /* get primary valid target index */
+               if (buf[2] & BIT_7)
+                       ddb_index[0] = buf[2] & 0x7f;
+
+               /* get secondary valid target index */
+               if (buf[11] & BIT_7)
+                       ddb_index[1] = buf[11] & 0x7f;
+       } else {
+               ret = QLA_ERROR;
+               goto exit_boot_info;
+       }
+
+       DEBUG2(ql4_printk(KERN_INFO, ha, "%s: Primary target ID %d, Secondary"
+                         " target ID %d\n", __func__, ddb_index[0],
+                         ddb_index[1]));
+
+exit_boot_info_free:
+       dma_free_coherent(&ha->pdev->dev, size, buf, buf_dma);
+exit_boot_info:
+       return ret;
+}
+
+/**
+ * qla4xxx_get_bidi_chap - Get a BIDI CHAP user and password
+ * @ha: pointer to adapter structure
+ * @username: CHAP username to be returned
+ * @password: CHAP password to be returned
+ *
+ * If a boot entry has BIDI CHAP enabled then we need to set the BIDI CHAP
+ * user and password in the sysfs entry in /sys/firmware/iscsi_boot#/.
+ * So from the CHAP cache find the first BIDI CHAP entry and set it
+ * to the boot record in sysfs.
+ **/
+static int qla4xxx_get_bidi_chap(struct scsi_qla_host *ha, char *username,
+                           char *password)
+{
+       int i, ret = -EINVAL;
+       int max_chap_entries = 0;
+       struct ql4_chap_table *chap_table;
+
+       if (is_qla8022(ha))
+               max_chap_entries = (ha->hw.flt_chap_size / 2) /
+                                               sizeof(struct ql4_chap_table);
+       else
+               max_chap_entries = MAX_CHAP_ENTRIES_40XX;
+
+       if (!ha->chap_list) {
+               ql4_printk(KERN_ERR, ha, "Do not have CHAP table cache\n");
+               return ret;
+       }
+
+       mutex_lock(&ha->chap_sem);
+       for (i = 0; i < max_chap_entries; i++) {
+               chap_table = (struct ql4_chap_table *)ha->chap_list + i;
+               if (chap_table->cookie !=
+                   __constant_cpu_to_le16(CHAP_VALID_COOKIE)) {
+                       continue;
+               }
+
+               if (chap_table->flags & BIT_7) /* local */
+                       continue;
+
+               if (!(chap_table->flags & BIT_6)) /* Not BIDI */
+                       continue;
+
+               strncpy(password, chap_table->secret, QL4_CHAP_MAX_SECRET_LEN);
+               strncpy(username, chap_table->name, QL4_CHAP_MAX_NAME_LEN);
+               ret = 0;
+               break;
+       }
+       mutex_unlock(&ha->chap_sem);
+
+       return ret;
+}
+
+
+static int qla4xxx_get_boot_target(struct scsi_qla_host *ha,
+                                  struct ql4_boot_session_info *boot_sess,
+                                  uint16_t ddb_index)
+{
+       struct ql4_conn_info *boot_conn = &boot_sess->conn_list[0];
+       struct dev_db_entry *fw_ddb_entry;
+       dma_addr_t fw_ddb_entry_dma;
+       uint16_t idx;
+       uint16_t options;
+       int ret = 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__));
+               ret = QLA_ERROR;
+               return ret;
+       }
+
+       if (qla4xxx_bootdb_by_index(ha, fw_ddb_entry,
+                                  fw_ddb_entry_dma, ddb_index)) {
+               DEBUG2(ql4_printk(KERN_ERR, ha,
+                                 "%s: Flash DDB read Failed\n", __func__));
+               ret = QLA_ERROR;
+               goto exit_boot_target;
+       }
+
+       /* Update target name and IP from DDB */
+       memcpy(boot_sess->target_name, fw_ddb_entry->iscsi_name,
+              min(sizeof(boot_sess->target_name),
+                  sizeof(fw_ddb_entry->iscsi_name)));
+
+       options = le16_to_cpu(fw_ddb_entry->options);
+       if (options & DDB_OPT_IPV6_DEVICE) {
+               memcpy(&boot_conn->dest_ipaddr.ip_address,
+                      &fw_ddb_entry->ip_addr[0], IPv6_ADDR_LEN);
+       } else {
+               boot_conn->dest_ipaddr.ip_type = 0x1;
+               memcpy(&boot_conn->dest_ipaddr.ip_address,
+                      &fw_ddb_entry->ip_addr[0], IP_ADDR_LEN);
+       }
+
+       boot_conn->dest_port = le16_to_cpu(fw_ddb_entry->port);
+
+       /* update chap information */
+       idx = __le16_to_cpu(fw_ddb_entry->chap_tbl_idx);
+
+       if (BIT_7 & le16_to_cpu(fw_ddb_entry->iscsi_options))   {
+
+               DEBUG2(ql4_printk(KERN_INFO, ha, "Setting chap\n"));
+
+               ret = qla4xxx_get_chap(ha, (char *)&boot_conn->chap.
+                                      target_chap_name,
+                                      (char *)&boot_conn->chap.target_secret,
+                                      idx);
+               if (ret) {
+                       ql4_printk(KERN_ERR, ha, "Failed to set chap\n");
+                       ret = QLA_ERROR;
+                       goto exit_boot_target;
+               }
+
+               boot_conn->chap.target_chap_name_length = QL4_CHAP_MAX_NAME_LEN;
+               boot_conn->chap.target_secret_length = QL4_CHAP_MAX_SECRET_LEN;
+       }
+
+       if (BIT_4 & le16_to_cpu(fw_ddb_entry->iscsi_options)) {
+
+               DEBUG2(ql4_printk(KERN_INFO, ha, "Setting BIDI chap\n"));
+
+               ret = qla4xxx_get_bidi_chap(ha,
+                                   (char *)&boot_conn->chap.intr_chap_name,
+                                   (char *)&boot_conn->chap.intr_secret);
+
+               if (ret) {
+                       ql4_printk(KERN_ERR, ha, "Failed to set BIDI chap\n");
+                       ret = QLA_ERROR;
+                       goto exit_boot_target;
+               }
+
+               boot_conn->chap.intr_chap_name_length = QL4_CHAP_MAX_NAME_LEN;
+               boot_conn->chap.intr_secret_length = QL4_CHAP_MAX_SECRET_LEN;
+       }
+
+exit_boot_target:
+       dma_free_coherent(&ha->pdev->dev, sizeof(*fw_ddb_entry),
+                         fw_ddb_entry, fw_ddb_entry_dma);
+       return ret;
+}
+
+static int qla4xxx_get_boot_info(struct scsi_qla_host *ha)
+{
+       uint16_t ddb_index[2];
+       int ret = QLA_ERROR;
+       int rval;
+
+       memset(ddb_index, 0, sizeof(ddb_index));
+       ddb_index[0] = 0xffff;
+       ddb_index[1] = 0xffff;
+       ret = get_fw_boot_info(ha, ddb_index);
+       if (ret != QLA_SUCCESS) {
+               DEBUG2(ql4_printk(KERN_ERR, ha,
+                                 "%s: Failed to set boot info.\n", __func__));
+               return ret;
+       }
+
+       if (ddb_index[0] == 0xffff)
+               goto sec_target;
+
+       rval = qla4xxx_get_boot_target(ha, &(ha->boot_tgt.boot_pri_sess),
+                                     ddb_index[0]);
+       if (rval != QLA_SUCCESS) {
+               DEBUG2(ql4_printk(KERN_ERR, ha, "%s: Failed to get "
+                                 "primary target\n", __func__));
+       } else
+               ret = QLA_SUCCESS;
+
+sec_target:
+       if (ddb_index[1] == 0xffff)
+               goto exit_get_boot_info;
+
+       rval = qla4xxx_get_boot_target(ha, &(ha->boot_tgt.boot_sec_sess),
+                                     ddb_index[1]);
+       if (rval != QLA_SUCCESS) {
+               DEBUG2(ql4_printk(KERN_ERR, ha, "%s: Failed to get "
+                                 "secondary target\n", __func__));
+       } else
+               ret = QLA_SUCCESS;
+
+exit_get_boot_info:
+       return ret;
+}
+
+static int qla4xxx_setup_boot_info(struct scsi_qla_host *ha)
+{
+       struct iscsi_boot_kobj *boot_kobj;
+
+       if (qla4xxx_get_boot_info(ha) != QLA_SUCCESS)
+               return 0;
+
+       ha->boot_kset = iscsi_boot_create_host_kset(ha->host->host_no);
+       if (!ha->boot_kset)
+               goto kset_free;
+
+       if (!scsi_host_get(ha->host))
+               goto kset_free;
+       boot_kobj = iscsi_boot_create_target(ha->boot_kset, 0, ha,
+                                            qla4xxx_show_boot_tgt_pri_info,
+                                            qla4xxx_tgt_get_attr_visibility,
+                                            qla4xxx_boot_release);
+       if (!boot_kobj)
+               goto put_host;
+
+       if (!scsi_host_get(ha->host))
+               goto kset_free;
+       boot_kobj = iscsi_boot_create_target(ha->boot_kset, 1, ha,
+                                            qla4xxx_show_boot_tgt_sec_info,
+                                            qla4xxx_tgt_get_attr_visibility,
+                                            qla4xxx_boot_release);
+       if (!boot_kobj)
+               goto put_host;
+
+       if (!scsi_host_get(ha->host))
+               goto kset_free;
+       boot_kobj = iscsi_boot_create_initiator(ha->boot_kset, 0, ha,
+                                              qla4xxx_show_boot_ini_info,
+                                              qla4xxx_ini_get_attr_visibility,
+                                              qla4xxx_boot_release);
+       if (!boot_kobj)
+               goto put_host;
+
+       if (!scsi_host_get(ha->host))
+               goto kset_free;
+       boot_kobj = iscsi_boot_create_ethernet(ha->boot_kset, 0, ha,
+                                              qla4xxx_show_boot_eth_info,
+                                              qla4xxx_eth_get_attr_visibility,
+                                              qla4xxx_boot_release);
+       if (!boot_kobj)
+               goto put_host;
+
+       return 0;
+
+put_host:
+       scsi_host_put(ha->host);
+kset_free:
+       iscsi_boot_destroy_kset(ha->boot_kset);
+       return -ENOMEM;
+}
+
+
+/**
+ * qla4xxx_create chap_list - Create CHAP list from FLASH
+ * @ha: pointer to adapter structure
+ *
+ * Read flash and make a list of CHAP entries, during login when a CHAP entry
+ * is received, it will be checked in this list. If entry exist then the CHAP
+ * entry index is set in the DDB. If CHAP entry does not exist in this list
+ * then a new entry is added in FLASH in CHAP table and the index obtained is
+ * used in the DDB.
+ **/
+static void qla4xxx_create_chap_list(struct scsi_qla_host *ha)
+{
+       int rval = 0;
+       uint8_t *chap_flash_data = NULL;
+       uint32_t offset;
+       dma_addr_t chap_dma;
+       uint32_t chap_size = 0;
+
+       if (is_qla40XX(ha))
+               chap_size = MAX_CHAP_ENTRIES_40XX  *
+                                       sizeof(struct ql4_chap_table);
+       else    /* Single region contains CHAP info for both
+                * ports which is divided into half for each port.
+                */
+               chap_size = ha->hw.flt_chap_size / 2;
+
+       chap_flash_data = dma_alloc_coherent(&ha->pdev->dev, chap_size,
+                                         &chap_dma, GFP_KERNEL);
+       if (!chap_flash_data) {
+               ql4_printk(KERN_ERR, ha, "No memory for chap_flash_data\n");
+               return;
+       }
+       if (is_qla40XX(ha))
+               offset = FLASH_CHAP_OFFSET;
+       else {
+               offset = FLASH_RAW_ACCESS_ADDR + (ha->hw.flt_region_chap << 2);
+               if (ha->port_num == 1)
+                       offset += chap_size;
+       }
+
+       rval = qla4xxx_get_flash(ha, chap_dma, offset, chap_size);
+       if (rval != QLA_SUCCESS)
+               goto exit_chap_list;
+
+       if (ha->chap_list == NULL)
+               ha->chap_list = vmalloc(chap_size);
+       if (ha->chap_list == NULL) {
+               ql4_printk(KERN_ERR, ha, "No memory for ha->chap_list\n");
+               goto exit_chap_list;
+       }
+
+       memcpy(ha->chap_list, chap_flash_data, chap_size);
+
+exit_chap_list:
+       dma_free_coherent(&ha->pdev->dev, chap_size,
+                       chap_flash_data, chap_dma);
+       return;
+}
+
 /**
  * qla4xxx_probe_adapter - callback function to probe HBA
  * @pdev: pointer to pci_dev structure
@@ -2628,7 +3254,9 @@ static int __devinit qla4xxx_probe_adapter(struct pci_dev *pdev,
        INIT_LIST_HEAD(&ha->free_srb_q);
 
        mutex_init(&ha->mbox_sem);
+       mutex_init(&ha->chap_sem);
        init_completion(&ha->mbx_intr_comp);
+       init_completion(&ha->disable_acb_comp);
 
        spin_lock_init(&ha->hardware_lock);
 
@@ -2758,6 +3386,12 @@ static int __devinit qla4xxx_probe_adapter(struct pci_dev *pdev,
               ha->host_no, ha->firmware_version[0], ha->firmware_version[1],
               ha->patch_number, ha->build_number);
 
+       qla4xxx_create_chap_list(ha);
+
+       if (qla4xxx_setup_boot_info(ha))
+               ql4_printk(KERN_ERR, ha, "%s:ISCSI boot info setup failed\n",
+                          __func__);
+
        qla4xxx_create_ifaces(ha);
        return 0;
 
@@ -2831,6 +3465,9 @@ static void __devexit qla4xxx_remove_adapter(struct pci_dev *pdev)
        /* destroy iface from sysfs */
        qla4xxx_destroy_ifaces(ha);
 
+       if (ha->boot_kset)
+               iscsi_boot_destroy_kset(ha->boot_kset);
+
        scsi_remove_host(ha->host);
 
        qla4xxx_free_adapter(ha);
@@ -3250,6 +3887,110 @@ static int qla4xxx_eh_host_reset(struct scsi_cmnd *cmd)
        return return_status;
 }
 
+static int qla4xxx_context_reset(struct scsi_qla_host *ha)
+{
+       uint32_t mbox_cmd[MBOX_REG_COUNT];
+       uint32_t mbox_sts[MBOX_REG_COUNT];
+       struct addr_ctrl_blk_def *acb = NULL;
+       uint32_t acb_len = sizeof(struct addr_ctrl_blk_def);
+       int rval = QLA_SUCCESS;
+       dma_addr_t acb_dma;
+
+       acb = dma_alloc_coherent(&ha->pdev->dev,
+                                sizeof(struct addr_ctrl_blk_def),
+                                &acb_dma, GFP_KERNEL);
+       if (!acb) {
+               ql4_printk(KERN_ERR, ha, "%s: Unable to alloc acb\n",
+                          __func__);
+               rval = -ENOMEM;
+               goto exit_port_reset;
+       }
+
+       memset(acb, 0, acb_len);
+
+       rval = qla4xxx_get_acb(ha, acb_dma, PRIMARI_ACB, acb_len);
+       if (rval != QLA_SUCCESS) {
+               rval = -EIO;
+               goto exit_free_acb;
+       }
+
+       rval = qla4xxx_disable_acb(ha);
+       if (rval != QLA_SUCCESS) {
+               rval = -EIO;
+               goto exit_free_acb;
+       }
+
+       wait_for_completion_timeout(&ha->disable_acb_comp,
+                                   DISABLE_ACB_TOV * HZ);
+
+       rval = qla4xxx_set_acb(ha, &mbox_cmd[0], &mbox_sts[0], acb_dma);
+       if (rval != QLA_SUCCESS) {
+               rval = -EIO;
+               goto exit_free_acb;
+       }
+
+exit_free_acb:
+       dma_free_coherent(&ha->pdev->dev, sizeof(struct addr_ctrl_blk_def),
+                         acb, acb_dma);
+exit_port_reset:
+       DEBUG2(ql4_printk(KERN_INFO, ha, "%s %s\n", __func__,
+                         rval == QLA_SUCCESS ? "SUCCEEDED" : "FAILED"));
+       return rval;
+}
+
+static int qla4xxx_host_reset(struct Scsi_Host *shost, int reset_type)
+{
+       struct scsi_qla_host *ha = to_qla_host(shost);
+       int rval = QLA_SUCCESS;
+
+       if (ql4xdontresethba) {
+               DEBUG2(ql4_printk(KERN_INFO, ha, "%s: Don't Reset HBA\n",
+                                 __func__));
+               rval = -EPERM;
+               goto exit_host_reset;
+       }
+
+       rval = qla4xxx_wait_for_hba_online(ha);
+       if (rval != QLA_SUCCESS) {
+               DEBUG2(ql4_printk(KERN_INFO, ha, "%s: Unable to reset host "
+                                 "adapter\n", __func__));
+               rval = -EIO;
+               goto exit_host_reset;
+       }
+
+       if (test_bit(DPC_RESET_HA, &ha->dpc_flags))
+               goto recover_adapter;
+
+       switch (reset_type) {
+       case SCSI_ADAPTER_RESET:
+               set_bit(DPC_RESET_HA, &ha->dpc_flags);
+               break;
+       case SCSI_FIRMWARE_RESET:
+               if (!test_bit(DPC_RESET_HA, &ha->dpc_flags)) {
+                       if (is_qla8022(ha))
+                               /* set firmware context reset */
+                               set_bit(DPC_RESET_HA_FW_CONTEXT,
+                                       &ha->dpc_flags);
+                       else {
+                               rval = qla4xxx_context_reset(ha);
+                               goto exit_host_reset;
+                       }
+               }
+               break;
+       }
+
+recover_adapter:
+       rval = qla4xxx_recover_adapter(ha);
+       if (rval != QLA_SUCCESS) {
+               DEBUG2(ql4_printk(KERN_INFO, ha, "%s: recover adapter fail\n",
+                                 __func__));
+               rval = -EIO;
+       }
+
+exit_host_reset:
+       return rval;
+}
+
 /* PCI AER driver recovers from all correctable errors w/o
  * driver intervention. For uncorrectable errors PCI AER
  * driver calls the following device driver's callbacks