Merge remote-tracking branch 'scsi/for-next'
[deliverable/linux.git] / drivers / scsi / be2iscsi / be_main.c
index f05e7737107d2a0fced40581b7f34bad70ca2820..6a6906f847dbba07ef38149a9912563d48ba3d82 100644 (file)
@@ -1,5 +1,5 @@
 /**
- * Copyright (C) 2005 - 2015 Emulex
+ * Copyright (C) 2005 - 2016 Broadcom
  * All rights reserved.
  *
  * This program is free software; you can redistribute it and/or
@@ -7,10 +7,10 @@
  * as published by the Free Software Foundation.  The full GNU General
  * Public License is included in this distribution in the file called COPYING.
  *
- * Written by: Jayamohan Kallickal (jayamohan.kallickal@avagotech.com)
+ * Written by: Jayamohan Kallickal (jayamohan.kallickal@broadcom.com)
  *
  * Contact Information:
- * linux-drivers@avagotech.com
+ * linux-drivers@broadcom.com
  *
  * Emulex
  * 3333 Susan Street
@@ -374,170 +374,6 @@ static int beiscsi_eh_device_reset(struct scsi_cmnd *sc)
        return iscsi_eh_device_reset(sc);
 }
 
-static ssize_t beiscsi_show_boot_tgt_info(void *data, int type, char *buf)
-{
-       struct beiscsi_hba *phba = data;
-       struct mgmt_session_info *boot_sess = &phba->boot_sess;
-       struct mgmt_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",
-                           (int)strlen(boot_sess->target_name),
-                           (char *)&boot_sess->target_name);
-               break;
-       case ISCSI_BOOT_TGT_IP_ADDR:
-               if (boot_conn->dest_ipaddr.ip_type == 0x1)
-                       rc = sprintf(buf, "%pI4\n",
-                               (char *)&boot_conn->dest_ipaddr.addr);
-               else
-                       rc = sprintf(str, "%pI6\n",
-                               (char *)&boot_conn->dest_ipaddr.addr);
-               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->negotiated_login_options.auth_data.chap.
-                            target_chap_name_length,
-                            (char *)&boot_conn->negotiated_login_options.
-                            auth_data.chap.target_chap_name);
-               break;
-       case ISCSI_BOOT_TGT_CHAP_SECRET:
-               rc = sprintf(str,  "%.*s\n",
-                            boot_conn->negotiated_login_options.auth_data.chap.
-                            target_secret_length,
-                            (char *)&boot_conn->negotiated_login_options.
-                            auth_data.chap.target_secret);
-               break;
-       case ISCSI_BOOT_TGT_REV_CHAP_NAME:
-               rc = sprintf(str,  "%.*s\n",
-                            boot_conn->negotiated_login_options.auth_data.chap.
-                            intr_chap_name_length,
-                            (char *)&boot_conn->negotiated_login_options.
-                            auth_data.chap.intr_chap_name);
-               break;
-       case ISCSI_BOOT_TGT_REV_CHAP_SECRET:
-               rc = sprintf(str,  "%.*s\n",
-                            boot_conn->negotiated_login_options.auth_data.chap.
-                            intr_secret_length,
-                            (char *)&boot_conn->negotiated_login_options.
-                            auth_data.chap.intr_secret);
-               break;
-       case ISCSI_BOOT_TGT_FLAGS:
-               rc = sprintf(str, "2\n");
-               break;
-       case ISCSI_BOOT_TGT_NIC_ASSOC:
-               rc = sprintf(str, "0\n");
-               break;
-       default:
-               rc = -ENOSYS;
-               break;
-       }
-       return rc;
-}
-
-static ssize_t beiscsi_show_boot_ini_info(void *data, int type, char *buf)
-{
-       struct beiscsi_hba *phba = data;
-       char *str = buf;
-       int rc;
-
-       switch (type) {
-       case ISCSI_BOOT_INI_INITIATOR_NAME:
-               rc = sprintf(str, "%s\n", phba->boot_sess.initiator_iscsiname);
-               break;
-       default:
-               rc = -ENOSYS;
-               break;
-       }
-       return rc;
-}
-
-static ssize_t beiscsi_show_boot_eth_info(void *data, int type, char *buf)
-{
-       struct beiscsi_hba *phba = data;
-       char *str = buf;
-       int rc;
-
-       switch (type) {
-       case ISCSI_BOOT_ETH_FLAGS:
-               rc = sprintf(str, "2\n");
-               break;
-       case ISCSI_BOOT_ETH_INDEX:
-               rc = sprintf(str, "0\n");
-               break;
-       case ISCSI_BOOT_ETH_MAC:
-               rc  = beiscsi_get_macaddr(str, phba);
-               break;
-       default:
-               rc = -ENOSYS;
-               break;
-       }
-       return rc;
-}
-
-
-static umode_t beiscsi_tgt_get_attr_visibility(void *data, int type)
-{
-       umode_t 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 umode_t beiscsi_ini_get_attr_visibility(void *data, int type)
-{
-       umode_t rc;
-
-       switch (type) {
-       case ISCSI_BOOT_INI_INITIATOR_NAME:
-               rc = S_IRUGO;
-               break;
-       default:
-               rc = 0;
-               break;
-       }
-       return rc;
-}
-
-
-static umode_t beiscsi_eth_get_attr_visibility(void *data, int type)
-{
-       umode_t 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;
-}
-
 /*------------------- PCI Driver operations and data ----------------- */
 static const struct pci_device_id beiscsi_pci_id_table[] = {
        { PCI_DEVICE(BE_VENDOR_ID, BE_DEVICE_ID1) },
@@ -850,12 +686,11 @@ static void hwi_ring_eq_db(struct beiscsi_hba *phba,
 static irqreturn_t be_isr_mcc(int irq, void *dev_id)
 {
        struct beiscsi_hba *phba;
-       struct be_eq_entry *eqe = NULL;
+       struct be_eq_entry *eqe;
        struct be_queue_info *eq;
        struct be_queue_info *mcc;
-       unsigned int num_eq_processed;
+       unsigned int mcc_events;
        struct be_eq_obj *pbe_eq;
-       unsigned long flags;
 
        pbe_eq = dev_id;
        eq = &pbe_eq->q;
@@ -863,27 +698,23 @@ static irqreturn_t be_isr_mcc(int irq, void *dev_id)
        mcc = &phba->ctrl.mcc_obj.cq;
        eqe = queue_tail_node(eq);
 
-       num_eq_processed = 0;
-
+       mcc_events = 0;
        while (eqe->dw[offsetof(struct amap_eq_entry, valid) / 32]
                                & EQE_VALID_MASK) {
                if (((eqe->dw[offsetof(struct amap_eq_entry,
                     resource_id) / 32] &
                     EQE_RESID_MASK) >> 16) == mcc->id) {
-                       spin_lock_irqsave(&phba->isr_lock, flags);
-                       pbe_eq->todo_mcc_cq = true;
-                       spin_unlock_irqrestore(&phba->isr_lock, flags);
+                       mcc_events++;
                }
                AMAP_SET_BITS(struct amap_eq_entry, valid, eqe, 0);
                queue_tail_inc(eq);
                eqe = queue_tail_node(eq);
-               num_eq_processed++;
        }
-       if (pbe_eq->todo_mcc_cq)
-               queue_work(phba->wq, &pbe_eq->work_cqs);
-       if (num_eq_processed)
-               hwi_ring_eq_db(phba, eq->id, 1, num_eq_processed, 1, 1);
 
+       if (mcc_events) {
+               queue_work(phba->wq, &pbe_eq->mcc_work);
+               hwi_ring_eq_db(phba, eq->id, 1, mcc_events, 1, 1);
+       }
        return IRQ_HANDLED;
 }
 
@@ -902,7 +733,6 @@ static irqreturn_t be_isr_msix(int irq, void *dev_id)
        eq = &pbe_eq->q;
 
        phba = pbe_eq->phba;
-
        /* disable interrupt till iopoll completes */
        hwi_ring_eq_db(phba, eq->id, 1, 0, 0, 1);
        irq_poll_sched(&pbe_eq->iopoll);
@@ -920,14 +750,13 @@ static irqreturn_t be_isr(int irq, void *dev_id)
        struct beiscsi_hba *phba;
        struct hwi_controller *phwi_ctrlr;
        struct hwi_context_memory *phwi_context;
-       struct be_eq_entry *eqe = NULL;
+       struct be_eq_entry *eqe;
        struct be_queue_info *eq;
        struct be_queue_info *mcc;
-       unsigned long flags, index;
-       unsigned int num_mcceq_processed, num_ioeq_processed;
+       unsigned int mcc_events, io_events;
        struct be_ctrl_info *ctrl;
        struct be_eq_obj *pbe_eq;
-       int isr;
+       int isr, rearm;
 
        phba = dev_id;
        ctrl = &phba->ctrl;
@@ -942,44 +771,35 @@ static irqreturn_t be_isr(int irq, void *dev_id)
 
        eq = &phwi_context->be_eq[0].q;
        mcc = &phba->ctrl.mcc_obj.cq;
-       index = 0;
        eqe = queue_tail_node(eq);
 
-       num_ioeq_processed = 0;
-       num_mcceq_processed = 0;
+       io_events = 0;
+       mcc_events = 0;
        while (eqe->dw[offsetof(struct amap_eq_entry, valid) / 32]
                                & EQE_VALID_MASK) {
                if (((eqe->dw[offsetof(struct amap_eq_entry,
-                    resource_id) / 32] &
-                    EQE_RESID_MASK) >> 16) == mcc->id) {
-                       spin_lock_irqsave(&phba->isr_lock, flags);
-                       pbe_eq->todo_mcc_cq = true;
-                       spin_unlock_irqrestore(&phba->isr_lock, flags);
-                       num_mcceq_processed++;
-               } else {
-                       irq_poll_sched(&pbe_eq->iopoll);
-                       num_ioeq_processed++;
-               }
+                     resource_id) / 32] & EQE_RESID_MASK) >> 16) == mcc->id)
+                       mcc_events++;
+               else
+                       io_events++;
                AMAP_SET_BITS(struct amap_eq_entry, valid, eqe, 0);
                queue_tail_inc(eq);
                eqe = queue_tail_node(eq);
        }
-       if (num_ioeq_processed || num_mcceq_processed) {
-               if (pbe_eq->todo_mcc_cq)
-                       queue_work(phba->wq, &pbe_eq->work_cqs);
-
-               if ((num_mcceq_processed) && (!num_ioeq_processed))
-                       hwi_ring_eq_db(phba, eq->id, 0,
-                                     (num_ioeq_processed +
-                                      num_mcceq_processed) , 1, 1);
-               else
-                       hwi_ring_eq_db(phba, eq->id, 0,
-                                      (num_ioeq_processed +
-                                       num_mcceq_processed), 0, 1);
-
-               return IRQ_HANDLED;
-       } else
+       if (!io_events && !mcc_events)
                return IRQ_NONE;
+
+       /* no need to rearm if interrupt is only for IOs */
+       rearm = 0;
+       if (mcc_events) {
+               queue_work(phba->wq, &pbe_eq->mcc_work);
+               /* rearm for MCCQ */
+               rearm = 1;
+       }
+       if (io_events)
+               irq_poll_sched(&pbe_eq->iopoll);
+       hwi_ring_eq_db(phba, eq->id, 0, (io_events + mcc_events), rearm, 1);
+       return IRQ_HANDLED;
 }
 
 
@@ -1077,57 +897,6 @@ void hwi_ring_cq_db(struct beiscsi_hba *phba,
        iowrite32(val, phba->db_va + DB_CQ_OFFSET);
 }
 
-static unsigned int
-beiscsi_process_async_pdu(struct beiscsi_conn *beiscsi_conn,
-                         struct beiscsi_hba *phba,
-                         struct pdu_base *ppdu,
-                         unsigned long pdu_len,
-                         void *pbuffer, unsigned long buf_len)
-{
-       struct iscsi_conn *conn = beiscsi_conn->conn;
-       struct iscsi_session *session = conn->session;
-       struct iscsi_task *task;
-       struct beiscsi_io_task *io_task;
-       struct iscsi_hdr *login_hdr;
-
-       switch (ppdu->dw[offsetof(struct amap_pdu_base, opcode) / 32] &
-                                               PDUBASE_OPCODE_MASK) {
-       case ISCSI_OP_NOOP_IN:
-               pbuffer = NULL;
-               buf_len = 0;
-               break;
-       case ISCSI_OP_ASYNC_EVENT:
-               break;
-       case ISCSI_OP_REJECT:
-               WARN_ON(!pbuffer);
-               WARN_ON(!(buf_len == 48));
-               beiscsi_log(phba, KERN_ERR,
-                           BEISCSI_LOG_CONFIG | BEISCSI_LOG_IO,
-                           "BM_%d : In ISCSI_OP_REJECT\n");
-               break;
-       case ISCSI_OP_LOGIN_RSP:
-       case ISCSI_OP_TEXT_RSP:
-               task = conn->login_task;
-               io_task = task->dd_data;
-               login_hdr = (struct iscsi_hdr *)ppdu;
-               login_hdr->itt = io_task->libiscsi_itt;
-               break;
-       default:
-               beiscsi_log(phba, KERN_WARNING,
-                           BEISCSI_LOG_IO | BEISCSI_LOG_CONFIG,
-                           "BM_%d : Unrecognized opcode 0x%x in async msg\n",
-                           (ppdu->
-                            dw[offsetof(struct amap_pdu_base, opcode) / 32]
-                            & PDUBASE_OPCODE_MASK));
-               return 1;
-       }
-
-       spin_lock_bh(&session->back_lock);
-       __iscsi_complete_pdu(conn, (struct iscsi_hdr *)ppdu, pbuffer, buf_len);
-       spin_unlock_bh(&session->back_lock);
-       return 0;
-}
-
 static struct sgl_handle *alloc_io_sgl_handle(struct beiscsi_hba *phba)
 {
        struct sgl_handle *psgl_handle;
@@ -1199,6 +968,9 @@ beiscsi_get_wrb_handle(struct hwi_wrb_context *pwrb_context,
                pwrb_context->alloc_index++;
        spin_unlock_bh(&pwrb_context->wrb_lock);
 
+       if (pwrb_handle)
+               memset(pwrb_handle->pwrb, 0, sizeof(*pwrb_handle->pwrb));
+
        return pwrb_handle;
 }
 
@@ -1440,11 +1212,10 @@ hwi_complete_drvr_msgs(struct beiscsi_conn *beiscsi_conn,
                       struct beiscsi_hba *phba, struct sol_cqe *psol)
 {
        struct hwi_wrb_context *pwrb_context;
-       struct wrb_handle *pwrb_handle = NULL;
+       uint16_t wrb_index, cid, cri_index;
        struct hwi_controller *phwi_ctrlr;
+       struct wrb_handle *pwrb_handle;
        struct iscsi_task *task;
-       struct beiscsi_io_task *io_task;
-       uint16_t wrb_index, cid, cri_index;
 
        phwi_ctrlr = phba->phwi_ctrlr;
        if (is_chip_be2_be3r(phba)) {
@@ -1463,9 +1234,6 @@ hwi_complete_drvr_msgs(struct beiscsi_conn *beiscsi_conn,
        pwrb_context = &phwi_ctrlr->wrb_context[cri_index];
        pwrb_handle = pwrb_context->pwrb_handle_basestd[wrb_index];
        task = pwrb_handle->pio_handle;
-
-       io_task = task->dd_data;
-       memset(io_task->pwrb_handle->pwrb, 0, sizeof(struct iscsi_wrb));
        iscsi_put_task(task);
 }
 
@@ -1614,431 +1382,428 @@ static void hwi_complete_cmd(struct beiscsi_conn *beiscsi_conn,
        spin_unlock_bh(&session->back_lock);
 }
 
-static struct list_head *hwi_get_async_busy_list(struct hwi_async_pdu_context
-                                         *pasync_ctx, unsigned int is_header,
-                                         unsigned int host_write_ptr)
+/**
+ * ASYNC PDUs include
+ * a. Unsolicited NOP-In (target initiated NOP-In)
+ * b. ASYNC Messages
+ * c. Reject PDU
+ * d. Login response
+ * These headers arrive unprocessed by the EP firmware.
+ * iSCSI layer processes them.
+ */
+static unsigned int
+beiscsi_complete_pdu(struct beiscsi_conn *beiscsi_conn,
+               struct pdu_base *phdr, void *pdata, unsigned int dlen)
 {
-       if (is_header)
-               return &pasync_ctx->async_entry[host_write_ptr].
-                   header_busy_list;
-       else
-               return &pasync_ctx->async_entry[host_write_ptr].data_busy_list;
+       struct beiscsi_hba *phba = beiscsi_conn->phba;
+       struct iscsi_conn *conn = beiscsi_conn->conn;
+       struct beiscsi_io_task *io_task;
+       struct iscsi_hdr *login_hdr;
+       struct iscsi_task *task;
+       u8 code;
+
+       code = AMAP_GET_BITS(struct amap_pdu_base, opcode, phdr);
+       switch (code) {
+       case ISCSI_OP_NOOP_IN:
+               pdata = NULL;
+               dlen = 0;
+               break;
+       case ISCSI_OP_ASYNC_EVENT:
+               break;
+       case ISCSI_OP_REJECT:
+               WARN_ON(!pdata);
+               WARN_ON(!(dlen == 48));
+               beiscsi_log(phba, KERN_ERR,
+                           BEISCSI_LOG_CONFIG | BEISCSI_LOG_IO,
+                           "BM_%d : In ISCSI_OP_REJECT\n");
+               break;
+       case ISCSI_OP_LOGIN_RSP:
+       case ISCSI_OP_TEXT_RSP:
+               task = conn->login_task;
+               io_task = task->dd_data;
+               login_hdr = (struct iscsi_hdr *)phdr;
+               login_hdr->itt = io_task->libiscsi_itt;
+               break;
+       default:
+               beiscsi_log(phba, KERN_WARNING,
+                           BEISCSI_LOG_IO | BEISCSI_LOG_CONFIG,
+                           "BM_%d : unrecognized async PDU opcode 0x%x\n",
+                           code);
+               return 1;
+       }
+       __iscsi_complete_pdu(conn, (struct iscsi_hdr *)phdr, pdata, dlen);
+       return 0;
+}
+
+static inline void
+beiscsi_hdl_put_handle(struct hd_async_context *pasync_ctx,
+                        struct hd_async_handle *pasync_handle)
+{
+       if (pasync_handle->is_header) {
+               list_add_tail(&pasync_handle->link,
+                               &pasync_ctx->async_header.free_list);
+               pasync_ctx->async_header.free_entries++;
+       } else {
+               list_add_tail(&pasync_handle->link,
+                               &pasync_ctx->async_data.free_list);
+               pasync_ctx->async_data.free_entries++;
+       }
 }
 
-static struct async_pdu_handle *
-hwi_get_async_handle(struct beiscsi_hba *phba,
-                    struct beiscsi_conn *beiscsi_conn,
-                    struct hwi_async_pdu_context *pasync_ctx,
-                    struct i_t_dpdu_cqe *pdpdu_cqe, unsigned int *pcq_index)
+static struct hd_async_handle *
+beiscsi_hdl_get_handle(struct beiscsi_conn *beiscsi_conn,
+                      struct hd_async_context *pasync_ctx,
+                      struct i_t_dpdu_cqe *pdpdu_cqe)
 {
+       struct beiscsi_hba *phba = beiscsi_conn->phba;
+       struct hd_async_handle *pasync_handle;
        struct be_bus_address phys_addr;
-       struct list_head *pbusy_list;
-       struct async_pdu_handle *pasync_handle = NULL;
-       unsigned char is_header = 0;
-       unsigned int index, dpl;
+       u8 final, error = 0;
+       u16 cid, code, ci;
+       u32 dpl;
 
+       cid = beiscsi_conn->beiscsi_conn_cid;
+       /**
+        * This function is invoked to get the right async_handle structure
+        * from a given DEF PDU CQ entry.
+        *
+        * - index in CQ entry gives the vertical index
+        * - address in CQ entry is the offset where the DMA last ended
+        * - final - no more notifications for this PDU
+        */
        if (is_chip_be2_be3r(phba)) {
                dpl = AMAP_GET_BITS(struct amap_i_t_dpdu_cqe,
                                    dpl, pdpdu_cqe);
-               index = AMAP_GET_BITS(struct amap_i_t_dpdu_cqe,
+               ci = AMAP_GET_BITS(struct amap_i_t_dpdu_cqe,
                                      index, pdpdu_cqe);
+               final = AMAP_GET_BITS(struct amap_i_t_dpdu_cqe,
+                                     final, pdpdu_cqe);
        } else {
                dpl = AMAP_GET_BITS(struct amap_i_t_dpdu_cqe_v2,
                                    dpl, pdpdu_cqe);
-               index = AMAP_GET_BITS(struct amap_i_t_dpdu_cqe_v2,
+               ci = AMAP_GET_BITS(struct amap_i_t_dpdu_cqe_v2,
                                      index, pdpdu_cqe);
+               final = AMAP_GET_BITS(struct amap_i_t_dpdu_cqe_v2,
+                                     final, pdpdu_cqe);
        }
 
-       phys_addr.u.a32.address_lo =
-               (pdpdu_cqe->dw[offsetof(struct amap_i_t_dpdu_cqe,
-                                       db_addr_lo) / 32] - dpl);
-       phys_addr.u.a32.address_hi =
-               pdpdu_cqe->dw[offsetof(struct amap_i_t_dpdu_cqe,
-                                      db_addr_hi) / 32];
-
-       phys_addr.u.a64.address =
-                       *((unsigned long long *)(&phys_addr.u.a64.address));
-
-       switch (pdpdu_cqe->dw[offsetof(struct amap_i_t_dpdu_cqe, code) / 32]
-                       & PDUCQE_CODE_MASK) {
+       /**
+        * DB addr Hi/Lo is same for BE and SKH.
+        * Subtract the dataplacementlength to get to the base.
+        */
+       phys_addr.u.a32.address_lo = AMAP_GET_BITS(struct amap_i_t_dpdu_cqe,
+                                                  db_addr_lo, pdpdu_cqe);
+       phys_addr.u.a32.address_lo -= dpl;
+       phys_addr.u.a32.address_hi = AMAP_GET_BITS(struct amap_i_t_dpdu_cqe,
+                                                  db_addr_hi, pdpdu_cqe);
+
+       code = AMAP_GET_BITS(struct amap_i_t_dpdu_cqe, code, pdpdu_cqe);
+       switch (code) {
        case UNSOL_HDR_NOTIFY:
-               is_header = 1;
-
-                pbusy_list = hwi_get_async_busy_list(pasync_ctx,
-                                                     is_header, index);
+               pasync_handle = pasync_ctx->async_entry[ci].header;
                break;
+       case UNSOL_DATA_DIGEST_ERROR_NOTIFY:
+               error = 1;
        case UNSOL_DATA_NOTIFY:
-                pbusy_list = hwi_get_async_busy_list(pasync_ctx,
-                                                     is_header, index);
+               pasync_handle = pasync_ctx->async_entry[ci].data;
                break;
+       /* called only for above codes */
        default:
-               pbusy_list = NULL;
-               beiscsi_log(phba, KERN_WARNING,
-                           BEISCSI_LOG_IO | BEISCSI_LOG_CONFIG,
-                           "BM_%d : Unexpected code=%d\n",
-                           pdpdu_cqe->dw[offsetof(struct amap_i_t_dpdu_cqe,
-                           code) / 32] & PDUCQE_CODE_MASK);
-               return NULL;
+               pasync_handle = NULL;
+               break;
        }
 
-       WARN_ON(list_empty(pbusy_list));
-       list_for_each_entry(pasync_handle, pbusy_list, link) {
-               if (pasync_handle->pa.u.a64.address == phys_addr.u.a64.address)
-                       break;
+       if (!pasync_handle) {
+               beiscsi_log(phba, KERN_ERR, BEISCSI_LOG_ISCSI,
+                           "BM_%d : cid %d async PDU handle not found - code %d ci %d addr %llx\n",
+                           cid, code, ci, phys_addr.u.a64.address);
+               return pasync_handle;
        }
 
-       WARN_ON(!pasync_handle);
+       if (pasync_handle->pa.u.a64.address != phys_addr.u.a64.address ||
+           pasync_handle->index != ci) {
+               /* driver bug - if ci does not match async handle index */
+               error = 1;
+               beiscsi_log(phba, KERN_ERR, BEISCSI_LOG_ISCSI,
+                           "BM_%d : cid %u async PDU handle mismatch - addr in %cQE %llx at %u:addr in CQE %llx ci %u\n",
+                           cid, pasync_handle->is_header ? 'H' : 'D',
+                           pasync_handle->pa.u.a64.address,
+                           pasync_handle->index,
+                           phys_addr.u.a64.address, ci);
+               /* FW has stale address - attempt continuing by dropping */
+       }
 
-       pasync_handle->cri = BE_GET_ASYNC_CRI_FROM_CID(
-                            beiscsi_conn->beiscsi_conn_cid);
-       pasync_handle->is_header = is_header;
+       /**
+        * Each CID is associated with unique CRI.
+        * ASYNC_CRI_FROM_CID mapping and CRI_FROM_CID are totaly different.
+        **/
+       pasync_handle->cri = BE_GET_ASYNC_CRI_FROM_CID(cid);
+       pasync_handle->is_final = final;
        pasync_handle->buffer_len = dpl;
-       *pcq_index = index;
+       /* empty the slot */
+       if (pasync_handle->is_header)
+               pasync_ctx->async_entry[ci].header = NULL;
+       else
+               pasync_ctx->async_entry[ci].data = NULL;
 
+       /**
+        * DEF PDU header and data buffers with errors should be simply
+        * dropped as there are no consumers for it.
+        */
+       if (error) {
+               beiscsi_hdl_put_handle(pasync_ctx, pasync_handle);
+               pasync_handle = NULL;
+       }
        return pasync_handle;
 }
 
-static unsigned int
-hwi_update_async_writables(struct beiscsi_hba *phba,
-                           struct hwi_async_pdu_context *pasync_ctx,
-                           unsigned int is_header, unsigned int cq_index)
+static void
+beiscsi_hdl_purge_handles(struct beiscsi_hba *phba,
+                         struct hd_async_context *pasync_ctx,
+                         u16 cri)
 {
-       struct list_head *pbusy_list;
-       struct async_pdu_handle *pasync_handle;
-       unsigned int num_entries, writables = 0;
-       unsigned int *pep_read_ptr, *pwritables;
-
-       num_entries = pasync_ctx->num_entries;
-       if (is_header) {
-               pep_read_ptr = &pasync_ctx->async_header.ep_read_ptr;
-               pwritables = &pasync_ctx->async_header.writables;
-       } else {
-               pep_read_ptr = &pasync_ctx->async_data.ep_read_ptr;
-               pwritables = &pasync_ctx->async_data.writables;
-       }
-
-       while ((*pep_read_ptr) != cq_index) {
-               (*pep_read_ptr)++;
-               *pep_read_ptr = (*pep_read_ptr) % num_entries;
-
-               pbusy_list = hwi_get_async_busy_list(pasync_ctx, is_header,
-                                                    *pep_read_ptr);
-               if (writables == 0)
-                       WARN_ON(list_empty(pbusy_list));
-
-               if (!list_empty(pbusy_list)) {
-                       pasync_handle = list_entry(pbusy_list->next,
-                                                  struct async_pdu_handle,
-                                                  link);
-                       WARN_ON(!pasync_handle);
-                       pasync_handle->consumed = 1;
-               }
-
-               writables++;
-       }
+       struct hd_async_handle *pasync_handle, *tmp_handle;
+       struct list_head *plist;
 
-       if (!writables) {
-               beiscsi_log(phba, KERN_ERR,
-                           BEISCSI_LOG_CONFIG | BEISCSI_LOG_IO,
-                           "BM_%d : Duplicate notification received - index 0x%x!!\n",
-                           cq_index);
-               WARN_ON(1);
+       plist  = &pasync_ctx->async_entry[cri].wq.list;
+       list_for_each_entry_safe(pasync_handle, tmp_handle, plist, link) {
+               list_del(&pasync_handle->link);
+               beiscsi_hdl_put_handle(pasync_ctx, pasync_handle);
        }
 
-       *pwritables = *pwritables + writables;
-       return 0;
+       INIT_LIST_HEAD(&pasync_ctx->async_entry[cri].wq.list);
+       pasync_ctx->async_entry[cri].wq.hdr_len = 0;
+       pasync_ctx->async_entry[cri].wq.bytes_received = 0;
+       pasync_ctx->async_entry[cri].wq.bytes_needed = 0;
 }
 
-static void hwi_free_async_msg(struct beiscsi_hba *phba,
-                              struct hwi_async_pdu_context *pasync_ctx,
-                              unsigned int cri)
+static unsigned int
+beiscsi_hdl_fwd_pdu(struct beiscsi_conn *beiscsi_conn,
+                   struct hd_async_context *pasync_ctx,
+                   u16 cri)
 {
-       struct async_pdu_handle *pasync_handle, *tmp_handle;
+       struct iscsi_session *session = beiscsi_conn->conn->session;
+       struct hd_async_handle *pasync_handle, *plast_handle;
+       struct beiscsi_hba *phba = beiscsi_conn->phba;
+       void *phdr = NULL, *pdata = NULL;
+       u32 dlen = 0, status = 0;
        struct list_head *plist;
 
-       plist  = &pasync_ctx->async_entry[cri].wait_queue.list;
-       list_for_each_entry_safe(pasync_handle, tmp_handle, plist, link) {
-               list_del(&pasync_handle->link);
-
-               if (pasync_handle->is_header) {
-                       list_add_tail(&pasync_handle->link,
-                                     &pasync_ctx->async_header.free_list);
-                       pasync_ctx->async_header.free_entries++;
-               } else {
-                       list_add_tail(&pasync_handle->link,
-                                     &pasync_ctx->async_data.free_list);
-                       pasync_ctx->async_data.free_entries++;
+       plist = &pasync_ctx->async_entry[cri].wq.list;
+       plast_handle = NULL;
+       list_for_each_entry(pasync_handle, plist, link) {
+               plast_handle = pasync_handle;
+               /* get the header, the first entry */
+               if (!phdr) {
+                       phdr = pasync_handle->pbuffer;
+                       continue;
                }
+               /* use first buffer to collect all the data */
+               if (!pdata) {
+                       pdata = pasync_handle->pbuffer;
+                       dlen = pasync_handle->buffer_len;
+                       continue;
+               }
+               memcpy(pdata + dlen, pasync_handle->pbuffer,
+                      pasync_handle->buffer_len);
+               dlen += pasync_handle->buffer_len;
        }
 
-       INIT_LIST_HEAD(&pasync_ctx->async_entry[cri].wait_queue.list);
-       pasync_ctx->async_entry[cri].wait_queue.hdr_received = 0;
-       pasync_ctx->async_entry[cri].wait_queue.bytes_received = 0;
+       if (!plast_handle->is_final) {
+               /* last handle should have final PDU notification from FW */
+               beiscsi_log(phba, KERN_ERR, BEISCSI_LOG_ISCSI,
+                           "BM_%d : cid %u %p fwd async PDU with last handle missing - HL%u:DN%u:DR%u\n",
+                           beiscsi_conn->beiscsi_conn_cid, plast_handle,
+                           pasync_ctx->async_entry[cri].wq.hdr_len,
+                           pasync_ctx->async_entry[cri].wq.bytes_needed,
+                           pasync_ctx->async_entry[cri].wq.bytes_received);
+       }
+       spin_lock_bh(&session->back_lock);
+       status = beiscsi_complete_pdu(beiscsi_conn, phdr, pdata, dlen);
+       spin_unlock_bh(&session->back_lock);
+       beiscsi_hdl_purge_handles(phba, pasync_ctx, cri);
+       return status;
 }
 
-static struct phys_addr *
-hwi_get_ring_address(struct hwi_async_pdu_context *pasync_ctx,
-                    unsigned int is_header, unsigned int host_write_ptr)
+static unsigned int
+beiscsi_hdl_gather_pdu(struct beiscsi_conn *beiscsi_conn,
+                      struct hd_async_context *pasync_ctx,
+                      struct hd_async_handle *pasync_handle)
 {
-       struct phys_addr *pasync_sge = NULL;
+       unsigned int bytes_needed = 0, status = 0;
+       u16 cri = pasync_handle->cri;
+       struct cri_wait_queue *wq;
+       struct beiscsi_hba *phba;
+       struct pdu_base *ppdu;
+       char *err = "";
 
-       if (is_header)
-               pasync_sge = pasync_ctx->async_header.ring_base;
-       else
-               pasync_sge = pasync_ctx->async_data.ring_base;
+       phba = beiscsi_conn->phba;
+       wq = &pasync_ctx->async_entry[cri].wq;
+       if (pasync_handle->is_header) {
+               /* check if PDU hdr is rcv'd when old hdr not completed */
+               if (wq->hdr_len) {
+                       err = "incomplete";
+                       goto drop_pdu;
+               }
+               ppdu = pasync_handle->pbuffer;
+               bytes_needed = AMAP_GET_BITS(struct amap_pdu_base,
+                                            data_len_hi, ppdu);
+               bytes_needed <<= 16;
+               bytes_needed |= be16_to_cpu(AMAP_GET_BITS(struct amap_pdu_base,
+                                                         data_len_lo, ppdu));
+               wq->hdr_len = pasync_handle->buffer_len;
+               wq->bytes_received = 0;
+               wq->bytes_needed = bytes_needed;
+               list_add_tail(&pasync_handle->link, &wq->list);
+               if (!bytes_needed)
+                       status = beiscsi_hdl_fwd_pdu(beiscsi_conn,
+                                                    pasync_ctx, cri);
+       } else {
+               /* check if data received has header and is needed */
+               if (!wq->hdr_len || !wq->bytes_needed) {
+                       err = "header less";
+                       goto drop_pdu;
+               }
+               wq->bytes_received += pasync_handle->buffer_len;
+               /* Something got overwritten? Better catch it here. */
+               if (wq->bytes_received > wq->bytes_needed) {
+                       err = "overflow";
+                       goto drop_pdu;
+               }
+               list_add_tail(&pasync_handle->link, &wq->list);
+               if (wq->bytes_received == wq->bytes_needed)
+                       status = beiscsi_hdl_fwd_pdu(beiscsi_conn,
+                                                    pasync_ctx, cri);
+       }
+       return status;
 
-       return pasync_sge + host_write_ptr;
+drop_pdu:
+       beiscsi_log(phba, KERN_ERR, BEISCSI_LOG_ISCSI,
+                   "BM_%d : cid %u async PDU %s - def-%c:HL%u:DN%u:DR%u\n",
+                   beiscsi_conn->beiscsi_conn_cid, err,
+                   pasync_handle->is_header ? 'H' : 'D',
+                   wq->hdr_len, wq->bytes_needed,
+                   pasync_handle->buffer_len);
+       /* discard this handle */
+       beiscsi_hdl_put_handle(pasync_ctx, pasync_handle);
+       /* free all the other handles in cri_wait_queue */
+       beiscsi_hdl_purge_handles(phba, pasync_ctx, cri);
+       /* try continuing */
+       return status;
 }
 
-static void hwi_post_async_buffers(struct beiscsi_hba *phba,
-                                   unsigned int is_header, uint8_t ulp_num)
+static void
+beiscsi_hdq_post_handles(struct beiscsi_hba *phba,
+                        u8 header, u8 ulp_num)
 {
+       struct hd_async_handle *pasync_handle, *tmp, **slot;
+       struct hd_async_context *pasync_ctx;
        struct hwi_controller *phwi_ctrlr;
-       struct hwi_async_pdu_context *pasync_ctx;
-       struct async_pdu_handle *pasync_handle;
-       struct list_head *pfree_link, *pbusy_list;
+       struct list_head *hfree_list;
        struct phys_addr *pasync_sge;
-       unsigned int ring_id, num_entries;
-       unsigned int host_write_num, doorbell_offset;
-       unsigned int writables;
-       unsigned int i = 0;
-       u32 doorbell = 0;
+       u32 ring_id, doorbell = 0;
+       u16 index, num_entries;
+       u32 doorbell_offset;
+       u16 prod = 0, cons;
 
        phwi_ctrlr = phba->phwi_ctrlr;
        pasync_ctx = HWI_GET_ASYNC_PDU_CTX(phwi_ctrlr, ulp_num);
        num_entries = pasync_ctx->num_entries;
-
-       if (is_header) {
-               writables = min(pasync_ctx->async_header.writables,
-                               pasync_ctx->async_header.free_entries);
-               pfree_link = pasync_ctx->async_header.free_list.next;
-               host_write_num = pasync_ctx->async_header.host_write_ptr;
+       if (header) {
+               cons = pasync_ctx->async_header.free_entries;
+               hfree_list = &pasync_ctx->async_header.free_list;
                ring_id = phwi_ctrlr->default_pdu_hdr[ulp_num].id;
                doorbell_offset = phwi_ctrlr->default_pdu_hdr[ulp_num].
-                                 doorbell_offset;
+                                       doorbell_offset;
        } else {
-               writables = min(pasync_ctx->async_data.writables,
-                               pasync_ctx->async_data.free_entries);
-               pfree_link = pasync_ctx->async_data.free_list.next;
-               host_write_num = pasync_ctx->async_data.host_write_ptr;
+               cons = pasync_ctx->async_data.free_entries;
+               hfree_list = &pasync_ctx->async_data.free_list;
                ring_id = phwi_ctrlr->default_pdu_data[ulp_num].id;
                doorbell_offset = phwi_ctrlr->default_pdu_data[ulp_num].
-                                 doorbell_offset;
+                                       doorbell_offset;
        }
+       /* number of entries posted must be in multiples of 8 */
+       if (cons % 8)
+               return;
 
-       writables = (writables / 8) * 8;
-       if (writables) {
-               for (i = 0; i < writables; i++) {
-                       pbusy_list =
-                           hwi_get_async_busy_list(pasync_ctx, is_header,
-                                                   host_write_num);
-                       pasync_handle =
-                           list_entry(pfree_link, struct async_pdu_handle,
-                                                               link);
-                       WARN_ON(!pasync_handle);
-                       pasync_handle->consumed = 0;
-
-                       pfree_link = pfree_link->next;
-
-                       pasync_sge = hwi_get_ring_address(pasync_ctx,
-                                               is_header, host_write_num);
-
-                       pasync_sge->hi = pasync_handle->pa.u.a32.address_lo;
-                       pasync_sge->lo = pasync_handle->pa.u.a32.address_hi;
-
-                       list_move(&pasync_handle->link, pbusy_list);
-
-                       host_write_num++;
-                       host_write_num = host_write_num % num_entries;
-               }
-
-               if (is_header) {
-                       pasync_ctx->async_header.host_write_ptr =
-                                                       host_write_num;
-                       pasync_ctx->async_header.free_entries -= writables;
-                       pasync_ctx->async_header.writables -= writables;
-                       pasync_ctx->async_header.busy_entries += writables;
-               } else {
-                       pasync_ctx->async_data.host_write_ptr = host_write_num;
-                       pasync_ctx->async_data.free_entries -= writables;
-                       pasync_ctx->async_data.writables -= writables;
-                       pasync_ctx->async_data.busy_entries += writables;
-               }
-
-               doorbell |= ring_id & DB_DEF_PDU_RING_ID_MASK;
-               doorbell |= 1 << DB_DEF_PDU_REARM_SHIFT;
-               doorbell |= 0 << DB_DEF_PDU_EVENT_SHIFT;
-               doorbell |= (writables & DB_DEF_PDU_CQPROC_MASK)
-                                       << DB_DEF_PDU_CQPROC_SHIFT;
-
-               iowrite32(doorbell, phba->db_va + doorbell_offset);
-       }
-}
-
-static void hwi_flush_default_pdu_buffer(struct beiscsi_hba *phba,
-                                        struct beiscsi_conn *beiscsi_conn,
-                                        struct i_t_dpdu_cqe *pdpdu_cqe)
-{
-       struct hwi_controller *phwi_ctrlr;
-       struct hwi_async_pdu_context *pasync_ctx;
-       struct async_pdu_handle *pasync_handle = NULL;
-       unsigned int cq_index = -1;
-       uint16_t cri_index = BE_GET_CRI_FROM_CID(
-                            beiscsi_conn->beiscsi_conn_cid);
-
-       phwi_ctrlr = phba->phwi_ctrlr;
-       pasync_ctx = HWI_GET_ASYNC_PDU_CTX(phwi_ctrlr,
-                    BEISCSI_GET_ULP_FROM_CRI(phwi_ctrlr,
-                    cri_index));
-
-       pasync_handle = hwi_get_async_handle(phba, beiscsi_conn, pasync_ctx,
-                                            pdpdu_cqe, &cq_index);
-       BUG_ON(pasync_handle->is_header != 0);
-       if (pasync_handle->consumed == 0)
-               hwi_update_async_writables(phba, pasync_ctx,
-                                          pasync_handle->is_header, cq_index);
-
-       hwi_free_async_msg(phba, pasync_ctx, pasync_handle->cri);
-       hwi_post_async_buffers(phba, pasync_handle->is_header,
-                              BEISCSI_GET_ULP_FROM_CRI(phwi_ctrlr,
-                              cri_index));
-}
-
-static unsigned int
-hwi_fwd_async_msg(struct beiscsi_conn *beiscsi_conn,
-                 struct beiscsi_hba *phba,
-                 struct hwi_async_pdu_context *pasync_ctx, unsigned short cri)
-{
-       struct list_head *plist;
-       struct async_pdu_handle *pasync_handle;
-       void *phdr = NULL;
-       unsigned int hdr_len = 0, buf_len = 0;
-       unsigned int status, index = 0, offset = 0;
-       void *pfirst_buffer = NULL;
-       unsigned int num_buf = 0;
-
-       plist = &pasync_ctx->async_entry[cri].wait_queue.list;
+       list_for_each_entry_safe(pasync_handle, tmp, hfree_list, link) {
+               list_del_init(&pasync_handle->link);
+               pasync_handle->is_final = 0;
+               pasync_handle->buffer_len = 0;
 
-       list_for_each_entry(pasync_handle, plist, link) {
-               if (index == 0) {
-                       phdr = pasync_handle->pbuffer;
-                       hdr_len = pasync_handle->buffer_len;
-               } else {
-                       buf_len = pasync_handle->buffer_len;
-                       if (!num_buf) {
-                               pfirst_buffer = pasync_handle->pbuffer;
-                               num_buf++;
-                       }
-                       memcpy(pfirst_buffer + offset,
-                              pasync_handle->pbuffer, buf_len);
-                       offset += buf_len;
+               /* handles can be consumed out of order, use index in handle */
+               index = pasync_handle->index;
+               WARN_ON(pasync_handle->is_header != header);
+               if (header)
+                       slot = &pasync_ctx->async_entry[index].header;
+               else
+                       slot = &pasync_ctx->async_entry[index].data;
+               /**
+                * The slot just tracks handle's hold and release, so
+                * overwriting at the same index won't do any harm but
+                * needs to be caught.
+                */
+               if (*slot != NULL) {
+                       beiscsi_log(phba, KERN_ERR, BEISCSI_LOG_ISCSI,
+                                   "BM_%d : async PDU %s slot at %u not empty\n",
+                                   header ? "header" : "data", index);
                }
-               index++;
+               /**
+                * We use same freed index as in completion to post so this
+                * operation is not required for refills. Its required only
+                * for ring creation.
+                */
+               if (header)
+                       pasync_sge = pasync_ctx->async_header.ring_base;
+               else
+                       pasync_sge = pasync_ctx->async_data.ring_base;
+               pasync_sge += index;
+               /* if its a refill then address is same; hi is lo */
+               WARN_ON(pasync_sge->hi &&
+                       pasync_sge->hi != pasync_handle->pa.u.a32.address_lo);
+               WARN_ON(pasync_sge->lo &&
+                       pasync_sge->lo != pasync_handle->pa.u.a32.address_hi);
+               pasync_sge->hi = pasync_handle->pa.u.a32.address_lo;
+               pasync_sge->lo = pasync_handle->pa.u.a32.address_hi;
+
+               *slot = pasync_handle;
+               if (++prod == cons)
+                       break;
        }
+       if (header)
+               pasync_ctx->async_header.free_entries -= prod;
+       else
+               pasync_ctx->async_data.free_entries -= prod;
 
-       status = beiscsi_process_async_pdu(beiscsi_conn, phba,
-                                           phdr, hdr_len, pfirst_buffer,
-                                           offset);
-
-       hwi_free_async_msg(phba, pasync_ctx, cri);
-       return 0;
-}
-
-static unsigned int
-hwi_gather_async_pdu(struct beiscsi_conn *beiscsi_conn,
-                    struct beiscsi_hba *phba,
-                    struct async_pdu_handle *pasync_handle)
-{
-       struct hwi_async_pdu_context *pasync_ctx;
-       struct hwi_controller *phwi_ctrlr;
-       unsigned int bytes_needed = 0, status = 0;
-       unsigned short cri = pasync_handle->cri;
-       struct pdu_base *ppdu;
-
-       phwi_ctrlr = phba->phwi_ctrlr;
-       pasync_ctx = HWI_GET_ASYNC_PDU_CTX(phwi_ctrlr,
-                    BEISCSI_GET_ULP_FROM_CRI(phwi_ctrlr,
-                    BE_GET_CRI_FROM_CID(beiscsi_conn->
-                                beiscsi_conn_cid)));
-
-       list_del(&pasync_handle->link);
-       if (pasync_handle->is_header) {
-               pasync_ctx->async_header.busy_entries--;
-               if (pasync_ctx->async_entry[cri].wait_queue.hdr_received) {
-                       hwi_free_async_msg(phba, pasync_ctx, cri);
-                       BUG();
-               }
-
-               pasync_ctx->async_entry[cri].wait_queue.bytes_received = 0;
-               pasync_ctx->async_entry[cri].wait_queue.hdr_received = 1;
-               pasync_ctx->async_entry[cri].wait_queue.hdr_len =
-                               (unsigned short)pasync_handle->buffer_len;
-               list_add_tail(&pasync_handle->link,
-                             &pasync_ctx->async_entry[cri].wait_queue.list);
-
-               ppdu = pasync_handle->pbuffer;
-               bytes_needed = ((((ppdu->dw[offsetof(struct amap_pdu_base,
-                       data_len_hi) / 32] & PDUBASE_DATALENHI_MASK) << 8) &
-                       0xFFFF0000) | ((be16_to_cpu((ppdu->
-                       dw[offsetof(struct amap_pdu_base, data_len_lo) / 32]
-                       & PDUBASE_DATALENLO_MASK) >> 16)) & 0x0000FFFF));
-
-               if (status == 0) {
-                       pasync_ctx->async_entry[cri].wait_queue.bytes_needed =
-                           bytes_needed;
-
-                       if (bytes_needed == 0)
-                               status = hwi_fwd_async_msg(beiscsi_conn, phba,
-                                                          pasync_ctx, cri);
-               }
-       } else {
-               pasync_ctx->async_data.busy_entries--;
-               if (pasync_ctx->async_entry[cri].wait_queue.hdr_received) {
-                       list_add_tail(&pasync_handle->link,
-                                     &pasync_ctx->async_entry[cri].wait_queue.
-                                     list);
-                       pasync_ctx->async_entry[cri].wait_queue.
-                               bytes_received +=
-                               (unsigned short)pasync_handle->buffer_len;
-
-                       if (pasync_ctx->async_entry[cri].wait_queue.
-                           bytes_received >=
-                           pasync_ctx->async_entry[cri].wait_queue.
-                           bytes_needed)
-                               status = hwi_fwd_async_msg(beiscsi_conn, phba,
-                                                          pasync_ctx, cri);
-               }
-       }
-       return status;
+       doorbell |= ring_id & DB_DEF_PDU_RING_ID_MASK;
+       doorbell |= 1 << DB_DEF_PDU_REARM_SHIFT;
+       doorbell |= 0 << DB_DEF_PDU_EVENT_SHIFT;
+       doorbell |= (prod & DB_DEF_PDU_CQPROC_MASK) << DB_DEF_PDU_CQPROC_SHIFT;
+       iowrite32(doorbell, phba->db_va + doorbell_offset);
 }
 
-static void hwi_process_default_pdu_ring(struct beiscsi_conn *beiscsi_conn,
-                                        struct beiscsi_hba *phba,
-                                        struct i_t_dpdu_cqe *pdpdu_cqe)
+static void
+beiscsi_hdq_process_compl(struct beiscsi_conn *beiscsi_conn,
+                         struct i_t_dpdu_cqe *pdpdu_cqe)
 {
+       struct beiscsi_hba *phba = beiscsi_conn->phba;
+       struct hd_async_handle *pasync_handle = NULL;
+       struct hd_async_context *pasync_ctx;
        struct hwi_controller *phwi_ctrlr;
-       struct hwi_async_pdu_context *pasync_ctx;
-       struct async_pdu_handle *pasync_handle = NULL;
-       unsigned int cq_index = -1;
-       uint16_t cri_index = BE_GET_CRI_FROM_CID(
-                            beiscsi_conn->beiscsi_conn_cid);
+       u16 cid_cri;
+       u8 ulp_num;
 
        phwi_ctrlr = phba->phwi_ctrlr;
-       pasync_ctx = HWI_GET_ASYNC_PDU_CTX(phwi_ctrlr,
-                    BEISCSI_GET_ULP_FROM_CRI(phwi_ctrlr,
-                    cri_index));
-
-       pasync_handle = hwi_get_async_handle(phba, beiscsi_conn, pasync_ctx,
-                                            pdpdu_cqe, &cq_index);
-
-       if (pasync_handle->consumed == 0)
-               hwi_update_async_writables(phba, pasync_ctx,
-                                          pasync_handle->is_header, cq_index);
+       cid_cri = BE_GET_CRI_FROM_CID(beiscsi_conn->beiscsi_conn_cid);
+       ulp_num = BEISCSI_GET_ULP_FROM_CRI(phwi_ctrlr, cid_cri);
+       pasync_ctx = HWI_GET_ASYNC_PDU_CTX(phwi_ctrlr, ulp_num);
+       pasync_handle = beiscsi_hdl_get_handle(beiscsi_conn, pasync_ctx,
+                                              pdpdu_cqe);
+       if (!pasync_handle)
+               return;
 
-       hwi_gather_async_pdu(beiscsi_conn, phba, pasync_handle);
-       hwi_post_async_buffers(phba, pasync_handle->is_header,
-                              BEISCSI_GET_ULP_FROM_CRI(
-                              phwi_ctrlr, cri_index));
+       beiscsi_hdl_gather_pdu(beiscsi_conn, pasync_ctx, pasync_handle);
+       beiscsi_hdq_post_handles(phba, pasync_handle->is_header, ulp_num);
 }
 
 void beiscsi_process_mcc_cq(struct beiscsi_hba *phba)
@@ -2051,6 +1816,9 @@ void beiscsi_process_mcc_cq(struct beiscsi_hba *phba)
        mcc_compl = queue_tail_node(mcc_cq);
        mcc_compl->flags = le32_to_cpu(mcc_compl->flags);
        while (mcc_compl->flags & CQE_FLAGS_VALID_MASK) {
+               if (beiscsi_hba_in_error(phba))
+                       return;
+
                if (num_processed >= 32) {
                        hwi_ring_cq_db(phba, mcc_cq->id,
                                        num_processed, 0);
@@ -2073,6 +1841,19 @@ void beiscsi_process_mcc_cq(struct beiscsi_hba *phba)
                hwi_ring_cq_db(phba, mcc_cq->id, num_processed, 1);
 }
 
+static void beiscsi_mcc_work(struct work_struct *work)
+{
+       struct be_eq_obj *pbe_eq;
+       struct beiscsi_hba *phba;
+
+       pbe_eq = container_of(work, struct be_eq_obj, mcc_work);
+       phba = pbe_eq->phba;
+       beiscsi_process_mcc_cq(phba);
+       /* rearm EQ for further interrupts */
+       if (!beiscsi_hba_in_error(phba))
+               hwi_ring_eq_db(phba, pbe_eq->q.id, 0, 0, 1, 1);
+}
+
 /**
  * beiscsi_process_cq()- Process the Completion Queue
  * @pbe_eq: Event Q on which the Completion has come
@@ -2101,6 +1882,9 @@ unsigned int beiscsi_process_cq(struct be_eq_obj *pbe_eq, int budget)
 
        while (sol->dw[offsetof(struct amap_sol_cqe, valid) / 32] &
               CQE_VALID_MASK) {
+               if (beiscsi_hba_in_error(phba))
+                       return 0;
+
                be_dws_le_to_cpu(sol, sizeof(struct sol_cqe));
 
                 code = (sol->dw[offsetof(struct amap_sol_cqe, code) /
@@ -2165,8 +1949,8 @@ unsigned int beiscsi_process_cq(struct be_eq_obj *pbe_eq, int budget)
                                    cqe_desc[code], code, cid);
 
                        spin_lock_bh(&phba->async_pdu_lock);
-                       hwi_process_default_pdu_ring(beiscsi_conn, phba,
-                                            (struct i_t_dpdu_cqe *)sol);
+                       beiscsi_hdq_process_compl(beiscsi_conn,
+                                                 (struct i_t_dpdu_cqe *)sol);
                        spin_unlock_bh(&phba->async_pdu_lock);
                        break;
                case UNSOL_DATA_NOTIFY:
@@ -2176,8 +1960,8 @@ unsigned int beiscsi_process_cq(struct be_eq_obj *pbe_eq, int budget)
                                    cqe_desc[code], code, cid);
 
                        spin_lock_bh(&phba->async_pdu_lock);
-                       hwi_process_default_pdu_ring(beiscsi_conn, phba,
-                                            (struct i_t_dpdu_cqe *)sol);
+                       beiscsi_hdq_process_compl(beiscsi_conn,
+                                                 (struct i_t_dpdu_cqe *)sol);
                        spin_unlock_bh(&phba->async_pdu_lock);
                        break;
                case CXN_INVALIDATE_INDEX_NOTIFY:
@@ -2213,8 +1997,9 @@ unsigned int beiscsi_process_cq(struct be_eq_obj *pbe_eq, int budget)
                                    "BM_%d :  Dropping %s[%d] on DPDU ring on CID : %d\n",
                                    cqe_desc[code], code, cid);
                        spin_lock_bh(&phba->async_pdu_lock);
-                       hwi_flush_default_pdu_buffer(phba, beiscsi_conn,
-                                            (struct i_t_dpdu_cqe *) sol);
+                       /* driver consumes the entry and drops the contents */
+                       beiscsi_hdq_process_compl(beiscsi_conn,
+                                                 (struct i_t_dpdu_cqe *)sol);
                        spin_unlock_bh(&phba->async_pdu_lock);
                        break;
                case CXN_KILLED_PDU_SIZE_EXCEEDS_DSL:
@@ -2262,60 +2047,32 @@ proc_next_cqe:
        return total;
 }
 
-void beiscsi_process_all_cqs(struct work_struct *work)
-{
-       unsigned long flags;
-       struct hwi_controller *phwi_ctrlr;
-       struct hwi_context_memory *phwi_context;
-       struct beiscsi_hba *phba;
-       struct be_eq_obj *pbe_eq =
-           container_of(work, struct be_eq_obj, work_cqs);
-
-       phba = pbe_eq->phba;
-       phwi_ctrlr = phba->phwi_ctrlr;
-       phwi_context = phwi_ctrlr->phwi_ctxt;
-
-       if (pbe_eq->todo_mcc_cq) {
-               spin_lock_irqsave(&phba->isr_lock, flags);
-               pbe_eq->todo_mcc_cq = false;
-               spin_unlock_irqrestore(&phba->isr_lock, flags);
-               beiscsi_process_mcc_cq(phba);
-       }
-
-       if (pbe_eq->todo_cq) {
-               spin_lock_irqsave(&phba->isr_lock, flags);
-               pbe_eq->todo_cq = false;
-               spin_unlock_irqrestore(&phba->isr_lock, flags);
-               beiscsi_process_cq(pbe_eq, BE2_MAX_NUM_CQ_PROC);
-       }
-
-       /* rearm EQ for further interrupts */
-       hwi_ring_eq_db(phba, pbe_eq->q.id, 0, 0, 1, 1);
-}
-
 static int be_iopoll(struct irq_poll *iop, int budget)
 {
-       unsigned int ret, num_eq_processed;
+       unsigned int ret, io_events;
        struct beiscsi_hba *phba;
        struct be_eq_obj *pbe_eq;
        struct be_eq_entry *eqe = NULL;
        struct be_queue_info *eq;
 
-       num_eq_processed = 0;
        pbe_eq = container_of(iop, struct be_eq_obj, iopoll);
        phba = pbe_eq->phba;
+       if (beiscsi_hba_in_error(phba)) {
+               irq_poll_complete(iop);
+               return 0;
+       }
+
+       io_events = 0;
        eq = &pbe_eq->q;
        eqe = queue_tail_node(eq);
-
        while (eqe->dw[offsetof(struct amap_eq_entry, valid) / 32] &
                        EQE_VALID_MASK) {
                AMAP_SET_BITS(struct amap_eq_entry, valid, eqe, 0);
                queue_tail_inc(eq);
                eqe = queue_tail_node(eq);
-               num_eq_processed++;
+               io_events++;
        }
-
-       hwi_ring_eq_db(phba, eq->id, 1, num_eq_processed, 0, 1);
+       hwi_ring_eq_db(phba, eq->id, 1, io_events, 0, 1);
 
        ret = beiscsi_process_cq(pbe_eq, budget);
        pbe_eq->cq_count += ret;
@@ -2325,7 +2082,8 @@ static int be_iopoll(struct irq_poll *iop, int budget)
                            BEISCSI_LOG_CONFIG | BEISCSI_LOG_IO,
                            "BM_%d : rearm pbe_eq->q.id =%d ret %d\n",
                            pbe_eq->q.id, ret);
-               hwi_ring_eq_db(phba, pbe_eq->q.id, 0, 0, 1, 1);
+               if (!beiscsi_hba_in_error(phba))
+                       hwi_ring_eq_db(phba, pbe_eq->q.id, 0, 0, 1, 1);
        }
        return ret;
 }
@@ -2691,20 +2449,20 @@ static void beiscsi_find_mem_req(struct beiscsi_hba *phba)
                                          (ulp_num * MEM_DESCR_OFFSET));
                        phba->mem_req[mem_descr_index] =
                                          BEISCSI_GET_CID_COUNT(phba, ulp_num) *
-                                         sizeof(struct async_pdu_handle);
+                                         sizeof(struct hd_async_handle);
 
                        mem_descr_index = (HWI_MEM_ASYNC_DATA_HANDLE_ULP0 +
                                          (ulp_num * MEM_DESCR_OFFSET));
                        phba->mem_req[mem_descr_index] =
                                          BEISCSI_GET_CID_COUNT(phba, ulp_num) *
-                                         sizeof(struct async_pdu_handle);
+                                         sizeof(struct hd_async_handle);
 
                        mem_descr_index = (HWI_MEM_ASYNC_PDU_CONTEXT_ULP0 +
                                          (ulp_num * MEM_DESCR_OFFSET));
                        phba->mem_req[mem_descr_index] =
-                                         sizeof(struct hwi_async_pdu_context) +
+                                         sizeof(struct hd_async_context) +
                                         (BEISCSI_GET_CID_COUNT(phba, ulp_num) *
-                                         sizeof(struct hwi_async_entry));
+                                         sizeof(struct hd_async_entry));
                }
        }
 }
@@ -2963,35 +2721,34 @@ static int hwi_init_async_pdu_ctx(struct beiscsi_hba *phba)
        uint8_t ulp_num;
        struct hwi_controller *phwi_ctrlr;
        struct hba_parameters *p = &phba->params;
-       struct hwi_async_pdu_context *pasync_ctx;
-       struct async_pdu_handle *pasync_header_h, *pasync_data_h;
+       struct hd_async_context *pasync_ctx;
+       struct hd_async_handle *pasync_header_h, *pasync_data_h;
        unsigned int index, idx, num_per_mem, num_async_data;
        struct be_mem_descriptor *mem_descr;
 
        for (ulp_num = 0; ulp_num < BEISCSI_ULP_COUNT; ulp_num++) {
                if (test_bit(ulp_num, &phba->fw_config.ulp_supported)) {
-
+                        /* get async_ctx for each ULP */
                        mem_descr = (struct be_mem_descriptor *)phba->init_mem;
                        mem_descr += (HWI_MEM_ASYNC_PDU_CONTEXT_ULP0 +
                                     (ulp_num * MEM_DESCR_OFFSET));
 
                        phwi_ctrlr = phba->phwi_ctrlr;
                        phwi_ctrlr->phwi_ctxt->pasync_ctx[ulp_num] =
-                               (struct hwi_async_pdu_context *)
+                               (struct hd_async_context *)
                                 mem_descr->mem_array[0].virtual_address;
 
                        pasync_ctx = phwi_ctrlr->phwi_ctxt->pasync_ctx[ulp_num];
                        memset(pasync_ctx, 0, sizeof(*pasync_ctx));
 
                        pasync_ctx->async_entry =
-                                       (struct hwi_async_entry *)
+                                       (struct hd_async_entry *)
                                        ((long unsigned int)pasync_ctx +
-                                       sizeof(struct hwi_async_pdu_context));
+                                       sizeof(struct hd_async_context));
 
                        pasync_ctx->num_entries = BEISCSI_GET_CID_COUNT(phba,
                                                  ulp_num);
-                       pasync_ctx->buffer_size = p->defpdu_hdr_sz;
-
+                       /* setup header buffers */
                        mem_descr = (struct be_mem_descriptor *)phba->init_mem;
                        mem_descr += HWI_MEM_ASYNC_HEADER_BUF_ULP0 +
                                (ulp_num * MEM_DESCR_OFFSET);
@@ -3008,6 +2765,7 @@ static int hwi_init_async_pdu_ctx(struct beiscsi_hba *phba)
                                            "BM_%d : No Virtual address for ULP : %d\n",
                                            ulp_num);
 
+                       pasync_ctx->async_header.buffer_size = p->defpdu_hdr_sz;
                        pasync_ctx->async_header.va_base =
                                mem_descr->mem_array[0].virtual_address;
 
@@ -3015,6 +2773,7 @@ static int hwi_init_async_pdu_ctx(struct beiscsi_hba *phba)
                                mem_descr->mem_array[0].
                                bus_address.u.a64.address;
 
+                       /* setup header buffer sgls */
                        mem_descr = (struct be_mem_descriptor *)phba->init_mem;
                        mem_descr += HWI_MEM_ASYNC_HEADER_RING_ULP0 +
                                     (ulp_num * MEM_DESCR_OFFSET);
@@ -3034,6 +2793,7 @@ static int hwi_init_async_pdu_ctx(struct beiscsi_hba *phba)
                        pasync_ctx->async_header.ring_base =
                                mem_descr->mem_array[0].virtual_address;
 
+                       /* setup header buffer handles */
                        mem_descr = (struct be_mem_descriptor *)phba->init_mem;
                        mem_descr += HWI_MEM_ASYNC_HEADER_HANDLE_ULP0 +
                                     (ulp_num * MEM_DESCR_OFFSET);
@@ -3052,9 +2812,9 @@ static int hwi_init_async_pdu_ctx(struct beiscsi_hba *phba)
 
                        pasync_ctx->async_header.handle_base =
                                mem_descr->mem_array[0].virtual_address;
-                       pasync_ctx->async_header.writables = 0;
                        INIT_LIST_HEAD(&pasync_ctx->async_header.free_list);
 
+                       /* setup data buffer sgls */
                        mem_descr = (struct be_mem_descriptor *)phba->init_mem;
                        mem_descr += HWI_MEM_ASYNC_DATA_RING_ULP0 +
                                     (ulp_num * MEM_DESCR_OFFSET);
@@ -3074,6 +2834,7 @@ static int hwi_init_async_pdu_ctx(struct beiscsi_hba *phba)
                        pasync_ctx->async_data.ring_base =
                                mem_descr->mem_array[0].virtual_address;
 
+                       /* setup data buffer handles */
                        mem_descr = (struct be_mem_descriptor *)phba->init_mem;
                        mem_descr += HWI_MEM_ASYNC_DATA_HANDLE_ULP0 +
                                     (ulp_num * MEM_DESCR_OFFSET);
@@ -3085,16 +2846,16 @@ static int hwi_init_async_pdu_ctx(struct beiscsi_hba *phba)
 
                        pasync_ctx->async_data.handle_base =
                                mem_descr->mem_array[0].virtual_address;
-                       pasync_ctx->async_data.writables = 0;
                        INIT_LIST_HEAD(&pasync_ctx->async_data.free_list);
 
                        pasync_header_h =
-                               (struct async_pdu_handle *)
+                               (struct hd_async_handle *)
                                pasync_ctx->async_header.handle_base;
                        pasync_data_h =
-                               (struct async_pdu_handle *)
+                               (struct hd_async_handle *)
                                pasync_ctx->async_data.handle_base;
 
+                       /* setup data buffers */
                        mem_descr = (struct be_mem_descriptor *)phba->init_mem;
                        mem_descr += HWI_MEM_ASYNC_DATA_BUF_ULP0 +
                                     (ulp_num * MEM_DESCR_OFFSET);
@@ -3112,6 +2873,7 @@ static int hwi_init_async_pdu_ctx(struct beiscsi_hba *phba)
                                            ulp_num);
 
                        idx = 0;
+                       pasync_ctx->async_data.buffer_size = p->defpdu_data_sz;
                        pasync_ctx->async_data.va_base =
                                mem_descr->mem_array[idx].virtual_address;
                        pasync_ctx->async_data.pa_base.u.a64.address =
@@ -3125,7 +2887,8 @@ static int hwi_init_async_pdu_ctx(struct beiscsi_hba *phba)
                        for (index = 0; index < BEISCSI_GET_CID_COUNT
                                        (phba, ulp_num); index++) {
                                pasync_header_h->cri = -1;
-                               pasync_header_h->index = (char)index;
+                               pasync_header_h->is_header = 1;
+                               pasync_header_h->index = index;
                                INIT_LIST_HEAD(&pasync_header_h->link);
                                pasync_header_h->pbuffer =
                                        (void *)((unsigned long)
@@ -3142,14 +2905,13 @@ static int hwi_init_async_pdu_ctx(struct beiscsi_hba *phba)
                                              free_list);
                                pasync_header_h++;
                                pasync_ctx->async_header.free_entries++;
-                               pasync_ctx->async_header.writables++;
-
-                               INIT_LIST_HEAD(&pasync_ctx->async_entry[index].
-                                              wait_queue.list);
                                INIT_LIST_HEAD(&pasync_ctx->async_entry[index].
-                                              header_busy_list);
+                                               wq.list);
+                               pasync_ctx->async_entry[index].header = NULL;
+
                                pasync_data_h->cri = -1;
-                               pasync_data_h->index = (char)index;
+                               pasync_data_h->is_header = 0;
+                               pasync_data_h->index = index;
                                INIT_LIST_HEAD(&pasync_data_h->link);
 
                                if (!num_async_data) {
@@ -3184,16 +2946,8 @@ static int hwi_init_async_pdu_ctx(struct beiscsi_hba *phba)
                                              free_list);
                                pasync_data_h++;
                                pasync_ctx->async_data.free_entries++;
-                               pasync_ctx->async_data.writables++;
-
-                               INIT_LIST_HEAD(&pasync_ctx->async_entry[index].
-                                              data_busy_list);
+                               pasync_ctx->async_entry[index].data = NULL;
                        }
-
-                       pasync_ctx->async_header.host_write_ptr = 0;
-                       pasync_ctx->async_header.ep_read_ptr = -1;
-                       pasync_ctx->async_data.host_write_ptr = 0;
-                       pasync_ctx->async_data.ep_read_ptr = -1;
                }
        }
 
@@ -3265,8 +3019,8 @@ static int be_fill_queue(struct be_queue_info *q,
 static int beiscsi_create_eqs(struct beiscsi_hba *phba,
                             struct hwi_context_memory *phwi_context)
 {
+       int ret = -ENOMEM, eq_for_mcc;
        unsigned int i, num_eq_pages;
-       int ret = 0, eq_for_mcc;
        struct be_queue_info *eq;
        struct be_dma_mem *mem;
        void *eq_vaddress;
@@ -3284,8 +3038,8 @@ static int beiscsi_create_eqs(struct beiscsi_hba *phba,
                mem = &eq->dma_mem;
                phwi_context->be_eq[i].phba = phba;
                eq_vaddress = pci_alloc_consistent(phba->pcidev,
-                                                    num_eq_pages * PAGE_SIZE,
-                                                    &paddr);
+                                                  num_eq_pages * PAGE_SIZE,
+                                                  &paddr);
                if (!eq_vaddress)
                        goto create_eq_error;
 
@@ -3313,6 +3067,7 @@ static int beiscsi_create_eqs(struct beiscsi_hba *phba,
                            phwi_context->be_eq[i].q.id);
        }
        return 0;
+
 create_eq_error:
        for (i = 0; i < (phba->num_cpus + eq_for_mcc); i++) {
                eq = &phwi_context->be_eq[i].q;
@@ -3329,11 +3084,11 @@ static int beiscsi_create_cqs(struct beiscsi_hba *phba,
                             struct hwi_context_memory *phwi_context)
 {
        unsigned int i, num_cq_pages;
-       int ret = 0;
        struct be_queue_info *cq, *eq;
        struct be_dma_mem *mem;
        struct be_eq_obj *pbe_eq;
        void *cq_vaddress;
+       int ret = -ENOMEM;
        dma_addr_t paddr;
 
        num_cq_pages = PAGES_REQUIRED(phba->params.num_cq_entries * \
@@ -3347,10 +3102,11 @@ static int beiscsi_create_cqs(struct beiscsi_hba *phba,
                pbe_eq->phba = phba;
                mem = &cq->dma_mem;
                cq_vaddress = pci_alloc_consistent(phba->pcidev,
-                                                    num_cq_pages * PAGE_SIZE,
-                                                    &paddr);
+                                                  num_cq_pages * PAGE_SIZE,
+                                                  &paddr);
                if (!cq_vaddress)
                        goto create_cq_error;
+
                ret = be_fill_queue(cq, phba->params.num_cq_entries,
                                    sizeof(struct sol_cqe), cq_vaddress);
                if (ret) {
@@ -3385,7 +3141,6 @@ create_cq_error:
                                            mem->va, mem->dma);
        }
        return ret;
-
 }
 
 static int
@@ -3437,7 +3192,6 @@ beiscsi_create_def_hdr(struct beiscsi_hba *phba,
                    "BM_%d : iscsi hdr def pdu id for ULP : %d is %d\n",
                    ulp_num,
                    phwi_context->be_def_hdrq[ulp_num].id);
-       hwi_post_async_buffers(phba, BEISCSI_DEFQ_HDR, ulp_num);
        return 0;
 }
 
@@ -3492,11 +3246,9 @@ beiscsi_create_def_data(struct beiscsi_hba *phba,
                    ulp_num,
                    phwi_context->be_def_dataq[ulp_num].id);
 
-       hwi_post_async_buffers(phba, BEISCSI_DEFQ_DATA, ulp_num);
        beiscsi_log(phba, KERN_INFO, BEISCSI_LOG_INIT,
                    "BM_%d : DEFAULT PDU DATA RING CREATED"
                    "on ULP : %d\n", ulp_num);
-
        return 0;
 }
 
@@ -3716,10 +3468,53 @@ static void free_wrb_handles(struct beiscsi_hba *phba)
 
 static void be_mcc_queues_destroy(struct beiscsi_hba *phba)
 {
-       struct be_queue_info *q;
        struct be_ctrl_info *ctrl = &phba->ctrl;
+       struct be_dma_mem *ptag_mem;
+       struct be_queue_info *q;
+       int i, tag;
 
        q = &phba->ctrl.mcc_obj.q;
+       for (i = 0; i < MAX_MCC_CMD; i++) {
+               tag = i + 1;
+               if (!test_bit(MCC_TAG_STATE_RUNNING,
+                             &ctrl->ptag_state[tag].tag_state))
+                       continue;
+
+               if (test_bit(MCC_TAG_STATE_TIMEOUT,
+                            &ctrl->ptag_state[tag].tag_state)) {
+                       ptag_mem = &ctrl->ptag_state[tag].tag_mem_state;
+                       if (ptag_mem->size) {
+                               pci_free_consistent(ctrl->pdev,
+                                                   ptag_mem->size,
+                                                   ptag_mem->va,
+                                                   ptag_mem->dma);
+                               ptag_mem->size = 0;
+                       }
+                       continue;
+               }
+               /**
+                * If MCC is still active and waiting then wake up the process.
+                * We are here only because port is going offline. The process
+                * sees that (BEISCSI_HBA_ONLINE is cleared) and EIO error is
+                * returned for the operation and allocated memory cleaned up.
+                */
+               if (waitqueue_active(&ctrl->mcc_wait[tag])) {
+                       ctrl->mcc_tag_status[tag] = MCC_STATUS_FAILED;
+                       ctrl->mcc_tag_status[tag] |= CQE_VALID_MASK;
+                       wake_up_interruptible(&ctrl->mcc_wait[tag]);
+                       /*
+                        * Control tag info gets reinitialized in enable
+                        * so wait for the process to clear running state.
+                        */
+                       while (test_bit(MCC_TAG_STATE_RUNNING,
+                                       &ctrl->ptag_state[tag].tag_state))
+                               schedule_timeout_uninterruptible(HZ);
+               }
+               /**
+                * For MCC with tag_states MCC_TAG_STATE_ASYNC and
+                * MCC_TAG_STATE_IGNORE nothing needs to done.
+                */
+       }
        if (q->created) {
                beiscsi_cmd_q_destroy(ctrl, q, QTYPE_MCCQ);
                be_queue_free(phba, q);
@@ -3732,68 +3527,6 @@ static void be_mcc_queues_destroy(struct beiscsi_hba *phba)
        }
 }
 
-static void hwi_cleanup(struct beiscsi_hba *phba)
-{
-       struct be_queue_info *q;
-       struct be_ctrl_info *ctrl = &phba->ctrl;
-       struct hwi_controller *phwi_ctrlr;
-       struct hwi_context_memory *phwi_context;
-       struct hwi_async_pdu_context *pasync_ctx;
-       int i, eq_for_mcc, ulp_num;
-
-       phwi_ctrlr = phba->phwi_ctrlr;
-       phwi_context = phwi_ctrlr->phwi_ctxt;
-
-       be_cmd_iscsi_remove_template_hdr(ctrl);
-
-       for (i = 0; i < phba->params.cxns_per_ctrl; i++) {
-               q = &phwi_context->be_wrbq[i];
-               if (q->created)
-                       beiscsi_cmd_q_destroy(ctrl, q, QTYPE_WRBQ);
-       }
-       kfree(phwi_context->be_wrbq);
-       free_wrb_handles(phba);
-
-       for (ulp_num = 0; ulp_num < BEISCSI_ULP_COUNT; ulp_num++) {
-               if (test_bit(ulp_num, &phba->fw_config.ulp_supported)) {
-
-                       q = &phwi_context->be_def_hdrq[ulp_num];
-                       if (q->created)
-                               beiscsi_cmd_q_destroy(ctrl, q, QTYPE_DPDUQ);
-
-                       q = &phwi_context->be_def_dataq[ulp_num];
-                       if (q->created)
-                               beiscsi_cmd_q_destroy(ctrl, q, QTYPE_DPDUQ);
-
-                       pasync_ctx = phwi_ctrlr->phwi_ctxt->pasync_ctx[ulp_num];
-               }
-       }
-
-       beiscsi_cmd_q_destroy(ctrl, NULL, QTYPE_SGL);
-
-       for (i = 0; i < (phba->num_cpus); i++) {
-               q = &phwi_context->be_cq[i];
-               if (q->created) {
-                       be_queue_free(phba, q);
-                       beiscsi_cmd_q_destroy(ctrl, q, QTYPE_CQ);
-               }
-       }
-
-       be_mcc_queues_destroy(phba);
-       if (phba->msix_enabled)
-               eq_for_mcc = 1;
-       else
-               eq_for_mcc = 0;
-       for (i = 0; i < (phba->num_cpus + eq_for_mcc); i++) {
-               q = &phwi_context->be_eq[i].q;
-               if (q->created) {
-                       be_queue_free(phba, q);
-                       beiscsi_cmd_q_destroy(ctrl, q, QTYPE_EQ);
-               }
-       }
-       be_cmd_fw_uninit(ctrl);
-}
-
 static int be_mcc_queues_create(struct beiscsi_hba *phba,
                                struct hwi_context_memory *phwi_context)
 {
@@ -3875,7 +3608,119 @@ static void find_num_cpus(struct beiscsi_hba *phba)
        }
 }
 
-static int hwi_init_port(struct beiscsi_hba *phba)
+static void hwi_purge_eq(struct beiscsi_hba *phba)
+{
+       struct hwi_controller *phwi_ctrlr;
+       struct hwi_context_memory *phwi_context;
+       struct be_queue_info *eq;
+       struct be_eq_entry *eqe = NULL;
+       int i, eq_msix;
+       unsigned int num_processed;
+
+       if (beiscsi_hba_in_error(phba))
+               return;
+
+       phwi_ctrlr = phba->phwi_ctrlr;
+       phwi_context = phwi_ctrlr->phwi_ctxt;
+       if (phba->msix_enabled)
+               eq_msix = 1;
+       else
+               eq_msix = 0;
+
+       for (i = 0; i < (phba->num_cpus + eq_msix); i++) {
+               eq = &phwi_context->be_eq[i].q;
+               eqe = queue_tail_node(eq);
+               num_processed = 0;
+               while (eqe->dw[offsetof(struct amap_eq_entry, valid) / 32]
+                                       & EQE_VALID_MASK) {
+                       AMAP_SET_BITS(struct amap_eq_entry, valid, eqe, 0);
+                       queue_tail_inc(eq);
+                       eqe = queue_tail_node(eq);
+                       num_processed++;
+               }
+
+               if (num_processed)
+                       hwi_ring_eq_db(phba, eq->id, 1, num_processed, 1, 1);
+       }
+}
+
+static void hwi_cleanup_port(struct beiscsi_hba *phba)
+{
+       struct be_queue_info *q;
+       struct be_ctrl_info *ctrl = &phba->ctrl;
+       struct hwi_controller *phwi_ctrlr;
+       struct hwi_context_memory *phwi_context;
+       struct hd_async_context *pasync_ctx;
+       int i, eq_for_mcc, ulp_num;
+
+       for (ulp_num = 0; ulp_num < BEISCSI_ULP_COUNT; ulp_num++)
+               if (test_bit(ulp_num, &phba->fw_config.ulp_supported))
+                       beiscsi_cmd_iscsi_cleanup(phba, ulp_num);
+
+       /**
+        * Purge all EQ entries that may have been left out. This is to
+        * workaround a problem we've seen occasionally where driver gets an
+        * interrupt with EQ entry bit set after stopping the controller.
+        */
+       hwi_purge_eq(phba);
+
+       phwi_ctrlr = phba->phwi_ctrlr;
+       phwi_context = phwi_ctrlr->phwi_ctxt;
+
+       be_cmd_iscsi_remove_template_hdr(ctrl);
+
+       for (i = 0; i < phba->params.cxns_per_ctrl; i++) {
+               q = &phwi_context->be_wrbq[i];
+               if (q->created)
+                       beiscsi_cmd_q_destroy(ctrl, q, QTYPE_WRBQ);
+       }
+       kfree(phwi_context->be_wrbq);
+       free_wrb_handles(phba);
+
+       for (ulp_num = 0; ulp_num < BEISCSI_ULP_COUNT; ulp_num++) {
+               if (test_bit(ulp_num, &phba->fw_config.ulp_supported)) {
+
+                       q = &phwi_context->be_def_hdrq[ulp_num];
+                       if (q->created)
+                               beiscsi_cmd_q_destroy(ctrl, q, QTYPE_DPDUQ);
+
+                       q = &phwi_context->be_def_dataq[ulp_num];
+                       if (q->created)
+                               beiscsi_cmd_q_destroy(ctrl, q, QTYPE_DPDUQ);
+
+                       pasync_ctx = phwi_ctrlr->phwi_ctxt->pasync_ctx[ulp_num];
+               }
+       }
+
+       beiscsi_cmd_q_destroy(ctrl, NULL, QTYPE_SGL);
+
+       for (i = 0; i < (phba->num_cpus); i++) {
+               q = &phwi_context->be_cq[i];
+               if (q->created) {
+                       be_queue_free(phba, q);
+                       beiscsi_cmd_q_destroy(ctrl, q, QTYPE_CQ);
+               }
+       }
+
+       be_mcc_queues_destroy(phba);
+       if (phba->msix_enabled)
+               eq_for_mcc = 1;
+       else
+               eq_for_mcc = 0;
+       for (i = 0; i < (phba->num_cpus + eq_for_mcc); i++) {
+               q = &phwi_context->be_eq[i].q;
+               if (q->created) {
+                       be_queue_free(phba, q);
+                       beiscsi_cmd_q_destroy(ctrl, q, QTYPE_EQ);
+               }
+       }
+       /* this ensures complete FW cleanup */
+       beiscsi_cmd_function_reset(phba);
+       /* last communication, indicate driver is unloading */
+       beiscsi_cmd_special_wrb(&phba->ctrl, 0);
+}
+
+static int hwi_init_port(struct beiscsi_hba *phba)
 {
        struct hwi_controller *phwi_ctrlr;
        struct hwi_context_memory *phwi_context;
@@ -3887,9 +3732,8 @@ static int hwi_init_port(struct beiscsi_hba *phba)
        phwi_context = phwi_ctrlr->phwi_ctxt;
        phwi_context->max_eqd = 128;
        phwi_context->min_eqd = 0;
-       phwi_context->cur_eqd = 0;
-       be_cmd_fw_initialize(&phba->ctrl);
-       /* set optic state to unknown */
+       phwi_context->cur_eqd = 32;
+       /* set port optic state to unknown */
        phba->optic_state = 0xff;
 
        status = beiscsi_create_eqs(phba, phwi_context);
@@ -3903,7 +3747,7 @@ static int hwi_init_port(struct beiscsi_hba *phba)
        if (status != 0)
                goto error;
 
-       status = mgmt_check_supported_fw(ctrl, phba);
+       status = beiscsi_check_supported_fw(ctrl, phba);
        if (status != 0) {
                beiscsi_log(phba, KERN_ERR, BEISCSI_LOG_INIT,
                            "BM_%d : Unsupported fw version\n");
@@ -3919,7 +3763,6 @@ static int hwi_init_port(struct beiscsi_hba *phba)
 
        for (ulp_num = 0; ulp_num < BEISCSI_ULP_COUNT; ulp_num++) {
                if (test_bit(ulp_num, &phba->fw_config.ulp_supported)) {
-
                        def_pdu_ring_sz =
                                BEISCSI_GET_CID_COUNT(phba, ulp_num) *
                                sizeof(struct phys_addr);
@@ -3945,6 +3788,15 @@ static int hwi_init_port(struct beiscsi_hba *phba)
                                            ulp_num);
                                goto error;
                        }
+                       /**
+                        * Now that the default PDU rings have been created,
+                        * let EP know about it.
+                        * Call beiscsi_cmd_iscsi_cleanup before posting?
+                        */
+                       beiscsi_hdq_post_handles(phba, BEISCSI_DEFQ_HDR,
+                                                ulp_num);
+                       beiscsi_hdq_post_handles(phba, BEISCSI_DEFQ_DATA,
+                                                ulp_num);
                }
        }
 
@@ -3973,7 +3825,7 @@ static int hwi_init_port(struct beiscsi_hba *phba)
 
                if (test_bit(ulp_num, &phba->fw_config.ulp_supported)) {
                        uint16_t cri = 0;
-                       struct hwi_async_pdu_context *pasync_ctx;
+                       struct hd_async_context *pasync_ctx;
 
                        pasync_ctx = HWI_GET_ASYNC_PDU_CTX(
                                     phwi_ctrlr, ulp_num);
@@ -3985,6 +3837,14 @@ static int hwi_init_port(struct beiscsi_hba *phba)
                                        phwi_ctrlr->wrb_context[cri].cid] =
                                        async_arr_idx++;
                        }
+                       /**
+                        * Now that the default PDU rings have been created,
+                        * let EP know about it.
+                        */
+                       beiscsi_hdq_post_handles(phba, BEISCSI_DEFQ_HDR,
+                                                ulp_num);
+                       beiscsi_hdq_post_handles(phba, BEISCSI_DEFQ_DATA,
+                                                ulp_num);
                }
        }
 
@@ -3995,7 +3855,7 @@ static int hwi_init_port(struct beiscsi_hba *phba)
 error:
        beiscsi_log(phba, KERN_ERR, BEISCSI_LOG_INIT,
                    "BM_%d : hwi_init_port failed");
-       hwi_cleanup(phba);
+       hwi_cleanup_port(phba);
        return status;
 }
 
@@ -4354,149 +4214,6 @@ static void hwi_disable_intr(struct beiscsi_hba *phba)
                            "BM_%d : In hwi_disable_intr, Already Disabled\n");
 }
 
-/**
- * beiscsi_get_boot_info()- Get the boot session info
- * @phba: The device priv structure instance
- *
- * Get the boot target info and store in driver priv structure
- *
- * return values
- *     Success: 0
- *     Failure: Non-Zero Value
- **/
-static int beiscsi_get_boot_info(struct beiscsi_hba *phba)
-{
-       struct be_cmd_get_session_resp *session_resp;
-       struct be_dma_mem nonemb_cmd;
-       unsigned int tag;
-       unsigned int s_handle;
-       int ret = -ENOMEM;
-
-       /* Get the session handle of the boot target */
-       ret = be_mgmt_get_boot_shandle(phba, &s_handle);
-       if (ret) {
-               beiscsi_log(phba, KERN_ERR,
-                           BEISCSI_LOG_INIT | BEISCSI_LOG_CONFIG,
-                           "BM_%d : No boot session\n");
-
-               if (ret == -ENXIO)
-                       phba->get_boot = 0;
-
-
-               return ret;
-       }
-       phba->get_boot = 0;
-       nonemb_cmd.va = pci_zalloc_consistent(phba->ctrl.pdev,
-                                             sizeof(*session_resp),
-                                             &nonemb_cmd.dma);
-       if (nonemb_cmd.va == NULL) {
-               beiscsi_log(phba, KERN_ERR,
-                           BEISCSI_LOG_INIT | BEISCSI_LOG_CONFIG,
-                           "BM_%d : Failed to allocate memory for"
-                           "beiscsi_get_session_info\n");
-
-               return -ENOMEM;
-       }
-
-       tag = mgmt_get_session_info(phba, s_handle,
-                                   &nonemb_cmd);
-       if (!tag) {
-               beiscsi_log(phba, KERN_ERR,
-                           BEISCSI_LOG_INIT | BEISCSI_LOG_CONFIG,
-                           "BM_%d : beiscsi_get_session_info"
-                           " Failed\n");
-
-               goto boot_freemem;
-       }
-
-       ret = beiscsi_mccq_compl_wait(phba, tag, NULL, &nonemb_cmd);
-       if (ret) {
-               beiscsi_log(phba, KERN_ERR,
-                           BEISCSI_LOG_INIT | BEISCSI_LOG_CONFIG,
-                           "BM_%d : beiscsi_get_session_info Failed");
-
-               if (ret != -EBUSY)
-                       goto boot_freemem;
-               else
-                       return ret;
-       }
-
-       session_resp = nonemb_cmd.va ;
-
-       memcpy(&phba->boot_sess, &session_resp->session_info,
-              sizeof(struct mgmt_session_info));
-
-        beiscsi_logout_fw_sess(phba,
-                               phba->boot_sess.session_handle);
-       ret = 0;
-
-boot_freemem:
-       pci_free_consistent(phba->ctrl.pdev, nonemb_cmd.size,
-                   nonemb_cmd.va, nonemb_cmd.dma);
-       return ret;
-}
-
-static void beiscsi_boot_release(void *data)
-{
-       struct beiscsi_hba *phba = data;
-
-       scsi_host_put(phba->shost);
-}
-
-static int beiscsi_setup_boot_info(struct beiscsi_hba *phba)
-{
-       struct iscsi_boot_kobj *boot_kobj;
-
-       /* it has been created previously */
-       if (phba->boot_kset)
-               return 0;
-
-       /* get boot info using mgmt cmd */
-       if (beiscsi_get_boot_info(phba))
-               /* Try to see if we can carry on without this */
-               return 0;
-
-       phba->boot_kset = iscsi_boot_create_host_kset(phba->shost->host_no);
-       if (!phba->boot_kset)
-               return -ENOMEM;
-
-       /* get a ref because the show function will ref the phba */
-       if (!scsi_host_get(phba->shost))
-               goto free_kset;
-       boot_kobj = iscsi_boot_create_target(phba->boot_kset, 0, phba,
-                                            beiscsi_show_boot_tgt_info,
-                                            beiscsi_tgt_get_attr_visibility,
-                                            beiscsi_boot_release);
-       if (!boot_kobj)
-               goto put_shost;
-
-       if (!scsi_host_get(phba->shost))
-               goto free_kset;
-       boot_kobj = iscsi_boot_create_initiator(phba->boot_kset, 0, phba,
-                                               beiscsi_show_boot_ini_info,
-                                               beiscsi_ini_get_attr_visibility,
-                                               beiscsi_boot_release);
-       if (!boot_kobj)
-               goto put_shost;
-
-       if (!scsi_host_get(phba->shost))
-               goto free_kset;
-       boot_kobj = iscsi_boot_create_ethernet(phba->boot_kset, 0, phba,
-                                              beiscsi_show_boot_eth_info,
-                                              beiscsi_eth_get_attr_visibility,
-                                              beiscsi_boot_release);
-       if (!boot_kobj)
-               goto put_shost;
-       return 0;
-
-put_shost:
-       scsi_host_put(phba->shost);
-free_kset:
-       iscsi_boot_destroy_kset(phba->boot_kset);
-       phba->boot_kset = NULL;
-       return -ENOMEM;
-}
-
 static int beiscsi_init_port(struct beiscsi_hba *phba)
 {
        int ret;
@@ -4516,7 +4233,8 @@ static int beiscsi_init_port(struct beiscsi_hba *phba)
                goto do_cleanup_ctrlr;
        }
 
-       if (hba_setup_cid_tbls(phba)) {
+       ret = hba_setup_cid_tbls(phba);
+       if (ret < 0) {
                beiscsi_log(phba, KERN_ERR, BEISCSI_LOG_INIT,
                            "BM_%d : Failed in hba_setup_cid_tbls\n");
                kfree(phba->io_sgl_hndl_base);
@@ -4527,61 +4245,15 @@ static int beiscsi_init_port(struct beiscsi_hba *phba)
        return ret;
 
 do_cleanup_ctrlr:
-       hwi_cleanup(phba);
+       hwi_cleanup_port(phba);
        return ret;
 }
 
-static void hwi_purge_eq(struct beiscsi_hba *phba)
-{
-       struct hwi_controller *phwi_ctrlr;
-       struct hwi_context_memory *phwi_context;
-       struct be_queue_info *eq;
-       struct be_eq_entry *eqe = NULL;
-       int i, eq_msix;
-       unsigned int num_processed;
-
-       phwi_ctrlr = phba->phwi_ctrlr;
-       phwi_context = phwi_ctrlr->phwi_ctxt;
-       if (phba->msix_enabled)
-               eq_msix = 1;
-       else
-               eq_msix = 0;
-
-       for (i = 0; i < (phba->num_cpus + eq_msix); i++) {
-               eq = &phwi_context->be_eq[i].q;
-               eqe = queue_tail_node(eq);
-               num_processed = 0;
-               while (eqe->dw[offsetof(struct amap_eq_entry, valid) / 32]
-                                       & EQE_VALID_MASK) {
-                       AMAP_SET_BITS(struct amap_eq_entry, valid, eqe, 0);
-                       queue_tail_inc(eq);
-                       eqe = queue_tail_node(eq);
-                       num_processed++;
-               }
-
-               if (num_processed)
-                       hwi_ring_eq_db(phba, eq->id, 1, num_processed, 1, 1);
-       }
-}
-
-static void beiscsi_clean_port(struct beiscsi_hba *phba)
+static void beiscsi_cleanup_port(struct beiscsi_hba *phba)
 {
-       int mgmt_status, ulp_num;
        struct ulp_cid_info *ptr_cid_info = NULL;
+       int ulp_num;
 
-       for (ulp_num = 0; ulp_num < BEISCSI_ULP_COUNT; ulp_num++) {
-               if (test_bit(ulp_num, (void *)&phba->fw_config.ulp_supported)) {
-                       mgmt_status = mgmt_epfw_cleanup(phba, ulp_num);
-                       if (mgmt_status)
-                               beiscsi_log(phba, KERN_WARNING,
-                                           BEISCSI_LOG_INIT,
-                                           "BM_%d : mgmt_epfw_cleanup FAILED"
-                                           " for ULP_%d\n", ulp_num);
-               }
-       }
-
-       hwi_purge_eq(phba);
-       hwi_cleanup(phba);
        kfree(phba->io_sgl_hndl_base);
        kfree(phba->eh_sgl_hndl_base);
        kfree(phba->ep_array);
@@ -4598,7 +4270,6 @@ static void beiscsi_clean_port(struct beiscsi_hba *phba)
                        }
                }
        }
-
 }
 
 /**
@@ -4625,16 +4296,12 @@ beiscsi_free_mgmt_task_handles(struct beiscsi_conn *beiscsi_conn,
        io_task = task->dd_data;
 
        if (io_task->pwrb_handle) {
-               memset(io_task->pwrb_handle->pwrb, 0,
-                      sizeof(struct iscsi_wrb));
-               free_wrb_handle(phba, pwrb_context,
-                               io_task->pwrb_handle);
+               free_wrb_handle(phba, pwrb_context, io_task->pwrb_handle);
                io_task->pwrb_handle = NULL;
        }
 
        if (io_task->psgl_handle) {
-               free_mgmt_sgl_handle(phba,
-                                    io_task->psgl_handle);
+               free_mgmt_sgl_handle(phba, io_task->psgl_handle);
                io_task->psgl_handle = NULL;
        }
 
@@ -4671,6 +4338,7 @@ static void beiscsi_cleanup_task(struct iscsi_task *task)
                pci_pool_free(beiscsi_sess->bhs_pool, io_task->cmd_bhs,
                              io_task->bhs_pa.u.a64.address);
                io_task->cmd_bhs = NULL;
+               task->hdr = NULL;
        }
 
        if (task->sc) {
@@ -4686,7 +4354,8 @@ static void beiscsi_cleanup_task(struct iscsi_task *task)
                }
 
                if (io_task->scsi_cmnd) {
-                       scsi_dma_unmap(io_task->scsi_cmnd);
+                       if (io_task->num_sg)
+                               scsi_dma_unmap(io_task->scsi_cmnd);
                        io_task->scsi_cmnd = NULL;
                }
        } else {
@@ -5051,7 +4720,6 @@ static int beiscsi_mtask(struct iscsi_task *task)
 
        cid = beiscsi_conn->beiscsi_conn_cid;
        pwrb = io_task->pwrb_handle->pwrb;
-       memset(pwrb, 0, sizeof(*pwrb));
 
        if (is_chip_be2_be3r(phba)) {
                AMAP_SET_BITS(struct amap_iscsi_wrb, cmdsn_itt, pwrb,
@@ -5165,6 +4833,15 @@ static int beiscsi_task_xmit(struct iscsi_task *task)
        int num_sg;
        unsigned int  writedir = 0, xferlen = 0;
 
+       phba = io_task->conn->phba;
+       /**
+        * HBA in error includes BEISCSI_HBA_FW_TIMEOUT. IO path might be
+        * operational if FW still gets heartbeat from EP FW. Is management
+        * path really needed to continue further?
+        */
+       if (!beiscsi_hba_is_online(phba))
+               return -EIO;
+
        if (!io_task->conn->login_in_progress)
                task->hdr->exp_statsn = 0;
 
@@ -5172,8 +4849,8 @@ static int beiscsi_task_xmit(struct iscsi_task *task)
                return beiscsi_mtask(task);
 
        io_task->scsi_cmnd = sc;
+       io_task->num_sg = 0;
        num_sg = scsi_dma_map(sc);
-       phba = io_task->conn->phba;
        if (num_sg < 0) {
                beiscsi_log(phba, KERN_ERR,
                            BEISCSI_LOG_IO | BEISCSI_LOG_ISCSI,
@@ -5184,6 +4861,11 @@ static int beiscsi_task_xmit(struct iscsi_task *task)
 
                return num_sg;
        }
+       /**
+        * For scsi cmd task, check num_sg before unmapping in cleanup_task.
+        * For management task, cleanup_task checks mtask_addr before unmapping.
+        */
+       io_task->num_sg = num_sg;
        xferlen = scsi_bufflen(sc);
        sg = scsi_sglist(sc);
        if (sc->sc_data_direction == DMA_TO_DEVICE)
@@ -5213,6 +4895,12 @@ static int beiscsi_bsg_request(struct bsg_job *job)
        shost = iscsi_job_to_shost(job);
        phba = iscsi_host_priv(shost);
 
+       if (!beiscsi_hba_is_online(phba)) {
+               beiscsi_log(phba, KERN_INFO, BEISCSI_LOG_CONFIG,
+                           "BM_%d : HBA in error 0x%lx\n", phba->state);
+               return -ENXIO;
+       }
+
        switch (bsg_req->msgcode) {
        case ISCSI_BSG_HST_VENDOR:
                nonemb_cmd.va = pci_alloc_consistent(phba->ctrl.pdev,
@@ -5240,6 +4928,14 @@ static int beiscsi_bsg_request(struct bsg_job *job)
                                        phba->ctrl.mcc_tag_status[tag],
                                        msecs_to_jiffies(
                                        BEISCSI_HOST_MBX_TIMEOUT));
+
+               if (!test_bit(BEISCSI_HBA_ONLINE, &phba->state)) {
+                       clear_bit(MCC_TAG_STATE_RUNNING,
+                                 &phba->ctrl.ptag_state[tag].tag_state);
+                       pci_free_consistent(phba->ctrl.pdev, nonemb_cmd.size,
+                                           nonemb_cmd.va, nonemb_cmd.dma);
+                       return -EIO;
+               }
                extd_status = (phba->ctrl.mcc_tag_status[tag] &
                               CQE_STATUS_ADDL_MASK) >> CQE_STATUS_ADDL_SHIFT;
                status = phba->ctrl.mcc_tag_status[tag] & CQE_STATUS_MASK;
@@ -5283,106 +4979,294 @@ void beiscsi_hba_attrs_init(struct beiscsi_hba *phba)
        beiscsi_log_enable_init(phba, beiscsi_log_enable);
 }
 
-/*
- * beiscsi_quiesce()- Cleanup Driver resources
- * @phba: Instance Priv structure
- * @unload_state:i Clean or EEH unload state
- *
- * Free the OS and HW resources held by the driver
- **/
-static void beiscsi_quiesce(struct beiscsi_hba *phba,
-               uint32_t unload_state)
+void beiscsi_start_boot_work(struct beiscsi_hba *phba, unsigned int s_handle)
 {
-       struct hwi_controller *phwi_ctrlr;
-       struct hwi_context_memory *phwi_context;
-       struct be_eq_obj *pbe_eq;
-       unsigned int i, msix_vec;
+       if (phba->boot_struct.boot_kset)
+               return;
 
-       phwi_ctrlr = phba->phwi_ctrlr;
-       phwi_context = phwi_ctrlr->phwi_ctxt;
-       hwi_disable_intr(phba);
-       if (phba->msix_enabled) {
-               for (i = 0; i <= phba->num_cpus; i++) {
-                       msix_vec = phba->msix_entries[i].vector;
-                       free_irq(msix_vec, &phwi_context->be_eq[i]);
-                       kfree(phba->msi_name[i]);
-               }
-       } else
-               if (phba->pcidev->irq)
-                       free_irq(phba->pcidev->irq, phba);
-       pci_disable_msix(phba->pcidev);
-       cancel_delayed_work_sync(&phba->beiscsi_hw_check_task);
+       /* skip if boot work is already in progress */
+       if (test_and_set_bit(BEISCSI_HBA_BOOT_WORK, &phba->state))
+               return;
 
-       for (i = 0; i < phba->num_cpus; i++) {
-               pbe_eq = &phwi_context->be_eq[i];
-               irq_poll_disable(&pbe_eq->iopoll);
+       phba->boot_struct.retry = 3;
+       phba->boot_struct.tag = 0;
+       phba->boot_struct.s_handle = s_handle;
+       phba->boot_struct.action = BEISCSI_BOOT_GET_SHANDLE;
+       schedule_work(&phba->boot_work);
+}
+
+static ssize_t beiscsi_show_boot_tgt_info(void *data, int type, char *buf)
+{
+       struct beiscsi_hba *phba = data;
+       struct mgmt_session_info *boot_sess = &phba->boot_struct.boot_sess;
+       struct mgmt_conn_info *boot_conn = &boot_sess->conn_list[0];
+       char *str = buf;
+       int rc = -EPERM;
+
+       switch (type) {
+       case ISCSI_BOOT_TGT_NAME:
+               rc = sprintf(buf, "%.*s\n",
+                           (int)strlen(boot_sess->target_name),
+                           (char *)&boot_sess->target_name);
+               break;
+       case ISCSI_BOOT_TGT_IP_ADDR:
+               if (boot_conn->dest_ipaddr.ip_type == BEISCSI_IP_TYPE_V4)
+                       rc = sprintf(buf, "%pI4\n",
+                               (char *)&boot_conn->dest_ipaddr.addr);
+               else
+                       rc = sprintf(str, "%pI6\n",
+                               (char *)&boot_conn->dest_ipaddr.addr);
+               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->negotiated_login_options.auth_data.chap.
+                            target_chap_name_length,
+                            (char *)&boot_conn->negotiated_login_options.
+                            auth_data.chap.target_chap_name);
+               break;
+       case ISCSI_BOOT_TGT_CHAP_SECRET:
+               rc = sprintf(str,  "%.*s\n",
+                            boot_conn->negotiated_login_options.auth_data.chap.
+                            target_secret_length,
+                            (char *)&boot_conn->negotiated_login_options.
+                            auth_data.chap.target_secret);
+               break;
+       case ISCSI_BOOT_TGT_REV_CHAP_NAME:
+               rc = sprintf(str,  "%.*s\n",
+                            boot_conn->negotiated_login_options.auth_data.chap.
+                            intr_chap_name_length,
+                            (char *)&boot_conn->negotiated_login_options.
+                            auth_data.chap.intr_chap_name);
+               break;
+       case ISCSI_BOOT_TGT_REV_CHAP_SECRET:
+               rc = sprintf(str,  "%.*s\n",
+                            boot_conn->negotiated_login_options.auth_data.chap.
+                            intr_secret_length,
+                            (char *)&boot_conn->negotiated_login_options.
+                            auth_data.chap.intr_secret);
+               break;
+       case ISCSI_BOOT_TGT_FLAGS:
+               rc = sprintf(str, "2\n");
+               break;
+       case ISCSI_BOOT_TGT_NIC_ASSOC:
+               rc = sprintf(str, "0\n");
+               break;
        }
+       return rc;
+}
 
-       if (unload_state == BEISCSI_CLEAN_UNLOAD) {
-               destroy_workqueue(phba->wq);
-               beiscsi_clean_port(phba);
-               beiscsi_free_mem(phba);
+static ssize_t beiscsi_show_boot_ini_info(void *data, int type, char *buf)
+{
+       struct beiscsi_hba *phba = data;
+       char *str = buf;
+       int rc = -EPERM;
 
-               beiscsi_unmap_pci_function(phba);
-               pci_free_consistent(phba->pcidev,
-                                   phba->ctrl.mbox_mem_alloced.size,
-                                   phba->ctrl.mbox_mem_alloced.va,
-                                   phba->ctrl.mbox_mem_alloced.dma);
-       } else {
-               hwi_purge_eq(phba);
-               hwi_cleanup(phba);
+       switch (type) {
+       case ISCSI_BOOT_INI_INITIATOR_NAME:
+               rc = sprintf(str, "%s\n",
+                            phba->boot_struct.boot_sess.initiator_iscsiname);
+               break;
        }
+       return rc;
+}
+
+static ssize_t beiscsi_show_boot_eth_info(void *data, int type, char *buf)
+{
+       struct beiscsi_hba *phba = data;
+       char *str = buf;
+       int rc = -EPERM;
 
+       switch (type) {
+       case ISCSI_BOOT_ETH_FLAGS:
+               rc = sprintf(str, "2\n");
+               break;
+       case ISCSI_BOOT_ETH_INDEX:
+               rc = sprintf(str, "0\n");
+               break;
+       case ISCSI_BOOT_ETH_MAC:
+               rc  = beiscsi_get_macaddr(str, phba);
+               break;
+       }
+       return rc;
 }
 
-static void beiscsi_remove(struct pci_dev *pcidev)
+static umode_t beiscsi_tgt_get_attr_visibility(void *data, int type)
 {
-       struct beiscsi_hba *phba = NULL;
+       umode_t rc = 0;
 
-       phba = pci_get_drvdata(pcidev);
-       if (!phba) {
-               dev_err(&pcidev->dev, "beiscsi_remove called with no phba\n");
-               return;
+       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;
        }
+       return rc;
+}
 
-       beiscsi_destroy_def_ifaces(phba);
-       iscsi_boot_destroy_kset(phba->boot_kset);
-       iscsi_host_remove(phba->shost);
-       beiscsi_quiesce(phba, BEISCSI_CLEAN_UNLOAD);
-       pci_dev_put(phba->pcidev);
-       iscsi_host_free(phba->shost);
-       pci_disable_pcie_error_reporting(pcidev);
-       pci_set_drvdata(pcidev, NULL);
-       pci_release_regions(pcidev);
-       pci_disable_device(pcidev);
+static umode_t beiscsi_ini_get_attr_visibility(void *data, int type)
+{
+       umode_t rc = 0;
+
+       switch (type) {
+       case ISCSI_BOOT_INI_INITIATOR_NAME:
+               rc = S_IRUGO;
+               break;
+       }
+       return rc;
 }
 
-static void beiscsi_msix_enable(struct beiscsi_hba *phba)
+static umode_t beiscsi_eth_get_attr_visibility(void *data, int type)
 {
-       int i, status;
+       umode_t rc = 0;
 
-       for (i = 0; i <= phba->num_cpus; i++)
-               phba->msix_entries[i].entry = i;
+       switch (type) {
+       case ISCSI_BOOT_ETH_FLAGS:
+       case ISCSI_BOOT_ETH_MAC:
+       case ISCSI_BOOT_ETH_INDEX:
+               rc = S_IRUGO;
+               break;
+       }
+       return rc;
+}
 
-       status = pci_enable_msix_range(phba->pcidev, phba->msix_entries,
-                                      phba->num_cpus + 1, phba->num_cpus + 1);
-       if (status > 0)
-               phba->msix_enabled = true;
+static void beiscsi_boot_kobj_release(void *data)
+{
+       struct beiscsi_hba *phba = data;
+
+       scsi_host_put(phba->shost);
+}
+
+static int beiscsi_boot_create_kset(struct beiscsi_hba *phba)
+{
+       struct boot_struct *bs = &phba->boot_struct;
+       struct iscsi_boot_kobj *boot_kobj;
+
+       if (bs->boot_kset) {
+               __beiscsi_log(phba, KERN_ERR,
+                             "BM_%d: boot_kset already created\n");
+               return 0;
+       }
+
+       bs->boot_kset = iscsi_boot_create_host_kset(phba->shost->host_no);
+       if (!bs->boot_kset) {
+               __beiscsi_log(phba, KERN_ERR,
+                             "BM_%d: boot_kset alloc failed\n");
+               return -ENOMEM;
+       }
+
+       /* get shost ref because the show function will refer phba */
+       if (!scsi_host_get(phba->shost))
+               goto free_kset;
+
+       boot_kobj = iscsi_boot_create_target(bs->boot_kset, 0, phba,
+                                            beiscsi_show_boot_tgt_info,
+                                            beiscsi_tgt_get_attr_visibility,
+                                            beiscsi_boot_kobj_release);
+       if (!boot_kobj)
+               goto put_shost;
+
+       if (!scsi_host_get(phba->shost))
+               goto free_kset;
+
+       boot_kobj = iscsi_boot_create_initiator(bs->boot_kset, 0, phba,
+                                               beiscsi_show_boot_ini_info,
+                                               beiscsi_ini_get_attr_visibility,
+                                               beiscsi_boot_kobj_release);
+       if (!boot_kobj)
+               goto put_shost;
+
+       if (!scsi_host_get(phba->shost))
+               goto free_kset;
+
+       boot_kobj = iscsi_boot_create_ethernet(bs->boot_kset, 0, phba,
+                                              beiscsi_show_boot_eth_info,
+                                              beiscsi_eth_get_attr_visibility,
+                                              beiscsi_boot_kobj_release);
+       if (!boot_kobj)
+               goto put_shost;
+
+       return 0;
+
+put_shost:
+       scsi_host_put(phba->shost);
+free_kset:
+       iscsi_boot_destroy_kset(bs->boot_kset);
+       bs->boot_kset = NULL;
+       return -ENOMEM;
+}
+
+static void beiscsi_boot_work(struct work_struct *work)
+{
+       struct beiscsi_hba *phba =
+               container_of(work, struct beiscsi_hba, boot_work);
+       struct boot_struct *bs = &phba->boot_struct;
+       unsigned int tag = 0;
 
-       return;
+       if (!beiscsi_hba_is_online(phba))
+               return;
+
+       beiscsi_log(phba, KERN_INFO,
+                   BEISCSI_LOG_CONFIG | BEISCSI_LOG_MBOX,
+                   "BM_%d : %s action %d\n",
+                   __func__, phba->boot_struct.action);
+
+       switch (phba->boot_struct.action) {
+       case BEISCSI_BOOT_REOPEN_SESS:
+               tag = beiscsi_boot_reopen_sess(phba);
+               break;
+       case BEISCSI_BOOT_GET_SHANDLE:
+               tag = __beiscsi_boot_get_shandle(phba, 1);
+               break;
+       case BEISCSI_BOOT_GET_SINFO:
+               tag = beiscsi_boot_get_sinfo(phba);
+               break;
+       case BEISCSI_BOOT_LOGOUT_SESS:
+               tag = beiscsi_boot_logout_sess(phba);
+               break;
+       case BEISCSI_BOOT_CREATE_KSET:
+               beiscsi_boot_create_kset(phba);
+               /**
+                * updated boot_kset is made visible to all before
+                * ending the boot work.
+                */
+               mb();
+               clear_bit(BEISCSI_HBA_BOOT_WORK, &phba->state);
+               return;
+       }
+       if (!tag) {
+               if (bs->retry--)
+                       schedule_work(&phba->boot_work);
+               else
+                       clear_bit(BEISCSI_HBA_BOOT_WORK, &phba->state);
+       }
 }
 
-static void be_eqd_update(struct beiscsi_hba *phba)
+static void beiscsi_eqd_update_work(struct work_struct *work)
 {
+       struct hwi_context_memory *phwi_context;
        struct be_set_eqd set_eqd[MAX_CPUS];
-       struct be_aic_obj *aic;
-       struct be_eq_obj *pbe_eq;
        struct hwi_controller *phwi_ctrlr;
-       struct hwi_context_memory *phwi_context;
+       struct be_eq_obj *pbe_eq;
+       struct beiscsi_hba *phba;
+       unsigned int pps, delta;
+       struct be_aic_obj *aic;
        int eqd, i, num = 0;
-       ulong now;
-       u32 pps, delta;
-       unsigned int tag;
+       unsigned long now;
+
+       phba = container_of(work, struct beiscsi_hba, eqd_update.work);
+       if (!beiscsi_hba_is_online(phba))
+               return;
 
        phwi_ctrlr = phba->phwi_ctrlr;
        phwi_context = phwi_ctrlr->phwi_ctxt;
@@ -5391,13 +5275,13 @@ static void be_eqd_update(struct beiscsi_hba *phba)
                aic = &phba->aic_obj[i];
                pbe_eq = &phwi_context->be_eq[i];
                now = jiffies;
-               if (!aic->jiffs || time_before(now, aic->jiffs) ||
+               if (!aic->jiffies || time_before(now, aic->jiffies) ||
                    pbe_eq->cq_count < aic->eq_prev) {
-                       aic->jiffs = now;
+                       aic->jiffies = now;
                        aic->eq_prev = pbe_eq->cq_count;
                        continue;
                }
-               delta = jiffies_to_msecs(now - aic->jiffs);
+               delta = jiffies_to_msecs(now - aic->jiffies);
                pps = (((u32)(pbe_eq->cq_count - aic->eq_prev) * 1000) / delta);
                eqd = (pps / 1500) << 2;
 
@@ -5406,7 +5290,7 @@ static void be_eqd_update(struct beiscsi_hba *phba)
                eqd = min_t(u32, eqd, phwi_context->max_eqd);
                eqd = max_t(u32, eqd, phwi_context->min_eqd);
 
-               aic->jiffs = now;
+               aic->jiffies = now;
                aic->eq_prev = pbe_eq->cq_count;
 
                if (eqd != aic->prev_eqd) {
@@ -5416,53 +5300,242 @@ static void be_eqd_update(struct beiscsi_hba *phba)
                        num++;
                }
        }
-       if (num) {
-               tag = be_cmd_modify_eq_delay(phba, set_eqd, num);
-               if (tag)
-                       beiscsi_mccq_compl_wait(phba, tag, NULL, NULL);
+       if (num)
+               /* completion of this is ignored */
+               beiscsi_modify_eq_delay(phba, set_eqd, num);
+
+       schedule_delayed_work(&phba->eqd_update,
+                             msecs_to_jiffies(BEISCSI_EQD_UPDATE_INTERVAL));
+}
+
+static void beiscsi_msix_enable(struct beiscsi_hba *phba)
+{
+       int i, status;
+
+       for (i = 0; i <= phba->num_cpus; i++)
+               phba->msix_entries[i].entry = i;
+
+       status = pci_enable_msix_range(phba->pcidev, phba->msix_entries,
+                                      phba->num_cpus + 1, phba->num_cpus + 1);
+       if (status > 0)
+               phba->msix_enabled = true;
+}
+
+static void beiscsi_hw_tpe_check(unsigned long ptr)
+{
+       struct beiscsi_hba *phba;
+       u32 wait;
+
+       phba = (struct beiscsi_hba *)ptr;
+       /* if not TPE, do nothing */
+       if (!beiscsi_detect_tpe(phba))
+               return;
+
+       /* wait default 4000ms before recovering */
+       wait = 4000;
+       if (phba->ue2rp > BEISCSI_UE_DETECT_INTERVAL)
+               wait = phba->ue2rp - BEISCSI_UE_DETECT_INTERVAL;
+       queue_delayed_work(phba->wq, &phba->recover_port,
+                          msecs_to_jiffies(wait));
+}
+
+static void beiscsi_hw_health_check(unsigned long ptr)
+{
+       struct beiscsi_hba *phba;
+
+       phba = (struct beiscsi_hba *)ptr;
+       beiscsi_detect_ue(phba);
+       if (beiscsi_detect_ue(phba)) {
+               __beiscsi_log(phba, KERN_ERR,
+                             "BM_%d : port in error: %lx\n", phba->state);
+               /* sessions are no longer valid, so first fail the sessions */
+               queue_work(phba->wq, &phba->sess_work);
+
+               /* detect UER supported */
+               if (!test_bit(BEISCSI_HBA_UER_SUPP, &phba->state))
+                       return;
+               /* modify this timer to check TPE */
+               phba->hw_check.function = beiscsi_hw_tpe_check;
        }
+
+       mod_timer(&phba->hw_check,
+                 jiffies + msecs_to_jiffies(BEISCSI_UE_DETECT_INTERVAL));
 }
 
-static void be_check_boot_session(struct beiscsi_hba *phba)
+/*
+ * beiscsi_enable_port()- Enables the disabled port.
+ * Only port resources freed in disable function are reallocated.
+ * This is called in HBA error handling path.
+ *
+ * @phba: Instance of driver private structure
+ *
+ **/
+static int beiscsi_enable_port(struct beiscsi_hba *phba)
 {
-       if (beiscsi_setup_boot_info(phba))
-               beiscsi_log(phba, KERN_ERR, BEISCSI_LOG_INIT,
-                           "BM_%d : Could not set up "
-                           "iSCSI boot info on async event.\n");
+       struct hwi_context_memory *phwi_context;
+       struct hwi_controller *phwi_ctrlr;
+       struct be_eq_obj *pbe_eq;
+       int ret, i;
+
+       if (test_bit(BEISCSI_HBA_ONLINE, &phba->state)) {
+               __beiscsi_log(phba, KERN_ERR,
+                             "BM_%d : %s : port is online %lx\n",
+                             __func__, phba->state);
+               return 0;
+       }
+
+       ret = beiscsi_init_sliport(phba);
+       if (ret)
+               return ret;
+
+       if (enable_msix)
+               find_num_cpus(phba);
+       else
+               phba->num_cpus = 1;
+       if (enable_msix) {
+               beiscsi_msix_enable(phba);
+               if (!phba->msix_enabled)
+                       phba->num_cpus = 1;
+       }
+
+       beiscsi_get_params(phba);
+       /* Re-enable UER. If different TPE occurs then it is recoverable. */
+       beiscsi_set_uer_feature(phba);
+
+       phba->shost->max_id = phba->params.cxns_per_ctrl;
+       phba->shost->can_queue = phba->params.ios_per_ctrl;
+       ret = hwi_init_controller(phba);
+       if (ret) {
+               __beiscsi_log(phba, KERN_ERR,
+                             "BM_%d : init controller failed %d\n", ret);
+               goto disable_msix;
+       }
+
+       for (i = 0; i < MAX_MCC_CMD; i++) {
+               init_waitqueue_head(&phba->ctrl.mcc_wait[i + 1]);
+               phba->ctrl.mcc_tag[i] = i + 1;
+               phba->ctrl.mcc_tag_status[i + 1] = 0;
+               phba->ctrl.mcc_tag_available++;
+       }
+
+       phwi_ctrlr = phba->phwi_ctrlr;
+       phwi_context = phwi_ctrlr->phwi_ctxt;
+       for (i = 0; i < phba->num_cpus; i++) {
+               pbe_eq = &phwi_context->be_eq[i];
+               irq_poll_init(&pbe_eq->iopoll, be_iopoll_budget, be_iopoll);
+       }
+
+       i = (phba->msix_enabled) ? i : 0;
+       /* Work item for MCC handling */
+       pbe_eq = &phwi_context->be_eq[i];
+       INIT_WORK(&pbe_eq->mcc_work, beiscsi_mcc_work);
+
+       ret = beiscsi_init_irqs(phba);
+       if (ret < 0) {
+               __beiscsi_log(phba, KERN_ERR,
+                             "BM_%d : setup IRQs failed %d\n", ret);
+               goto cleanup_port;
+       }
+       hwi_enable_intr(phba);
+       /* port operational: clear all error bits */
+       set_bit(BEISCSI_HBA_ONLINE, &phba->state);
+       __beiscsi_log(phba, KERN_INFO,
+                     "BM_%d : port online: 0x%lx\n", phba->state);
+
+       /* start hw_check timer and eqd_update work */
+       schedule_delayed_work(&phba->eqd_update,
+                             msecs_to_jiffies(BEISCSI_EQD_UPDATE_INTERVAL));
+
+       /**
+        * Timer function gets modified for TPE detection.
+        * Always reinit to do health check first.
+        */
+       phba->hw_check.function = beiscsi_hw_health_check;
+       mod_timer(&phba->hw_check,
+                 jiffies + msecs_to_jiffies(BEISCSI_UE_DETECT_INTERVAL));
+       return 0;
+
+cleanup_port:
+       for (i = 0; i < phba->num_cpus; i++) {
+               pbe_eq = &phwi_context->be_eq[i];
+               irq_poll_disable(&pbe_eq->iopoll);
+       }
+       hwi_cleanup_port(phba);
+
+disable_msix:
+       if (phba->msix_enabled)
+               pci_disable_msix(phba->pcidev);
+
+       return ret;
 }
 
 /*
- * beiscsi_hw_health_check()- Check adapter health
- * @work: work item to check HW health
+ * beiscsi_disable_port()- Disable port and cleanup driver resources.
+ * This is called in HBA error handling and driver removal.
+ * @phba: Instance Priv structure
+ * @unload: indicate driver is unloading
  *
- * Check if adapter in an unrecoverable state or not.
+ * Free the OS and HW resources held by the driver
  **/
-static void
-beiscsi_hw_health_check(struct work_struct *work)
+static void beiscsi_disable_port(struct beiscsi_hba *phba, int unload)
 {
-       struct beiscsi_hba *phba =
-               container_of(work, struct beiscsi_hba,
-                            beiscsi_hw_check_task.work);
+       struct hwi_context_memory *phwi_context;
+       struct hwi_controller *phwi_ctrlr;
+       struct be_eq_obj *pbe_eq;
+       unsigned int i, msix_vec;
 
-       be_eqd_update(phba);
+       if (!test_and_clear_bit(BEISCSI_HBA_ONLINE, &phba->state))
+               return;
 
-       if (phba->state & BE_ADAPTER_CHECK_BOOT) {
-               if ((phba->get_boot > 0) && (!phba->boot_kset)) {
-                       phba->get_boot--;
-                       if (!(phba->get_boot % BE_GET_BOOT_TO))
-                               be_check_boot_session(phba);
-               } else {
-                       phba->state &= ~BE_ADAPTER_CHECK_BOOT;
-                       phba->get_boot = 0;
+       phwi_ctrlr = phba->phwi_ctrlr;
+       phwi_context = phwi_ctrlr->phwi_ctxt;
+       hwi_disable_intr(phba);
+       if (phba->msix_enabled) {
+               for (i = 0; i <= phba->num_cpus; i++) {
+                       msix_vec = phba->msix_entries[i].vector;
+                       free_irq(msix_vec, &phwi_context->be_eq[i]);
+                       kfree(phba->msi_name[i]);
                }
+       } else
+               if (phba->pcidev->irq)
+                       free_irq(phba->pcidev->irq, phba);
+       pci_disable_msix(phba->pcidev);
+
+       for (i = 0; i < phba->num_cpus; i++) {
+               pbe_eq = &phwi_context->be_eq[i];
+               irq_poll_disable(&pbe_eq->iopoll);
+       }
+       cancel_delayed_work_sync(&phba->eqd_update);
+       cancel_work_sync(&phba->boot_work);
+       /* WQ might be running cancel queued mcc_work if we are not exiting */
+       if (!unload && beiscsi_hba_in_error(phba)) {
+               pbe_eq = &phwi_context->be_eq[i];
+               cancel_work_sync(&pbe_eq->mcc_work);
        }
+       hwi_cleanup_port(phba);
+}
 
-       beiscsi_ue_detect(phba);
+static void beiscsi_sess_work(struct work_struct *work)
+{
+       struct beiscsi_hba *phba;
 
-       schedule_delayed_work(&phba->beiscsi_hw_check_task,
-                             msecs_to_jiffies(1000));
+       phba = container_of(work, struct beiscsi_hba, sess_work);
+       /*
+        * This work gets scheduled only in case of HBA error.
+        * Old sessions are gone so need to be re-established.
+        * iscsi_session_failure needs process context hence this work.
+        */
+       iscsi_host_for_each_session(phba->shost, beiscsi_session_fail);
 }
 
+static void beiscsi_recover_port(struct work_struct *work)
+{
+       struct beiscsi_hba *phba;
+
+       phba = container_of(work, struct beiscsi_hba, recover_port.work);
+       beiscsi_disable_port(phba, 0);
+       beiscsi_enable_port(phba);
+}
 
 static pci_ers_result_t beiscsi_eeh_err_detected(struct pci_dev *pdev,
                pci_channel_state_t state)
@@ -5470,12 +5543,18 @@ static pci_ers_result_t beiscsi_eeh_err_detected(struct pci_dev *pdev,
        struct beiscsi_hba *phba = NULL;
 
        phba = (struct beiscsi_hba *)pci_get_drvdata(pdev);
-       phba->state |= BE_ADAPTER_PCI_ERR;
+       set_bit(BEISCSI_HBA_PCI_ERR, &phba->state);
 
        beiscsi_log(phba, KERN_ERR, BEISCSI_LOG_INIT,
                    "BM_%d : EEH error detected\n");
 
-       beiscsi_quiesce(phba, BEISCSI_EEH_UNLOAD);
+       /* first stop UE detection when PCI error detected */
+       del_timer_sync(&phba->hw_check);
+       cancel_delayed_work_sync(&phba->recover_port);
+
+       /* sessions are no longer valid, so first fail the sessions */
+       iscsi_host_for_each_session(phba->shost, beiscsi_session_fail);
+       beiscsi_disable_port(phba, 0);
 
        if (state == pci_channel_io_perm_failure) {
                beiscsi_log(phba, KERN_ERR, BEISCSI_LOG_INIT,
@@ -5515,9 +5594,8 @@ static pci_ers_result_t beiscsi_eeh_reset(struct pci_dev *pdev)
        pci_set_power_state(pdev, PCI_D0);
        pci_restore_state(pdev);
 
-       /* Wait for the CHIP Reset to complete */
-       status = be_chk_reset_complete(phba);
-       if (!status) {
+       status = beiscsi_check_fw_rdy(phba);
+       if (status) {
                beiscsi_log(phba, KERN_WARNING, BEISCSI_LOG_INIT,
                            "BM_%d : EEH Reset Completed\n");
        } else {
@@ -5532,87 +5610,16 @@ static pci_ers_result_t beiscsi_eeh_reset(struct pci_dev *pdev)
 
 static void beiscsi_eeh_resume(struct pci_dev *pdev)
 {
-       int ret = 0, i;
-       struct be_eq_obj *pbe_eq;
-       struct beiscsi_hba *phba = NULL;
-       struct hwi_controller *phwi_ctrlr;
-       struct hwi_context_memory *phwi_context;
+       struct beiscsi_hba *phba;
+       int ret;
 
        phba = (struct beiscsi_hba *)pci_get_drvdata(pdev);
        pci_save_state(pdev);
 
-       if (enable_msix)
-               find_num_cpus(phba);
-       else
-               phba->num_cpus = 1;
-
-       if (enable_msix) {
-               beiscsi_msix_enable(phba);
-               if (!phba->msix_enabled)
-                       phba->num_cpus = 1;
-       }
-
-       ret = beiscsi_cmd_reset_function(phba);
-       if (ret) {
-               beiscsi_log(phba, KERN_ERR, BEISCSI_LOG_INIT,
-                           "BM_%d : Reset Failed\n");
-               goto ret_err;
-       }
-
-       ret = be_chk_reset_complete(phba);
-       if (ret) {
-               beiscsi_log(phba, KERN_ERR, BEISCSI_LOG_INIT,
-                           "BM_%d : Failed to get out of reset.\n");
-               goto ret_err;
-       }
-
-       beiscsi_get_params(phba);
-       phba->shost->max_id = phba->params.cxns_per_ctrl;
-       phba->shost->can_queue = phba->params.ios_per_ctrl;
-       ret = hwi_init_controller(phba);
-       if (ret) {
-               beiscsi_log(phba, KERN_ERR, BEISCSI_LOG_INIT,
-                           "BM_%d : beiscsi_eeh_resume -"
-                            "Failed to initialize beiscsi_hba.\n");
-               goto ret_err;
-       }
-
-       for (i = 0; i < MAX_MCC_CMD; i++) {
-               init_waitqueue_head(&phba->ctrl.mcc_wait[i + 1]);
-               phba->ctrl.mcc_tag[i] = i + 1;
-               phba->ctrl.mcc_tag_status[i + 1] = 0;
-               phba->ctrl.mcc_tag_available++;
-       }
-
-       phwi_ctrlr = phba->phwi_ctrlr;
-       phwi_context = phwi_ctrlr->phwi_ctxt;
-
-       for (i = 0; i < phba->num_cpus; i++) {
-               pbe_eq = &phwi_context->be_eq[i];
-               irq_poll_init(&pbe_eq->iopoll, be_iopoll_budget,
-                               be_iopoll);
-       }
-
-       i = (phba->msix_enabled) ? i : 0;
-       /* Work item for MCC handling */
-       pbe_eq = &phwi_context->be_eq[i];
-       INIT_WORK(&pbe_eq->work_cqs, beiscsi_process_all_cqs);
-
-       ret = beiscsi_init_irqs(phba);
-       if (ret < 0) {
-               beiscsi_log(phba, KERN_ERR, BEISCSI_LOG_INIT,
-                           "BM_%d : beiscsi_eeh_resume - "
-                           "Failed to beiscsi_init_irqs\n");
-               goto ret_err;
-       }
-
-       hwi_enable_intr(phba);
-       phba->state &= ~BE_ADAPTER_PCI_ERR;
-
-       return;
-ret_err:
-       beiscsi_log(phba, KERN_ERR, BEISCSI_LOG_INIT,
-                   "BM_%d : AER EEH Resume Failed\n");
+       ret = beiscsi_enable_port(phba);
+       if (ret)
+               __beiscsi_log(phba, KERN_ERR,
+                             "BM_%d : AER EEH resume failed\n");
 }
 
 static int beiscsi_dev_probe(struct pci_dev *pcidev,
@@ -5622,7 +5629,8 @@ static int beiscsi_dev_probe(struct pci_dev *pcidev,
        struct hwi_controller *phwi_ctrlr;
        struct hwi_context_memory *phwi_context;
        struct be_eq_obj *pbe_eq;
-       int ret = 0, i;
+       unsigned int s_handle;
+       int ret, i;
 
        ret = beiscsi_enable_pci(pcidev);
        if (ret < 0) {
@@ -5635,6 +5643,7 @@ static int beiscsi_dev_probe(struct pci_dev *pcidev,
        if (!phba) {
                dev_err(&pcidev->dev,
                        "beiscsi_dev_probe - Failed in beiscsi_hba_alloc\n");
+               ret = -ENOMEM;
                goto disable_pci;
        }
 
@@ -5650,10 +5659,8 @@ static int beiscsi_dev_probe(struct pci_dev *pcidev,
        /* Initialize Driver configuration Paramters */
        beiscsi_hba_attrs_init(phba);
 
-       phba->fw_timeout = false;
        phba->mac_addr_set = false;
 
-
        switch (pcidev->device) {
        case BE_DEVICE_ID1:
        case OC_DEVICE_ID1:
@@ -5677,39 +5684,26 @@ static int beiscsi_dev_probe(struct pci_dev *pcidev,
        ret = be_ctrl_init(phba, pcidev);
        if (ret) {
                beiscsi_log(phba, KERN_ERR, BEISCSI_LOG_INIT,
-                           "BM_%d : beiscsi_dev_probe-"
-                           "Failed in be_ctrl_init\n");
+                           "BM_%d : be_ctrl_init failed\n");
                goto hba_free;
        }
 
-       /*
-        * FUNCTION_RESET should clean up any stale info in FW for this fn
-        */
-       ret = beiscsi_cmd_reset_function(phba);
-       if (ret) {
-               beiscsi_log(phba, KERN_ERR, BEISCSI_LOG_INIT,
-                           "BM_%d : Reset Failed\n");
-               goto hba_free;
-       }
-       ret = be_chk_reset_complete(phba);
-       if (ret) {
-               beiscsi_log(phba, KERN_ERR, BEISCSI_LOG_INIT,
-                           "BM_%d : Failed to get out of reset.\n");
+       ret = beiscsi_init_sliport(phba);
+       if (ret)
                goto hba_free;
-       }
 
        spin_lock_init(&phba->io_sgl_lock);
        spin_lock_init(&phba->mgmt_sgl_lock);
-       spin_lock_init(&phba->isr_lock);
        spin_lock_init(&phba->async_pdu_lock);
-       ret = mgmt_get_fw_config(&phba->ctrl, phba);
+       ret = beiscsi_get_fw_config(&phba->ctrl, phba);
        if (ret != 0) {
                beiscsi_log(phba, KERN_ERR, BEISCSI_LOG_INIT,
                            "BM_%d : Error getting fw config\n");
                goto free_port;
        }
-       mgmt_get_port_name(&phba->ctrl, phba);
+       beiscsi_get_port_name(&phba->ctrl, phba);
        beiscsi_get_params(phba);
+       beiscsi_set_uer_feature(phba);
 
        if (enable_msix)
                find_num_cpus(phba);
@@ -5754,25 +5748,24 @@ static int beiscsi_dev_probe(struct pci_dev *pcidev,
                beiscsi_log(phba, KERN_ERR, BEISCSI_LOG_INIT,
                            "BM_%d : beiscsi_dev_probe-"
                            "Failed to allocate work queue\n");
+               ret = -ENOMEM;
                goto free_twq;
        }
 
-       INIT_DELAYED_WORK(&phba->beiscsi_hw_check_task,
-                         beiscsi_hw_health_check);
+       INIT_DELAYED_WORK(&phba->eqd_update, beiscsi_eqd_update_work);
 
        phwi_ctrlr = phba->phwi_ctrlr;
        phwi_context = phwi_ctrlr->phwi_ctxt;
 
        for (i = 0; i < phba->num_cpus; i++) {
                pbe_eq = &phwi_context->be_eq[i];
-               irq_poll_init(&pbe_eq->iopoll, be_iopoll_budget,
-                               be_iopoll);
+               irq_poll_init(&pbe_eq->iopoll, be_iopoll_budget, be_iopoll);
        }
 
        i = (phba->msix_enabled) ? i : 0;
        /* Work item for MCC handling */
        pbe_eq = &phwi_context->be_eq[i];
-       INIT_WORK(&pbe_eq->work_cqs, beiscsi_process_all_cqs);
+       INIT_WORK(&pbe_eq->mcc_work, beiscsi_mcc_work);
 
        ret = beiscsi_init_irqs(phba);
        if (ret < 0) {
@@ -5783,22 +5776,42 @@ static int beiscsi_dev_probe(struct pci_dev *pcidev,
        }
        hwi_enable_intr(phba);
 
-       if (iscsi_host_add(phba->shost, &phba->pcidev->dev))
+       ret = iscsi_host_add(phba->shost, &phba->pcidev->dev);
+       if (ret)
                goto free_blkenbld;
 
-       if (beiscsi_setup_boot_info(phba))
-               /*
-                * log error but continue, because we may not be using
-                * iscsi boot.
+       /* set online bit after port is operational */
+       set_bit(BEISCSI_HBA_ONLINE, &phba->state);
+       __beiscsi_log(phba, KERN_INFO,
+                     "BM_%d : port online: 0x%lx\n", phba->state);
+
+       INIT_WORK(&phba->boot_work, beiscsi_boot_work);
+       ret = beiscsi_boot_get_shandle(phba, &s_handle);
+       if (ret > 0) {
+               beiscsi_start_boot_work(phba, s_handle);
+               /**
+                * Set this bit after starting the work to let
+                * probe handle it first.
+                * ASYNC event can too schedule this work.
                 */
-               beiscsi_log(phba, KERN_ERR, BEISCSI_LOG_INIT,
-                           "BM_%d : Could not set up "
-                           "iSCSI boot info.\n");
+               set_bit(BEISCSI_HBA_BOOT_FOUND, &phba->state);
+       }
 
-       beiscsi_create_def_ifaces(phba);
-       schedule_delayed_work(&phba->beiscsi_hw_check_task,
-                             msecs_to_jiffies(1000));
+       beiscsi_iface_create_default(phba);
+       schedule_delayed_work(&phba->eqd_update,
+                             msecs_to_jiffies(BEISCSI_EQD_UPDATE_INTERVAL));
 
+       INIT_WORK(&phba->sess_work, beiscsi_sess_work);
+       INIT_DELAYED_WORK(&phba->recover_port, beiscsi_recover_port);
+       /**
+        * Start UE detection here. UE before this will cause stall in probe
+        * and eventually fail the probe.
+        */
+       init_timer(&phba->hw_check);
+       phba->hw_check.function = beiscsi_hw_health_check;
+       phba->hw_check.data = (unsigned long)phba;
+       mod_timer(&phba->hw_check,
+                 jiffies + msecs_to_jiffies(BEISCSI_UE_DETECT_INTERVAL));
        beiscsi_log(phba, KERN_INFO, BEISCSI_LOG_INIT,
                    "\n\n\n BM_%d : SUCCESS - DRIVER LOADED\n\n\n");
        return 0;
@@ -5810,7 +5823,8 @@ free_blkenbld:
                irq_poll_disable(&pbe_eq->iopoll);
        }
 free_twq:
-       beiscsi_clean_port(phba);
+       hwi_cleanup_port(phba);
+       beiscsi_cleanup_port(phba);
        beiscsi_free_mem(phba);
 free_port:
        pci_free_consistent(phba->pcidev,
@@ -5830,6 +5844,49 @@ disable_pci:
        return ret;
 }
 
+static void beiscsi_remove(struct pci_dev *pcidev)
+{
+       struct beiscsi_hba *phba = NULL;
+
+       phba = pci_get_drvdata(pcidev);
+       if (!phba) {
+               dev_err(&pcidev->dev, "beiscsi_remove called with no phba\n");
+               return;
+       }
+
+       /* first stop UE detection before unloading */
+       del_timer_sync(&phba->hw_check);
+       cancel_delayed_work_sync(&phba->recover_port);
+       cancel_work_sync(&phba->sess_work);
+
+       beiscsi_iface_destroy_default(phba);
+       iscsi_host_remove(phba->shost);
+       beiscsi_disable_port(phba, 1);
+
+       /* after cancelling boot_work */
+       iscsi_boot_destroy_kset(phba->boot_struct.boot_kset);
+
+       /* free all resources */
+       destroy_workqueue(phba->wq);
+       beiscsi_cleanup_port(phba);
+       beiscsi_free_mem(phba);
+
+       /* ctrl uninit */
+       beiscsi_unmap_pci_function(phba);
+       pci_free_consistent(phba->pcidev,
+                           phba->ctrl.mbox_mem_alloced.size,
+                           phba->ctrl.mbox_mem_alloced.va,
+                           phba->ctrl.mbox_mem_alloced.dma);
+
+       pci_dev_put(phba->pcidev);
+       iscsi_host_free(phba->shost);
+       pci_disable_pcie_error_reporting(pcidev);
+       pci_set_drvdata(pcidev, NULL);
+       pci_release_regions(pcidev);
+       pci_disable_device(pcidev);
+}
+
+
 static struct pci_error_handlers beiscsi_eeh_handlers = {
        .error_detected = beiscsi_eeh_err_detected,
        .slot_reset = beiscsi_eeh_reset,
@@ -5846,9 +5903,9 @@ struct iscsi_transport beiscsi_iscsi_transport = {
        .create_conn = beiscsi_conn_create,
        .bind_conn = beiscsi_conn_bind,
        .destroy_conn = iscsi_conn_teardown,
-       .attr_is_visible = be2iscsi_attr_is_visible,
-       .set_iface_param = be2iscsi_iface_set_param,
-       .get_iface_param = be2iscsi_iface_get_param,
+       .attr_is_visible = beiscsi_attr_is_visible,
+       .set_iface_param = beiscsi_iface_set_param,
+       .get_iface_param = beiscsi_iface_get_param,
        .set_param = beiscsi_set_param,
        .get_conn_param = iscsi_conn_get_param,
        .get_session_param = iscsi_session_get_param,
@@ -5877,7 +5934,6 @@ static struct pci_driver beiscsi_pci_driver = {
        .err_handler = &beiscsi_eeh_handlers
 };
 
-
 static int __init beiscsi_module_init(void)
 {
        int ret;
This page took 0.066568 seconds and 5 git commands to generate.