13287e96aSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only 261d8658bSDupuis, Chad /* 361d8658bSDupuis, Chad * QLogic FCoE Offload Driver 45d1c8b5bSChad Dupuis * Copyright (c) 2016-2018 Cavium Inc. 561d8658bSDupuis, Chad */ 661d8658bSDupuis, Chad #include "qedf.h" 761d8658bSDupuis, Chad 861d8658bSDupuis, Chad /* It's assumed that the lock is held when calling this function. */ 961d8658bSDupuis, Chad static int qedf_initiate_els(struct qedf_rport *fcport, unsigned int op, 1061d8658bSDupuis, Chad void *data, uint32_t data_len, 1161d8658bSDupuis, Chad void (*cb_func)(struct qedf_els_cb_arg *cb_arg), 1261d8658bSDupuis, Chad struct qedf_els_cb_arg *cb_arg, uint32_t timer_msec) 1361d8658bSDupuis, Chad { 14f3690a89SChad Dupuis struct qedf_ctx *qedf; 15f3690a89SChad Dupuis struct fc_lport *lport; 1661d8658bSDupuis, Chad struct qedf_ioreq *els_req; 1761d8658bSDupuis, Chad struct qedf_mp_req *mp_req; 1861d8658bSDupuis, Chad struct fc_frame_header *fc_hdr; 1921dd79e8STomer Tayar struct e4_fcoe_task_context *task; 2061d8658bSDupuis, Chad int rc = 0; 2161d8658bSDupuis, Chad uint32_t did, sid; 2261d8658bSDupuis, Chad uint16_t xid; 23be086e7cSMintz, Yuval struct fcoe_wqe *sqe; 24be086e7cSMintz, Yuval unsigned long flags; 25be086e7cSMintz, Yuval u16 sqe_idx; 2661d8658bSDupuis, Chad 27f3690a89SChad Dupuis if (!fcport) { 28f3690a89SChad Dupuis QEDF_ERR(NULL, "fcport is NULL"); 29f3690a89SChad Dupuis rc = -EINVAL; 30f3690a89SChad Dupuis goto els_err; 31f3690a89SChad Dupuis } 32f3690a89SChad Dupuis 33f3690a89SChad Dupuis qedf = fcport->qedf; 34f3690a89SChad Dupuis lport = qedf->lport; 35f3690a89SChad Dupuis 3661d8658bSDupuis, Chad QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_ELS, "Sending ELS\n"); 3761d8658bSDupuis, Chad 3861d8658bSDupuis, Chad rc = fc_remote_port_chkready(fcport->rport); 3961d8658bSDupuis, Chad if (rc) { 4061d8658bSDupuis, Chad QEDF_ERR(&(qedf->dbg_ctx), "els 0x%x: rport not ready\n", op); 4161d8658bSDupuis, Chad rc = -EAGAIN; 4261d8658bSDupuis, Chad goto els_err; 4361d8658bSDupuis, Chad } 4461d8658bSDupuis, Chad if (lport->state != LPORT_ST_READY || !(lport->link_up)) { 4561d8658bSDupuis, Chad QEDF_ERR(&(qedf->dbg_ctx), "els 0x%x: link is not ready\n", 4661d8658bSDupuis, Chad op); 4761d8658bSDupuis, Chad rc = -EAGAIN; 4861d8658bSDupuis, Chad goto els_err; 4961d8658bSDupuis, Chad } 5061d8658bSDupuis, Chad 5157a3548aSChad Dupuis if (!test_bit(QEDF_RPORT_SESSION_READY, &fcport->flags)) { 5261d8658bSDupuis, Chad QEDF_ERR(&(qedf->dbg_ctx), "els 0x%x: fcport not ready\n", op); 5361d8658bSDupuis, Chad rc = -EINVAL; 5461d8658bSDupuis, Chad goto els_err; 5561d8658bSDupuis, Chad } 5661d8658bSDupuis, Chad 5761d8658bSDupuis, Chad els_req = qedf_alloc_cmd(fcport, QEDF_ELS); 5861d8658bSDupuis, Chad if (!els_req) { 59f1c43590SChad Dupuis QEDF_INFO(&qedf->dbg_ctx, QEDF_LOG_ELS, 60f1c43590SChad Dupuis "Failed to alloc ELS request 0x%x\n", op); 6161d8658bSDupuis, Chad rc = -ENOMEM; 6261d8658bSDupuis, Chad goto els_err; 6361d8658bSDupuis, Chad } 6461d8658bSDupuis, Chad 6561d8658bSDupuis, Chad QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_ELS, "initiate_els els_req = " 6661d8658bSDupuis, Chad "0x%p cb_arg = %p xid = %x\n", els_req, cb_arg, 6761d8658bSDupuis, Chad els_req->xid); 6861d8658bSDupuis, Chad els_req->sc_cmd = NULL; 6961d8658bSDupuis, Chad els_req->cmd_type = QEDF_ELS; 7061d8658bSDupuis, Chad els_req->fcport = fcport; 7161d8658bSDupuis, Chad els_req->cb_func = cb_func; 7261d8658bSDupuis, Chad cb_arg->io_req = els_req; 7361d8658bSDupuis, Chad cb_arg->op = op; 7461d8658bSDupuis, Chad els_req->cb_arg = cb_arg; 7561d8658bSDupuis, Chad els_req->data_xfer_len = data_len; 7661d8658bSDupuis, Chad 7761d8658bSDupuis, Chad /* Record which cpu this request is associated with */ 7861d8658bSDupuis, Chad els_req->cpu = smp_processor_id(); 7961d8658bSDupuis, Chad 8061d8658bSDupuis, Chad mp_req = (struct qedf_mp_req *)&(els_req->mp_req); 8161d8658bSDupuis, Chad rc = qedf_init_mp_req(els_req); 8261d8658bSDupuis, Chad if (rc) { 8361d8658bSDupuis, Chad QEDF_ERR(&(qedf->dbg_ctx), "ELS MP request init failed\n"); 8461d8658bSDupuis, Chad kref_put(&els_req->refcount, qedf_release_cmd); 8561d8658bSDupuis, Chad goto els_err; 8661d8658bSDupuis, Chad } else { 8761d8658bSDupuis, Chad rc = 0; 8861d8658bSDupuis, Chad } 8961d8658bSDupuis, Chad 9061d8658bSDupuis, Chad /* Fill ELS Payload */ 9161d8658bSDupuis, Chad if ((op >= ELS_LS_RJT) && (op <= ELS_AUTH_ELS)) { 9261d8658bSDupuis, Chad memcpy(mp_req->req_buf, data, data_len); 9361d8658bSDupuis, Chad } else { 9461d8658bSDupuis, Chad QEDF_ERR(&(qedf->dbg_ctx), "Invalid ELS op 0x%x\n", op); 9561d8658bSDupuis, Chad els_req->cb_func = NULL; 9661d8658bSDupuis, Chad els_req->cb_arg = NULL; 9761d8658bSDupuis, Chad kref_put(&els_req->refcount, qedf_release_cmd); 9861d8658bSDupuis, Chad rc = -EINVAL; 9961d8658bSDupuis, Chad } 10061d8658bSDupuis, Chad 10161d8658bSDupuis, Chad if (rc) 10261d8658bSDupuis, Chad goto els_err; 10361d8658bSDupuis, Chad 10461d8658bSDupuis, Chad /* Fill FC header */ 10561d8658bSDupuis, Chad fc_hdr = &(mp_req->req_fc_hdr); 10661d8658bSDupuis, Chad 10761d8658bSDupuis, Chad did = fcport->rdata->ids.port_id; 10861d8658bSDupuis, Chad sid = fcport->sid; 10961d8658bSDupuis, Chad 11087ea6bddSGustavo A. R. Silva __fc_fill_fc_hdr(fc_hdr, FC_RCTL_ELS_REQ, did, sid, 11161d8658bSDupuis, Chad FC_TYPE_ELS, FC_FC_FIRST_SEQ | FC_FC_END_SEQ | 11261d8658bSDupuis, Chad FC_FC_SEQ_INIT, 0); 11361d8658bSDupuis, Chad 11461d8658bSDupuis, Chad /* Obtain exchange id */ 11561d8658bSDupuis, Chad xid = els_req->xid; 11661d8658bSDupuis, Chad 117be086e7cSMintz, Yuval spin_lock_irqsave(&fcport->rport_lock, flags); 118be086e7cSMintz, Yuval 119be086e7cSMintz, Yuval sqe_idx = qedf_get_sqe_idx(fcport); 120be086e7cSMintz, Yuval sqe = &fcport->sq[sqe_idx]; 121be086e7cSMintz, Yuval memset(sqe, 0, sizeof(struct fcoe_wqe)); 122be086e7cSMintz, Yuval 12361d8658bSDupuis, Chad /* Initialize task context for this IO request */ 12461d8658bSDupuis, Chad task = qedf_get_task_mem(&qedf->tasks, xid); 125be086e7cSMintz, Yuval qedf_init_mp_task(els_req, task, sqe); 12661d8658bSDupuis, Chad 12761d8658bSDupuis, Chad /* Put timer on original I/O request */ 12861d8658bSDupuis, Chad if (timer_msec) 12961d8658bSDupuis, Chad qedf_cmd_timer_set(qedf, els_req, timer_msec); 13061d8658bSDupuis, Chad 13161d8658bSDupuis, Chad /* Ring doorbell */ 13261d8658bSDupuis, Chad QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_ELS, "Ringing doorbell for ELS " 13361d8658bSDupuis, Chad "req\n"); 13461d8658bSDupuis, Chad qedf_ring_doorbell(fcport); 1355d5e5565SShyam Sundar set_bit(QEDF_CMD_OUTSTANDING, &els_req->flags); 1365d5e5565SShyam Sundar 137be086e7cSMintz, Yuval spin_unlock_irqrestore(&fcport->rport_lock, flags); 13861d8658bSDupuis, Chad els_err: 13961d8658bSDupuis, Chad return rc; 14061d8658bSDupuis, Chad } 14161d8658bSDupuis, Chad 14261d8658bSDupuis, Chad void qedf_process_els_compl(struct qedf_ctx *qedf, struct fcoe_cqe *cqe, 14361d8658bSDupuis, Chad struct qedf_ioreq *els_req) 14461d8658bSDupuis, Chad { 14561d8658bSDupuis, Chad struct fcoe_cqe_midpath_info *mp_info; 1463079285bSSaurav Kashyap struct qedf_rport *fcport; 14761d8658bSDupuis, Chad 14861d8658bSDupuis, Chad QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_ELS, "Entered with xid = 0x%x" 14961d8658bSDupuis, Chad " cmd_type = %d.\n", els_req->xid, els_req->cmd_type); 15061d8658bSDupuis, Chad 15122ddec31SSaurav Kashyap if ((els_req->event == QEDF_IOREQ_EV_ELS_FLUSH) 15222ddec31SSaurav Kashyap || (els_req->event == QEDF_IOREQ_EV_CLEANUP_SUCCESS) 15322ddec31SSaurav Kashyap || (els_req->event == QEDF_IOREQ_EV_CLEANUP_FAILED)) { 15422ddec31SSaurav Kashyap QEDF_INFO(&qedf->dbg_ctx, QEDF_LOG_IO, 15522ddec31SSaurav Kashyap "ELS completion xid=0x%x after flush event=0x%x", 15622ddec31SSaurav Kashyap els_req->xid, els_req->event); 15722ddec31SSaurav Kashyap return; 15822ddec31SSaurav Kashyap } 15922ddec31SSaurav Kashyap 1603079285bSSaurav Kashyap fcport = els_req->fcport; 1613079285bSSaurav Kashyap 1623079285bSSaurav Kashyap /* When flush is active, 1633079285bSSaurav Kashyap * let the cmds be completed from the cleanup context 1643079285bSSaurav Kashyap */ 1653079285bSSaurav Kashyap if (test_bit(QEDF_RPORT_IN_TARGET_RESET, &fcport->flags) || 1663079285bSSaurav Kashyap test_bit(QEDF_RPORT_IN_LUN_RESET, &fcport->flags)) { 1673079285bSSaurav Kashyap QEDF_INFO(&qedf->dbg_ctx, QEDF_LOG_IO, 1683079285bSSaurav Kashyap "Dropping ELS completion xid=0x%x as fcport is flushing", 1693079285bSSaurav Kashyap els_req->xid); 1703079285bSSaurav Kashyap return; 1713079285bSSaurav Kashyap } 1723079285bSSaurav Kashyap 1735d5e5565SShyam Sundar clear_bit(QEDF_CMD_OUTSTANDING, &els_req->flags); 1745d5e5565SShyam Sundar 17561d8658bSDupuis, Chad /* Kill the ELS timer */ 17661d8658bSDupuis, Chad cancel_delayed_work(&els_req->timeout_work); 17761d8658bSDupuis, Chad 17861d8658bSDupuis, Chad /* Get ELS response length from CQE */ 17961d8658bSDupuis, Chad mp_info = &cqe->cqe_info.midpath_info; 18061d8658bSDupuis, Chad els_req->mp_req.resp_len = mp_info->data_placement_size; 18161d8658bSDupuis, Chad 18261d8658bSDupuis, Chad /* Parse ELS response */ 18361d8658bSDupuis, Chad if ((els_req->cb_func) && (els_req->cb_arg)) { 18461d8658bSDupuis, Chad els_req->cb_func(els_req->cb_arg); 18561d8658bSDupuis, Chad els_req->cb_arg = NULL; 18661d8658bSDupuis, Chad } 18761d8658bSDupuis, Chad 18861d8658bSDupuis, Chad kref_put(&els_req->refcount, qedf_release_cmd); 18961d8658bSDupuis, Chad } 19061d8658bSDupuis, Chad 19161d8658bSDupuis, Chad static void qedf_rrq_compl(struct qedf_els_cb_arg *cb_arg) 19261d8658bSDupuis, Chad { 19361d8658bSDupuis, Chad struct qedf_ioreq *orig_io_req; 19461d8658bSDupuis, Chad struct qedf_ioreq *rrq_req; 19561d8658bSDupuis, Chad struct qedf_ctx *qedf; 19661d8658bSDupuis, Chad int refcount; 19761d8658bSDupuis, Chad 19861d8658bSDupuis, Chad rrq_req = cb_arg->io_req; 19961d8658bSDupuis, Chad qedf = rrq_req->fcport->qedf; 20061d8658bSDupuis, Chad 20161d8658bSDupuis, Chad QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_ELS, "Entered.\n"); 20261d8658bSDupuis, Chad 20361d8658bSDupuis, Chad orig_io_req = cb_arg->aborted_io_req; 20461d8658bSDupuis, Chad 205e82e6ff7SSaurav Kashyap if (!orig_io_req) { 206e82e6ff7SSaurav Kashyap QEDF_ERR(&qedf->dbg_ctx, 207e82e6ff7SSaurav Kashyap "Original io_req is NULL, rrq_req = %p.\n", rrq_req); 20861d8658bSDupuis, Chad goto out_free; 209e82e6ff7SSaurav Kashyap } 21061d8658bSDupuis, Chad 2111afca6b5SDupuis, Chad refcount = kref_read(&orig_io_req->refcount); 21261d8658bSDupuis, Chad QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_ELS, "rrq_compl: orig io = %p," 21361d8658bSDupuis, Chad " orig xid = 0x%x, rrq_xid = 0x%x, refcount=%d\n", 21461d8658bSDupuis, Chad orig_io_req, orig_io_req->xid, rrq_req->xid, refcount); 21561d8658bSDupuis, Chad 21669ef2c69SSaurav Kashyap /* 21769ef2c69SSaurav Kashyap * This should return the aborted io_req to the command pool. Note that 21869ef2c69SSaurav Kashyap * we need to check the refcound in case the original request was 21969ef2c69SSaurav Kashyap * flushed but we get a completion on this xid. 22069ef2c69SSaurav Kashyap */ 22169ef2c69SSaurav Kashyap if (orig_io_req && refcount > 0) 22261d8658bSDupuis, Chad kref_put(&orig_io_req->refcount, qedf_release_cmd); 22361d8658bSDupuis, Chad 22461d8658bSDupuis, Chad out_free: 225adf48842SChad Dupuis /* 226adf48842SChad Dupuis * Release a reference to the rrq request if we timed out as the 227adf48842SChad Dupuis * rrq completion handler is called directly from the timeout handler 228adf48842SChad Dupuis * and not from els_compl where the reference would have normally been 229adf48842SChad Dupuis * released. 230adf48842SChad Dupuis */ 231adf48842SChad Dupuis if (rrq_req->event == QEDF_IOREQ_EV_ELS_TMO) 232adf48842SChad Dupuis kref_put(&rrq_req->refcount, qedf_release_cmd); 23361d8658bSDupuis, Chad kfree(cb_arg); 23461d8658bSDupuis, Chad } 23561d8658bSDupuis, Chad 23661d8658bSDupuis, Chad /* Assumes kref is already held by caller */ 23761d8658bSDupuis, Chad int qedf_send_rrq(struct qedf_ioreq *aborted_io_req) 23861d8658bSDupuis, Chad { 23961d8658bSDupuis, Chad 24061d8658bSDupuis, Chad struct fc_els_rrq rrq; 24161d8658bSDupuis, Chad struct qedf_rport *fcport; 24261d8658bSDupuis, Chad struct fc_lport *lport; 24361d8658bSDupuis, Chad struct qedf_els_cb_arg *cb_arg = NULL; 24461d8658bSDupuis, Chad struct qedf_ctx *qedf; 24561d8658bSDupuis, Chad uint32_t sid; 24661d8658bSDupuis, Chad uint32_t r_a_tov; 24761d8658bSDupuis, Chad int rc; 24869ef2c69SSaurav Kashyap int refcount; 24961d8658bSDupuis, Chad 25061d8658bSDupuis, Chad if (!aborted_io_req) { 25161d8658bSDupuis, Chad QEDF_ERR(NULL, "abort_io_req is NULL.\n"); 25261d8658bSDupuis, Chad return -EINVAL; 25361d8658bSDupuis, Chad } 25461d8658bSDupuis, Chad 25561d8658bSDupuis, Chad fcport = aborted_io_req->fcport; 25661d8658bSDupuis, Chad 25769ef2c69SSaurav Kashyap if (!fcport) { 25869ef2c69SSaurav Kashyap refcount = kref_read(&aborted_io_req->refcount); 25969ef2c69SSaurav Kashyap QEDF_ERR(NULL, 26069ef2c69SSaurav Kashyap "RRQ work was queued prior to a flush xid=0x%x, refcount=%d.\n", 26169ef2c69SSaurav Kashyap aborted_io_req->xid, refcount); 26269ef2c69SSaurav Kashyap kref_put(&aborted_io_req->refcount, qedf_release_cmd); 26369ef2c69SSaurav Kashyap return -EINVAL; 26469ef2c69SSaurav Kashyap } 26569ef2c69SSaurav Kashyap 26661d8658bSDupuis, Chad /* Check that fcport is still offloaded */ 26757a3548aSChad Dupuis if (!test_bit(QEDF_RPORT_SESSION_READY, &fcport->flags)) { 26861d8658bSDupuis, Chad QEDF_ERR(NULL, "fcport is no longer offloaded.\n"); 26961d8658bSDupuis, Chad return -EINVAL; 27061d8658bSDupuis, Chad } 27161d8658bSDupuis, Chad 27261d8658bSDupuis, Chad if (!fcport->qedf) { 27361d8658bSDupuis, Chad QEDF_ERR(NULL, "fcport->qedf is NULL.\n"); 27461d8658bSDupuis, Chad return -EINVAL; 27561d8658bSDupuis, Chad } 27661d8658bSDupuis, Chad 27761d8658bSDupuis, Chad qedf = fcport->qedf; 27869ef2c69SSaurav Kashyap 27969ef2c69SSaurav Kashyap /* 28069ef2c69SSaurav Kashyap * Sanity check that we can send a RRQ to make sure that refcount isn't 28169ef2c69SSaurav Kashyap * 0 28269ef2c69SSaurav Kashyap */ 28369ef2c69SSaurav Kashyap refcount = kref_read(&aborted_io_req->refcount); 28469ef2c69SSaurav Kashyap if (refcount != 1) { 28569ef2c69SSaurav Kashyap QEDF_INFO(&qedf->dbg_ctx, QEDF_LOG_ELS, 28669ef2c69SSaurav Kashyap "refcount for xid=%x io_req=%p refcount=%d is not 1.\n", 28769ef2c69SSaurav Kashyap aborted_io_req->xid, aborted_io_req, refcount); 28869ef2c69SSaurav Kashyap return -EINVAL; 28969ef2c69SSaurav Kashyap } 29069ef2c69SSaurav Kashyap 29161d8658bSDupuis, Chad lport = qedf->lport; 29261d8658bSDupuis, Chad sid = fcport->sid; 29361d8658bSDupuis, Chad r_a_tov = lport->r_a_tov; 29461d8658bSDupuis, Chad 29561d8658bSDupuis, Chad QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_ELS, "Sending RRQ orig " 29661d8658bSDupuis, Chad "io = %p, orig_xid = 0x%x\n", aborted_io_req, 29761d8658bSDupuis, Chad aborted_io_req->xid); 29861d8658bSDupuis, Chad memset(&rrq, 0, sizeof(rrq)); 29961d8658bSDupuis, Chad 30061d8658bSDupuis, Chad cb_arg = kzalloc(sizeof(struct qedf_els_cb_arg), GFP_NOIO); 30161d8658bSDupuis, Chad if (!cb_arg) { 30261d8658bSDupuis, Chad QEDF_ERR(&(qedf->dbg_ctx), "Unable to allocate cb_arg for " 30361d8658bSDupuis, Chad "RRQ\n"); 30461d8658bSDupuis, Chad rc = -ENOMEM; 30561d8658bSDupuis, Chad goto rrq_err; 30661d8658bSDupuis, Chad } 30761d8658bSDupuis, Chad 30861d8658bSDupuis, Chad cb_arg->aborted_io_req = aborted_io_req; 30961d8658bSDupuis, Chad 31061d8658bSDupuis, Chad rrq.rrq_cmd = ELS_RRQ; 31161d8658bSDupuis, Chad hton24(rrq.rrq_s_id, sid); 31261d8658bSDupuis, Chad rrq.rrq_ox_id = htons(aborted_io_req->xid); 31361d8658bSDupuis, Chad rrq.rrq_rx_id = 31461d8658bSDupuis, Chad htons(aborted_io_req->task->tstorm_st_context.read_write.rx_id); 31561d8658bSDupuis, Chad 31661d8658bSDupuis, Chad rc = qedf_initiate_els(fcport, ELS_RRQ, &rrq, sizeof(rrq), 31761d8658bSDupuis, Chad qedf_rrq_compl, cb_arg, r_a_tov); 31861d8658bSDupuis, Chad 31961d8658bSDupuis, Chad rrq_err: 32061d8658bSDupuis, Chad if (rc) { 32161d8658bSDupuis, Chad QEDF_ERR(&(qedf->dbg_ctx), "RRQ failed - release orig io " 32261d8658bSDupuis, Chad "req 0x%x\n", aborted_io_req->xid); 32361d8658bSDupuis, Chad kfree(cb_arg); 32461d8658bSDupuis, Chad kref_put(&aborted_io_req->refcount, qedf_release_cmd); 32561d8658bSDupuis, Chad } 32661d8658bSDupuis, Chad return rc; 32761d8658bSDupuis, Chad } 32861d8658bSDupuis, Chad 32961d8658bSDupuis, Chad static void qedf_process_l2_frame_compl(struct qedf_rport *fcport, 33061d8658bSDupuis, Chad struct fc_frame *fp, 33161d8658bSDupuis, Chad u16 l2_oxid) 33261d8658bSDupuis, Chad { 33361d8658bSDupuis, Chad struct fc_lport *lport = fcport->qedf->lport; 33461d8658bSDupuis, Chad struct fc_frame_header *fh; 33561d8658bSDupuis, Chad u32 crc; 33661d8658bSDupuis, Chad 33761d8658bSDupuis, Chad fh = (struct fc_frame_header *)fc_frame_header_get(fp); 33861d8658bSDupuis, Chad 33961d8658bSDupuis, Chad /* Set the OXID we return to what libfc used */ 34061d8658bSDupuis, Chad if (l2_oxid != FC_XID_UNKNOWN) 34161d8658bSDupuis, Chad fh->fh_ox_id = htons(l2_oxid); 34261d8658bSDupuis, Chad 34361d8658bSDupuis, Chad /* Setup header fields */ 34461d8658bSDupuis, Chad fh->fh_r_ctl = FC_RCTL_ELS_REP; 34561d8658bSDupuis, Chad fh->fh_type = FC_TYPE_ELS; 34661d8658bSDupuis, Chad /* Last sequence, end sequence */ 34761d8658bSDupuis, Chad fh->fh_f_ctl[0] = 0x98; 34861d8658bSDupuis, Chad hton24(fh->fh_d_id, lport->port_id); 34961d8658bSDupuis, Chad hton24(fh->fh_s_id, fcport->rdata->ids.port_id); 35061d8658bSDupuis, Chad fh->fh_rx_id = 0xffff; 35161d8658bSDupuis, Chad 35261d8658bSDupuis, Chad /* Set frame attributes */ 35361d8658bSDupuis, Chad crc = fcoe_fc_crc(fp); 35461d8658bSDupuis, Chad fc_frame_init(fp); 35561d8658bSDupuis, Chad fr_dev(fp) = lport; 35661d8658bSDupuis, Chad fr_sof(fp) = FC_SOF_I3; 35761d8658bSDupuis, Chad fr_eof(fp) = FC_EOF_T; 35861d8658bSDupuis, Chad fr_crc(fp) = cpu_to_le32(~crc); 35961d8658bSDupuis, Chad 36061d8658bSDupuis, Chad /* Send completed request to libfc */ 36161d8658bSDupuis, Chad fc_exch_recv(lport, fp); 36261d8658bSDupuis, Chad } 36361d8658bSDupuis, Chad 36461d8658bSDupuis, Chad /* 36561d8658bSDupuis, Chad * In instances where an ELS command times out we may need to restart the 36661d8658bSDupuis, Chad * rport by logging out and then logging back in. 36761d8658bSDupuis, Chad */ 36861d8658bSDupuis, Chad void qedf_restart_rport(struct qedf_rport *fcport) 36961d8658bSDupuis, Chad { 37061d8658bSDupuis, Chad struct fc_lport *lport; 37161d8658bSDupuis, Chad struct fc_rport_priv *rdata; 37261d8658bSDupuis, Chad u32 port_id; 37378a8ab3cSHannes Reinecke unsigned long flags; 37461d8658bSDupuis, Chad 375e82e6ff7SSaurav Kashyap if (!fcport) { 376e82e6ff7SSaurav Kashyap QEDF_ERR(NULL, "fcport is NULL.\n"); 37761d8658bSDupuis, Chad return; 378e82e6ff7SSaurav Kashyap } 37961d8658bSDupuis, Chad 38078a8ab3cSHannes Reinecke spin_lock_irqsave(&fcport->rport_lock, flags); 38144c7c859SChad Dupuis if (test_bit(QEDF_RPORT_IN_RESET, &fcport->flags) || 38244c7c859SChad Dupuis !test_bit(QEDF_RPORT_SESSION_READY, &fcport->flags) || 38344c7c859SChad Dupuis test_bit(QEDF_RPORT_UPLOADING_CONNECTION, &fcport->flags)) { 38444c7c859SChad Dupuis QEDF_ERR(&(fcport->qedf->dbg_ctx), "fcport %p already in reset or not offloaded.\n", 38544c7c859SChad Dupuis fcport); 38678a8ab3cSHannes Reinecke spin_unlock_irqrestore(&fcport->rport_lock, flags); 38744c7c859SChad Dupuis return; 38844c7c859SChad Dupuis } 38944c7c859SChad Dupuis 39044c7c859SChad Dupuis /* Set that we are now in reset */ 39144c7c859SChad Dupuis set_bit(QEDF_RPORT_IN_RESET, &fcport->flags); 39278a8ab3cSHannes Reinecke spin_unlock_irqrestore(&fcport->rport_lock, flags); 39344c7c859SChad Dupuis 39461d8658bSDupuis, Chad rdata = fcport->rdata; 39556efc304SHannes Reinecke if (rdata && !kref_get_unless_zero(&rdata->kref)) { 39656efc304SHannes Reinecke fcport->rdata = NULL; 39756efc304SHannes Reinecke rdata = NULL; 39856efc304SHannes Reinecke } 39956efc304SHannes Reinecke 40056efc304SHannes Reinecke if (rdata && rdata->rp_state == RPORT_ST_READY) { 40161d8658bSDupuis, Chad lport = fcport->qedf->lport; 40261d8658bSDupuis, Chad port_id = rdata->ids.port_id; 40361d8658bSDupuis, Chad QEDF_ERR(&(fcport->qedf->dbg_ctx), 40461d8658bSDupuis, Chad "LOGO port_id=%x.\n", port_id); 40561d8658bSDupuis, Chad fc_rport_logoff(rdata); 40656efc304SHannes Reinecke kref_put(&rdata->kref, fc_rport_destroy); 4076d1368e8SHannes Reinecke mutex_lock(&lport->disc.disc_mutex); 40861d8658bSDupuis, Chad /* Recreate the rport and log back in */ 40961d8658bSDupuis, Chad rdata = fc_rport_create(lport, port_id); 4106d1368e8SHannes Reinecke mutex_unlock(&lport->disc.disc_mutex); 411fbbef0daSDaniel Wagner if (rdata) 41261d8658bSDupuis, Chad fc_rport_login(rdata); 4136d1368e8SHannes Reinecke fcport->rdata = rdata; 41461d8658bSDupuis, Chad } 41544c7c859SChad Dupuis clear_bit(QEDF_RPORT_IN_RESET, &fcport->flags); 41661d8658bSDupuis, Chad } 41761d8658bSDupuis, Chad 41861d8658bSDupuis, Chad static void qedf_l2_els_compl(struct qedf_els_cb_arg *cb_arg) 41961d8658bSDupuis, Chad { 42061d8658bSDupuis, Chad struct qedf_ioreq *els_req; 42161d8658bSDupuis, Chad struct qedf_rport *fcport; 42261d8658bSDupuis, Chad struct qedf_mp_req *mp_req; 42361d8658bSDupuis, Chad struct fc_frame *fp; 42461d8658bSDupuis, Chad struct fc_frame_header *fh, *mp_fc_hdr; 42561d8658bSDupuis, Chad void *resp_buf, *fc_payload; 42661d8658bSDupuis, Chad u32 resp_len; 42761d8658bSDupuis, Chad u16 l2_oxid; 42861d8658bSDupuis, Chad 42961d8658bSDupuis, Chad l2_oxid = cb_arg->l2_oxid; 43061d8658bSDupuis, Chad els_req = cb_arg->io_req; 43161d8658bSDupuis, Chad 43261d8658bSDupuis, Chad if (!els_req) { 43361d8658bSDupuis, Chad QEDF_ERR(NULL, "els_req is NULL.\n"); 43461d8658bSDupuis, Chad goto free_arg; 43561d8658bSDupuis, Chad } 43661d8658bSDupuis, Chad 43761d8658bSDupuis, Chad /* 43861d8658bSDupuis, Chad * If we are flushing the command just free the cb_arg as none of the 43961d8658bSDupuis, Chad * response data will be valid. 44061d8658bSDupuis, Chad */ 441e82e6ff7SSaurav Kashyap if (els_req->event == QEDF_IOREQ_EV_ELS_FLUSH) { 442e82e6ff7SSaurav Kashyap QEDF_ERR(NULL, "els_req xid=0x%x event is flush.\n", 443e82e6ff7SSaurav Kashyap els_req->xid); 44461d8658bSDupuis, Chad goto free_arg; 445e82e6ff7SSaurav Kashyap } 44661d8658bSDupuis, Chad 44761d8658bSDupuis, Chad fcport = els_req->fcport; 44861d8658bSDupuis, Chad mp_req = &(els_req->mp_req); 44961d8658bSDupuis, Chad mp_fc_hdr = &(mp_req->resp_fc_hdr); 45061d8658bSDupuis, Chad resp_len = mp_req->resp_len; 45161d8658bSDupuis, Chad resp_buf = mp_req->resp_buf; 45261d8658bSDupuis, Chad 45361d8658bSDupuis, Chad /* 45461d8658bSDupuis, Chad * If a middle path ELS command times out, don't try to return 45561d8658bSDupuis, Chad * the command but rather do any internal cleanup and then libfc 45661d8658bSDupuis, Chad * timeout the command and clean up its internal resources. 45761d8658bSDupuis, Chad */ 45861d8658bSDupuis, Chad if (els_req->event == QEDF_IOREQ_EV_ELS_TMO) { 45961d8658bSDupuis, Chad /* 46061d8658bSDupuis, Chad * If ADISC times out, libfc will timeout the exchange and then 46161d8658bSDupuis, Chad * try to send a PLOGI which will timeout since the session is 46261d8658bSDupuis, Chad * still offloaded. Force libfc to logout the session which 46361d8658bSDupuis, Chad * will offload the connection and allow the PLOGI response to 46461d8658bSDupuis, Chad * flow over the LL2 path. 46561d8658bSDupuis, Chad */ 46661d8658bSDupuis, Chad if (cb_arg->op == ELS_ADISC) 46761d8658bSDupuis, Chad qedf_restart_rport(fcport); 46861d8658bSDupuis, Chad return; 46961d8658bSDupuis, Chad } 47061d8658bSDupuis, Chad 47161d8658bSDupuis, Chad if (sizeof(struct fc_frame_header) + resp_len > QEDF_PAGE_SIZE) { 47261d8658bSDupuis, Chad QEDF_ERR(&(fcport->qedf->dbg_ctx), "resp_len is " 47361d8658bSDupuis, Chad "beyond page size.\n"); 47461d8658bSDupuis, Chad goto free_arg; 47561d8658bSDupuis, Chad } 47661d8658bSDupuis, Chad 47761d8658bSDupuis, Chad fp = fc_frame_alloc(fcport->qedf->lport, resp_len); 47861d8658bSDupuis, Chad if (!fp) { 47961d8658bSDupuis, Chad QEDF_ERR(&(fcport->qedf->dbg_ctx), 48061d8658bSDupuis, Chad "fc_frame_alloc failure.\n"); 48161d8658bSDupuis, Chad return; 48261d8658bSDupuis, Chad } 48361d8658bSDupuis, Chad 48461d8658bSDupuis, Chad /* Copy frame header from firmware into fp */ 48561d8658bSDupuis, Chad fh = (struct fc_frame_header *)fc_frame_header_get(fp); 48661d8658bSDupuis, Chad memcpy(fh, mp_fc_hdr, sizeof(struct fc_frame_header)); 48761d8658bSDupuis, Chad 48861d8658bSDupuis, Chad /* Copy payload from firmware into fp */ 48961d8658bSDupuis, Chad fc_payload = fc_frame_payload_get(fp, resp_len); 49061d8658bSDupuis, Chad memcpy(fc_payload, resp_buf, resp_len); 49161d8658bSDupuis, Chad 49261d8658bSDupuis, Chad QEDF_INFO(&(fcport->qedf->dbg_ctx), QEDF_LOG_ELS, 49361d8658bSDupuis, Chad "Completing OX_ID 0x%x back to libfc.\n", l2_oxid); 49461d8658bSDupuis, Chad qedf_process_l2_frame_compl(fcport, fp, l2_oxid); 49561d8658bSDupuis, Chad 49661d8658bSDupuis, Chad free_arg: 49761d8658bSDupuis, Chad kfree(cb_arg); 49861d8658bSDupuis, Chad } 49961d8658bSDupuis, Chad 50061d8658bSDupuis, Chad int qedf_send_adisc(struct qedf_rport *fcport, struct fc_frame *fp) 50161d8658bSDupuis, Chad { 50261d8658bSDupuis, Chad struct fc_els_adisc *adisc; 50361d8658bSDupuis, Chad struct fc_frame_header *fh; 50461d8658bSDupuis, Chad struct fc_lport *lport = fcport->qedf->lport; 50561d8658bSDupuis, Chad struct qedf_els_cb_arg *cb_arg = NULL; 50661d8658bSDupuis, Chad struct qedf_ctx *qedf; 50761d8658bSDupuis, Chad uint32_t r_a_tov = lport->r_a_tov; 50861d8658bSDupuis, Chad int rc; 50961d8658bSDupuis, Chad 51061d8658bSDupuis, Chad qedf = fcport->qedf; 51161d8658bSDupuis, Chad fh = fc_frame_header_get(fp); 51261d8658bSDupuis, Chad 51361d8658bSDupuis, Chad cb_arg = kzalloc(sizeof(struct qedf_els_cb_arg), GFP_NOIO); 51461d8658bSDupuis, Chad if (!cb_arg) { 51561d8658bSDupuis, Chad QEDF_ERR(&(qedf->dbg_ctx), "Unable to allocate cb_arg for " 51661d8658bSDupuis, Chad "ADISC\n"); 51761d8658bSDupuis, Chad rc = -ENOMEM; 51861d8658bSDupuis, Chad goto adisc_err; 51961d8658bSDupuis, Chad } 52061d8658bSDupuis, Chad cb_arg->l2_oxid = ntohs(fh->fh_ox_id); 52161d8658bSDupuis, Chad 52261d8658bSDupuis, Chad QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_ELS, 52361d8658bSDupuis, Chad "Sending ADISC ox_id=0x%x.\n", cb_arg->l2_oxid); 52461d8658bSDupuis, Chad 52561d8658bSDupuis, Chad adisc = fc_frame_payload_get(fp, sizeof(*adisc)); 52661d8658bSDupuis, Chad 52761d8658bSDupuis, Chad rc = qedf_initiate_els(fcport, ELS_ADISC, adisc, sizeof(*adisc), 52861d8658bSDupuis, Chad qedf_l2_els_compl, cb_arg, r_a_tov); 52961d8658bSDupuis, Chad 53061d8658bSDupuis, Chad adisc_err: 53161d8658bSDupuis, Chad if (rc) { 53261d8658bSDupuis, Chad QEDF_ERR(&(qedf->dbg_ctx), "ADISC failed.\n"); 53361d8658bSDupuis, Chad kfree(cb_arg); 53461d8658bSDupuis, Chad } 53561d8658bSDupuis, Chad return rc; 53661d8658bSDupuis, Chad } 53761d8658bSDupuis, Chad 53861d8658bSDupuis, Chad static void qedf_srr_compl(struct qedf_els_cb_arg *cb_arg) 53961d8658bSDupuis, Chad { 54061d8658bSDupuis, Chad struct qedf_ioreq *orig_io_req; 54161d8658bSDupuis, Chad struct qedf_ioreq *srr_req; 54261d8658bSDupuis, Chad struct qedf_mp_req *mp_req; 54361d8658bSDupuis, Chad struct fc_frame_header *mp_fc_hdr, *fh; 54461d8658bSDupuis, Chad struct fc_frame *fp; 54561d8658bSDupuis, Chad void *resp_buf, *fc_payload; 54661d8658bSDupuis, Chad u32 resp_len; 54761d8658bSDupuis, Chad struct fc_lport *lport; 54861d8658bSDupuis, Chad struct qedf_ctx *qedf; 54961d8658bSDupuis, Chad int refcount; 55061d8658bSDupuis, Chad u8 opcode; 55161d8658bSDupuis, Chad 55261d8658bSDupuis, Chad srr_req = cb_arg->io_req; 55361d8658bSDupuis, Chad qedf = srr_req->fcport->qedf; 55461d8658bSDupuis, Chad lport = qedf->lport; 55561d8658bSDupuis, Chad 55661d8658bSDupuis, Chad orig_io_req = cb_arg->aborted_io_req; 55761d8658bSDupuis, Chad 558e82e6ff7SSaurav Kashyap if (!orig_io_req) { 559e82e6ff7SSaurav Kashyap QEDF_ERR(NULL, "orig_io_req is NULL.\n"); 56061d8658bSDupuis, Chad goto out_free; 561e82e6ff7SSaurav Kashyap } 56261d8658bSDupuis, Chad 56361d8658bSDupuis, Chad clear_bit(QEDF_CMD_SRR_SENT, &orig_io_req->flags); 56461d8658bSDupuis, Chad 56561d8658bSDupuis, Chad if (srr_req->event != QEDF_IOREQ_EV_ELS_TMO && 56661d8658bSDupuis, Chad srr_req->event != QEDF_IOREQ_EV_ELS_ERR_DETECT) 56761d8658bSDupuis, Chad cancel_delayed_work_sync(&orig_io_req->timeout_work); 56861d8658bSDupuis, Chad 5691afca6b5SDupuis, Chad refcount = kref_read(&orig_io_req->refcount); 57061d8658bSDupuis, Chad QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_ELS, "Entered: orig_io=%p," 57161d8658bSDupuis, Chad " orig_io_xid=0x%x, rec_xid=0x%x, refcount=%d\n", 57261d8658bSDupuis, Chad orig_io_req, orig_io_req->xid, srr_req->xid, refcount); 57361d8658bSDupuis, Chad 57461d8658bSDupuis, Chad /* If a SRR times out, simply free resources */ 575e82e6ff7SSaurav Kashyap if (srr_req->event == QEDF_IOREQ_EV_ELS_TMO) { 576e82e6ff7SSaurav Kashyap QEDF_ERR(&qedf->dbg_ctx, 577e82e6ff7SSaurav Kashyap "ELS timeout rec_xid=0x%x.\n", srr_req->xid); 57847c4ccd3SChristophe JAILLET goto out_put; 579e82e6ff7SSaurav Kashyap } 58061d8658bSDupuis, Chad 58161d8658bSDupuis, Chad /* Normalize response data into struct fc_frame */ 58261d8658bSDupuis, Chad mp_req = &(srr_req->mp_req); 58361d8658bSDupuis, Chad mp_fc_hdr = &(mp_req->resp_fc_hdr); 58461d8658bSDupuis, Chad resp_len = mp_req->resp_len; 58561d8658bSDupuis, Chad resp_buf = mp_req->resp_buf; 58661d8658bSDupuis, Chad 58761d8658bSDupuis, Chad fp = fc_frame_alloc(lport, resp_len); 58861d8658bSDupuis, Chad if (!fp) { 58961d8658bSDupuis, Chad QEDF_ERR(&(qedf->dbg_ctx), 59061d8658bSDupuis, Chad "fc_frame_alloc failure.\n"); 59147c4ccd3SChristophe JAILLET goto out_put; 59261d8658bSDupuis, Chad } 59361d8658bSDupuis, Chad 59461d8658bSDupuis, Chad /* Copy frame header from firmware into fp */ 59561d8658bSDupuis, Chad fh = (struct fc_frame_header *)fc_frame_header_get(fp); 59661d8658bSDupuis, Chad memcpy(fh, mp_fc_hdr, sizeof(struct fc_frame_header)); 59761d8658bSDupuis, Chad 59861d8658bSDupuis, Chad /* Copy payload from firmware into fp */ 59961d8658bSDupuis, Chad fc_payload = fc_frame_payload_get(fp, resp_len); 60061d8658bSDupuis, Chad memcpy(fc_payload, resp_buf, resp_len); 60161d8658bSDupuis, Chad 60261d8658bSDupuis, Chad opcode = fc_frame_payload_op(fp); 60361d8658bSDupuis, Chad switch (opcode) { 60461d8658bSDupuis, Chad case ELS_LS_ACC: 60561d8658bSDupuis, Chad QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_ELS, 60661d8658bSDupuis, Chad "SRR success.\n"); 60761d8658bSDupuis, Chad break; 60861d8658bSDupuis, Chad case ELS_LS_RJT: 60961d8658bSDupuis, Chad QEDF_INFO(&qedf->dbg_ctx, QEDF_LOG_ELS, 61061d8658bSDupuis, Chad "SRR rejected.\n"); 61161d8658bSDupuis, Chad qedf_initiate_abts(orig_io_req, true); 61261d8658bSDupuis, Chad break; 61361d8658bSDupuis, Chad } 61461d8658bSDupuis, Chad 61561d8658bSDupuis, Chad fc_frame_free(fp); 61647c4ccd3SChristophe JAILLET out_put: 61761d8658bSDupuis, Chad /* Put reference for original command since SRR completed */ 61861d8658bSDupuis, Chad kref_put(&orig_io_req->refcount, qedf_release_cmd); 61947c4ccd3SChristophe JAILLET out_free: 62061d8658bSDupuis, Chad kfree(cb_arg); 62161d8658bSDupuis, Chad } 62261d8658bSDupuis, Chad 62361d8658bSDupuis, Chad static int qedf_send_srr(struct qedf_ioreq *orig_io_req, u32 offset, u8 r_ctl) 62461d8658bSDupuis, Chad { 62561d8658bSDupuis, Chad struct fcp_srr srr; 62661d8658bSDupuis, Chad struct qedf_ctx *qedf; 62761d8658bSDupuis, Chad struct qedf_rport *fcport; 62861d8658bSDupuis, Chad struct fc_lport *lport; 62961d8658bSDupuis, Chad struct qedf_els_cb_arg *cb_arg = NULL; 630cc23c661SYueHaibing u32 r_a_tov; 63161d8658bSDupuis, Chad int rc; 63261d8658bSDupuis, Chad 63361d8658bSDupuis, Chad if (!orig_io_req) { 63461d8658bSDupuis, Chad QEDF_ERR(NULL, "orig_io_req is NULL.\n"); 63561d8658bSDupuis, Chad return -EINVAL; 63661d8658bSDupuis, Chad } 63761d8658bSDupuis, Chad 63861d8658bSDupuis, Chad fcport = orig_io_req->fcport; 63961d8658bSDupuis, Chad 64061d8658bSDupuis, Chad /* Check that fcport is still offloaded */ 64157a3548aSChad Dupuis if (!test_bit(QEDF_RPORT_SESSION_READY, &fcport->flags)) { 64261d8658bSDupuis, Chad QEDF_ERR(NULL, "fcport is no longer offloaded.\n"); 64361d8658bSDupuis, Chad return -EINVAL; 64461d8658bSDupuis, Chad } 64561d8658bSDupuis, Chad 64661d8658bSDupuis, Chad if (!fcport->qedf) { 64761d8658bSDupuis, Chad QEDF_ERR(NULL, "fcport->qedf is NULL.\n"); 64861d8658bSDupuis, Chad return -EINVAL; 64961d8658bSDupuis, Chad } 65061d8658bSDupuis, Chad 65161d8658bSDupuis, Chad /* Take reference until SRR command completion */ 65261d8658bSDupuis, Chad kref_get(&orig_io_req->refcount); 65361d8658bSDupuis, Chad 65461d8658bSDupuis, Chad qedf = fcport->qedf; 65561d8658bSDupuis, Chad lport = qedf->lport; 65661d8658bSDupuis, Chad r_a_tov = lport->r_a_tov; 65761d8658bSDupuis, Chad 65861d8658bSDupuis, Chad QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_ELS, "Sending SRR orig_io=%p, " 65961d8658bSDupuis, Chad "orig_xid=0x%x\n", orig_io_req, orig_io_req->xid); 66061d8658bSDupuis, Chad memset(&srr, 0, sizeof(srr)); 66161d8658bSDupuis, Chad 66261d8658bSDupuis, Chad cb_arg = kzalloc(sizeof(struct qedf_els_cb_arg), GFP_NOIO); 66361d8658bSDupuis, Chad if (!cb_arg) { 66461d8658bSDupuis, Chad QEDF_ERR(&(qedf->dbg_ctx), "Unable to allocate cb_arg for " 66561d8658bSDupuis, Chad "SRR\n"); 66661d8658bSDupuis, Chad rc = -ENOMEM; 66761d8658bSDupuis, Chad goto srr_err; 66861d8658bSDupuis, Chad } 66961d8658bSDupuis, Chad 67061d8658bSDupuis, Chad cb_arg->aborted_io_req = orig_io_req; 67161d8658bSDupuis, Chad 67261d8658bSDupuis, Chad srr.srr_op = ELS_SRR; 67361d8658bSDupuis, Chad srr.srr_ox_id = htons(orig_io_req->xid); 67461d8658bSDupuis, Chad srr.srr_rx_id = htons(orig_io_req->rx_id); 67561d8658bSDupuis, Chad srr.srr_rel_off = htonl(offset); 67661d8658bSDupuis, Chad srr.srr_r_ctl = r_ctl; 67761d8658bSDupuis, Chad 67861d8658bSDupuis, Chad rc = qedf_initiate_els(fcport, ELS_SRR, &srr, sizeof(srr), 67961d8658bSDupuis, Chad qedf_srr_compl, cb_arg, r_a_tov); 68061d8658bSDupuis, Chad 68161d8658bSDupuis, Chad srr_err: 68261d8658bSDupuis, Chad if (rc) { 68361d8658bSDupuis, Chad QEDF_ERR(&(qedf->dbg_ctx), "SRR failed - release orig_io_req" 68461d8658bSDupuis, Chad "=0x%x\n", orig_io_req->xid); 68561d8658bSDupuis, Chad kfree(cb_arg); 68661d8658bSDupuis, Chad /* If we fail to queue SRR, send ABTS to orig_io */ 68761d8658bSDupuis, Chad qedf_initiate_abts(orig_io_req, true); 68861d8658bSDupuis, Chad kref_put(&orig_io_req->refcount, qedf_release_cmd); 68961d8658bSDupuis, Chad } else 69061d8658bSDupuis, Chad /* Tell other threads that SRR is in progress */ 69161d8658bSDupuis, Chad set_bit(QEDF_CMD_SRR_SENT, &orig_io_req->flags); 69261d8658bSDupuis, Chad 69361d8658bSDupuis, Chad return rc; 69461d8658bSDupuis, Chad } 69561d8658bSDupuis, Chad 69661d8658bSDupuis, Chad static void qedf_initiate_seq_cleanup(struct qedf_ioreq *orig_io_req, 69761d8658bSDupuis, Chad u32 offset, u8 r_ctl) 69861d8658bSDupuis, Chad { 69961d8658bSDupuis, Chad struct qedf_rport *fcport; 70061d8658bSDupuis, Chad unsigned long flags; 70161d8658bSDupuis, Chad struct qedf_els_cb_arg *cb_arg; 702be086e7cSMintz, Yuval struct fcoe_wqe *sqe; 703be086e7cSMintz, Yuval u16 sqe_idx; 70461d8658bSDupuis, Chad 70561d8658bSDupuis, Chad fcport = orig_io_req->fcport; 70661d8658bSDupuis, Chad 70761d8658bSDupuis, Chad QEDF_INFO(&(fcport->qedf->dbg_ctx), QEDF_LOG_ELS, 70861d8658bSDupuis, Chad "Doing sequence cleanup for xid=0x%x offset=%u.\n", 70961d8658bSDupuis, Chad orig_io_req->xid, offset); 71061d8658bSDupuis, Chad 71161d8658bSDupuis, Chad cb_arg = kzalloc(sizeof(struct qedf_els_cb_arg), GFP_NOIO); 71261d8658bSDupuis, Chad if (!cb_arg) { 71361d8658bSDupuis, Chad QEDF_ERR(&(fcport->qedf->dbg_ctx), "Unable to allocate cb_arg " 71461d8658bSDupuis, Chad "for sequence cleanup\n"); 71561d8658bSDupuis, Chad return; 71661d8658bSDupuis, Chad } 71761d8658bSDupuis, Chad 71861d8658bSDupuis, Chad /* Get reference for cleanup request */ 71961d8658bSDupuis, Chad kref_get(&orig_io_req->refcount); 72061d8658bSDupuis, Chad 72161d8658bSDupuis, Chad orig_io_req->cmd_type = QEDF_SEQ_CLEANUP; 72261d8658bSDupuis, Chad cb_arg->offset = offset; 72361d8658bSDupuis, Chad cb_arg->r_ctl = r_ctl; 72461d8658bSDupuis, Chad orig_io_req->cb_arg = cb_arg; 72561d8658bSDupuis, Chad 72661d8658bSDupuis, Chad qedf_cmd_timer_set(fcport->qedf, orig_io_req, 72761d8658bSDupuis, Chad QEDF_CLEANUP_TIMEOUT * HZ); 72861d8658bSDupuis, Chad 72961d8658bSDupuis, Chad spin_lock_irqsave(&fcport->rport_lock, flags); 73061d8658bSDupuis, Chad 731be086e7cSMintz, Yuval sqe_idx = qedf_get_sqe_idx(fcport); 732be086e7cSMintz, Yuval sqe = &fcport->sq[sqe_idx]; 733be086e7cSMintz, Yuval memset(sqe, 0, sizeof(struct fcoe_wqe)); 734be086e7cSMintz, Yuval orig_io_req->task_params->sqe = sqe; 735be086e7cSMintz, Yuval 736be086e7cSMintz, Yuval init_initiator_sequence_recovery_fcoe_task(orig_io_req->task_params, 737be086e7cSMintz, Yuval offset); 73861d8658bSDupuis, Chad qedf_ring_doorbell(fcport); 73961d8658bSDupuis, Chad 74061d8658bSDupuis, Chad spin_unlock_irqrestore(&fcport->rport_lock, flags); 74161d8658bSDupuis, Chad } 74261d8658bSDupuis, Chad 74361d8658bSDupuis, Chad void qedf_process_seq_cleanup_compl(struct qedf_ctx *qedf, 74461d8658bSDupuis, Chad struct fcoe_cqe *cqe, struct qedf_ioreq *io_req) 74561d8658bSDupuis, Chad { 74661d8658bSDupuis, Chad int rc; 74761d8658bSDupuis, Chad struct qedf_els_cb_arg *cb_arg; 74861d8658bSDupuis, Chad 74961d8658bSDupuis, Chad cb_arg = io_req->cb_arg; 75061d8658bSDupuis, Chad 75161d8658bSDupuis, Chad /* If we timed out just free resources */ 752e82e6ff7SSaurav Kashyap if (io_req->event == QEDF_IOREQ_EV_ELS_TMO || !cqe) { 753e82e6ff7SSaurav Kashyap QEDF_ERR(&qedf->dbg_ctx, 754e82e6ff7SSaurav Kashyap "cqe is NULL or timeout event (0x%x)", io_req->event); 75561d8658bSDupuis, Chad goto free; 756e82e6ff7SSaurav Kashyap } 75761d8658bSDupuis, Chad 75861d8658bSDupuis, Chad /* Kill the timer we put on the request */ 75961d8658bSDupuis, Chad cancel_delayed_work_sync(&io_req->timeout_work); 76061d8658bSDupuis, Chad 76161d8658bSDupuis, Chad rc = qedf_send_srr(io_req, cb_arg->offset, cb_arg->r_ctl); 76261d8658bSDupuis, Chad if (rc) 76361d8658bSDupuis, Chad QEDF_ERR(&(qedf->dbg_ctx), "Unable to send SRR, I/O will " 76461d8658bSDupuis, Chad "abort, xid=0x%x.\n", io_req->xid); 76561d8658bSDupuis, Chad free: 76661d8658bSDupuis, Chad kfree(cb_arg); 76761d8658bSDupuis, Chad kref_put(&io_req->refcount, qedf_release_cmd); 76861d8658bSDupuis, Chad } 76961d8658bSDupuis, Chad 77061d8658bSDupuis, Chad static bool qedf_requeue_io_req(struct qedf_ioreq *orig_io_req) 77161d8658bSDupuis, Chad { 77261d8658bSDupuis, Chad struct qedf_rport *fcport; 77361d8658bSDupuis, Chad struct qedf_ioreq *new_io_req; 77461d8658bSDupuis, Chad unsigned long flags; 77561d8658bSDupuis, Chad bool rc = false; 77661d8658bSDupuis, Chad 77761d8658bSDupuis, Chad fcport = orig_io_req->fcport; 77861d8658bSDupuis, Chad if (!fcport) { 77961d8658bSDupuis, Chad QEDF_ERR(NULL, "fcport is NULL.\n"); 78061d8658bSDupuis, Chad goto out; 78161d8658bSDupuis, Chad } 78261d8658bSDupuis, Chad 78361d8658bSDupuis, Chad if (!orig_io_req->sc_cmd) { 78461d8658bSDupuis, Chad QEDF_ERR(&(fcport->qedf->dbg_ctx), "sc_cmd is NULL for " 78561d8658bSDupuis, Chad "xid=0x%x.\n", orig_io_req->xid); 78661d8658bSDupuis, Chad goto out; 78761d8658bSDupuis, Chad } 78861d8658bSDupuis, Chad 78961d8658bSDupuis, Chad new_io_req = qedf_alloc_cmd(fcport, QEDF_SCSI_CMD); 79061d8658bSDupuis, Chad if (!new_io_req) { 79161d8658bSDupuis, Chad QEDF_ERR(&(fcport->qedf->dbg_ctx), "Could not allocate new " 79261d8658bSDupuis, Chad "io_req.\n"); 79361d8658bSDupuis, Chad goto out; 79461d8658bSDupuis, Chad } 79561d8658bSDupuis, Chad 79661d8658bSDupuis, Chad new_io_req->sc_cmd = orig_io_req->sc_cmd; 79761d8658bSDupuis, Chad 79861d8658bSDupuis, Chad /* 79961d8658bSDupuis, Chad * This keeps the sc_cmd struct from being returned to the tape 80061d8658bSDupuis, Chad * driver and being requeued twice. We do need to put a reference 80161d8658bSDupuis, Chad * for the original I/O request since we will not do a SCSI completion 80261d8658bSDupuis, Chad * for it. 80361d8658bSDupuis, Chad */ 80461d8658bSDupuis, Chad orig_io_req->sc_cmd = NULL; 80561d8658bSDupuis, Chad kref_put(&orig_io_req->refcount, qedf_release_cmd); 80661d8658bSDupuis, Chad 80761d8658bSDupuis, Chad spin_lock_irqsave(&fcport->rport_lock, flags); 80861d8658bSDupuis, Chad 80961d8658bSDupuis, Chad /* kref for new command released in qedf_post_io_req on error */ 81061d8658bSDupuis, Chad if (qedf_post_io_req(fcport, new_io_req)) { 81161d8658bSDupuis, Chad QEDF_ERR(&(fcport->qedf->dbg_ctx), "Unable to post io_req\n"); 81261d8658bSDupuis, Chad /* Return SQE to pool */ 81361d8658bSDupuis, Chad atomic_inc(&fcport->free_sqes); 81461d8658bSDupuis, Chad } else { 81561d8658bSDupuis, Chad QEDF_INFO(&(fcport->qedf->dbg_ctx), QEDF_LOG_ELS, 81661d8658bSDupuis, Chad "Reissued SCSI command from orig_xid=0x%x on " 81761d8658bSDupuis, Chad "new_xid=0x%x.\n", orig_io_req->xid, new_io_req->xid); 81861d8658bSDupuis, Chad /* 81961d8658bSDupuis, Chad * Abort the original I/O but do not return SCSI command as 82061d8658bSDupuis, Chad * it has been reissued on another OX_ID. 82161d8658bSDupuis, Chad */ 82261d8658bSDupuis, Chad spin_unlock_irqrestore(&fcport->rport_lock, flags); 82361d8658bSDupuis, Chad qedf_initiate_abts(orig_io_req, false); 82461d8658bSDupuis, Chad goto out; 82561d8658bSDupuis, Chad } 82661d8658bSDupuis, Chad 82761d8658bSDupuis, Chad spin_unlock_irqrestore(&fcport->rport_lock, flags); 82861d8658bSDupuis, Chad out: 82961d8658bSDupuis, Chad return rc; 83061d8658bSDupuis, Chad } 83161d8658bSDupuis, Chad 83261d8658bSDupuis, Chad 83361d8658bSDupuis, Chad static void qedf_rec_compl(struct qedf_els_cb_arg *cb_arg) 83461d8658bSDupuis, Chad { 83561d8658bSDupuis, Chad struct qedf_ioreq *orig_io_req; 83661d8658bSDupuis, Chad struct qedf_ioreq *rec_req; 83761d8658bSDupuis, Chad struct qedf_mp_req *mp_req; 83861d8658bSDupuis, Chad struct fc_frame_header *mp_fc_hdr, *fh; 83961d8658bSDupuis, Chad struct fc_frame *fp; 84061d8658bSDupuis, Chad void *resp_buf, *fc_payload; 84161d8658bSDupuis, Chad u32 resp_len; 84261d8658bSDupuis, Chad struct fc_lport *lport; 84361d8658bSDupuis, Chad struct qedf_ctx *qedf; 84461d8658bSDupuis, Chad int refcount; 84561d8658bSDupuis, Chad enum fc_rctl r_ctl; 84661d8658bSDupuis, Chad struct fc_els_ls_rjt *rjt; 84761d8658bSDupuis, Chad struct fc_els_rec_acc *acc; 84861d8658bSDupuis, Chad u8 opcode; 84961d8658bSDupuis, Chad u32 offset, e_stat; 85061d8658bSDupuis, Chad struct scsi_cmnd *sc_cmd; 85161d8658bSDupuis, Chad bool srr_needed = false; 85261d8658bSDupuis, Chad 85361d8658bSDupuis, Chad rec_req = cb_arg->io_req; 85461d8658bSDupuis, Chad qedf = rec_req->fcport->qedf; 85561d8658bSDupuis, Chad lport = qedf->lport; 85661d8658bSDupuis, Chad 85761d8658bSDupuis, Chad orig_io_req = cb_arg->aborted_io_req; 85861d8658bSDupuis, Chad 859e82e6ff7SSaurav Kashyap if (!orig_io_req) { 860e82e6ff7SSaurav Kashyap QEDF_ERR(NULL, "orig_io_req is NULL.\n"); 86161d8658bSDupuis, Chad goto out_free; 862e82e6ff7SSaurav Kashyap } 86361d8658bSDupuis, Chad 86461d8658bSDupuis, Chad if (rec_req->event != QEDF_IOREQ_EV_ELS_TMO && 86561d8658bSDupuis, Chad rec_req->event != QEDF_IOREQ_EV_ELS_ERR_DETECT) 86661d8658bSDupuis, Chad cancel_delayed_work_sync(&orig_io_req->timeout_work); 86761d8658bSDupuis, Chad 8681afca6b5SDupuis, Chad refcount = kref_read(&orig_io_req->refcount); 86961d8658bSDupuis, Chad QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_ELS, "Entered: orig_io=%p," 87061d8658bSDupuis, Chad " orig_io_xid=0x%x, rec_xid=0x%x, refcount=%d\n", 87161d8658bSDupuis, Chad orig_io_req, orig_io_req->xid, rec_req->xid, refcount); 87261d8658bSDupuis, Chad 87361d8658bSDupuis, Chad /* If a REC times out, free resources */ 874e82e6ff7SSaurav Kashyap if (rec_req->event == QEDF_IOREQ_EV_ELS_TMO) { 875e82e6ff7SSaurav Kashyap QEDF_ERR(&qedf->dbg_ctx, 876e82e6ff7SSaurav Kashyap "Got TMO event, orig_io_req %p orig_io_xid=0x%x.\n", 877e82e6ff7SSaurav Kashyap orig_io_req, orig_io_req->xid); 87847c4ccd3SChristophe JAILLET goto out_put; 879e82e6ff7SSaurav Kashyap } 88061d8658bSDupuis, Chad 88161d8658bSDupuis, Chad /* Normalize response data into struct fc_frame */ 88261d8658bSDupuis, Chad mp_req = &(rec_req->mp_req); 88361d8658bSDupuis, Chad mp_fc_hdr = &(mp_req->resp_fc_hdr); 88461d8658bSDupuis, Chad resp_len = mp_req->resp_len; 88561d8658bSDupuis, Chad acc = resp_buf = mp_req->resp_buf; 88661d8658bSDupuis, Chad 88761d8658bSDupuis, Chad fp = fc_frame_alloc(lport, resp_len); 88861d8658bSDupuis, Chad if (!fp) { 88961d8658bSDupuis, Chad QEDF_ERR(&(qedf->dbg_ctx), 89061d8658bSDupuis, Chad "fc_frame_alloc failure.\n"); 89147c4ccd3SChristophe JAILLET goto out_put; 89261d8658bSDupuis, Chad } 89361d8658bSDupuis, Chad 89461d8658bSDupuis, Chad /* Copy frame header from firmware into fp */ 89561d8658bSDupuis, Chad fh = (struct fc_frame_header *)fc_frame_header_get(fp); 89661d8658bSDupuis, Chad memcpy(fh, mp_fc_hdr, sizeof(struct fc_frame_header)); 89761d8658bSDupuis, Chad 89861d8658bSDupuis, Chad /* Copy payload from firmware into fp */ 89961d8658bSDupuis, Chad fc_payload = fc_frame_payload_get(fp, resp_len); 90061d8658bSDupuis, Chad memcpy(fc_payload, resp_buf, resp_len); 90161d8658bSDupuis, Chad 90261d8658bSDupuis, Chad opcode = fc_frame_payload_op(fp); 90361d8658bSDupuis, Chad if (opcode == ELS_LS_RJT) { 90461d8658bSDupuis, Chad rjt = fc_frame_payload_get(fp, sizeof(*rjt)); 9057fb8ff08SSaurav Kashyap if (!rjt) { 9067fb8ff08SSaurav Kashyap QEDF_ERR(&qedf->dbg_ctx, "payload get failed"); 9077fb8ff08SSaurav Kashyap goto out_free_frame; 9087fb8ff08SSaurav Kashyap } 9097fb8ff08SSaurav Kashyap 91061d8658bSDupuis, Chad QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_ELS, 91161d8658bSDupuis, Chad "Received LS_RJT for REC: er_reason=0x%x, " 91261d8658bSDupuis, Chad "er_explan=0x%x.\n", rjt->er_reason, rjt->er_explan); 91361d8658bSDupuis, Chad /* 91461d8658bSDupuis, Chad * The following response(s) mean that we need to reissue the 91561d8658bSDupuis, Chad * request on another exchange. We need to do this without 91661d8658bSDupuis, Chad * informing the upper layers lest it cause an application 91761d8658bSDupuis, Chad * error. 91861d8658bSDupuis, Chad */ 91961d8658bSDupuis, Chad if ((rjt->er_reason == ELS_RJT_LOGIC || 92061d8658bSDupuis, Chad rjt->er_reason == ELS_RJT_UNAB) && 92161d8658bSDupuis, Chad rjt->er_explan == ELS_EXPL_OXID_RXID) { 92261d8658bSDupuis, Chad QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_ELS, 92361d8658bSDupuis, Chad "Handle CMD LOST case.\n"); 92461d8658bSDupuis, Chad qedf_requeue_io_req(orig_io_req); 92561d8658bSDupuis, Chad } 92661d8658bSDupuis, Chad } else if (opcode == ELS_LS_ACC) { 92761d8658bSDupuis, Chad offset = ntohl(acc->reca_fc4value); 92861d8658bSDupuis, Chad e_stat = ntohl(acc->reca_e_stat); 92961d8658bSDupuis, Chad QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_ELS, 93061d8658bSDupuis, Chad "Received LS_ACC for REC: offset=0x%x, e_stat=0x%x.\n", 93161d8658bSDupuis, Chad offset, e_stat); 93261d8658bSDupuis, Chad if (e_stat & ESB_ST_SEQ_INIT) { 93361d8658bSDupuis, Chad QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_ELS, 93461d8658bSDupuis, Chad "Target has the seq init\n"); 93561d8658bSDupuis, Chad goto out_free_frame; 93661d8658bSDupuis, Chad } 93761d8658bSDupuis, Chad sc_cmd = orig_io_req->sc_cmd; 93861d8658bSDupuis, Chad if (!sc_cmd) { 93961d8658bSDupuis, Chad QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_ELS, 94061d8658bSDupuis, Chad "sc_cmd is NULL for xid=0x%x.\n", 94161d8658bSDupuis, Chad orig_io_req->xid); 94261d8658bSDupuis, Chad goto out_free_frame; 94361d8658bSDupuis, Chad } 94461d8658bSDupuis, Chad /* SCSI write case */ 94561d8658bSDupuis, Chad if (sc_cmd->sc_data_direction == DMA_TO_DEVICE) { 94661d8658bSDupuis, Chad if (offset == orig_io_req->data_xfer_len) { 94761d8658bSDupuis, Chad QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_ELS, 94861d8658bSDupuis, Chad "WRITE - response lost.\n"); 94961d8658bSDupuis, Chad r_ctl = FC_RCTL_DD_CMD_STATUS; 95061d8658bSDupuis, Chad srr_needed = true; 95161d8658bSDupuis, Chad offset = 0; 95261d8658bSDupuis, Chad } else { 95361d8658bSDupuis, Chad QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_ELS, 95461d8658bSDupuis, Chad "WRITE - XFER_RDY/DATA lost.\n"); 95561d8658bSDupuis, Chad r_ctl = FC_RCTL_DD_DATA_DESC; 95661d8658bSDupuis, Chad /* Use data from warning CQE instead of REC */ 95761d8658bSDupuis, Chad offset = orig_io_req->tx_buf_off; 95861d8658bSDupuis, Chad } 95961d8658bSDupuis, Chad /* SCSI read case */ 96061d8658bSDupuis, Chad } else { 96161d8658bSDupuis, Chad if (orig_io_req->rx_buf_off == 96261d8658bSDupuis, Chad orig_io_req->data_xfer_len) { 96361d8658bSDupuis, Chad QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_ELS, 96461d8658bSDupuis, Chad "READ - response lost.\n"); 96561d8658bSDupuis, Chad srr_needed = true; 96661d8658bSDupuis, Chad r_ctl = FC_RCTL_DD_CMD_STATUS; 96761d8658bSDupuis, Chad offset = 0; 96861d8658bSDupuis, Chad } else { 96961d8658bSDupuis, Chad QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_ELS, 97061d8658bSDupuis, Chad "READ - DATA lost.\n"); 97161d8658bSDupuis, Chad /* 97261d8658bSDupuis, Chad * For read case we always set the offset to 0 97361d8658bSDupuis, Chad * for sequence recovery task. 97461d8658bSDupuis, Chad */ 97561d8658bSDupuis, Chad offset = 0; 97661d8658bSDupuis, Chad r_ctl = FC_RCTL_DD_SOL_DATA; 97761d8658bSDupuis, Chad } 97861d8658bSDupuis, Chad } 97961d8658bSDupuis, Chad 98061d8658bSDupuis, Chad if (srr_needed) 98161d8658bSDupuis, Chad qedf_send_srr(orig_io_req, offset, r_ctl); 98261d8658bSDupuis, Chad else 98361d8658bSDupuis, Chad qedf_initiate_seq_cleanup(orig_io_req, offset, r_ctl); 98461d8658bSDupuis, Chad } 98561d8658bSDupuis, Chad 98661d8658bSDupuis, Chad out_free_frame: 98761d8658bSDupuis, Chad fc_frame_free(fp); 98847c4ccd3SChristophe JAILLET out_put: 98961d8658bSDupuis, Chad /* Put reference for original command since REC completed */ 99061d8658bSDupuis, Chad kref_put(&orig_io_req->refcount, qedf_release_cmd); 99147c4ccd3SChristophe JAILLET out_free: 99261d8658bSDupuis, Chad kfree(cb_arg); 99361d8658bSDupuis, Chad } 99461d8658bSDupuis, Chad 99561d8658bSDupuis, Chad /* Assumes kref is already held by caller */ 99661d8658bSDupuis, Chad int qedf_send_rec(struct qedf_ioreq *orig_io_req) 99761d8658bSDupuis, Chad { 99861d8658bSDupuis, Chad 99961d8658bSDupuis, Chad struct fc_els_rec rec; 100061d8658bSDupuis, Chad struct qedf_rport *fcport; 100161d8658bSDupuis, Chad struct fc_lport *lport; 100261d8658bSDupuis, Chad struct qedf_els_cb_arg *cb_arg = NULL; 100361d8658bSDupuis, Chad struct qedf_ctx *qedf; 100461d8658bSDupuis, Chad uint32_t sid; 100561d8658bSDupuis, Chad uint32_t r_a_tov; 100661d8658bSDupuis, Chad int rc; 100761d8658bSDupuis, Chad 100861d8658bSDupuis, Chad if (!orig_io_req) { 100961d8658bSDupuis, Chad QEDF_ERR(NULL, "orig_io_req is NULL.\n"); 101061d8658bSDupuis, Chad return -EINVAL; 101161d8658bSDupuis, Chad } 101261d8658bSDupuis, Chad 101361d8658bSDupuis, Chad fcport = orig_io_req->fcport; 101461d8658bSDupuis, Chad 101561d8658bSDupuis, Chad /* Check that fcport is still offloaded */ 101661d8658bSDupuis, Chad if (!test_bit(QEDF_RPORT_SESSION_READY, &fcport->flags)) { 101761d8658bSDupuis, Chad QEDF_ERR(NULL, "fcport is no longer offloaded.\n"); 101861d8658bSDupuis, Chad return -EINVAL; 101961d8658bSDupuis, Chad } 102061d8658bSDupuis, Chad 102161d8658bSDupuis, Chad if (!fcport->qedf) { 102261d8658bSDupuis, Chad QEDF_ERR(NULL, "fcport->qedf is NULL.\n"); 102361d8658bSDupuis, Chad return -EINVAL; 102461d8658bSDupuis, Chad } 102561d8658bSDupuis, Chad 102661d8658bSDupuis, Chad /* Take reference until REC command completion */ 102761d8658bSDupuis, Chad kref_get(&orig_io_req->refcount); 102861d8658bSDupuis, Chad 102961d8658bSDupuis, Chad qedf = fcport->qedf; 103061d8658bSDupuis, Chad lport = qedf->lport; 103161d8658bSDupuis, Chad sid = fcport->sid; 103261d8658bSDupuis, Chad r_a_tov = lport->r_a_tov; 103361d8658bSDupuis, Chad 103461d8658bSDupuis, Chad memset(&rec, 0, sizeof(rec)); 103561d8658bSDupuis, Chad 103661d8658bSDupuis, Chad cb_arg = kzalloc(sizeof(struct qedf_els_cb_arg), GFP_NOIO); 103761d8658bSDupuis, Chad if (!cb_arg) { 103861d8658bSDupuis, Chad QEDF_ERR(&(qedf->dbg_ctx), "Unable to allocate cb_arg for " 103961d8658bSDupuis, Chad "REC\n"); 104061d8658bSDupuis, Chad rc = -ENOMEM; 104161d8658bSDupuis, Chad goto rec_err; 104261d8658bSDupuis, Chad } 104361d8658bSDupuis, Chad 104461d8658bSDupuis, Chad cb_arg->aborted_io_req = orig_io_req; 104561d8658bSDupuis, Chad 104661d8658bSDupuis, Chad rec.rec_cmd = ELS_REC; 104761d8658bSDupuis, Chad hton24(rec.rec_s_id, sid); 104861d8658bSDupuis, Chad rec.rec_ox_id = htons(orig_io_req->xid); 104961d8658bSDupuis, Chad rec.rec_rx_id = 105061d8658bSDupuis, Chad htons(orig_io_req->task->tstorm_st_context.read_write.rx_id); 105161d8658bSDupuis, Chad 105261d8658bSDupuis, Chad QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_ELS, "Sending REC orig_io=%p, " 105361d8658bSDupuis, Chad "orig_xid=0x%x rx_id=0x%x\n", orig_io_req, 105461d8658bSDupuis, Chad orig_io_req->xid, rec.rec_rx_id); 105561d8658bSDupuis, Chad rc = qedf_initiate_els(fcport, ELS_REC, &rec, sizeof(rec), 105661d8658bSDupuis, Chad qedf_rec_compl, cb_arg, r_a_tov); 105761d8658bSDupuis, Chad 105861d8658bSDupuis, Chad rec_err: 105961d8658bSDupuis, Chad if (rc) { 106061d8658bSDupuis, Chad QEDF_ERR(&(qedf->dbg_ctx), "REC failed - release orig_io_req" 106161d8658bSDupuis, Chad "=0x%x\n", orig_io_req->xid); 106261d8658bSDupuis, Chad kfree(cb_arg); 106361d8658bSDupuis, Chad kref_put(&orig_io_req->refcount, qedf_release_cmd); 106461d8658bSDupuis, Chad } 106561d8658bSDupuis, Chad return rc; 106661d8658bSDupuis, Chad } 1067