18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * QLogic FCoE Offload Driver 48c2ecf20Sopenharmony_ci * Copyright (c) 2016-2018 Cavium Inc. 58c2ecf20Sopenharmony_ci */ 68c2ecf20Sopenharmony_ci#include "qedf.h" 78c2ecf20Sopenharmony_ci 88c2ecf20Sopenharmony_ci/* It's assumed that the lock is held when calling this function. */ 98c2ecf20Sopenharmony_cistatic int qedf_initiate_els(struct qedf_rport *fcport, unsigned int op, 108c2ecf20Sopenharmony_ci void *data, uint32_t data_len, 118c2ecf20Sopenharmony_ci void (*cb_func)(struct qedf_els_cb_arg *cb_arg), 128c2ecf20Sopenharmony_ci struct qedf_els_cb_arg *cb_arg, uint32_t timer_msec) 138c2ecf20Sopenharmony_ci{ 148c2ecf20Sopenharmony_ci struct qedf_ctx *qedf; 158c2ecf20Sopenharmony_ci struct fc_lport *lport; 168c2ecf20Sopenharmony_ci struct qedf_ioreq *els_req; 178c2ecf20Sopenharmony_ci struct qedf_mp_req *mp_req; 188c2ecf20Sopenharmony_ci struct fc_frame_header *fc_hdr; 198c2ecf20Sopenharmony_ci struct e4_fcoe_task_context *task; 208c2ecf20Sopenharmony_ci int rc = 0; 218c2ecf20Sopenharmony_ci uint32_t did, sid; 228c2ecf20Sopenharmony_ci uint16_t xid; 238c2ecf20Sopenharmony_ci struct fcoe_wqe *sqe; 248c2ecf20Sopenharmony_ci unsigned long flags; 258c2ecf20Sopenharmony_ci u16 sqe_idx; 268c2ecf20Sopenharmony_ci 278c2ecf20Sopenharmony_ci if (!fcport) { 288c2ecf20Sopenharmony_ci QEDF_ERR(NULL, "fcport is NULL"); 298c2ecf20Sopenharmony_ci rc = -EINVAL; 308c2ecf20Sopenharmony_ci goto els_err; 318c2ecf20Sopenharmony_ci } 328c2ecf20Sopenharmony_ci 338c2ecf20Sopenharmony_ci qedf = fcport->qedf; 348c2ecf20Sopenharmony_ci lport = qedf->lport; 358c2ecf20Sopenharmony_ci 368c2ecf20Sopenharmony_ci QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_ELS, "Sending ELS\n"); 378c2ecf20Sopenharmony_ci 388c2ecf20Sopenharmony_ci rc = fc_remote_port_chkready(fcport->rport); 398c2ecf20Sopenharmony_ci if (rc) { 408c2ecf20Sopenharmony_ci QEDF_ERR(&(qedf->dbg_ctx), "els 0x%x: rport not ready\n", op); 418c2ecf20Sopenharmony_ci rc = -EAGAIN; 428c2ecf20Sopenharmony_ci goto els_err; 438c2ecf20Sopenharmony_ci } 448c2ecf20Sopenharmony_ci if (lport->state != LPORT_ST_READY || !(lport->link_up)) { 458c2ecf20Sopenharmony_ci QEDF_ERR(&(qedf->dbg_ctx), "els 0x%x: link is not ready\n", 468c2ecf20Sopenharmony_ci op); 478c2ecf20Sopenharmony_ci rc = -EAGAIN; 488c2ecf20Sopenharmony_ci goto els_err; 498c2ecf20Sopenharmony_ci } 508c2ecf20Sopenharmony_ci 518c2ecf20Sopenharmony_ci if (!test_bit(QEDF_RPORT_SESSION_READY, &fcport->flags)) { 528c2ecf20Sopenharmony_ci QEDF_ERR(&(qedf->dbg_ctx), "els 0x%x: fcport not ready\n", op); 538c2ecf20Sopenharmony_ci rc = -EINVAL; 548c2ecf20Sopenharmony_ci goto els_err; 558c2ecf20Sopenharmony_ci } 568c2ecf20Sopenharmony_ci 578c2ecf20Sopenharmony_ci els_req = qedf_alloc_cmd(fcport, QEDF_ELS); 588c2ecf20Sopenharmony_ci if (!els_req) { 598c2ecf20Sopenharmony_ci QEDF_INFO(&qedf->dbg_ctx, QEDF_LOG_ELS, 608c2ecf20Sopenharmony_ci "Failed to alloc ELS request 0x%x\n", op); 618c2ecf20Sopenharmony_ci rc = -ENOMEM; 628c2ecf20Sopenharmony_ci goto els_err; 638c2ecf20Sopenharmony_ci } 648c2ecf20Sopenharmony_ci 658c2ecf20Sopenharmony_ci QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_ELS, "initiate_els els_req = " 668c2ecf20Sopenharmony_ci "0x%p cb_arg = %p xid = %x\n", els_req, cb_arg, 678c2ecf20Sopenharmony_ci els_req->xid); 688c2ecf20Sopenharmony_ci els_req->sc_cmd = NULL; 698c2ecf20Sopenharmony_ci els_req->cmd_type = QEDF_ELS; 708c2ecf20Sopenharmony_ci els_req->fcport = fcport; 718c2ecf20Sopenharmony_ci els_req->cb_func = cb_func; 728c2ecf20Sopenharmony_ci cb_arg->io_req = els_req; 738c2ecf20Sopenharmony_ci cb_arg->op = op; 748c2ecf20Sopenharmony_ci els_req->cb_arg = cb_arg; 758c2ecf20Sopenharmony_ci els_req->data_xfer_len = data_len; 768c2ecf20Sopenharmony_ci 778c2ecf20Sopenharmony_ci /* Record which cpu this request is associated with */ 788c2ecf20Sopenharmony_ci els_req->cpu = smp_processor_id(); 798c2ecf20Sopenharmony_ci 808c2ecf20Sopenharmony_ci mp_req = (struct qedf_mp_req *)&(els_req->mp_req); 818c2ecf20Sopenharmony_ci rc = qedf_init_mp_req(els_req); 828c2ecf20Sopenharmony_ci if (rc) { 838c2ecf20Sopenharmony_ci QEDF_ERR(&(qedf->dbg_ctx), "ELS MP request init failed\n"); 848c2ecf20Sopenharmony_ci kref_put(&els_req->refcount, qedf_release_cmd); 858c2ecf20Sopenharmony_ci goto els_err; 868c2ecf20Sopenharmony_ci } else { 878c2ecf20Sopenharmony_ci rc = 0; 888c2ecf20Sopenharmony_ci } 898c2ecf20Sopenharmony_ci 908c2ecf20Sopenharmony_ci /* Fill ELS Payload */ 918c2ecf20Sopenharmony_ci if ((op >= ELS_LS_RJT) && (op <= ELS_AUTH_ELS)) { 928c2ecf20Sopenharmony_ci memcpy(mp_req->req_buf, data, data_len); 938c2ecf20Sopenharmony_ci } else { 948c2ecf20Sopenharmony_ci QEDF_ERR(&(qedf->dbg_ctx), "Invalid ELS op 0x%x\n", op); 958c2ecf20Sopenharmony_ci els_req->cb_func = NULL; 968c2ecf20Sopenharmony_ci els_req->cb_arg = NULL; 978c2ecf20Sopenharmony_ci kref_put(&els_req->refcount, qedf_release_cmd); 988c2ecf20Sopenharmony_ci rc = -EINVAL; 998c2ecf20Sopenharmony_ci } 1008c2ecf20Sopenharmony_ci 1018c2ecf20Sopenharmony_ci if (rc) 1028c2ecf20Sopenharmony_ci goto els_err; 1038c2ecf20Sopenharmony_ci 1048c2ecf20Sopenharmony_ci /* Fill FC header */ 1058c2ecf20Sopenharmony_ci fc_hdr = &(mp_req->req_fc_hdr); 1068c2ecf20Sopenharmony_ci 1078c2ecf20Sopenharmony_ci did = fcport->rdata->ids.port_id; 1088c2ecf20Sopenharmony_ci sid = fcport->sid; 1098c2ecf20Sopenharmony_ci 1108c2ecf20Sopenharmony_ci __fc_fill_fc_hdr(fc_hdr, FC_RCTL_ELS_REQ, did, sid, 1118c2ecf20Sopenharmony_ci FC_TYPE_ELS, FC_FC_FIRST_SEQ | FC_FC_END_SEQ | 1128c2ecf20Sopenharmony_ci FC_FC_SEQ_INIT, 0); 1138c2ecf20Sopenharmony_ci 1148c2ecf20Sopenharmony_ci /* Obtain exchange id */ 1158c2ecf20Sopenharmony_ci xid = els_req->xid; 1168c2ecf20Sopenharmony_ci 1178c2ecf20Sopenharmony_ci spin_lock_irqsave(&fcport->rport_lock, flags); 1188c2ecf20Sopenharmony_ci 1198c2ecf20Sopenharmony_ci sqe_idx = qedf_get_sqe_idx(fcport); 1208c2ecf20Sopenharmony_ci sqe = &fcport->sq[sqe_idx]; 1218c2ecf20Sopenharmony_ci memset(sqe, 0, sizeof(struct fcoe_wqe)); 1228c2ecf20Sopenharmony_ci 1238c2ecf20Sopenharmony_ci /* Initialize task context for this IO request */ 1248c2ecf20Sopenharmony_ci task = qedf_get_task_mem(&qedf->tasks, xid); 1258c2ecf20Sopenharmony_ci qedf_init_mp_task(els_req, task, sqe); 1268c2ecf20Sopenharmony_ci 1278c2ecf20Sopenharmony_ci /* Put timer on els request */ 1288c2ecf20Sopenharmony_ci if (timer_msec) 1298c2ecf20Sopenharmony_ci qedf_cmd_timer_set(qedf, els_req, timer_msec); 1308c2ecf20Sopenharmony_ci 1318c2ecf20Sopenharmony_ci /* Ring doorbell */ 1328c2ecf20Sopenharmony_ci QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_ELS, "Ringing doorbell for ELS " 1338c2ecf20Sopenharmony_ci "req\n"); 1348c2ecf20Sopenharmony_ci qedf_ring_doorbell(fcport); 1358c2ecf20Sopenharmony_ci set_bit(QEDF_CMD_OUTSTANDING, &els_req->flags); 1368c2ecf20Sopenharmony_ci 1378c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&fcport->rport_lock, flags); 1388c2ecf20Sopenharmony_ciels_err: 1398c2ecf20Sopenharmony_ci return rc; 1408c2ecf20Sopenharmony_ci} 1418c2ecf20Sopenharmony_ci 1428c2ecf20Sopenharmony_civoid qedf_process_els_compl(struct qedf_ctx *qedf, struct fcoe_cqe *cqe, 1438c2ecf20Sopenharmony_ci struct qedf_ioreq *els_req) 1448c2ecf20Sopenharmony_ci{ 1458c2ecf20Sopenharmony_ci struct fcoe_cqe_midpath_info *mp_info; 1468c2ecf20Sopenharmony_ci struct qedf_rport *fcport; 1478c2ecf20Sopenharmony_ci 1488c2ecf20Sopenharmony_ci QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_ELS, "Entered with xid = 0x%x" 1498c2ecf20Sopenharmony_ci " cmd_type = %d.\n", els_req->xid, els_req->cmd_type); 1508c2ecf20Sopenharmony_ci 1518c2ecf20Sopenharmony_ci if ((els_req->event == QEDF_IOREQ_EV_ELS_FLUSH) 1528c2ecf20Sopenharmony_ci || (els_req->event == QEDF_IOREQ_EV_CLEANUP_SUCCESS) 1538c2ecf20Sopenharmony_ci || (els_req->event == QEDF_IOREQ_EV_CLEANUP_FAILED)) { 1548c2ecf20Sopenharmony_ci QEDF_INFO(&qedf->dbg_ctx, QEDF_LOG_IO, 1558c2ecf20Sopenharmony_ci "ELS completion xid=0x%x after flush event=0x%x", 1568c2ecf20Sopenharmony_ci els_req->xid, els_req->event); 1578c2ecf20Sopenharmony_ci return; 1588c2ecf20Sopenharmony_ci } 1598c2ecf20Sopenharmony_ci 1608c2ecf20Sopenharmony_ci fcport = els_req->fcport; 1618c2ecf20Sopenharmony_ci 1628c2ecf20Sopenharmony_ci /* When flush is active, 1638c2ecf20Sopenharmony_ci * let the cmds be completed from the cleanup context 1648c2ecf20Sopenharmony_ci */ 1658c2ecf20Sopenharmony_ci if (test_bit(QEDF_RPORT_IN_TARGET_RESET, &fcport->flags) || 1668c2ecf20Sopenharmony_ci test_bit(QEDF_RPORT_IN_LUN_RESET, &fcport->flags)) { 1678c2ecf20Sopenharmony_ci QEDF_INFO(&qedf->dbg_ctx, QEDF_LOG_IO, 1688c2ecf20Sopenharmony_ci "Dropping ELS completion xid=0x%x as fcport is flushing", 1698c2ecf20Sopenharmony_ci els_req->xid); 1708c2ecf20Sopenharmony_ci return; 1718c2ecf20Sopenharmony_ci } 1728c2ecf20Sopenharmony_ci 1738c2ecf20Sopenharmony_ci clear_bit(QEDF_CMD_OUTSTANDING, &els_req->flags); 1748c2ecf20Sopenharmony_ci 1758c2ecf20Sopenharmony_ci /* Kill the ELS timer */ 1768c2ecf20Sopenharmony_ci cancel_delayed_work(&els_req->timeout_work); 1778c2ecf20Sopenharmony_ci 1788c2ecf20Sopenharmony_ci /* Get ELS response length from CQE */ 1798c2ecf20Sopenharmony_ci mp_info = &cqe->cqe_info.midpath_info; 1808c2ecf20Sopenharmony_ci els_req->mp_req.resp_len = mp_info->data_placement_size; 1818c2ecf20Sopenharmony_ci 1828c2ecf20Sopenharmony_ci /* Parse ELS response */ 1838c2ecf20Sopenharmony_ci if ((els_req->cb_func) && (els_req->cb_arg)) { 1848c2ecf20Sopenharmony_ci els_req->cb_func(els_req->cb_arg); 1858c2ecf20Sopenharmony_ci els_req->cb_arg = NULL; 1868c2ecf20Sopenharmony_ci } 1878c2ecf20Sopenharmony_ci 1888c2ecf20Sopenharmony_ci kref_put(&els_req->refcount, qedf_release_cmd); 1898c2ecf20Sopenharmony_ci} 1908c2ecf20Sopenharmony_ci 1918c2ecf20Sopenharmony_cistatic void qedf_rrq_compl(struct qedf_els_cb_arg *cb_arg) 1928c2ecf20Sopenharmony_ci{ 1938c2ecf20Sopenharmony_ci struct qedf_ioreq *orig_io_req; 1948c2ecf20Sopenharmony_ci struct qedf_ioreq *rrq_req; 1958c2ecf20Sopenharmony_ci struct qedf_ctx *qedf; 1968c2ecf20Sopenharmony_ci int refcount; 1978c2ecf20Sopenharmony_ci 1988c2ecf20Sopenharmony_ci rrq_req = cb_arg->io_req; 1998c2ecf20Sopenharmony_ci qedf = rrq_req->fcport->qedf; 2008c2ecf20Sopenharmony_ci 2018c2ecf20Sopenharmony_ci QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_ELS, "Entered.\n"); 2028c2ecf20Sopenharmony_ci 2038c2ecf20Sopenharmony_ci orig_io_req = cb_arg->aborted_io_req; 2048c2ecf20Sopenharmony_ci 2058c2ecf20Sopenharmony_ci if (!orig_io_req) { 2068c2ecf20Sopenharmony_ci QEDF_ERR(&qedf->dbg_ctx, 2078c2ecf20Sopenharmony_ci "Original io_req is NULL, rrq_req = %p.\n", rrq_req); 2088c2ecf20Sopenharmony_ci goto out_free; 2098c2ecf20Sopenharmony_ci } 2108c2ecf20Sopenharmony_ci 2118c2ecf20Sopenharmony_ci refcount = kref_read(&orig_io_req->refcount); 2128c2ecf20Sopenharmony_ci QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_ELS, "rrq_compl: orig io = %p," 2138c2ecf20Sopenharmony_ci " orig xid = 0x%x, rrq_xid = 0x%x, refcount=%d\n", 2148c2ecf20Sopenharmony_ci orig_io_req, orig_io_req->xid, rrq_req->xid, refcount); 2158c2ecf20Sopenharmony_ci 2168c2ecf20Sopenharmony_ci /* 2178c2ecf20Sopenharmony_ci * This should return the aborted io_req to the command pool. Note that 2188c2ecf20Sopenharmony_ci * we need to check the refcound in case the original request was 2198c2ecf20Sopenharmony_ci * flushed but we get a completion on this xid. 2208c2ecf20Sopenharmony_ci */ 2218c2ecf20Sopenharmony_ci if (orig_io_req && refcount > 0) 2228c2ecf20Sopenharmony_ci kref_put(&orig_io_req->refcount, qedf_release_cmd); 2238c2ecf20Sopenharmony_ci 2248c2ecf20Sopenharmony_ciout_free: 2258c2ecf20Sopenharmony_ci /* 2268c2ecf20Sopenharmony_ci * Release a reference to the rrq request if we timed out as the 2278c2ecf20Sopenharmony_ci * rrq completion handler is called directly from the timeout handler 2288c2ecf20Sopenharmony_ci * and not from els_compl where the reference would have normally been 2298c2ecf20Sopenharmony_ci * released. 2308c2ecf20Sopenharmony_ci */ 2318c2ecf20Sopenharmony_ci if (rrq_req->event == QEDF_IOREQ_EV_ELS_TMO) 2328c2ecf20Sopenharmony_ci kref_put(&rrq_req->refcount, qedf_release_cmd); 2338c2ecf20Sopenharmony_ci kfree(cb_arg); 2348c2ecf20Sopenharmony_ci} 2358c2ecf20Sopenharmony_ci 2368c2ecf20Sopenharmony_ci/* Assumes kref is already held by caller */ 2378c2ecf20Sopenharmony_ciint qedf_send_rrq(struct qedf_ioreq *aborted_io_req) 2388c2ecf20Sopenharmony_ci{ 2398c2ecf20Sopenharmony_ci 2408c2ecf20Sopenharmony_ci struct fc_els_rrq rrq; 2418c2ecf20Sopenharmony_ci struct qedf_rport *fcport; 2428c2ecf20Sopenharmony_ci struct fc_lport *lport; 2438c2ecf20Sopenharmony_ci struct qedf_els_cb_arg *cb_arg = NULL; 2448c2ecf20Sopenharmony_ci struct qedf_ctx *qedf; 2458c2ecf20Sopenharmony_ci uint32_t sid; 2468c2ecf20Sopenharmony_ci uint32_t r_a_tov; 2478c2ecf20Sopenharmony_ci int rc; 2488c2ecf20Sopenharmony_ci int refcount; 2498c2ecf20Sopenharmony_ci 2508c2ecf20Sopenharmony_ci if (!aborted_io_req) { 2518c2ecf20Sopenharmony_ci QEDF_ERR(NULL, "abort_io_req is NULL.\n"); 2528c2ecf20Sopenharmony_ci return -EINVAL; 2538c2ecf20Sopenharmony_ci } 2548c2ecf20Sopenharmony_ci 2558c2ecf20Sopenharmony_ci fcport = aborted_io_req->fcport; 2568c2ecf20Sopenharmony_ci 2578c2ecf20Sopenharmony_ci if (!fcport) { 2588c2ecf20Sopenharmony_ci refcount = kref_read(&aborted_io_req->refcount); 2598c2ecf20Sopenharmony_ci QEDF_ERR(NULL, 2608c2ecf20Sopenharmony_ci "RRQ work was queued prior to a flush xid=0x%x, refcount=%d.\n", 2618c2ecf20Sopenharmony_ci aborted_io_req->xid, refcount); 2628c2ecf20Sopenharmony_ci kref_put(&aborted_io_req->refcount, qedf_release_cmd); 2638c2ecf20Sopenharmony_ci return -EINVAL; 2648c2ecf20Sopenharmony_ci } 2658c2ecf20Sopenharmony_ci 2668c2ecf20Sopenharmony_ci /* Check that fcport is still offloaded */ 2678c2ecf20Sopenharmony_ci if (!test_bit(QEDF_RPORT_SESSION_READY, &fcport->flags)) { 2688c2ecf20Sopenharmony_ci QEDF_ERR(NULL, "fcport is no longer offloaded.\n"); 2698c2ecf20Sopenharmony_ci return -EINVAL; 2708c2ecf20Sopenharmony_ci } 2718c2ecf20Sopenharmony_ci 2728c2ecf20Sopenharmony_ci if (!fcport->qedf) { 2738c2ecf20Sopenharmony_ci QEDF_ERR(NULL, "fcport->qedf is NULL.\n"); 2748c2ecf20Sopenharmony_ci return -EINVAL; 2758c2ecf20Sopenharmony_ci } 2768c2ecf20Sopenharmony_ci 2778c2ecf20Sopenharmony_ci qedf = fcport->qedf; 2788c2ecf20Sopenharmony_ci 2798c2ecf20Sopenharmony_ci /* 2808c2ecf20Sopenharmony_ci * Sanity check that we can send a RRQ to make sure that refcount isn't 2818c2ecf20Sopenharmony_ci * 0 2828c2ecf20Sopenharmony_ci */ 2838c2ecf20Sopenharmony_ci refcount = kref_read(&aborted_io_req->refcount); 2848c2ecf20Sopenharmony_ci if (refcount != 1) { 2858c2ecf20Sopenharmony_ci QEDF_INFO(&qedf->dbg_ctx, QEDF_LOG_ELS, 2868c2ecf20Sopenharmony_ci "refcount for xid=%x io_req=%p refcount=%d is not 1.\n", 2878c2ecf20Sopenharmony_ci aborted_io_req->xid, aborted_io_req, refcount); 2888c2ecf20Sopenharmony_ci return -EINVAL; 2898c2ecf20Sopenharmony_ci } 2908c2ecf20Sopenharmony_ci 2918c2ecf20Sopenharmony_ci lport = qedf->lport; 2928c2ecf20Sopenharmony_ci sid = fcport->sid; 2938c2ecf20Sopenharmony_ci r_a_tov = lport->r_a_tov; 2948c2ecf20Sopenharmony_ci 2958c2ecf20Sopenharmony_ci QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_ELS, "Sending RRQ orig " 2968c2ecf20Sopenharmony_ci "io = %p, orig_xid = 0x%x\n", aborted_io_req, 2978c2ecf20Sopenharmony_ci aborted_io_req->xid); 2988c2ecf20Sopenharmony_ci memset(&rrq, 0, sizeof(rrq)); 2998c2ecf20Sopenharmony_ci 3008c2ecf20Sopenharmony_ci cb_arg = kzalloc(sizeof(struct qedf_els_cb_arg), GFP_NOIO); 3018c2ecf20Sopenharmony_ci if (!cb_arg) { 3028c2ecf20Sopenharmony_ci QEDF_ERR(&(qedf->dbg_ctx), "Unable to allocate cb_arg for " 3038c2ecf20Sopenharmony_ci "RRQ\n"); 3048c2ecf20Sopenharmony_ci rc = -ENOMEM; 3058c2ecf20Sopenharmony_ci goto rrq_err; 3068c2ecf20Sopenharmony_ci } 3078c2ecf20Sopenharmony_ci 3088c2ecf20Sopenharmony_ci cb_arg->aborted_io_req = aborted_io_req; 3098c2ecf20Sopenharmony_ci 3108c2ecf20Sopenharmony_ci rrq.rrq_cmd = ELS_RRQ; 3118c2ecf20Sopenharmony_ci hton24(rrq.rrq_s_id, sid); 3128c2ecf20Sopenharmony_ci rrq.rrq_ox_id = htons(aborted_io_req->xid); 3138c2ecf20Sopenharmony_ci rrq.rrq_rx_id = 3148c2ecf20Sopenharmony_ci htons(aborted_io_req->task->tstorm_st_context.read_write.rx_id); 3158c2ecf20Sopenharmony_ci 3168c2ecf20Sopenharmony_ci rc = qedf_initiate_els(fcport, ELS_RRQ, &rrq, sizeof(rrq), 3178c2ecf20Sopenharmony_ci qedf_rrq_compl, cb_arg, r_a_tov); 3188c2ecf20Sopenharmony_ci 3198c2ecf20Sopenharmony_cirrq_err: 3208c2ecf20Sopenharmony_ci if (rc) { 3218c2ecf20Sopenharmony_ci QEDF_ERR(&(qedf->dbg_ctx), "RRQ failed - release orig io " 3228c2ecf20Sopenharmony_ci "req 0x%x\n", aborted_io_req->xid); 3238c2ecf20Sopenharmony_ci kfree(cb_arg); 3248c2ecf20Sopenharmony_ci kref_put(&aborted_io_req->refcount, qedf_release_cmd); 3258c2ecf20Sopenharmony_ci } 3268c2ecf20Sopenharmony_ci return rc; 3278c2ecf20Sopenharmony_ci} 3288c2ecf20Sopenharmony_ci 3298c2ecf20Sopenharmony_cistatic void qedf_process_l2_frame_compl(struct qedf_rport *fcport, 3308c2ecf20Sopenharmony_ci struct fc_frame *fp, 3318c2ecf20Sopenharmony_ci u16 l2_oxid) 3328c2ecf20Sopenharmony_ci{ 3338c2ecf20Sopenharmony_ci struct fc_lport *lport = fcport->qedf->lport; 3348c2ecf20Sopenharmony_ci struct fc_frame_header *fh; 3358c2ecf20Sopenharmony_ci u32 crc; 3368c2ecf20Sopenharmony_ci 3378c2ecf20Sopenharmony_ci fh = (struct fc_frame_header *)fc_frame_header_get(fp); 3388c2ecf20Sopenharmony_ci 3398c2ecf20Sopenharmony_ci /* Set the OXID we return to what libfc used */ 3408c2ecf20Sopenharmony_ci if (l2_oxid != FC_XID_UNKNOWN) 3418c2ecf20Sopenharmony_ci fh->fh_ox_id = htons(l2_oxid); 3428c2ecf20Sopenharmony_ci 3438c2ecf20Sopenharmony_ci /* Setup header fields */ 3448c2ecf20Sopenharmony_ci fh->fh_r_ctl = FC_RCTL_ELS_REP; 3458c2ecf20Sopenharmony_ci fh->fh_type = FC_TYPE_ELS; 3468c2ecf20Sopenharmony_ci /* Last sequence, end sequence */ 3478c2ecf20Sopenharmony_ci fh->fh_f_ctl[0] = 0x98; 3488c2ecf20Sopenharmony_ci hton24(fh->fh_d_id, lport->port_id); 3498c2ecf20Sopenharmony_ci hton24(fh->fh_s_id, fcport->rdata->ids.port_id); 3508c2ecf20Sopenharmony_ci fh->fh_rx_id = 0xffff; 3518c2ecf20Sopenharmony_ci 3528c2ecf20Sopenharmony_ci /* Set frame attributes */ 3538c2ecf20Sopenharmony_ci crc = fcoe_fc_crc(fp); 3548c2ecf20Sopenharmony_ci fc_frame_init(fp); 3558c2ecf20Sopenharmony_ci fr_dev(fp) = lport; 3568c2ecf20Sopenharmony_ci fr_sof(fp) = FC_SOF_I3; 3578c2ecf20Sopenharmony_ci fr_eof(fp) = FC_EOF_T; 3588c2ecf20Sopenharmony_ci fr_crc(fp) = cpu_to_le32(~crc); 3598c2ecf20Sopenharmony_ci 3608c2ecf20Sopenharmony_ci /* Send completed request to libfc */ 3618c2ecf20Sopenharmony_ci fc_exch_recv(lport, fp); 3628c2ecf20Sopenharmony_ci} 3638c2ecf20Sopenharmony_ci 3648c2ecf20Sopenharmony_ci/* 3658c2ecf20Sopenharmony_ci * In instances where an ELS command times out we may need to restart the 3668c2ecf20Sopenharmony_ci * rport by logging out and then logging back in. 3678c2ecf20Sopenharmony_ci */ 3688c2ecf20Sopenharmony_civoid qedf_restart_rport(struct qedf_rport *fcport) 3698c2ecf20Sopenharmony_ci{ 3708c2ecf20Sopenharmony_ci struct fc_lport *lport; 3718c2ecf20Sopenharmony_ci struct fc_rport_priv *rdata; 3728c2ecf20Sopenharmony_ci u32 port_id; 3738c2ecf20Sopenharmony_ci unsigned long flags; 3748c2ecf20Sopenharmony_ci 3758c2ecf20Sopenharmony_ci if (!fcport) { 3768c2ecf20Sopenharmony_ci QEDF_ERR(NULL, "fcport is NULL.\n"); 3778c2ecf20Sopenharmony_ci return; 3788c2ecf20Sopenharmony_ci } 3798c2ecf20Sopenharmony_ci 3808c2ecf20Sopenharmony_ci spin_lock_irqsave(&fcport->rport_lock, flags); 3818c2ecf20Sopenharmony_ci if (test_bit(QEDF_RPORT_IN_RESET, &fcport->flags) || 3828c2ecf20Sopenharmony_ci !test_bit(QEDF_RPORT_SESSION_READY, &fcport->flags) || 3838c2ecf20Sopenharmony_ci test_bit(QEDF_RPORT_UPLOADING_CONNECTION, &fcport->flags)) { 3848c2ecf20Sopenharmony_ci QEDF_ERR(&(fcport->qedf->dbg_ctx), "fcport %p already in reset or not offloaded.\n", 3858c2ecf20Sopenharmony_ci fcport); 3868c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&fcport->rport_lock, flags); 3878c2ecf20Sopenharmony_ci return; 3888c2ecf20Sopenharmony_ci } 3898c2ecf20Sopenharmony_ci 3908c2ecf20Sopenharmony_ci /* Set that we are now in reset */ 3918c2ecf20Sopenharmony_ci set_bit(QEDF_RPORT_IN_RESET, &fcport->flags); 3928c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&fcport->rport_lock, flags); 3938c2ecf20Sopenharmony_ci 3948c2ecf20Sopenharmony_ci rdata = fcport->rdata; 3958c2ecf20Sopenharmony_ci if (rdata && !kref_get_unless_zero(&rdata->kref)) { 3968c2ecf20Sopenharmony_ci fcport->rdata = NULL; 3978c2ecf20Sopenharmony_ci rdata = NULL; 3988c2ecf20Sopenharmony_ci } 3998c2ecf20Sopenharmony_ci 4008c2ecf20Sopenharmony_ci if (rdata && rdata->rp_state == RPORT_ST_READY) { 4018c2ecf20Sopenharmony_ci lport = fcport->qedf->lport; 4028c2ecf20Sopenharmony_ci port_id = rdata->ids.port_id; 4038c2ecf20Sopenharmony_ci QEDF_ERR(&(fcport->qedf->dbg_ctx), 4048c2ecf20Sopenharmony_ci "LOGO port_id=%x.\n", port_id); 4058c2ecf20Sopenharmony_ci fc_rport_logoff(rdata); 4068c2ecf20Sopenharmony_ci kref_put(&rdata->kref, fc_rport_destroy); 4078c2ecf20Sopenharmony_ci mutex_lock(&lport->disc.disc_mutex); 4088c2ecf20Sopenharmony_ci /* Recreate the rport and log back in */ 4098c2ecf20Sopenharmony_ci rdata = fc_rport_create(lport, port_id); 4108c2ecf20Sopenharmony_ci mutex_unlock(&lport->disc.disc_mutex); 4118c2ecf20Sopenharmony_ci if (rdata) 4128c2ecf20Sopenharmony_ci fc_rport_login(rdata); 4138c2ecf20Sopenharmony_ci fcport->rdata = rdata; 4148c2ecf20Sopenharmony_ci } 4158c2ecf20Sopenharmony_ci clear_bit(QEDF_RPORT_IN_RESET, &fcport->flags); 4168c2ecf20Sopenharmony_ci} 4178c2ecf20Sopenharmony_ci 4188c2ecf20Sopenharmony_cistatic void qedf_l2_els_compl(struct qedf_els_cb_arg *cb_arg) 4198c2ecf20Sopenharmony_ci{ 4208c2ecf20Sopenharmony_ci struct qedf_ioreq *els_req; 4218c2ecf20Sopenharmony_ci struct qedf_rport *fcport; 4228c2ecf20Sopenharmony_ci struct qedf_mp_req *mp_req; 4238c2ecf20Sopenharmony_ci struct fc_frame *fp; 4248c2ecf20Sopenharmony_ci struct fc_frame_header *fh, *mp_fc_hdr; 4258c2ecf20Sopenharmony_ci void *resp_buf, *fc_payload; 4268c2ecf20Sopenharmony_ci u32 resp_len; 4278c2ecf20Sopenharmony_ci u16 l2_oxid; 4288c2ecf20Sopenharmony_ci 4298c2ecf20Sopenharmony_ci l2_oxid = cb_arg->l2_oxid; 4308c2ecf20Sopenharmony_ci els_req = cb_arg->io_req; 4318c2ecf20Sopenharmony_ci 4328c2ecf20Sopenharmony_ci if (!els_req) { 4338c2ecf20Sopenharmony_ci QEDF_ERR(NULL, "els_req is NULL.\n"); 4348c2ecf20Sopenharmony_ci goto free_arg; 4358c2ecf20Sopenharmony_ci } 4368c2ecf20Sopenharmony_ci 4378c2ecf20Sopenharmony_ci /* 4388c2ecf20Sopenharmony_ci * If we are flushing the command just free the cb_arg as none of the 4398c2ecf20Sopenharmony_ci * response data will be valid. 4408c2ecf20Sopenharmony_ci */ 4418c2ecf20Sopenharmony_ci if (els_req->event == QEDF_IOREQ_EV_ELS_FLUSH) { 4428c2ecf20Sopenharmony_ci QEDF_ERR(NULL, "els_req xid=0x%x event is flush.\n", 4438c2ecf20Sopenharmony_ci els_req->xid); 4448c2ecf20Sopenharmony_ci goto free_arg; 4458c2ecf20Sopenharmony_ci } 4468c2ecf20Sopenharmony_ci 4478c2ecf20Sopenharmony_ci fcport = els_req->fcport; 4488c2ecf20Sopenharmony_ci mp_req = &(els_req->mp_req); 4498c2ecf20Sopenharmony_ci mp_fc_hdr = &(mp_req->resp_fc_hdr); 4508c2ecf20Sopenharmony_ci resp_len = mp_req->resp_len; 4518c2ecf20Sopenharmony_ci resp_buf = mp_req->resp_buf; 4528c2ecf20Sopenharmony_ci 4538c2ecf20Sopenharmony_ci /* 4548c2ecf20Sopenharmony_ci * If a middle path ELS command times out, don't try to return 4558c2ecf20Sopenharmony_ci * the command but rather do any internal cleanup and then libfc 4568c2ecf20Sopenharmony_ci * timeout the command and clean up its internal resources. 4578c2ecf20Sopenharmony_ci */ 4588c2ecf20Sopenharmony_ci if (els_req->event == QEDF_IOREQ_EV_ELS_TMO) { 4598c2ecf20Sopenharmony_ci /* 4608c2ecf20Sopenharmony_ci * If ADISC times out, libfc will timeout the exchange and then 4618c2ecf20Sopenharmony_ci * try to send a PLOGI which will timeout since the session is 4628c2ecf20Sopenharmony_ci * still offloaded. Force libfc to logout the session which 4638c2ecf20Sopenharmony_ci * will offload the connection and allow the PLOGI response to 4648c2ecf20Sopenharmony_ci * flow over the LL2 path. 4658c2ecf20Sopenharmony_ci */ 4668c2ecf20Sopenharmony_ci if (cb_arg->op == ELS_ADISC) 4678c2ecf20Sopenharmony_ci qedf_restart_rport(fcport); 4688c2ecf20Sopenharmony_ci return; 4698c2ecf20Sopenharmony_ci } 4708c2ecf20Sopenharmony_ci 4718c2ecf20Sopenharmony_ci if (sizeof(struct fc_frame_header) + resp_len > QEDF_PAGE_SIZE) { 4728c2ecf20Sopenharmony_ci QEDF_ERR(&(fcport->qedf->dbg_ctx), "resp_len is " 4738c2ecf20Sopenharmony_ci "beyond page size.\n"); 4748c2ecf20Sopenharmony_ci goto free_arg; 4758c2ecf20Sopenharmony_ci } 4768c2ecf20Sopenharmony_ci 4778c2ecf20Sopenharmony_ci fp = fc_frame_alloc(fcport->qedf->lport, resp_len); 4788c2ecf20Sopenharmony_ci if (!fp) { 4798c2ecf20Sopenharmony_ci QEDF_ERR(&(fcport->qedf->dbg_ctx), 4808c2ecf20Sopenharmony_ci "fc_frame_alloc failure.\n"); 4818c2ecf20Sopenharmony_ci return; 4828c2ecf20Sopenharmony_ci } 4838c2ecf20Sopenharmony_ci 4848c2ecf20Sopenharmony_ci /* Copy frame header from firmware into fp */ 4858c2ecf20Sopenharmony_ci fh = (struct fc_frame_header *)fc_frame_header_get(fp); 4868c2ecf20Sopenharmony_ci memcpy(fh, mp_fc_hdr, sizeof(struct fc_frame_header)); 4878c2ecf20Sopenharmony_ci 4888c2ecf20Sopenharmony_ci /* Copy payload from firmware into fp */ 4898c2ecf20Sopenharmony_ci fc_payload = fc_frame_payload_get(fp, resp_len); 4908c2ecf20Sopenharmony_ci memcpy(fc_payload, resp_buf, resp_len); 4918c2ecf20Sopenharmony_ci 4928c2ecf20Sopenharmony_ci QEDF_INFO(&(fcport->qedf->dbg_ctx), QEDF_LOG_ELS, 4938c2ecf20Sopenharmony_ci "Completing OX_ID 0x%x back to libfc.\n", l2_oxid); 4948c2ecf20Sopenharmony_ci qedf_process_l2_frame_compl(fcport, fp, l2_oxid); 4958c2ecf20Sopenharmony_ci 4968c2ecf20Sopenharmony_cifree_arg: 4978c2ecf20Sopenharmony_ci kfree(cb_arg); 4988c2ecf20Sopenharmony_ci} 4998c2ecf20Sopenharmony_ci 5008c2ecf20Sopenharmony_ciint qedf_send_adisc(struct qedf_rport *fcport, struct fc_frame *fp) 5018c2ecf20Sopenharmony_ci{ 5028c2ecf20Sopenharmony_ci struct fc_els_adisc *adisc; 5038c2ecf20Sopenharmony_ci struct fc_frame_header *fh; 5048c2ecf20Sopenharmony_ci struct fc_lport *lport = fcport->qedf->lport; 5058c2ecf20Sopenharmony_ci struct qedf_els_cb_arg *cb_arg = NULL; 5068c2ecf20Sopenharmony_ci struct qedf_ctx *qedf; 5078c2ecf20Sopenharmony_ci uint32_t r_a_tov = lport->r_a_tov; 5088c2ecf20Sopenharmony_ci int rc; 5098c2ecf20Sopenharmony_ci 5108c2ecf20Sopenharmony_ci qedf = fcport->qedf; 5118c2ecf20Sopenharmony_ci fh = fc_frame_header_get(fp); 5128c2ecf20Sopenharmony_ci 5138c2ecf20Sopenharmony_ci cb_arg = kzalloc(sizeof(struct qedf_els_cb_arg), GFP_NOIO); 5148c2ecf20Sopenharmony_ci if (!cb_arg) { 5158c2ecf20Sopenharmony_ci QEDF_ERR(&(qedf->dbg_ctx), "Unable to allocate cb_arg for " 5168c2ecf20Sopenharmony_ci "ADISC\n"); 5178c2ecf20Sopenharmony_ci rc = -ENOMEM; 5188c2ecf20Sopenharmony_ci goto adisc_err; 5198c2ecf20Sopenharmony_ci } 5208c2ecf20Sopenharmony_ci cb_arg->l2_oxid = ntohs(fh->fh_ox_id); 5218c2ecf20Sopenharmony_ci 5228c2ecf20Sopenharmony_ci QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_ELS, 5238c2ecf20Sopenharmony_ci "Sending ADISC ox_id=0x%x.\n", cb_arg->l2_oxid); 5248c2ecf20Sopenharmony_ci 5258c2ecf20Sopenharmony_ci adisc = fc_frame_payload_get(fp, sizeof(*adisc)); 5268c2ecf20Sopenharmony_ci 5278c2ecf20Sopenharmony_ci rc = qedf_initiate_els(fcport, ELS_ADISC, adisc, sizeof(*adisc), 5288c2ecf20Sopenharmony_ci qedf_l2_els_compl, cb_arg, r_a_tov); 5298c2ecf20Sopenharmony_ci 5308c2ecf20Sopenharmony_ciadisc_err: 5318c2ecf20Sopenharmony_ci if (rc) { 5328c2ecf20Sopenharmony_ci QEDF_ERR(&(qedf->dbg_ctx), "ADISC failed.\n"); 5338c2ecf20Sopenharmony_ci kfree(cb_arg); 5348c2ecf20Sopenharmony_ci } 5358c2ecf20Sopenharmony_ci return rc; 5368c2ecf20Sopenharmony_ci} 5378c2ecf20Sopenharmony_ci 5388c2ecf20Sopenharmony_cistatic void qedf_srr_compl(struct qedf_els_cb_arg *cb_arg) 5398c2ecf20Sopenharmony_ci{ 5408c2ecf20Sopenharmony_ci struct qedf_ioreq *orig_io_req; 5418c2ecf20Sopenharmony_ci struct qedf_ioreq *srr_req; 5428c2ecf20Sopenharmony_ci struct qedf_mp_req *mp_req; 5438c2ecf20Sopenharmony_ci struct fc_frame_header *mp_fc_hdr, *fh; 5448c2ecf20Sopenharmony_ci struct fc_frame *fp; 5458c2ecf20Sopenharmony_ci void *resp_buf, *fc_payload; 5468c2ecf20Sopenharmony_ci u32 resp_len; 5478c2ecf20Sopenharmony_ci struct fc_lport *lport; 5488c2ecf20Sopenharmony_ci struct qedf_ctx *qedf; 5498c2ecf20Sopenharmony_ci int refcount; 5508c2ecf20Sopenharmony_ci u8 opcode; 5518c2ecf20Sopenharmony_ci 5528c2ecf20Sopenharmony_ci srr_req = cb_arg->io_req; 5538c2ecf20Sopenharmony_ci qedf = srr_req->fcport->qedf; 5548c2ecf20Sopenharmony_ci lport = qedf->lport; 5558c2ecf20Sopenharmony_ci 5568c2ecf20Sopenharmony_ci orig_io_req = cb_arg->aborted_io_req; 5578c2ecf20Sopenharmony_ci 5588c2ecf20Sopenharmony_ci if (!orig_io_req) { 5598c2ecf20Sopenharmony_ci QEDF_ERR(NULL, "orig_io_req is NULL.\n"); 5608c2ecf20Sopenharmony_ci goto out_free; 5618c2ecf20Sopenharmony_ci } 5628c2ecf20Sopenharmony_ci 5638c2ecf20Sopenharmony_ci clear_bit(QEDF_CMD_SRR_SENT, &orig_io_req->flags); 5648c2ecf20Sopenharmony_ci 5658c2ecf20Sopenharmony_ci if (srr_req->event != QEDF_IOREQ_EV_ELS_TMO && 5668c2ecf20Sopenharmony_ci srr_req->event != QEDF_IOREQ_EV_ELS_ERR_DETECT) 5678c2ecf20Sopenharmony_ci cancel_delayed_work_sync(&orig_io_req->timeout_work); 5688c2ecf20Sopenharmony_ci 5698c2ecf20Sopenharmony_ci refcount = kref_read(&orig_io_req->refcount); 5708c2ecf20Sopenharmony_ci QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_ELS, "Entered: orig_io=%p," 5718c2ecf20Sopenharmony_ci " orig_io_xid=0x%x, rec_xid=0x%x, refcount=%d\n", 5728c2ecf20Sopenharmony_ci orig_io_req, orig_io_req->xid, srr_req->xid, refcount); 5738c2ecf20Sopenharmony_ci 5748c2ecf20Sopenharmony_ci /* If a SRR times out, simply free resources */ 5758c2ecf20Sopenharmony_ci if (srr_req->event == QEDF_IOREQ_EV_ELS_TMO) { 5768c2ecf20Sopenharmony_ci QEDF_ERR(&qedf->dbg_ctx, 5778c2ecf20Sopenharmony_ci "ELS timeout rec_xid=0x%x.\n", srr_req->xid); 5788c2ecf20Sopenharmony_ci goto out_put; 5798c2ecf20Sopenharmony_ci } 5808c2ecf20Sopenharmony_ci 5818c2ecf20Sopenharmony_ci /* Normalize response data into struct fc_frame */ 5828c2ecf20Sopenharmony_ci mp_req = &(srr_req->mp_req); 5838c2ecf20Sopenharmony_ci mp_fc_hdr = &(mp_req->resp_fc_hdr); 5848c2ecf20Sopenharmony_ci resp_len = mp_req->resp_len; 5858c2ecf20Sopenharmony_ci resp_buf = mp_req->resp_buf; 5868c2ecf20Sopenharmony_ci 5878c2ecf20Sopenharmony_ci fp = fc_frame_alloc(lport, resp_len); 5888c2ecf20Sopenharmony_ci if (!fp) { 5898c2ecf20Sopenharmony_ci QEDF_ERR(&(qedf->dbg_ctx), 5908c2ecf20Sopenharmony_ci "fc_frame_alloc failure.\n"); 5918c2ecf20Sopenharmony_ci goto out_put; 5928c2ecf20Sopenharmony_ci } 5938c2ecf20Sopenharmony_ci 5948c2ecf20Sopenharmony_ci /* Copy frame header from firmware into fp */ 5958c2ecf20Sopenharmony_ci fh = (struct fc_frame_header *)fc_frame_header_get(fp); 5968c2ecf20Sopenharmony_ci memcpy(fh, mp_fc_hdr, sizeof(struct fc_frame_header)); 5978c2ecf20Sopenharmony_ci 5988c2ecf20Sopenharmony_ci /* Copy payload from firmware into fp */ 5998c2ecf20Sopenharmony_ci fc_payload = fc_frame_payload_get(fp, resp_len); 6008c2ecf20Sopenharmony_ci memcpy(fc_payload, resp_buf, resp_len); 6018c2ecf20Sopenharmony_ci 6028c2ecf20Sopenharmony_ci opcode = fc_frame_payload_op(fp); 6038c2ecf20Sopenharmony_ci switch (opcode) { 6048c2ecf20Sopenharmony_ci case ELS_LS_ACC: 6058c2ecf20Sopenharmony_ci QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_ELS, 6068c2ecf20Sopenharmony_ci "SRR success.\n"); 6078c2ecf20Sopenharmony_ci break; 6088c2ecf20Sopenharmony_ci case ELS_LS_RJT: 6098c2ecf20Sopenharmony_ci QEDF_INFO(&qedf->dbg_ctx, QEDF_LOG_ELS, 6108c2ecf20Sopenharmony_ci "SRR rejected.\n"); 6118c2ecf20Sopenharmony_ci qedf_initiate_abts(orig_io_req, true); 6128c2ecf20Sopenharmony_ci break; 6138c2ecf20Sopenharmony_ci } 6148c2ecf20Sopenharmony_ci 6158c2ecf20Sopenharmony_ci fc_frame_free(fp); 6168c2ecf20Sopenharmony_ciout_put: 6178c2ecf20Sopenharmony_ci /* Put reference for original command since SRR completed */ 6188c2ecf20Sopenharmony_ci kref_put(&orig_io_req->refcount, qedf_release_cmd); 6198c2ecf20Sopenharmony_ciout_free: 6208c2ecf20Sopenharmony_ci kfree(cb_arg); 6218c2ecf20Sopenharmony_ci} 6228c2ecf20Sopenharmony_ci 6238c2ecf20Sopenharmony_cistatic int qedf_send_srr(struct qedf_ioreq *orig_io_req, u32 offset, u8 r_ctl) 6248c2ecf20Sopenharmony_ci{ 6258c2ecf20Sopenharmony_ci struct fcp_srr srr; 6268c2ecf20Sopenharmony_ci struct qedf_ctx *qedf; 6278c2ecf20Sopenharmony_ci struct qedf_rport *fcport; 6288c2ecf20Sopenharmony_ci struct fc_lport *lport; 6298c2ecf20Sopenharmony_ci struct qedf_els_cb_arg *cb_arg = NULL; 6308c2ecf20Sopenharmony_ci u32 r_a_tov; 6318c2ecf20Sopenharmony_ci int rc; 6328c2ecf20Sopenharmony_ci 6338c2ecf20Sopenharmony_ci if (!orig_io_req) { 6348c2ecf20Sopenharmony_ci QEDF_ERR(NULL, "orig_io_req is NULL.\n"); 6358c2ecf20Sopenharmony_ci return -EINVAL; 6368c2ecf20Sopenharmony_ci } 6378c2ecf20Sopenharmony_ci 6388c2ecf20Sopenharmony_ci fcport = orig_io_req->fcport; 6398c2ecf20Sopenharmony_ci 6408c2ecf20Sopenharmony_ci /* Check that fcport is still offloaded */ 6418c2ecf20Sopenharmony_ci if (!test_bit(QEDF_RPORT_SESSION_READY, &fcport->flags)) { 6428c2ecf20Sopenharmony_ci QEDF_ERR(NULL, "fcport is no longer offloaded.\n"); 6438c2ecf20Sopenharmony_ci return -EINVAL; 6448c2ecf20Sopenharmony_ci } 6458c2ecf20Sopenharmony_ci 6468c2ecf20Sopenharmony_ci if (!fcport->qedf) { 6478c2ecf20Sopenharmony_ci QEDF_ERR(NULL, "fcport->qedf is NULL.\n"); 6488c2ecf20Sopenharmony_ci return -EINVAL; 6498c2ecf20Sopenharmony_ci } 6508c2ecf20Sopenharmony_ci 6518c2ecf20Sopenharmony_ci /* Take reference until SRR command completion */ 6528c2ecf20Sopenharmony_ci kref_get(&orig_io_req->refcount); 6538c2ecf20Sopenharmony_ci 6548c2ecf20Sopenharmony_ci qedf = fcport->qedf; 6558c2ecf20Sopenharmony_ci lport = qedf->lport; 6568c2ecf20Sopenharmony_ci r_a_tov = lport->r_a_tov; 6578c2ecf20Sopenharmony_ci 6588c2ecf20Sopenharmony_ci QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_ELS, "Sending SRR orig_io=%p, " 6598c2ecf20Sopenharmony_ci "orig_xid=0x%x\n", orig_io_req, orig_io_req->xid); 6608c2ecf20Sopenharmony_ci memset(&srr, 0, sizeof(srr)); 6618c2ecf20Sopenharmony_ci 6628c2ecf20Sopenharmony_ci cb_arg = kzalloc(sizeof(struct qedf_els_cb_arg), GFP_NOIO); 6638c2ecf20Sopenharmony_ci if (!cb_arg) { 6648c2ecf20Sopenharmony_ci QEDF_ERR(&(qedf->dbg_ctx), "Unable to allocate cb_arg for " 6658c2ecf20Sopenharmony_ci "SRR\n"); 6668c2ecf20Sopenharmony_ci rc = -ENOMEM; 6678c2ecf20Sopenharmony_ci goto srr_err; 6688c2ecf20Sopenharmony_ci } 6698c2ecf20Sopenharmony_ci 6708c2ecf20Sopenharmony_ci cb_arg->aborted_io_req = orig_io_req; 6718c2ecf20Sopenharmony_ci 6728c2ecf20Sopenharmony_ci srr.srr_op = ELS_SRR; 6738c2ecf20Sopenharmony_ci srr.srr_ox_id = htons(orig_io_req->xid); 6748c2ecf20Sopenharmony_ci srr.srr_rx_id = htons(orig_io_req->rx_id); 6758c2ecf20Sopenharmony_ci srr.srr_rel_off = htonl(offset); 6768c2ecf20Sopenharmony_ci srr.srr_r_ctl = r_ctl; 6778c2ecf20Sopenharmony_ci 6788c2ecf20Sopenharmony_ci rc = qedf_initiate_els(fcport, ELS_SRR, &srr, sizeof(srr), 6798c2ecf20Sopenharmony_ci qedf_srr_compl, cb_arg, r_a_tov); 6808c2ecf20Sopenharmony_ci 6818c2ecf20Sopenharmony_cisrr_err: 6828c2ecf20Sopenharmony_ci if (rc) { 6838c2ecf20Sopenharmony_ci QEDF_ERR(&(qedf->dbg_ctx), "SRR failed - release orig_io_req" 6848c2ecf20Sopenharmony_ci "=0x%x\n", orig_io_req->xid); 6858c2ecf20Sopenharmony_ci kfree(cb_arg); 6868c2ecf20Sopenharmony_ci /* If we fail to queue SRR, send ABTS to orig_io */ 6878c2ecf20Sopenharmony_ci qedf_initiate_abts(orig_io_req, true); 6888c2ecf20Sopenharmony_ci kref_put(&orig_io_req->refcount, qedf_release_cmd); 6898c2ecf20Sopenharmony_ci } else 6908c2ecf20Sopenharmony_ci /* Tell other threads that SRR is in progress */ 6918c2ecf20Sopenharmony_ci set_bit(QEDF_CMD_SRR_SENT, &orig_io_req->flags); 6928c2ecf20Sopenharmony_ci 6938c2ecf20Sopenharmony_ci return rc; 6948c2ecf20Sopenharmony_ci} 6958c2ecf20Sopenharmony_ci 6968c2ecf20Sopenharmony_cistatic void qedf_initiate_seq_cleanup(struct qedf_ioreq *orig_io_req, 6978c2ecf20Sopenharmony_ci u32 offset, u8 r_ctl) 6988c2ecf20Sopenharmony_ci{ 6998c2ecf20Sopenharmony_ci struct qedf_rport *fcport; 7008c2ecf20Sopenharmony_ci unsigned long flags; 7018c2ecf20Sopenharmony_ci struct qedf_els_cb_arg *cb_arg; 7028c2ecf20Sopenharmony_ci struct fcoe_wqe *sqe; 7038c2ecf20Sopenharmony_ci u16 sqe_idx; 7048c2ecf20Sopenharmony_ci 7058c2ecf20Sopenharmony_ci fcport = orig_io_req->fcport; 7068c2ecf20Sopenharmony_ci 7078c2ecf20Sopenharmony_ci QEDF_INFO(&(fcport->qedf->dbg_ctx), QEDF_LOG_ELS, 7088c2ecf20Sopenharmony_ci "Doing sequence cleanup for xid=0x%x offset=%u.\n", 7098c2ecf20Sopenharmony_ci orig_io_req->xid, offset); 7108c2ecf20Sopenharmony_ci 7118c2ecf20Sopenharmony_ci cb_arg = kzalloc(sizeof(struct qedf_els_cb_arg), GFP_NOIO); 7128c2ecf20Sopenharmony_ci if (!cb_arg) { 7138c2ecf20Sopenharmony_ci QEDF_ERR(&(fcport->qedf->dbg_ctx), "Unable to allocate cb_arg " 7148c2ecf20Sopenharmony_ci "for sequence cleanup\n"); 7158c2ecf20Sopenharmony_ci return; 7168c2ecf20Sopenharmony_ci } 7178c2ecf20Sopenharmony_ci 7188c2ecf20Sopenharmony_ci /* Get reference for cleanup request */ 7198c2ecf20Sopenharmony_ci kref_get(&orig_io_req->refcount); 7208c2ecf20Sopenharmony_ci 7218c2ecf20Sopenharmony_ci orig_io_req->cmd_type = QEDF_SEQ_CLEANUP; 7228c2ecf20Sopenharmony_ci cb_arg->offset = offset; 7238c2ecf20Sopenharmony_ci cb_arg->r_ctl = r_ctl; 7248c2ecf20Sopenharmony_ci orig_io_req->cb_arg = cb_arg; 7258c2ecf20Sopenharmony_ci 7268c2ecf20Sopenharmony_ci qedf_cmd_timer_set(fcport->qedf, orig_io_req, 7278c2ecf20Sopenharmony_ci QEDF_CLEANUP_TIMEOUT * HZ); 7288c2ecf20Sopenharmony_ci 7298c2ecf20Sopenharmony_ci spin_lock_irqsave(&fcport->rport_lock, flags); 7308c2ecf20Sopenharmony_ci 7318c2ecf20Sopenharmony_ci sqe_idx = qedf_get_sqe_idx(fcport); 7328c2ecf20Sopenharmony_ci sqe = &fcport->sq[sqe_idx]; 7338c2ecf20Sopenharmony_ci memset(sqe, 0, sizeof(struct fcoe_wqe)); 7348c2ecf20Sopenharmony_ci orig_io_req->task_params->sqe = sqe; 7358c2ecf20Sopenharmony_ci 7368c2ecf20Sopenharmony_ci init_initiator_sequence_recovery_fcoe_task(orig_io_req->task_params, 7378c2ecf20Sopenharmony_ci offset); 7388c2ecf20Sopenharmony_ci qedf_ring_doorbell(fcport); 7398c2ecf20Sopenharmony_ci 7408c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&fcport->rport_lock, flags); 7418c2ecf20Sopenharmony_ci} 7428c2ecf20Sopenharmony_ci 7438c2ecf20Sopenharmony_civoid qedf_process_seq_cleanup_compl(struct qedf_ctx *qedf, 7448c2ecf20Sopenharmony_ci struct fcoe_cqe *cqe, struct qedf_ioreq *io_req) 7458c2ecf20Sopenharmony_ci{ 7468c2ecf20Sopenharmony_ci int rc; 7478c2ecf20Sopenharmony_ci struct qedf_els_cb_arg *cb_arg; 7488c2ecf20Sopenharmony_ci 7498c2ecf20Sopenharmony_ci cb_arg = io_req->cb_arg; 7508c2ecf20Sopenharmony_ci 7518c2ecf20Sopenharmony_ci /* If we timed out just free resources */ 7528c2ecf20Sopenharmony_ci if (io_req->event == QEDF_IOREQ_EV_ELS_TMO || !cqe) { 7538c2ecf20Sopenharmony_ci QEDF_ERR(&qedf->dbg_ctx, 7548c2ecf20Sopenharmony_ci "cqe is NULL or timeout event (0x%x)", io_req->event); 7558c2ecf20Sopenharmony_ci goto free; 7568c2ecf20Sopenharmony_ci } 7578c2ecf20Sopenharmony_ci 7588c2ecf20Sopenharmony_ci /* Kill the timer we put on the request */ 7598c2ecf20Sopenharmony_ci cancel_delayed_work_sync(&io_req->timeout_work); 7608c2ecf20Sopenharmony_ci 7618c2ecf20Sopenharmony_ci rc = qedf_send_srr(io_req, cb_arg->offset, cb_arg->r_ctl); 7628c2ecf20Sopenharmony_ci if (rc) 7638c2ecf20Sopenharmony_ci QEDF_ERR(&(qedf->dbg_ctx), "Unable to send SRR, I/O will " 7648c2ecf20Sopenharmony_ci "abort, xid=0x%x.\n", io_req->xid); 7658c2ecf20Sopenharmony_cifree: 7668c2ecf20Sopenharmony_ci kfree(cb_arg); 7678c2ecf20Sopenharmony_ci kref_put(&io_req->refcount, qedf_release_cmd); 7688c2ecf20Sopenharmony_ci} 7698c2ecf20Sopenharmony_ci 7708c2ecf20Sopenharmony_cistatic bool qedf_requeue_io_req(struct qedf_ioreq *orig_io_req) 7718c2ecf20Sopenharmony_ci{ 7728c2ecf20Sopenharmony_ci struct qedf_rport *fcport; 7738c2ecf20Sopenharmony_ci struct qedf_ioreq *new_io_req; 7748c2ecf20Sopenharmony_ci unsigned long flags; 7758c2ecf20Sopenharmony_ci bool rc = false; 7768c2ecf20Sopenharmony_ci 7778c2ecf20Sopenharmony_ci fcport = orig_io_req->fcport; 7788c2ecf20Sopenharmony_ci if (!fcport) { 7798c2ecf20Sopenharmony_ci QEDF_ERR(NULL, "fcport is NULL.\n"); 7808c2ecf20Sopenharmony_ci goto out; 7818c2ecf20Sopenharmony_ci } 7828c2ecf20Sopenharmony_ci 7838c2ecf20Sopenharmony_ci if (!orig_io_req->sc_cmd) { 7848c2ecf20Sopenharmony_ci QEDF_ERR(&(fcport->qedf->dbg_ctx), "sc_cmd is NULL for " 7858c2ecf20Sopenharmony_ci "xid=0x%x.\n", orig_io_req->xid); 7868c2ecf20Sopenharmony_ci goto out; 7878c2ecf20Sopenharmony_ci } 7888c2ecf20Sopenharmony_ci 7898c2ecf20Sopenharmony_ci new_io_req = qedf_alloc_cmd(fcport, QEDF_SCSI_CMD); 7908c2ecf20Sopenharmony_ci if (!new_io_req) { 7918c2ecf20Sopenharmony_ci QEDF_ERR(&(fcport->qedf->dbg_ctx), "Could not allocate new " 7928c2ecf20Sopenharmony_ci "io_req.\n"); 7938c2ecf20Sopenharmony_ci goto out; 7948c2ecf20Sopenharmony_ci } 7958c2ecf20Sopenharmony_ci 7968c2ecf20Sopenharmony_ci new_io_req->sc_cmd = orig_io_req->sc_cmd; 7978c2ecf20Sopenharmony_ci 7988c2ecf20Sopenharmony_ci /* 7998c2ecf20Sopenharmony_ci * This keeps the sc_cmd struct from being returned to the tape 8008c2ecf20Sopenharmony_ci * driver and being requeued twice. We do need to put a reference 8018c2ecf20Sopenharmony_ci * for the original I/O request since we will not do a SCSI completion 8028c2ecf20Sopenharmony_ci * for it. 8038c2ecf20Sopenharmony_ci */ 8048c2ecf20Sopenharmony_ci orig_io_req->sc_cmd = NULL; 8058c2ecf20Sopenharmony_ci kref_put(&orig_io_req->refcount, qedf_release_cmd); 8068c2ecf20Sopenharmony_ci 8078c2ecf20Sopenharmony_ci spin_lock_irqsave(&fcport->rport_lock, flags); 8088c2ecf20Sopenharmony_ci 8098c2ecf20Sopenharmony_ci /* kref for new command released in qedf_post_io_req on error */ 8108c2ecf20Sopenharmony_ci if (qedf_post_io_req(fcport, new_io_req)) { 8118c2ecf20Sopenharmony_ci QEDF_ERR(&(fcport->qedf->dbg_ctx), "Unable to post io_req\n"); 8128c2ecf20Sopenharmony_ci /* Return SQE to pool */ 8138c2ecf20Sopenharmony_ci atomic_inc(&fcport->free_sqes); 8148c2ecf20Sopenharmony_ci } else { 8158c2ecf20Sopenharmony_ci QEDF_INFO(&(fcport->qedf->dbg_ctx), QEDF_LOG_ELS, 8168c2ecf20Sopenharmony_ci "Reissued SCSI command from orig_xid=0x%x on " 8178c2ecf20Sopenharmony_ci "new_xid=0x%x.\n", orig_io_req->xid, new_io_req->xid); 8188c2ecf20Sopenharmony_ci /* 8198c2ecf20Sopenharmony_ci * Abort the original I/O but do not return SCSI command as 8208c2ecf20Sopenharmony_ci * it has been reissued on another OX_ID. 8218c2ecf20Sopenharmony_ci */ 8228c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&fcport->rport_lock, flags); 8238c2ecf20Sopenharmony_ci qedf_initiate_abts(orig_io_req, false); 8248c2ecf20Sopenharmony_ci goto out; 8258c2ecf20Sopenharmony_ci } 8268c2ecf20Sopenharmony_ci 8278c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&fcport->rport_lock, flags); 8288c2ecf20Sopenharmony_ciout: 8298c2ecf20Sopenharmony_ci return rc; 8308c2ecf20Sopenharmony_ci} 8318c2ecf20Sopenharmony_ci 8328c2ecf20Sopenharmony_ci 8338c2ecf20Sopenharmony_cistatic void qedf_rec_compl(struct qedf_els_cb_arg *cb_arg) 8348c2ecf20Sopenharmony_ci{ 8358c2ecf20Sopenharmony_ci struct qedf_ioreq *orig_io_req; 8368c2ecf20Sopenharmony_ci struct qedf_ioreq *rec_req; 8378c2ecf20Sopenharmony_ci struct qedf_mp_req *mp_req; 8388c2ecf20Sopenharmony_ci struct fc_frame_header *mp_fc_hdr, *fh; 8398c2ecf20Sopenharmony_ci struct fc_frame *fp; 8408c2ecf20Sopenharmony_ci void *resp_buf, *fc_payload; 8418c2ecf20Sopenharmony_ci u32 resp_len; 8428c2ecf20Sopenharmony_ci struct fc_lport *lport; 8438c2ecf20Sopenharmony_ci struct qedf_ctx *qedf; 8448c2ecf20Sopenharmony_ci int refcount; 8458c2ecf20Sopenharmony_ci enum fc_rctl r_ctl; 8468c2ecf20Sopenharmony_ci struct fc_els_ls_rjt *rjt; 8478c2ecf20Sopenharmony_ci struct fc_els_rec_acc *acc; 8488c2ecf20Sopenharmony_ci u8 opcode; 8498c2ecf20Sopenharmony_ci u32 offset, e_stat; 8508c2ecf20Sopenharmony_ci struct scsi_cmnd *sc_cmd; 8518c2ecf20Sopenharmony_ci bool srr_needed = false; 8528c2ecf20Sopenharmony_ci 8538c2ecf20Sopenharmony_ci rec_req = cb_arg->io_req; 8548c2ecf20Sopenharmony_ci qedf = rec_req->fcport->qedf; 8558c2ecf20Sopenharmony_ci lport = qedf->lport; 8568c2ecf20Sopenharmony_ci 8578c2ecf20Sopenharmony_ci orig_io_req = cb_arg->aborted_io_req; 8588c2ecf20Sopenharmony_ci 8598c2ecf20Sopenharmony_ci if (!orig_io_req) { 8608c2ecf20Sopenharmony_ci QEDF_ERR(NULL, "orig_io_req is NULL.\n"); 8618c2ecf20Sopenharmony_ci goto out_free; 8628c2ecf20Sopenharmony_ci } 8638c2ecf20Sopenharmony_ci 8648c2ecf20Sopenharmony_ci if (rec_req->event != QEDF_IOREQ_EV_ELS_TMO && 8658c2ecf20Sopenharmony_ci rec_req->event != QEDF_IOREQ_EV_ELS_ERR_DETECT) 8668c2ecf20Sopenharmony_ci cancel_delayed_work_sync(&orig_io_req->timeout_work); 8678c2ecf20Sopenharmony_ci 8688c2ecf20Sopenharmony_ci refcount = kref_read(&orig_io_req->refcount); 8698c2ecf20Sopenharmony_ci QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_ELS, "Entered: orig_io=%p," 8708c2ecf20Sopenharmony_ci " orig_io_xid=0x%x, rec_xid=0x%x, refcount=%d\n", 8718c2ecf20Sopenharmony_ci orig_io_req, orig_io_req->xid, rec_req->xid, refcount); 8728c2ecf20Sopenharmony_ci 8738c2ecf20Sopenharmony_ci /* If a REC times out, free resources */ 8748c2ecf20Sopenharmony_ci if (rec_req->event == QEDF_IOREQ_EV_ELS_TMO) { 8758c2ecf20Sopenharmony_ci QEDF_ERR(&qedf->dbg_ctx, 8768c2ecf20Sopenharmony_ci "Got TMO event, orig_io_req %p orig_io_xid=0x%x.\n", 8778c2ecf20Sopenharmony_ci orig_io_req, orig_io_req->xid); 8788c2ecf20Sopenharmony_ci goto out_put; 8798c2ecf20Sopenharmony_ci } 8808c2ecf20Sopenharmony_ci 8818c2ecf20Sopenharmony_ci /* Normalize response data into struct fc_frame */ 8828c2ecf20Sopenharmony_ci mp_req = &(rec_req->mp_req); 8838c2ecf20Sopenharmony_ci mp_fc_hdr = &(mp_req->resp_fc_hdr); 8848c2ecf20Sopenharmony_ci resp_len = mp_req->resp_len; 8858c2ecf20Sopenharmony_ci acc = resp_buf = mp_req->resp_buf; 8868c2ecf20Sopenharmony_ci 8878c2ecf20Sopenharmony_ci fp = fc_frame_alloc(lport, resp_len); 8888c2ecf20Sopenharmony_ci if (!fp) { 8898c2ecf20Sopenharmony_ci QEDF_ERR(&(qedf->dbg_ctx), 8908c2ecf20Sopenharmony_ci "fc_frame_alloc failure.\n"); 8918c2ecf20Sopenharmony_ci goto out_put; 8928c2ecf20Sopenharmony_ci } 8938c2ecf20Sopenharmony_ci 8948c2ecf20Sopenharmony_ci /* Copy frame header from firmware into fp */ 8958c2ecf20Sopenharmony_ci fh = (struct fc_frame_header *)fc_frame_header_get(fp); 8968c2ecf20Sopenharmony_ci memcpy(fh, mp_fc_hdr, sizeof(struct fc_frame_header)); 8978c2ecf20Sopenharmony_ci 8988c2ecf20Sopenharmony_ci /* Copy payload from firmware into fp */ 8998c2ecf20Sopenharmony_ci fc_payload = fc_frame_payload_get(fp, resp_len); 9008c2ecf20Sopenharmony_ci memcpy(fc_payload, resp_buf, resp_len); 9018c2ecf20Sopenharmony_ci 9028c2ecf20Sopenharmony_ci opcode = fc_frame_payload_op(fp); 9038c2ecf20Sopenharmony_ci if (opcode == ELS_LS_RJT) { 9048c2ecf20Sopenharmony_ci rjt = fc_frame_payload_get(fp, sizeof(*rjt)); 9058c2ecf20Sopenharmony_ci if (!rjt) { 9068c2ecf20Sopenharmony_ci QEDF_ERR(&qedf->dbg_ctx, "payload get failed"); 9078c2ecf20Sopenharmony_ci goto out_free_frame; 9088c2ecf20Sopenharmony_ci } 9098c2ecf20Sopenharmony_ci 9108c2ecf20Sopenharmony_ci QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_ELS, 9118c2ecf20Sopenharmony_ci "Received LS_RJT for REC: er_reason=0x%x, " 9128c2ecf20Sopenharmony_ci "er_explan=0x%x.\n", rjt->er_reason, rjt->er_explan); 9138c2ecf20Sopenharmony_ci /* 9148c2ecf20Sopenharmony_ci * The following response(s) mean that we need to reissue the 9158c2ecf20Sopenharmony_ci * request on another exchange. We need to do this without 9168c2ecf20Sopenharmony_ci * informing the upper layers lest it cause an application 9178c2ecf20Sopenharmony_ci * error. 9188c2ecf20Sopenharmony_ci */ 9198c2ecf20Sopenharmony_ci if ((rjt->er_reason == ELS_RJT_LOGIC || 9208c2ecf20Sopenharmony_ci rjt->er_reason == ELS_RJT_UNAB) && 9218c2ecf20Sopenharmony_ci rjt->er_explan == ELS_EXPL_OXID_RXID) { 9228c2ecf20Sopenharmony_ci QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_ELS, 9238c2ecf20Sopenharmony_ci "Handle CMD LOST case.\n"); 9248c2ecf20Sopenharmony_ci qedf_requeue_io_req(orig_io_req); 9258c2ecf20Sopenharmony_ci } 9268c2ecf20Sopenharmony_ci } else if (opcode == ELS_LS_ACC) { 9278c2ecf20Sopenharmony_ci offset = ntohl(acc->reca_fc4value); 9288c2ecf20Sopenharmony_ci e_stat = ntohl(acc->reca_e_stat); 9298c2ecf20Sopenharmony_ci QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_ELS, 9308c2ecf20Sopenharmony_ci "Received LS_ACC for REC: offset=0x%x, e_stat=0x%x.\n", 9318c2ecf20Sopenharmony_ci offset, e_stat); 9328c2ecf20Sopenharmony_ci if (e_stat & ESB_ST_SEQ_INIT) { 9338c2ecf20Sopenharmony_ci QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_ELS, 9348c2ecf20Sopenharmony_ci "Target has the seq init\n"); 9358c2ecf20Sopenharmony_ci goto out_free_frame; 9368c2ecf20Sopenharmony_ci } 9378c2ecf20Sopenharmony_ci sc_cmd = orig_io_req->sc_cmd; 9388c2ecf20Sopenharmony_ci if (!sc_cmd) { 9398c2ecf20Sopenharmony_ci QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_ELS, 9408c2ecf20Sopenharmony_ci "sc_cmd is NULL for xid=0x%x.\n", 9418c2ecf20Sopenharmony_ci orig_io_req->xid); 9428c2ecf20Sopenharmony_ci goto out_free_frame; 9438c2ecf20Sopenharmony_ci } 9448c2ecf20Sopenharmony_ci /* SCSI write case */ 9458c2ecf20Sopenharmony_ci if (sc_cmd->sc_data_direction == DMA_TO_DEVICE) { 9468c2ecf20Sopenharmony_ci if (offset == orig_io_req->data_xfer_len) { 9478c2ecf20Sopenharmony_ci QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_ELS, 9488c2ecf20Sopenharmony_ci "WRITE - response lost.\n"); 9498c2ecf20Sopenharmony_ci r_ctl = FC_RCTL_DD_CMD_STATUS; 9508c2ecf20Sopenharmony_ci srr_needed = true; 9518c2ecf20Sopenharmony_ci offset = 0; 9528c2ecf20Sopenharmony_ci } else { 9538c2ecf20Sopenharmony_ci QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_ELS, 9548c2ecf20Sopenharmony_ci "WRITE - XFER_RDY/DATA lost.\n"); 9558c2ecf20Sopenharmony_ci r_ctl = FC_RCTL_DD_DATA_DESC; 9568c2ecf20Sopenharmony_ci /* Use data from warning CQE instead of REC */ 9578c2ecf20Sopenharmony_ci offset = orig_io_req->tx_buf_off; 9588c2ecf20Sopenharmony_ci } 9598c2ecf20Sopenharmony_ci /* SCSI read case */ 9608c2ecf20Sopenharmony_ci } else { 9618c2ecf20Sopenharmony_ci if (orig_io_req->rx_buf_off == 9628c2ecf20Sopenharmony_ci orig_io_req->data_xfer_len) { 9638c2ecf20Sopenharmony_ci QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_ELS, 9648c2ecf20Sopenharmony_ci "READ - response lost.\n"); 9658c2ecf20Sopenharmony_ci srr_needed = true; 9668c2ecf20Sopenharmony_ci r_ctl = FC_RCTL_DD_CMD_STATUS; 9678c2ecf20Sopenharmony_ci offset = 0; 9688c2ecf20Sopenharmony_ci } else { 9698c2ecf20Sopenharmony_ci QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_ELS, 9708c2ecf20Sopenharmony_ci "READ - DATA lost.\n"); 9718c2ecf20Sopenharmony_ci /* 9728c2ecf20Sopenharmony_ci * For read case we always set the offset to 0 9738c2ecf20Sopenharmony_ci * for sequence recovery task. 9748c2ecf20Sopenharmony_ci */ 9758c2ecf20Sopenharmony_ci offset = 0; 9768c2ecf20Sopenharmony_ci r_ctl = FC_RCTL_DD_SOL_DATA; 9778c2ecf20Sopenharmony_ci } 9788c2ecf20Sopenharmony_ci } 9798c2ecf20Sopenharmony_ci 9808c2ecf20Sopenharmony_ci if (srr_needed) 9818c2ecf20Sopenharmony_ci qedf_send_srr(orig_io_req, offset, r_ctl); 9828c2ecf20Sopenharmony_ci else 9838c2ecf20Sopenharmony_ci qedf_initiate_seq_cleanup(orig_io_req, offset, r_ctl); 9848c2ecf20Sopenharmony_ci } 9858c2ecf20Sopenharmony_ci 9868c2ecf20Sopenharmony_ciout_free_frame: 9878c2ecf20Sopenharmony_ci fc_frame_free(fp); 9888c2ecf20Sopenharmony_ciout_put: 9898c2ecf20Sopenharmony_ci /* Put reference for original command since REC completed */ 9908c2ecf20Sopenharmony_ci kref_put(&orig_io_req->refcount, qedf_release_cmd); 9918c2ecf20Sopenharmony_ciout_free: 9928c2ecf20Sopenharmony_ci kfree(cb_arg); 9938c2ecf20Sopenharmony_ci} 9948c2ecf20Sopenharmony_ci 9958c2ecf20Sopenharmony_ci/* Assumes kref is already held by caller */ 9968c2ecf20Sopenharmony_ciint qedf_send_rec(struct qedf_ioreq *orig_io_req) 9978c2ecf20Sopenharmony_ci{ 9988c2ecf20Sopenharmony_ci 9998c2ecf20Sopenharmony_ci struct fc_els_rec rec; 10008c2ecf20Sopenharmony_ci struct qedf_rport *fcport; 10018c2ecf20Sopenharmony_ci struct fc_lport *lport; 10028c2ecf20Sopenharmony_ci struct qedf_els_cb_arg *cb_arg = NULL; 10038c2ecf20Sopenharmony_ci struct qedf_ctx *qedf; 10048c2ecf20Sopenharmony_ci uint32_t sid; 10058c2ecf20Sopenharmony_ci uint32_t r_a_tov; 10068c2ecf20Sopenharmony_ci int rc; 10078c2ecf20Sopenharmony_ci 10088c2ecf20Sopenharmony_ci if (!orig_io_req) { 10098c2ecf20Sopenharmony_ci QEDF_ERR(NULL, "orig_io_req is NULL.\n"); 10108c2ecf20Sopenharmony_ci return -EINVAL; 10118c2ecf20Sopenharmony_ci } 10128c2ecf20Sopenharmony_ci 10138c2ecf20Sopenharmony_ci fcport = orig_io_req->fcport; 10148c2ecf20Sopenharmony_ci 10158c2ecf20Sopenharmony_ci /* Check that fcport is still offloaded */ 10168c2ecf20Sopenharmony_ci if (!test_bit(QEDF_RPORT_SESSION_READY, &fcport->flags)) { 10178c2ecf20Sopenharmony_ci QEDF_ERR(NULL, "fcport is no longer offloaded.\n"); 10188c2ecf20Sopenharmony_ci return -EINVAL; 10198c2ecf20Sopenharmony_ci } 10208c2ecf20Sopenharmony_ci 10218c2ecf20Sopenharmony_ci if (!fcport->qedf) { 10228c2ecf20Sopenharmony_ci QEDF_ERR(NULL, "fcport->qedf is NULL.\n"); 10238c2ecf20Sopenharmony_ci return -EINVAL; 10248c2ecf20Sopenharmony_ci } 10258c2ecf20Sopenharmony_ci 10268c2ecf20Sopenharmony_ci /* Take reference until REC command completion */ 10278c2ecf20Sopenharmony_ci kref_get(&orig_io_req->refcount); 10288c2ecf20Sopenharmony_ci 10298c2ecf20Sopenharmony_ci qedf = fcport->qedf; 10308c2ecf20Sopenharmony_ci lport = qedf->lport; 10318c2ecf20Sopenharmony_ci sid = fcport->sid; 10328c2ecf20Sopenharmony_ci r_a_tov = lport->r_a_tov; 10338c2ecf20Sopenharmony_ci 10348c2ecf20Sopenharmony_ci memset(&rec, 0, sizeof(rec)); 10358c2ecf20Sopenharmony_ci 10368c2ecf20Sopenharmony_ci cb_arg = kzalloc(sizeof(struct qedf_els_cb_arg), GFP_NOIO); 10378c2ecf20Sopenharmony_ci if (!cb_arg) { 10388c2ecf20Sopenharmony_ci QEDF_ERR(&(qedf->dbg_ctx), "Unable to allocate cb_arg for " 10398c2ecf20Sopenharmony_ci "REC\n"); 10408c2ecf20Sopenharmony_ci rc = -ENOMEM; 10418c2ecf20Sopenharmony_ci goto rec_err; 10428c2ecf20Sopenharmony_ci } 10438c2ecf20Sopenharmony_ci 10448c2ecf20Sopenharmony_ci cb_arg->aborted_io_req = orig_io_req; 10458c2ecf20Sopenharmony_ci 10468c2ecf20Sopenharmony_ci rec.rec_cmd = ELS_REC; 10478c2ecf20Sopenharmony_ci hton24(rec.rec_s_id, sid); 10488c2ecf20Sopenharmony_ci rec.rec_ox_id = htons(orig_io_req->xid); 10498c2ecf20Sopenharmony_ci rec.rec_rx_id = 10508c2ecf20Sopenharmony_ci htons(orig_io_req->task->tstorm_st_context.read_write.rx_id); 10518c2ecf20Sopenharmony_ci 10528c2ecf20Sopenharmony_ci QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_ELS, "Sending REC orig_io=%p, " 10538c2ecf20Sopenharmony_ci "orig_xid=0x%x rx_id=0x%x\n", orig_io_req, 10548c2ecf20Sopenharmony_ci orig_io_req->xid, rec.rec_rx_id); 10558c2ecf20Sopenharmony_ci rc = qedf_initiate_els(fcport, ELS_REC, &rec, sizeof(rec), 10568c2ecf20Sopenharmony_ci qedf_rec_compl, cb_arg, r_a_tov); 10578c2ecf20Sopenharmony_ci 10588c2ecf20Sopenharmony_cirec_err: 10598c2ecf20Sopenharmony_ci if (rc) { 10608c2ecf20Sopenharmony_ci QEDF_ERR(&(qedf->dbg_ctx), "REC failed - release orig_io_req" 10618c2ecf20Sopenharmony_ci "=0x%x\n", orig_io_req->xid); 10628c2ecf20Sopenharmony_ci kfree(cb_arg); 10638c2ecf20Sopenharmony_ci kref_put(&orig_io_req->refcount, qedf_release_cmd); 10648c2ecf20Sopenharmony_ci } 10658c2ecf20Sopenharmony_ci return rc; 10668c2ecf20Sopenharmony_ci} 1067