static void qedi_reset_uio_rings(struct qedi_uio_dev *udev);
 static void qedi_ll2_free_skbs(struct qedi_ctx *qedi);
 static struct nvm_iscsi_block *qedi_get_nvram_block(struct qedi_ctx *qedi);
+static void qedi_recovery_handler(struct work_struct *work);
 
 static int qedi_iscsi_event_cb(void *context, u8 fw_event_code, void *fw_handle)
 {
        return;
 }
 
+static void qedi_schedule_recovery_handler(void *dev)
+{
+       struct qedi_ctx *qedi = dev;
+
+       QEDI_ERR(&qedi->dbg_ctx, "Recovery handler scheduled.\n");
+
+       if (test_and_set_bit(QEDI_IN_RECOVERY, &qedi->flags))
+               return;
+
+       atomic_set(&qedi->link_state, QEDI_LINK_DOWN);
+
+       schedule_delayed_work(&qedi->recovery_work, 0);
+}
+
 static void qedi_link_update(void *dev, struct qed_link_output *link)
 {
        struct qedi_ctx *qedi = (struct qedi_ctx *)dev;
 static struct qed_iscsi_cb_ops qedi_cb_ops = {
        {
                .link_update =          qedi_link_update,
+               .schedule_recovery_handler = qedi_schedule_recovery_handler,
                .get_protocol_tlv_data = qedi_get_protocol_tlv_data,
                .get_generic_tlv_data = qedi_get_generic_tlv_data,
        }
        struct qedi_ctx *qedi = pci_get_drvdata(pdev);
        int rval;
 
-       if (qedi->tmf_thread) {
-               flush_workqueue(qedi->tmf_thread);
-               destroy_workqueue(qedi->tmf_thread);
-               qedi->tmf_thread = NULL;
-       }
+       if (mode == QEDI_MODE_NORMAL) {
+               if (qedi->tmf_thread) {
+                       flush_workqueue(qedi->tmf_thread);
+                       destroy_workqueue(qedi->tmf_thread);
+                       qedi->tmf_thread = NULL;
+               }
 
-       if (qedi->offload_thread) {
-               flush_workqueue(qedi->offload_thread);
-               destroy_workqueue(qedi->offload_thread);
-               qedi->offload_thread = NULL;
+               if (qedi->offload_thread) {
+                       flush_workqueue(qedi->offload_thread);
+                       destroy_workqueue(qedi->offload_thread);
+                       qedi->offload_thread = NULL;
+               }
        }
 
 #ifdef CONFIG_DEBUG_FS
                qedi_ops->ll2->stop(qedi->cdev);
        }
 
-       if (mode == QEDI_MODE_NORMAL)
-               qedi_free_iscsi_pf_param(qedi);
+       qedi_free_iscsi_pf_param(qedi);
 
        rval = qedi_ops->common->update_drv_state(qedi->cdev, false);
        if (rval)
                qedi_free_uio(qedi->udev);
                qedi_free_itt(qedi);
 
-               iscsi_host_remove(qedi->shost);
-               iscsi_host_free(qedi->shost);
-
                if (qedi->ll2_recv_thread) {
                        kthread_stop(qedi->ll2_recv_thread);
                        qedi->ll2_recv_thread = NULL;
 
                if (qedi->boot_kset)
                        iscsi_boot_destroy_kset(qedi->boot_kset);
+
+               iscsi_host_remove(qedi->shost);
+               iscsi_host_free(qedi->shost);
        }
 }
 
                  qedi->dev_info.common.num_hwfns,
                  qedi_ops->common->get_affin_hwfn_idx(qedi->cdev));
 
-       if (mode != QEDI_MODE_RECOVERY) {
-               rc = qedi_set_iscsi_pf_param(qedi);
-               if (rc) {
-                       rc = -ENOMEM;
-                       QEDI_ERR(&qedi->dbg_ctx,
-                                "Set iSCSI pf param fail\n");
-                       goto free_host;
-               }
+       rc = qedi_set_iscsi_pf_param(qedi);
+       if (rc) {
+               rc = -ENOMEM;
+               QEDI_ERR(&qedi->dbg_ctx,
+                        "Set iSCSI pf param fail\n");
+               goto free_host;
        }
 
        qedi_ops->common->update_pf_params(qedi->cdev, &qedi->pf_params);
                        goto free_cid_que;
                }
 
+               INIT_DELAYED_WORK(&qedi->recovery_work, qedi_recovery_handler);
+
                /* F/w needs 1st task context memory entry for performance */
                set_bit(QEDI_RESERVE_TASK_ID, qedi->task_idx_map);
                atomic_set(&qedi->num_offloads, 0);
        return rc;
 }
 
+static void qedi_mark_conn_recovery(struct iscsi_cls_session *cls_session)
+{
+       struct iscsi_session *session = cls_session->dd_data;
+       struct iscsi_conn *conn = session->leadconn;
+       struct qedi_conn *qedi_conn = conn->dd_data;
+
+       iscsi_conn_failure(qedi_conn->cls_conn->dd_data, ISCSI_ERR_CONN_FAILED);
+}
+
+static void qedi_recovery_handler(struct work_struct *work)
+{
+       struct qedi_ctx *qedi =
+                       container_of(work, struct qedi_ctx, recovery_work.work);
+
+       iscsi_host_for_each_session(qedi->shost, qedi_mark_conn_recovery);
+
+       /* Call common_ops->recovery_prolog to allow the MFW to quiesce
+        * any PCI transactions.
+        */
+       qedi_ops->common->recovery_prolog(qedi->cdev);
+
+       __qedi_remove(qedi->pdev, QEDI_MODE_RECOVERY);
+       __qedi_probe(qedi->pdev, QEDI_MODE_RECOVERY);
+       clear_bit(QEDI_IN_RECOVERY, &qedi->flags);
+}
+
 static int qedi_probe(struct pci_dev *pdev, const struct pci_device_id *id)
 {
        return __qedi_probe(pdev, QEDI_MODE_NORMAL);