diff options
Diffstat (limited to 'drivers/scsi/qedi/qedi_iscsi.c')
| -rw-r--r-- | drivers/scsi/qedi/qedi_iscsi.c | 248 |
1 files changed, 170 insertions, 78 deletions
diff --git a/drivers/scsi/qedi/qedi_iscsi.c b/drivers/scsi/qedi/qedi_iscsi.c index 6d6d6013e35b..6ed8ef97642c 100644 --- a/drivers/scsi/qedi/qedi_iscsi.c +++ b/drivers/scsi/qedi/qedi_iscsi.c @@ -1,10 +1,7 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * QLogic iSCSI Offload Driver * Copyright (c) 2016 Cavium Inc. - * - * This software is available under the terms of the GNU General Public License - * (GPL) Version 2, available from the file COPYING in the main directory of - * this source tree. */ #include <linux/blkdev.h> @@ -43,7 +40,7 @@ static int qedi_eh_host_reset(struct scsi_cmnd *cmd) return qedi_recover_all_conns(qedi); } -struct scsi_host_template qedi_host_template = { +const struct scsi_host_template qedi_host_template = { .module = THIS_MODULE, .name = "QLogic QEDI 25/40/100Gb iSCSI Initiator Driver", .proc_name = QEDI_MODULE_NAME, @@ -61,7 +58,8 @@ struct scsi_host_template qedi_host_template = { .max_sectors = 0xffff, .dma_boundary = QEDI_HW_DMA_BOUNDARY, .cmd_per_lun = 128, - .shost_attrs = qedi_shost_attrs, + .shost_groups = qedi_shost_groups, + .cmd_size = sizeof(struct iscsi_cmd), }; static void qedi_conn_free_login_resources(struct qedi_ctx *qedi, @@ -333,12 +331,22 @@ free_conn: void qedi_mark_device_missing(struct iscsi_cls_session *cls_session) { - iscsi_block_session(cls_session); + struct iscsi_session *session = cls_session->dd_data; + struct qedi_conn *qedi_conn = session->leadconn->dd_data; + + spin_lock_bh(&session->frwd_lock); + set_bit(QEDI_BLOCK_IO, &qedi_conn->qedi->flags); + spin_unlock_bh(&session->frwd_lock); } void qedi_mark_device_available(struct iscsi_cls_session *cls_session) { - iscsi_unblock_session(cls_session); + struct iscsi_session *session = cls_session->dd_data; + struct qedi_conn *qedi_conn = session->leadconn->dd_data; + + spin_lock_bh(&session->frwd_lock); + clear_bit(QEDI_BLOCK_IO, &qedi_conn->qedi->flags); + spin_unlock_bh(&session->frwd_lock); } static int qedi_bind_conn_to_iscsi_cid(struct qedi_ctx *qedi, @@ -380,6 +388,7 @@ static int qedi_conn_bind(struct iscsi_cls_session *cls_session, struct qedi_ctx *qedi = iscsi_host_priv(shost); struct qedi_endpoint *qedi_ep; struct iscsi_endpoint *ep; + int rc = 0; ep = iscsi_lookup_endpoint(transport_fd); if (!ep) @@ -387,26 +396,37 @@ static int qedi_conn_bind(struct iscsi_cls_session *cls_session, qedi_ep = ep->dd_data; if ((qedi_ep->state == EP_STATE_TCP_FIN_RCVD) || - (qedi_ep->state == EP_STATE_TCP_RST_RCVD)) - return -EINVAL; + (qedi_ep->state == EP_STATE_TCP_RST_RCVD)) { + rc = -EINVAL; + goto put_ep; + } + + if (iscsi_conn_bind(cls_session, cls_conn, is_leading)) { + rc = -EINVAL; + goto put_ep; + } - if (iscsi_conn_bind(cls_session, cls_conn, is_leading)) - return -EINVAL; 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; - qedi_conn->cmd_cleanup_cmpl = 0; + atomic_set(&qedi_conn->cmd_cleanup_cmpl, 0); + + if (qedi_bind_conn_to_iscsi_cid(qedi, qedi_conn)) { + rc = -EINVAL; + goto put_ep; + } - if (qedi_bind_conn_to_iscsi_cid(qedi, qedi_conn)) - return -EINVAL; spin_lock_init(&qedi_conn->tmf_work_lock); INIT_LIST_HEAD(&qedi_conn->tmf_work_list); init_waitqueue_head(&qedi_conn->wait_queue); - return 0; +put_ep: + iscsi_put_endpoint(ep); + return rc; } static int qedi_iscsi_update_conn(struct qedi_ctx *qedi, @@ -480,8 +500,8 @@ static u16 qedi_calc_mss(u16 pmtu, u8 is_ipv6, u8 tcp_ts_en, u8 vlan_en) static int qedi_iscsi_offload_conn(struct qedi_endpoint *qedi_ep) { - struct qedi_ctx *qedi = qedi_ep->qedi; struct qed_iscsi_params_offload *conn_info; + struct qedi_ctx *qedi = qedi_ep->qedi; int rval; int i; @@ -558,10 +578,37 @@ static int qedi_iscsi_offload_conn(struct qedi_endpoint *qedi_ep) "Default cq index [%d], mss [%d]\n", conn_info->default_cq, conn_info->mss); + /* Prepare the doorbell parameters */ + qedi_ep->db_data.agg_flags = 0; + qedi_ep->db_data.params = 0; + SET_FIELD(qedi_ep->db_data.params, ISCSI_DB_DATA_DEST, DB_DEST_XCM); + SET_FIELD(qedi_ep->db_data.params, ISCSI_DB_DATA_AGG_CMD, + DB_AGG_CMD_MAX); + SET_FIELD(qedi_ep->db_data.params, ISCSI_DB_DATA_AGG_VAL_SEL, + DQ_XCM_ISCSI_SQ_PROD_CMD); + SET_FIELD(qedi_ep->db_data.params, ISCSI_DB_DATA_BYPASS_EN, 1); + + /* Register doorbell with doorbell recovery mechanism */ + rval = qedi_ops->common->db_recovery_add(qedi->cdev, + qedi_ep->p_doorbell, + &qedi_ep->db_data, + DB_REC_WIDTH_32B, + DB_REC_KERNEL); + if (rval) { + kfree(conn_info); + return rval; + } + rval = qedi_ops->offload_conn(qedi->cdev, qedi_ep->handle, conn_info); - if (rval) + if (rval) { + /* delete doorbell from doorbell recovery mechanism */ + rval = qedi_ops->common->db_recovery_del(qedi->cdev, + qedi_ep->p_doorbell, + &qedi_ep->db_data); + QEDI_ERR(&qedi->dbg_ctx, "offload_conn returned %d, ep=%p\n", rval, qedi_ep); + } kfree(conn_info); return rval; @@ -579,18 +626,22 @@ static int qedi_conn_start(struct iscsi_cls_conn *cls_conn) rval = qedi_iscsi_update_conn(qedi, qedi_conn); if (rval) { iscsi_conn_printk(KERN_ALERT, conn, - "conn_start: FW oflload conn failed.\n"); + "conn_start: FW offload conn failed.\n"); rval = -EINVAL; goto start_err; } - clear_bit(QEDI_CONN_FW_CLEANUP, &qedi_conn->flags); + spin_lock(&qedi_conn->tmf_work_lock); + qedi_conn->fw_cleanup_works = 0; + qedi_conn->ep_disconnect_starting = false; + spin_unlock(&qedi_conn->tmf_work_lock); + qedi_conn->abrt_conn = 0; rval = iscsi_conn_start(cls_conn); if (rval) { iscsi_conn_printk(KERN_ALERT, conn, - "iscsi_conn_start: FW oflload conn failed!!\n"); + "iscsi_conn_start: FW offload conn failed!!\n"); } start_err: @@ -744,7 +795,7 @@ static int qedi_iscsi_send_generic_request(struct iscsi_task *task) rc = qedi_send_iscsi_logout(qedi_conn, task); break; case ISCSI_OP_SCSI_TMFUNC: - rc = qedi_iscsi_abort_work(qedi_conn, task); + rc = qedi_send_iscsi_tmf(qedi_conn, task); break; case ISCSI_OP_TEXT: rc = qedi_send_iscsi_text(qedi_conn, task); @@ -774,7 +825,6 @@ static int qedi_mtask_xmit(struct iscsi_conn *conn, struct iscsi_task *task) } cmd->conn = conn->dd_data; - cmd->scsi_cmd = NULL; return qedi_iscsi_send_generic_request(task); } @@ -785,6 +835,16 @@ static int qedi_task_xmit(struct iscsi_task *task) struct qedi_cmd *cmd = task->dd_data; struct scsi_cmnd *sc = task->sc; + /* Clear now so in cleanup_task we know it didn't make it */ + cmd->scsi_cmd = NULL; + cmd->task_id = U16_MAX; + + if (test_bit(QEDI_IN_SHUTDOWN, &qedi_conn->qedi->flags)) + return -ENODEV; + + if (test_bit(QEDI_BLOCK_IO, &qedi_conn->qedi->flags)) + return -EACCES; + cmd->state = 0; cmd->task = NULL; cmd->use_slowpath = false; @@ -800,6 +860,37 @@ static int qedi_task_xmit(struct iscsi_task *task) return qedi_iscsi_send_ioreq(task); } +static void qedi_offload_work(struct work_struct *work) +{ + struct qedi_endpoint *qedi_ep = + container_of(work, struct qedi_endpoint, offload_work); + struct qedi_ctx *qedi; + int wait_delay = 5 * HZ; + int ret; + + qedi = qedi_ep->qedi; + + ret = qedi_iscsi_offload_conn(qedi_ep); + if (ret) { + QEDI_ERR(&qedi->dbg_ctx, + "offload error: iscsi_cid=%u, qedi_ep=%p, ret=%d\n", + qedi_ep->iscsi_cid, qedi_ep, ret); + qedi_ep->state = EP_STATE_OFLDCONN_FAILED; + return; + } + + ret = wait_event_interruptible_timeout(qedi_ep->tcp_ofld_wait, + (qedi_ep->state == + EP_STATE_OFLDCONN_COMPL), + wait_delay); + if (ret <= 0 || qedi_ep->state != EP_STATE_OFLDCONN_COMPL) { + qedi_ep->state = EP_STATE_OFLDCONN_FAILED; + QEDI_ERR(&qedi->dbg_ctx, + "Offload conn TIMEOUT iscsi_cid=%u, qedi_ep=%p\n", + qedi_ep->iscsi_cid, qedi_ep); + } +} + static struct iscsi_endpoint * qedi_ep_connect(struct Scsi_Host *shost, struct sockaddr *dst_addr, int non_blocking) @@ -809,8 +900,6 @@ qedi_ep_connect(struct Scsi_Host *shost, struct sockaddr *dst_addr, struct qedi_endpoint *qedi_ep; struct sockaddr_in *addr; struct sockaddr_in6 *addr6; - struct qed_dev *cdev = NULL; - struct qedi_uio_dev *udev = NULL; struct iscsi_path path_req; u32 msg_type = ISCSI_KEVENT_IF_DOWN; u32 iscsi_cid = QEDI_CID_RESERVED; @@ -830,8 +919,6 @@ qedi_ep_connect(struct Scsi_Host *shost, struct sockaddr *dst_addr, } qedi = iscsi_host_priv(shost); - cdev = qedi->cdev; - udev = qedi->udev; if (test_bit(QEDI_IN_OFFLINE, &qedi->flags) || test_bit(QEDI_IN_RECOVERY, &qedi->flags)) { @@ -839,6 +926,11 @@ qedi_ep_connect(struct Scsi_Host *shost, struct sockaddr *dst_addr, return ERR_PTR(ret); } + if (atomic_read(&qedi->link_state) != QEDI_LINK_UP) { + QEDI_WARN(&qedi->dbg_ctx, "qedi link down\n"); + return ERR_PTR(-ENXIO); + } + ep = iscsi_create_endpoint(sizeof(struct qedi_endpoint)); if (!ep) { QEDI_ERR(&qedi->dbg_ctx, "endpoint create fail\n"); @@ -847,6 +939,7 @@ qedi_ep_connect(struct Scsi_Host *shost, struct sockaddr *dst_addr, } qedi_ep = ep->dd_data; memset(qedi_ep, 0, sizeof(struct qedi_endpoint)); + INIT_WORK(&qedi_ep->offload_work, qedi_offload_work); qedi_ep->state = EP_STATE_IDLE; qedi_ep->iscsi_cid = (u32)-1; qedi_ep->qedi = qedi; @@ -873,12 +966,6 @@ qedi_ep_connect(struct Scsi_Host *shost, struct sockaddr *dst_addr, QEDI_ERR(&qedi->dbg_ctx, "Invalid endpoint\n"); } - if (atomic_read(&qedi->link_state) != QEDI_LINK_UP) { - QEDI_WARN(&qedi->dbg_ctx, "qedi link down\n"); - ret = -ENXIO; - goto ep_conn_exit; - } - ret = qedi_alloc_sq(qedi, qedi_ep); if (ret) goto ep_conn_exit; @@ -979,42 +1066,50 @@ static void qedi_cleanup_active_cmd_list(struct qedi_conn *qedi_conn) { struct qedi_cmd *cmd, *cmd_tmp; + spin_lock(&qedi_conn->list_lock); list_for_each_entry_safe(cmd, cmd_tmp, &qedi_conn->active_cmd_list, io_cmd) { list_del_init(&cmd->io_cmd); qedi_conn->active_cmd_count--; } + spin_unlock(&qedi_conn->list_lock); } static void qedi_ep_disconnect(struct iscsi_endpoint *ep) { struct qedi_endpoint *qedi_ep; struct qedi_conn *qedi_conn = NULL; - struct iscsi_conn *conn = NULL; struct qedi_ctx *qedi; int ret = 0; - int wait_delay = 20 * HZ; + int wait_delay; int abrt_conn = 0; - int count = 10; + wait_delay = 60 * HZ + DEF_MAX_RT_TIME; qedi_ep = ep->dd_data; qedi = qedi_ep->qedi; flush_work(&qedi_ep->offload_work); + if (qedi_ep->state == EP_STATE_OFLDCONN_START) + goto ep_exit_recover; + if (qedi_ep->conn) { qedi_conn = qedi_ep->conn; - conn = qedi_conn->cls_conn->dd_data; - iscsi_suspend_queue(conn); abrt_conn = qedi_conn->abrt_conn; - while (count--) { - if (!test_bit(QEDI_CONN_FW_CLEANUP, - &qedi_conn->flags)) { - break; - } + QEDI_INFO(&qedi->dbg_ctx, QEDI_LOG_INFO, + "cid=0x%x qedi_ep=%p waiting for %d tmfs\n", + qedi_ep->iscsi_cid, qedi_ep, + qedi_conn->fw_cleanup_works); + + spin_lock(&qedi_conn->tmf_work_lock); + qedi_conn->ep_disconnect_starting = true; + while (qedi_conn->fw_cleanup_works > 0) { + spin_unlock(&qedi_conn->tmf_work_lock); msleep(1000); + spin_lock(&qedi_conn->tmf_work_lock); } + spin_unlock(&qedi_conn->tmf_work_lock); if (test_bit(QEDI_IN_RECOVERY, &qedi->flags)) { if (qedi_do_not_recover) { @@ -1064,7 +1159,20 @@ static void qedi_ep_disconnect(struct iscsi_endpoint *ep) break; } + if (!abrt_conn) + wait_delay += qedi->pf_params.iscsi_pf_params.two_msl_timer; + qedi_ep->state = EP_STATE_DISCONN_START; + + if (test_bit(QEDI_IN_SHUTDOWN, &qedi->flags) || + test_bit(QEDI_IN_RECOVERY, &qedi->flags)) + goto ep_release_conn; + + /* Delete doorbell from doorbell recovery mechanism */ + ret = qedi_ops->common->db_recovery_del(qedi->cdev, + qedi_ep->p_doorbell, + &qedi_ep->db_data); + ret = qedi_ops->destroy_conn(qedi->cdev, qedi_ep->handle, abrt_conn); if (ret) { QEDI_WARN(&qedi->dbg_ctx, @@ -1158,37 +1266,6 @@ static int qedi_data_avail(struct qedi_ctx *qedi, u16 vlanid) return rc; } -static void qedi_offload_work(struct work_struct *work) -{ - struct qedi_endpoint *qedi_ep = - container_of(work, struct qedi_endpoint, offload_work); - struct qedi_ctx *qedi; - int wait_delay = 20 * HZ; - int ret; - - qedi = qedi_ep->qedi; - - ret = qedi_iscsi_offload_conn(qedi_ep); - if (ret) { - QEDI_ERR(&qedi->dbg_ctx, - "offload error: iscsi_cid=%u, qedi_ep=%p, ret=%d\n", - qedi_ep->iscsi_cid, qedi_ep, ret); - qedi_ep->state = EP_STATE_OFLDCONN_FAILED; - return; - } - - ret = wait_event_interruptible_timeout(qedi_ep->tcp_ofld_wait, - (qedi_ep->state == - EP_STATE_OFLDCONN_COMPL), - wait_delay); - if ((ret <= 0) || (qedi_ep->state != EP_STATE_OFLDCONN_COMPL)) { - qedi_ep->state = EP_STATE_OFLDCONN_FAILED; - QEDI_ERR(&qedi->dbg_ctx, - "Offload conn TIMEOUT iscsi_cid=%u, qedi_ep=%p\n", - qedi_ep->iscsi_cid, qedi_ep); - } -} - static int qedi_set_path(struct Scsi_Host *shost, struct iscsi_path *path_data) { struct qedi_ctx *qedi; @@ -1217,6 +1294,10 @@ static int qedi_set_path(struct Scsi_Host *shost, struct iscsi_path *path_data) } iscsi_cid = (u32)path_data->handle; + if (iscsi_cid >= qedi->max_active_conns) { + ret = -EINVAL; + goto set_path_exit; + } qedi_ep = qedi->ep_tbl[iscsi_cid]; QEDI_INFO(&qedi->dbg_ctx, QEDI_LOG_INFO, "iscsi_cid=0x%x, qedi_ep=%p\n", iscsi_cid, qedi_ep); @@ -1300,7 +1381,6 @@ static int qedi_set_path(struct Scsi_Host *shost, struct iscsi_path *path_data) qedi_ep->dst_addr, qedi_ep->dst_port); } - INIT_WORK(&qedi_ep->offload_work, qedi_offload_work); queue_work(qedi->offload_thread, &qedi_ep->offload_work); ret = 0; @@ -1368,13 +1448,24 @@ static umode_t qedi_attr_is_visible(int param_type, int param) static void qedi_cleanup_task(struct iscsi_task *task) { - if (!task->sc || task->state == ISCSI_TASK_PENDING) { + struct qedi_cmd *cmd; + + if (task->state == ISCSI_TASK_PENDING) { QEDI_INFO(NULL, QEDI_LOG_IO, "Returning ref_cnt=%d\n", refcount_read(&task->refcount)); return; } - qedi_iscsi_unmap_sg_list(task->dd_data); + if (task->sc) + qedi_iscsi_unmap_sg_list(task->dd_data); + + cmd = task->dd_data; + if (cmd->task_id != U16_MAX) + qedi_clear_task_idx(iscsi_host_priv(task->conn->session->host), + cmd->task_id); + + cmd->task_id = U16_MAX; + cmd->scsi_cmd = NULL; } struct iscsi_transport qedi_iscsi_transport = { @@ -1386,6 +1477,7 @@ struct iscsi_transport qedi_iscsi_transport = { .destroy_session = qedi_session_destroy, .create_conn = qedi_conn_create, .bind_conn = qedi_conn_bind, + .unbind_conn = iscsi_conn_unbind, .start_conn = qedi_conn_start, .stop_conn = iscsi_conn_stop, .destroy_conn = qedi_conn_destroy, @@ -1538,7 +1630,7 @@ static const struct { }, }; -char *qedi_get_iscsi_error(enum iscsi_error_types err_code) +static char *qedi_get_iscsi_error(enum iscsi_error_types err_code) { int i; char *msg = NULL; |
