aboutsummaryrefslogtreecommitdiff
path: root/drivers/scsi/qedi
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/scsi/qedi')
-rw-r--r--drivers/scsi/qedi/qedi.h3
-rw-r--r--drivers/scsi/qedi/qedi_gbl.h1
-rw-r--r--drivers/scsi/qedi/qedi_iscsi.c18
-rw-r--r--drivers/scsi/qedi/qedi_iscsi.h1
-rw-r--r--drivers/scsi/qedi/qedi_main.c104
5 files changed, 104 insertions, 23 deletions
diff --git a/drivers/scsi/qedi/qedi.h b/drivers/scsi/qedi/qedi.h
index 9513fd320ffd..9498279ae80d 100644
--- a/drivers/scsi/qedi/qedi.h
+++ b/drivers/scsi/qedi/qedi.h
@@ -36,6 +36,7 @@ struct qedi_endpoint;
*/
#define QEDI_MODE_NORMAL 0
#define QEDI_MODE_RECOVERY 1
+#define QEDI_MODE_SHUTDOWN 2
#define ISCSI_WQE_SET_PTU_INVALIDATE 1
#define QEDI_MAX_ISCSI_TASK 4096
@@ -278,6 +279,7 @@ struct qedi_ctx {
#define QEDI_IOTHREAD_WAKE 2
#define QEDI_IN_RECOVERY 5
#define QEDI_IN_OFFLINE 6
+#define QEDI_IN_SHUTDOWN 7
u8 mac[ETH_ALEN];
u32 src_ip[4];
@@ -331,6 +333,7 @@ struct qedi_ctx {
u16 ll2_mtu;
struct workqueue_struct *dpc_wq;
+ struct delayed_work recovery_work;
spinlock_t task_idx_lock; /* To protect gbl context */
s32 last_tidx_alloc;
diff --git a/drivers/scsi/qedi/qedi_gbl.h b/drivers/scsi/qedi/qedi_gbl.h
index 8ba7c771ce4d..116645c08c71 100644
--- a/drivers/scsi/qedi/qedi_gbl.h
+++ b/drivers/scsi/qedi/qedi_gbl.h
@@ -73,5 +73,6 @@ void qedi_remove_sysfs_ctx_attr(struct qedi_ctx *qedi);
void qedi_clearsq(struct qedi_ctx *qedi,
struct qedi_conn *qedi_conn,
struct iscsi_task *task);
+void qedi_clear_session_ctx(struct iscsi_cls_session *cls_sess);
#endif
diff --git a/drivers/scsi/qedi/qedi_iscsi.c b/drivers/scsi/qedi/qedi_iscsi.c
index 8829880a54c3..1f4a5fb00a05 100644
--- a/drivers/scsi/qedi/qedi_iscsi.c
+++ b/drivers/scsi/qedi/qedi_iscsi.c
@@ -392,6 +392,7 @@ static int qedi_conn_bind(struct iscsi_cls_session *cls_session,
qedi_ep->conn = qedi_conn;
qedi_conn->ep = qedi_ep;
+ qedi_conn->iscsi_ep = ep;
qedi_conn->iscsi_conn_id = qedi_ep->iscsi_cid;
qedi_conn->fw_cid = qedi_ep->fw_cid;
qedi_conn->cmd_cleanup_req = 0;
@@ -782,6 +783,9 @@ static int qedi_task_xmit(struct iscsi_task *task)
struct qedi_cmd *cmd = task->dd_data;
struct scsi_cmnd *sc = task->sc;
+ if (test_bit(QEDI_IN_SHUTDOWN, &qedi_conn->qedi->flags))
+ return -ENODEV;
+
cmd->state = 0;
cmd->task = NULL;
cmd->use_slowpath = false;
@@ -1596,6 +1600,20 @@ void qedi_process_iscsi_error(struct qedi_endpoint *ep,
qedi_start_conn_recovery(qedi_conn->qedi, qedi_conn);
}
+void qedi_clear_session_ctx(struct iscsi_cls_session *cls_sess)
+{
+ struct iscsi_session *session = cls_sess->dd_data;
+ struct iscsi_conn *conn = session->leadconn;
+ struct qedi_conn *qedi_conn = conn->dd_data;
+
+ if (iscsi_is_session_online(cls_sess))
+ qedi_ep_disconnect(qedi_conn->iscsi_ep);
+
+ qedi_conn_destroy(qedi_conn->cls_conn);
+
+ qedi_session_destroy(cls_sess);
+}
+
void qedi_process_tcp_error(struct qedi_endpoint *ep,
struct iscsi_eqe_data *data)
{
diff --git a/drivers/scsi/qedi/qedi_iscsi.h b/drivers/scsi/qedi/qedi_iscsi.h
index 67c3b7349271..39dc27c85e3c 100644
--- a/drivers/scsi/qedi/qedi_iscsi.h
+++ b/drivers/scsi/qedi/qedi_iscsi.h
@@ -149,6 +149,7 @@ struct qedi_conn {
struct iscsi_cls_conn *cls_conn;
struct qedi_ctx *qedi;
struct qedi_endpoint *ep;
+ struct iscsi_endpoint *iscsi_ep;
struct list_head active_cmd_list;
spinlock_t list_lock; /* internal conn lock */
u32 active_cmd_count;
diff --git a/drivers/scsi/qedi/qedi_main.c b/drivers/scsi/qedi/qedi_main.c
index acb930b8c6a6..b995b19865ca 100644
--- a/drivers/scsi/qedi/qedi_main.c
+++ b/drivers/scsi/qedi/qedi_main.c
@@ -58,6 +58,7 @@ static struct qedi_cmd *qedi_get_cmd_from_tid(struct qedi_ctx *qedi, u32 tid);
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)
{
@@ -1113,6 +1114,20 @@ exit_get_data:
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;
@@ -1130,6 +1145,7 @@ static void qedi_link_update(void *dev, struct qed_link_output *link)
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,
}
@@ -2328,16 +2344,22 @@ static void __qedi_remove(struct pci_dev *pdev, int mode)
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_SHUTDOWN)
+ iscsi_host_for_each_session(qedi->shost,
+ qedi_clear_session_ctx);
+
+ if (mode == QEDI_MODE_NORMAL || mode == QEDI_MODE_SHUTDOWN) {
+ 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
@@ -2353,8 +2375,7 @@ static void __qedi_remove(struct pci_dev *pdev, int mode)
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)
@@ -2367,15 +2388,12 @@ static void __qedi_remove(struct pci_dev *pdev, int mode)
qedi_destroy_fp(qedi);
- if (mode == QEDI_MODE_NORMAL) {
+ if (mode == QEDI_MODE_NORMAL || mode == QEDI_MODE_SHUTDOWN) {
qedi_release_cid_que(qedi);
qedi_cm_free_mem(qedi);
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;
@@ -2384,9 +2402,22 @@ static void __qedi_remove(struct pci_dev *pdev, int mode)
if (qedi->boot_kset)
iscsi_boot_destroy_kset(qedi->boot_kset);
+
+ iscsi_host_remove(qedi->shost);
+ iscsi_host_free(qedi->shost);
}
}
+static void qedi_shutdown(struct pci_dev *pdev)
+{
+ struct qedi_ctx *qedi = pci_get_drvdata(pdev);
+
+ QEDI_ERR(&qedi->dbg_ctx, "%s: Shutdown qedi\n", __func__);
+ if (test_and_set_bit(QEDI_IN_SHUTDOWN, &qedi->flags))
+ return;
+ __qedi_remove(pdev, QEDI_MODE_SHUTDOWN);
+}
+
static int __qedi_probe(struct pci_dev *pdev, int mode)
{
struct qedi_ctx *qedi;
@@ -2435,14 +2466,12 @@ static int __qedi_probe(struct pci_dev *pdev, int mode)
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);
@@ -2633,6 +2662,8 @@ static int __qedi_probe(struct pci_dev *pdev, int mode)
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);
@@ -2673,6 +2704,32 @@ exit_probe:
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);
@@ -2697,6 +2754,7 @@ static struct pci_driver qedi_pci_driver = {
.id_table = qedi_pci_tbl,
.probe = qedi_probe,
.remove = qedi_remove,
+ .shutdown = qedi_shutdown,
};
static int __init qedi_init(void)