162306a36Sopenharmony_ci/* 262306a36Sopenharmony_ci * bnx2fc_els.c: QLogic Linux FCoE offload driver. 362306a36Sopenharmony_ci * This file contains helper routines that handle ELS requests 462306a36Sopenharmony_ci * and responses. 562306a36Sopenharmony_ci * 662306a36Sopenharmony_ci * Copyright (c) 2008-2013 Broadcom Corporation 762306a36Sopenharmony_ci * Copyright (c) 2014-2016 QLogic Corporation 862306a36Sopenharmony_ci * Copyright (c) 2016-2017 Cavium Inc. 962306a36Sopenharmony_ci * 1062306a36Sopenharmony_ci * This program is free software; you can redistribute it and/or modify 1162306a36Sopenharmony_ci * it under the terms of the GNU General Public License as published by 1262306a36Sopenharmony_ci * the Free Software Foundation. 1362306a36Sopenharmony_ci * 1462306a36Sopenharmony_ci * Written by: Bhanu Prakash Gollapudi (bprakash@broadcom.com) 1562306a36Sopenharmony_ci */ 1662306a36Sopenharmony_ci 1762306a36Sopenharmony_ci#include "bnx2fc.h" 1862306a36Sopenharmony_ci 1962306a36Sopenharmony_cistatic void bnx2fc_logo_resp(struct fc_seq *seq, struct fc_frame *fp, 2062306a36Sopenharmony_ci void *arg); 2162306a36Sopenharmony_cistatic void bnx2fc_flogi_resp(struct fc_seq *seq, struct fc_frame *fp, 2262306a36Sopenharmony_ci void *arg); 2362306a36Sopenharmony_cistatic int bnx2fc_initiate_els(struct bnx2fc_rport *tgt, unsigned int op, 2462306a36Sopenharmony_ci void *data, u32 data_len, 2562306a36Sopenharmony_ci void (*cb_func)(struct bnx2fc_els_cb_arg *cb_arg), 2662306a36Sopenharmony_ci struct bnx2fc_els_cb_arg *cb_arg, u32 timer_msec); 2762306a36Sopenharmony_ci 2862306a36Sopenharmony_cistatic void bnx2fc_rrq_compl(struct bnx2fc_els_cb_arg *cb_arg) 2962306a36Sopenharmony_ci{ 3062306a36Sopenharmony_ci struct bnx2fc_cmd *orig_io_req; 3162306a36Sopenharmony_ci struct bnx2fc_cmd *rrq_req; 3262306a36Sopenharmony_ci int rc = 0; 3362306a36Sopenharmony_ci 3462306a36Sopenharmony_ci BUG_ON(!cb_arg); 3562306a36Sopenharmony_ci rrq_req = cb_arg->io_req; 3662306a36Sopenharmony_ci orig_io_req = cb_arg->aborted_io_req; 3762306a36Sopenharmony_ci BUG_ON(!orig_io_req); 3862306a36Sopenharmony_ci BNX2FC_ELS_DBG("rrq_compl: orig xid = 0x%x, rrq_xid = 0x%x\n", 3962306a36Sopenharmony_ci orig_io_req->xid, rrq_req->xid); 4062306a36Sopenharmony_ci 4162306a36Sopenharmony_ci kref_put(&orig_io_req->refcount, bnx2fc_cmd_release); 4262306a36Sopenharmony_ci 4362306a36Sopenharmony_ci if (test_and_clear_bit(BNX2FC_FLAG_ELS_TIMEOUT, &rrq_req->req_flags)) { 4462306a36Sopenharmony_ci /* 4562306a36Sopenharmony_ci * els req is timed out. cleanup the IO with FW and 4662306a36Sopenharmony_ci * drop the completion. Remove from active_cmd_queue. 4762306a36Sopenharmony_ci */ 4862306a36Sopenharmony_ci BNX2FC_ELS_DBG("rrq xid - 0x%x timed out, clean it up\n", 4962306a36Sopenharmony_ci rrq_req->xid); 5062306a36Sopenharmony_ci 5162306a36Sopenharmony_ci if (rrq_req->on_active_queue) { 5262306a36Sopenharmony_ci list_del_init(&rrq_req->link); 5362306a36Sopenharmony_ci rrq_req->on_active_queue = 0; 5462306a36Sopenharmony_ci rc = bnx2fc_initiate_cleanup(rrq_req); 5562306a36Sopenharmony_ci BUG_ON(rc); 5662306a36Sopenharmony_ci } 5762306a36Sopenharmony_ci } 5862306a36Sopenharmony_ci kfree(cb_arg); 5962306a36Sopenharmony_ci} 6062306a36Sopenharmony_ciint bnx2fc_send_rrq(struct bnx2fc_cmd *aborted_io_req) 6162306a36Sopenharmony_ci{ 6262306a36Sopenharmony_ci 6362306a36Sopenharmony_ci struct fc_els_rrq rrq; 6462306a36Sopenharmony_ci struct bnx2fc_rport *tgt = aborted_io_req->tgt; 6562306a36Sopenharmony_ci struct fc_lport *lport = NULL; 6662306a36Sopenharmony_ci struct bnx2fc_els_cb_arg *cb_arg = NULL; 6762306a36Sopenharmony_ci u32 sid = 0; 6862306a36Sopenharmony_ci u32 r_a_tov = 0; 6962306a36Sopenharmony_ci unsigned long start = jiffies; 7062306a36Sopenharmony_ci int rc; 7162306a36Sopenharmony_ci 7262306a36Sopenharmony_ci if (!test_bit(BNX2FC_FLAG_SESSION_READY, &tgt->flags)) 7362306a36Sopenharmony_ci return -EINVAL; 7462306a36Sopenharmony_ci 7562306a36Sopenharmony_ci lport = tgt->rdata->local_port; 7662306a36Sopenharmony_ci sid = tgt->sid; 7762306a36Sopenharmony_ci r_a_tov = lport->r_a_tov; 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_ci BNX2FC_ELS_DBG("Sending RRQ orig_xid = 0x%x\n", 8062306a36Sopenharmony_ci aborted_io_req->xid); 8162306a36Sopenharmony_ci memset(&rrq, 0, sizeof(rrq)); 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_ci cb_arg = kzalloc(sizeof(struct bnx2fc_els_cb_arg), GFP_NOIO); 8462306a36Sopenharmony_ci if (!cb_arg) { 8562306a36Sopenharmony_ci printk(KERN_ERR PFX "Unable to allocate cb_arg for RRQ\n"); 8662306a36Sopenharmony_ci rc = -ENOMEM; 8762306a36Sopenharmony_ci goto rrq_err; 8862306a36Sopenharmony_ci } 8962306a36Sopenharmony_ci 9062306a36Sopenharmony_ci cb_arg->aborted_io_req = aborted_io_req; 9162306a36Sopenharmony_ci 9262306a36Sopenharmony_ci rrq.rrq_cmd = ELS_RRQ; 9362306a36Sopenharmony_ci hton24(rrq.rrq_s_id, sid); 9462306a36Sopenharmony_ci rrq.rrq_ox_id = htons(aborted_io_req->xid); 9562306a36Sopenharmony_ci rrq.rrq_rx_id = htons(aborted_io_req->task->rxwr_txrd.var_ctx.rx_id); 9662306a36Sopenharmony_ci 9762306a36Sopenharmony_ciretry_rrq: 9862306a36Sopenharmony_ci rc = bnx2fc_initiate_els(tgt, ELS_RRQ, &rrq, sizeof(rrq), 9962306a36Sopenharmony_ci bnx2fc_rrq_compl, cb_arg, 10062306a36Sopenharmony_ci r_a_tov); 10162306a36Sopenharmony_ci if (rc == -ENOMEM) { 10262306a36Sopenharmony_ci if (time_after(jiffies, start + (10 * HZ))) { 10362306a36Sopenharmony_ci BNX2FC_ELS_DBG("rrq Failed\n"); 10462306a36Sopenharmony_ci rc = FAILED; 10562306a36Sopenharmony_ci goto rrq_err; 10662306a36Sopenharmony_ci } 10762306a36Sopenharmony_ci msleep(20); 10862306a36Sopenharmony_ci goto retry_rrq; 10962306a36Sopenharmony_ci } 11062306a36Sopenharmony_cirrq_err: 11162306a36Sopenharmony_ci if (rc) { 11262306a36Sopenharmony_ci BNX2FC_ELS_DBG("RRQ failed - release orig io req 0x%x\n", 11362306a36Sopenharmony_ci aborted_io_req->xid); 11462306a36Sopenharmony_ci kfree(cb_arg); 11562306a36Sopenharmony_ci spin_lock_bh(&tgt->tgt_lock); 11662306a36Sopenharmony_ci kref_put(&aborted_io_req->refcount, bnx2fc_cmd_release); 11762306a36Sopenharmony_ci spin_unlock_bh(&tgt->tgt_lock); 11862306a36Sopenharmony_ci } 11962306a36Sopenharmony_ci return rc; 12062306a36Sopenharmony_ci} 12162306a36Sopenharmony_ci 12262306a36Sopenharmony_cistatic void bnx2fc_l2_els_compl(struct bnx2fc_els_cb_arg *cb_arg) 12362306a36Sopenharmony_ci{ 12462306a36Sopenharmony_ci struct bnx2fc_cmd *els_req; 12562306a36Sopenharmony_ci struct bnx2fc_rport *tgt; 12662306a36Sopenharmony_ci struct bnx2fc_mp_req *mp_req; 12762306a36Sopenharmony_ci struct fc_frame_header *fc_hdr; 12862306a36Sopenharmony_ci unsigned char *buf; 12962306a36Sopenharmony_ci void *resp_buf; 13062306a36Sopenharmony_ci u32 resp_len, hdr_len; 13162306a36Sopenharmony_ci u16 l2_oxid; 13262306a36Sopenharmony_ci int frame_len; 13362306a36Sopenharmony_ci int rc = 0; 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_ci l2_oxid = cb_arg->l2_oxid; 13662306a36Sopenharmony_ci BNX2FC_ELS_DBG("ELS COMPL - l2_oxid = 0x%x\n", l2_oxid); 13762306a36Sopenharmony_ci 13862306a36Sopenharmony_ci els_req = cb_arg->io_req; 13962306a36Sopenharmony_ci if (test_and_clear_bit(BNX2FC_FLAG_ELS_TIMEOUT, &els_req->req_flags)) { 14062306a36Sopenharmony_ci /* 14162306a36Sopenharmony_ci * els req is timed out. cleanup the IO with FW and 14262306a36Sopenharmony_ci * drop the completion. libfc will handle the els timeout 14362306a36Sopenharmony_ci */ 14462306a36Sopenharmony_ci if (els_req->on_active_queue) { 14562306a36Sopenharmony_ci list_del_init(&els_req->link); 14662306a36Sopenharmony_ci els_req->on_active_queue = 0; 14762306a36Sopenharmony_ci rc = bnx2fc_initiate_cleanup(els_req); 14862306a36Sopenharmony_ci BUG_ON(rc); 14962306a36Sopenharmony_ci } 15062306a36Sopenharmony_ci goto free_arg; 15162306a36Sopenharmony_ci } 15262306a36Sopenharmony_ci 15362306a36Sopenharmony_ci tgt = els_req->tgt; 15462306a36Sopenharmony_ci mp_req = &(els_req->mp_req); 15562306a36Sopenharmony_ci fc_hdr = &(mp_req->resp_fc_hdr); 15662306a36Sopenharmony_ci resp_len = mp_req->resp_len; 15762306a36Sopenharmony_ci resp_buf = mp_req->resp_buf; 15862306a36Sopenharmony_ci 15962306a36Sopenharmony_ci buf = kzalloc(PAGE_SIZE, GFP_ATOMIC); 16062306a36Sopenharmony_ci if (!buf) { 16162306a36Sopenharmony_ci printk(KERN_ERR PFX "Unable to alloc mp buf\n"); 16262306a36Sopenharmony_ci goto free_arg; 16362306a36Sopenharmony_ci } 16462306a36Sopenharmony_ci hdr_len = sizeof(*fc_hdr); 16562306a36Sopenharmony_ci if (hdr_len + resp_len > PAGE_SIZE) { 16662306a36Sopenharmony_ci printk(KERN_ERR PFX "l2_els_compl: resp len is " 16762306a36Sopenharmony_ci "beyond page size\n"); 16862306a36Sopenharmony_ci goto free_buf; 16962306a36Sopenharmony_ci } 17062306a36Sopenharmony_ci memcpy(buf, fc_hdr, hdr_len); 17162306a36Sopenharmony_ci memcpy(buf + hdr_len, resp_buf, resp_len); 17262306a36Sopenharmony_ci frame_len = hdr_len + resp_len; 17362306a36Sopenharmony_ci 17462306a36Sopenharmony_ci bnx2fc_process_l2_frame_compl(tgt, buf, frame_len, l2_oxid); 17562306a36Sopenharmony_ci 17662306a36Sopenharmony_cifree_buf: 17762306a36Sopenharmony_ci kfree(buf); 17862306a36Sopenharmony_cifree_arg: 17962306a36Sopenharmony_ci kfree(cb_arg); 18062306a36Sopenharmony_ci} 18162306a36Sopenharmony_ci 18262306a36Sopenharmony_ciint bnx2fc_send_adisc(struct bnx2fc_rport *tgt, struct fc_frame *fp) 18362306a36Sopenharmony_ci{ 18462306a36Sopenharmony_ci struct fc_els_adisc *adisc; 18562306a36Sopenharmony_ci struct fc_frame_header *fh; 18662306a36Sopenharmony_ci struct bnx2fc_els_cb_arg *cb_arg; 18762306a36Sopenharmony_ci struct fc_lport *lport = tgt->rdata->local_port; 18862306a36Sopenharmony_ci u32 r_a_tov = lport->r_a_tov; 18962306a36Sopenharmony_ci int rc; 19062306a36Sopenharmony_ci 19162306a36Sopenharmony_ci fh = fc_frame_header_get(fp); 19262306a36Sopenharmony_ci cb_arg = kzalloc(sizeof(struct bnx2fc_els_cb_arg), GFP_ATOMIC); 19362306a36Sopenharmony_ci if (!cb_arg) { 19462306a36Sopenharmony_ci printk(KERN_ERR PFX "Unable to allocate cb_arg for ADISC\n"); 19562306a36Sopenharmony_ci return -ENOMEM; 19662306a36Sopenharmony_ci } 19762306a36Sopenharmony_ci 19862306a36Sopenharmony_ci cb_arg->l2_oxid = ntohs(fh->fh_ox_id); 19962306a36Sopenharmony_ci 20062306a36Sopenharmony_ci BNX2FC_ELS_DBG("send ADISC: l2_oxid = 0x%x\n", cb_arg->l2_oxid); 20162306a36Sopenharmony_ci adisc = fc_frame_payload_get(fp, sizeof(*adisc)); 20262306a36Sopenharmony_ci /* adisc is initialized by libfc */ 20362306a36Sopenharmony_ci rc = bnx2fc_initiate_els(tgt, ELS_ADISC, adisc, sizeof(*adisc), 20462306a36Sopenharmony_ci bnx2fc_l2_els_compl, cb_arg, 2 * r_a_tov); 20562306a36Sopenharmony_ci if (rc) 20662306a36Sopenharmony_ci kfree(cb_arg); 20762306a36Sopenharmony_ci return rc; 20862306a36Sopenharmony_ci} 20962306a36Sopenharmony_ci 21062306a36Sopenharmony_ciint bnx2fc_send_logo(struct bnx2fc_rport *tgt, struct fc_frame *fp) 21162306a36Sopenharmony_ci{ 21262306a36Sopenharmony_ci struct fc_els_logo *logo; 21362306a36Sopenharmony_ci struct fc_frame_header *fh; 21462306a36Sopenharmony_ci struct bnx2fc_els_cb_arg *cb_arg; 21562306a36Sopenharmony_ci struct fc_lport *lport = tgt->rdata->local_port; 21662306a36Sopenharmony_ci u32 r_a_tov = lport->r_a_tov; 21762306a36Sopenharmony_ci int rc; 21862306a36Sopenharmony_ci 21962306a36Sopenharmony_ci fh = fc_frame_header_get(fp); 22062306a36Sopenharmony_ci cb_arg = kzalloc(sizeof(struct bnx2fc_els_cb_arg), GFP_ATOMIC); 22162306a36Sopenharmony_ci if (!cb_arg) { 22262306a36Sopenharmony_ci printk(KERN_ERR PFX "Unable to allocate cb_arg for LOGO\n"); 22362306a36Sopenharmony_ci return -ENOMEM; 22462306a36Sopenharmony_ci } 22562306a36Sopenharmony_ci 22662306a36Sopenharmony_ci cb_arg->l2_oxid = ntohs(fh->fh_ox_id); 22762306a36Sopenharmony_ci 22862306a36Sopenharmony_ci BNX2FC_ELS_DBG("Send LOGO: l2_oxid = 0x%x\n", cb_arg->l2_oxid); 22962306a36Sopenharmony_ci logo = fc_frame_payload_get(fp, sizeof(*logo)); 23062306a36Sopenharmony_ci /* logo is initialized by libfc */ 23162306a36Sopenharmony_ci rc = bnx2fc_initiate_els(tgt, ELS_LOGO, logo, sizeof(*logo), 23262306a36Sopenharmony_ci bnx2fc_l2_els_compl, cb_arg, 2 * r_a_tov); 23362306a36Sopenharmony_ci if (rc) 23462306a36Sopenharmony_ci kfree(cb_arg); 23562306a36Sopenharmony_ci return rc; 23662306a36Sopenharmony_ci} 23762306a36Sopenharmony_ci 23862306a36Sopenharmony_ciint bnx2fc_send_rls(struct bnx2fc_rport *tgt, struct fc_frame *fp) 23962306a36Sopenharmony_ci{ 24062306a36Sopenharmony_ci struct fc_els_rls *rls; 24162306a36Sopenharmony_ci struct fc_frame_header *fh; 24262306a36Sopenharmony_ci struct bnx2fc_els_cb_arg *cb_arg; 24362306a36Sopenharmony_ci struct fc_lport *lport = tgt->rdata->local_port; 24462306a36Sopenharmony_ci u32 r_a_tov = lport->r_a_tov; 24562306a36Sopenharmony_ci int rc; 24662306a36Sopenharmony_ci 24762306a36Sopenharmony_ci fh = fc_frame_header_get(fp); 24862306a36Sopenharmony_ci cb_arg = kzalloc(sizeof(struct bnx2fc_els_cb_arg), GFP_ATOMIC); 24962306a36Sopenharmony_ci if (!cb_arg) { 25062306a36Sopenharmony_ci printk(KERN_ERR PFX "Unable to allocate cb_arg for LOGO\n"); 25162306a36Sopenharmony_ci return -ENOMEM; 25262306a36Sopenharmony_ci } 25362306a36Sopenharmony_ci 25462306a36Sopenharmony_ci cb_arg->l2_oxid = ntohs(fh->fh_ox_id); 25562306a36Sopenharmony_ci 25662306a36Sopenharmony_ci rls = fc_frame_payload_get(fp, sizeof(*rls)); 25762306a36Sopenharmony_ci /* rls is initialized by libfc */ 25862306a36Sopenharmony_ci rc = bnx2fc_initiate_els(tgt, ELS_RLS, rls, sizeof(*rls), 25962306a36Sopenharmony_ci bnx2fc_l2_els_compl, cb_arg, 2 * r_a_tov); 26062306a36Sopenharmony_ci if (rc) 26162306a36Sopenharmony_ci kfree(cb_arg); 26262306a36Sopenharmony_ci return rc; 26362306a36Sopenharmony_ci} 26462306a36Sopenharmony_ci 26562306a36Sopenharmony_cistatic void bnx2fc_srr_compl(struct bnx2fc_els_cb_arg *cb_arg) 26662306a36Sopenharmony_ci{ 26762306a36Sopenharmony_ci struct bnx2fc_mp_req *mp_req; 26862306a36Sopenharmony_ci struct fc_frame_header *fc_hdr, *fh; 26962306a36Sopenharmony_ci struct bnx2fc_cmd *srr_req; 27062306a36Sopenharmony_ci struct bnx2fc_cmd *orig_io_req; 27162306a36Sopenharmony_ci struct fc_frame *fp; 27262306a36Sopenharmony_ci unsigned char *buf; 27362306a36Sopenharmony_ci void *resp_buf; 27462306a36Sopenharmony_ci u32 resp_len, hdr_len; 27562306a36Sopenharmony_ci u8 opcode; 27662306a36Sopenharmony_ci int rc = 0; 27762306a36Sopenharmony_ci 27862306a36Sopenharmony_ci orig_io_req = cb_arg->aborted_io_req; 27962306a36Sopenharmony_ci srr_req = cb_arg->io_req; 28062306a36Sopenharmony_ci if (test_and_clear_bit(BNX2FC_FLAG_ELS_TIMEOUT, &srr_req->req_flags)) { 28162306a36Sopenharmony_ci /* SRR timedout */ 28262306a36Sopenharmony_ci BNX2FC_IO_DBG(srr_req, "srr timed out, abort " 28362306a36Sopenharmony_ci "orig_io - 0x%x\n", 28462306a36Sopenharmony_ci orig_io_req->xid); 28562306a36Sopenharmony_ci rc = bnx2fc_initiate_abts(srr_req); 28662306a36Sopenharmony_ci if (rc != SUCCESS) { 28762306a36Sopenharmony_ci BNX2FC_IO_DBG(srr_req, "srr_compl: initiate_abts " 28862306a36Sopenharmony_ci "failed. issue cleanup\n"); 28962306a36Sopenharmony_ci bnx2fc_initiate_cleanup(srr_req); 29062306a36Sopenharmony_ci } 29162306a36Sopenharmony_ci if (test_bit(BNX2FC_FLAG_IO_COMPL, &orig_io_req->req_flags) || 29262306a36Sopenharmony_ci test_bit(BNX2FC_FLAG_ISSUE_ABTS, &orig_io_req->req_flags)) { 29362306a36Sopenharmony_ci BNX2FC_IO_DBG(srr_req, "srr_compl:xid 0x%x flags = %lx", 29462306a36Sopenharmony_ci orig_io_req->xid, orig_io_req->req_flags); 29562306a36Sopenharmony_ci goto srr_compl_done; 29662306a36Sopenharmony_ci } 29762306a36Sopenharmony_ci orig_io_req->srr_retry++; 29862306a36Sopenharmony_ci if (orig_io_req->srr_retry <= SRR_RETRY_COUNT) { 29962306a36Sopenharmony_ci struct bnx2fc_rport *tgt = orig_io_req->tgt; 30062306a36Sopenharmony_ci spin_unlock_bh(&tgt->tgt_lock); 30162306a36Sopenharmony_ci rc = bnx2fc_send_srr(orig_io_req, 30262306a36Sopenharmony_ci orig_io_req->srr_offset, 30362306a36Sopenharmony_ci orig_io_req->srr_rctl); 30462306a36Sopenharmony_ci spin_lock_bh(&tgt->tgt_lock); 30562306a36Sopenharmony_ci if (!rc) 30662306a36Sopenharmony_ci goto srr_compl_done; 30762306a36Sopenharmony_ci } 30862306a36Sopenharmony_ci 30962306a36Sopenharmony_ci rc = bnx2fc_initiate_abts(orig_io_req); 31062306a36Sopenharmony_ci if (rc != SUCCESS) { 31162306a36Sopenharmony_ci BNX2FC_IO_DBG(srr_req, "srr_compl: initiate_abts " 31262306a36Sopenharmony_ci "failed xid = 0x%x. issue cleanup\n", 31362306a36Sopenharmony_ci orig_io_req->xid); 31462306a36Sopenharmony_ci bnx2fc_initiate_cleanup(orig_io_req); 31562306a36Sopenharmony_ci } 31662306a36Sopenharmony_ci goto srr_compl_done; 31762306a36Sopenharmony_ci } 31862306a36Sopenharmony_ci if (test_bit(BNX2FC_FLAG_IO_COMPL, &orig_io_req->req_flags) || 31962306a36Sopenharmony_ci test_bit(BNX2FC_FLAG_ISSUE_ABTS, &orig_io_req->req_flags)) { 32062306a36Sopenharmony_ci BNX2FC_IO_DBG(srr_req, "srr_compl:xid - 0x%x flags = %lx", 32162306a36Sopenharmony_ci orig_io_req->xid, orig_io_req->req_flags); 32262306a36Sopenharmony_ci goto srr_compl_done; 32362306a36Sopenharmony_ci } 32462306a36Sopenharmony_ci mp_req = &(srr_req->mp_req); 32562306a36Sopenharmony_ci fc_hdr = &(mp_req->resp_fc_hdr); 32662306a36Sopenharmony_ci resp_len = mp_req->resp_len; 32762306a36Sopenharmony_ci resp_buf = mp_req->resp_buf; 32862306a36Sopenharmony_ci 32962306a36Sopenharmony_ci hdr_len = sizeof(*fc_hdr); 33062306a36Sopenharmony_ci buf = kzalloc(PAGE_SIZE, GFP_ATOMIC); 33162306a36Sopenharmony_ci if (!buf) { 33262306a36Sopenharmony_ci printk(KERN_ERR PFX "srr buf: mem alloc failure\n"); 33362306a36Sopenharmony_ci goto srr_compl_done; 33462306a36Sopenharmony_ci } 33562306a36Sopenharmony_ci memcpy(buf, fc_hdr, hdr_len); 33662306a36Sopenharmony_ci memcpy(buf + hdr_len, resp_buf, resp_len); 33762306a36Sopenharmony_ci 33862306a36Sopenharmony_ci fp = fc_frame_alloc(NULL, resp_len); 33962306a36Sopenharmony_ci if (!fp) { 34062306a36Sopenharmony_ci printk(KERN_ERR PFX "fc_frame_alloc failure\n"); 34162306a36Sopenharmony_ci goto free_buf; 34262306a36Sopenharmony_ci } 34362306a36Sopenharmony_ci 34462306a36Sopenharmony_ci fh = (struct fc_frame_header *) fc_frame_header_get(fp); 34562306a36Sopenharmony_ci /* Copy FC Frame header and payload into the frame */ 34662306a36Sopenharmony_ci memcpy(fh, buf, hdr_len + resp_len); 34762306a36Sopenharmony_ci 34862306a36Sopenharmony_ci opcode = fc_frame_payload_op(fp); 34962306a36Sopenharmony_ci switch (opcode) { 35062306a36Sopenharmony_ci case ELS_LS_ACC: 35162306a36Sopenharmony_ci BNX2FC_IO_DBG(srr_req, "SRR success\n"); 35262306a36Sopenharmony_ci break; 35362306a36Sopenharmony_ci case ELS_LS_RJT: 35462306a36Sopenharmony_ci BNX2FC_IO_DBG(srr_req, "SRR rejected\n"); 35562306a36Sopenharmony_ci rc = bnx2fc_initiate_abts(orig_io_req); 35662306a36Sopenharmony_ci if (rc != SUCCESS) { 35762306a36Sopenharmony_ci BNX2FC_IO_DBG(srr_req, "srr_compl: initiate_abts " 35862306a36Sopenharmony_ci "failed xid = 0x%x. issue cleanup\n", 35962306a36Sopenharmony_ci orig_io_req->xid); 36062306a36Sopenharmony_ci bnx2fc_initiate_cleanup(orig_io_req); 36162306a36Sopenharmony_ci } 36262306a36Sopenharmony_ci break; 36362306a36Sopenharmony_ci default: 36462306a36Sopenharmony_ci BNX2FC_IO_DBG(srr_req, "srr compl - invalid opcode = %d\n", 36562306a36Sopenharmony_ci opcode); 36662306a36Sopenharmony_ci break; 36762306a36Sopenharmony_ci } 36862306a36Sopenharmony_ci fc_frame_free(fp); 36962306a36Sopenharmony_cifree_buf: 37062306a36Sopenharmony_ci kfree(buf); 37162306a36Sopenharmony_cisrr_compl_done: 37262306a36Sopenharmony_ci kref_put(&orig_io_req->refcount, bnx2fc_cmd_release); 37362306a36Sopenharmony_ci} 37462306a36Sopenharmony_ci 37562306a36Sopenharmony_cistatic void bnx2fc_rec_compl(struct bnx2fc_els_cb_arg *cb_arg) 37662306a36Sopenharmony_ci{ 37762306a36Sopenharmony_ci struct bnx2fc_cmd *orig_io_req, *new_io_req; 37862306a36Sopenharmony_ci struct bnx2fc_cmd *rec_req; 37962306a36Sopenharmony_ci struct bnx2fc_mp_req *mp_req; 38062306a36Sopenharmony_ci struct fc_frame_header *fc_hdr, *fh; 38162306a36Sopenharmony_ci struct fc_els_ls_rjt *rjt; 38262306a36Sopenharmony_ci struct fc_els_rec_acc *acc; 38362306a36Sopenharmony_ci struct bnx2fc_rport *tgt; 38462306a36Sopenharmony_ci struct fcoe_err_report_entry *err_entry; 38562306a36Sopenharmony_ci struct scsi_cmnd *sc_cmd; 38662306a36Sopenharmony_ci enum fc_rctl r_ctl; 38762306a36Sopenharmony_ci unsigned char *buf; 38862306a36Sopenharmony_ci void *resp_buf; 38962306a36Sopenharmony_ci struct fc_frame *fp; 39062306a36Sopenharmony_ci u8 opcode; 39162306a36Sopenharmony_ci u32 offset; 39262306a36Sopenharmony_ci u32 e_stat; 39362306a36Sopenharmony_ci u32 resp_len, hdr_len; 39462306a36Sopenharmony_ci int rc = 0; 39562306a36Sopenharmony_ci bool send_seq_clnp = false; 39662306a36Sopenharmony_ci bool abort_io = false; 39762306a36Sopenharmony_ci 39862306a36Sopenharmony_ci BNX2FC_MISC_DBG("Entered rec_compl callback\n"); 39962306a36Sopenharmony_ci rec_req = cb_arg->io_req; 40062306a36Sopenharmony_ci orig_io_req = cb_arg->aborted_io_req; 40162306a36Sopenharmony_ci BNX2FC_IO_DBG(rec_req, "rec_compl: orig xid = 0x%x", orig_io_req->xid); 40262306a36Sopenharmony_ci tgt = orig_io_req->tgt; 40362306a36Sopenharmony_ci 40462306a36Sopenharmony_ci /* Handle REC timeout case */ 40562306a36Sopenharmony_ci if (test_and_clear_bit(BNX2FC_FLAG_ELS_TIMEOUT, &rec_req->req_flags)) { 40662306a36Sopenharmony_ci BNX2FC_IO_DBG(rec_req, "timed out, abort " 40762306a36Sopenharmony_ci "orig_io - 0x%x\n", 40862306a36Sopenharmony_ci orig_io_req->xid); 40962306a36Sopenharmony_ci /* els req is timed out. send abts for els */ 41062306a36Sopenharmony_ci rc = bnx2fc_initiate_abts(rec_req); 41162306a36Sopenharmony_ci if (rc != SUCCESS) { 41262306a36Sopenharmony_ci BNX2FC_IO_DBG(rec_req, "rec_compl: initiate_abts " 41362306a36Sopenharmony_ci "failed. issue cleanup\n"); 41462306a36Sopenharmony_ci bnx2fc_initiate_cleanup(rec_req); 41562306a36Sopenharmony_ci } 41662306a36Sopenharmony_ci orig_io_req->rec_retry++; 41762306a36Sopenharmony_ci /* REC timedout. send ABTS to the orig IO req */ 41862306a36Sopenharmony_ci if (orig_io_req->rec_retry <= REC_RETRY_COUNT) { 41962306a36Sopenharmony_ci spin_unlock_bh(&tgt->tgt_lock); 42062306a36Sopenharmony_ci rc = bnx2fc_send_rec(orig_io_req); 42162306a36Sopenharmony_ci spin_lock_bh(&tgt->tgt_lock); 42262306a36Sopenharmony_ci if (!rc) 42362306a36Sopenharmony_ci goto rec_compl_done; 42462306a36Sopenharmony_ci } 42562306a36Sopenharmony_ci rc = bnx2fc_initiate_abts(orig_io_req); 42662306a36Sopenharmony_ci if (rc != SUCCESS) { 42762306a36Sopenharmony_ci BNX2FC_IO_DBG(rec_req, "rec_compl: initiate_abts " 42862306a36Sopenharmony_ci "failed xid = 0x%x. issue cleanup\n", 42962306a36Sopenharmony_ci orig_io_req->xid); 43062306a36Sopenharmony_ci bnx2fc_initiate_cleanup(orig_io_req); 43162306a36Sopenharmony_ci } 43262306a36Sopenharmony_ci goto rec_compl_done; 43362306a36Sopenharmony_ci } 43462306a36Sopenharmony_ci 43562306a36Sopenharmony_ci if (test_bit(BNX2FC_FLAG_IO_COMPL, &orig_io_req->req_flags)) { 43662306a36Sopenharmony_ci BNX2FC_IO_DBG(rec_req, "completed" 43762306a36Sopenharmony_ci "orig_io - 0x%x\n", 43862306a36Sopenharmony_ci orig_io_req->xid); 43962306a36Sopenharmony_ci goto rec_compl_done; 44062306a36Sopenharmony_ci } 44162306a36Sopenharmony_ci if (test_bit(BNX2FC_FLAG_ISSUE_ABTS, &orig_io_req->req_flags)) { 44262306a36Sopenharmony_ci BNX2FC_IO_DBG(rec_req, "abts in prog " 44362306a36Sopenharmony_ci "orig_io - 0x%x\n", 44462306a36Sopenharmony_ci orig_io_req->xid); 44562306a36Sopenharmony_ci goto rec_compl_done; 44662306a36Sopenharmony_ci } 44762306a36Sopenharmony_ci 44862306a36Sopenharmony_ci mp_req = &(rec_req->mp_req); 44962306a36Sopenharmony_ci fc_hdr = &(mp_req->resp_fc_hdr); 45062306a36Sopenharmony_ci resp_len = mp_req->resp_len; 45162306a36Sopenharmony_ci acc = resp_buf = mp_req->resp_buf; 45262306a36Sopenharmony_ci 45362306a36Sopenharmony_ci hdr_len = sizeof(*fc_hdr); 45462306a36Sopenharmony_ci 45562306a36Sopenharmony_ci buf = kzalloc(PAGE_SIZE, GFP_ATOMIC); 45662306a36Sopenharmony_ci if (!buf) { 45762306a36Sopenharmony_ci printk(KERN_ERR PFX "rec buf: mem alloc failure\n"); 45862306a36Sopenharmony_ci goto rec_compl_done; 45962306a36Sopenharmony_ci } 46062306a36Sopenharmony_ci memcpy(buf, fc_hdr, hdr_len); 46162306a36Sopenharmony_ci memcpy(buf + hdr_len, resp_buf, resp_len); 46262306a36Sopenharmony_ci 46362306a36Sopenharmony_ci fp = fc_frame_alloc(NULL, resp_len); 46462306a36Sopenharmony_ci if (!fp) { 46562306a36Sopenharmony_ci printk(KERN_ERR PFX "fc_frame_alloc failure\n"); 46662306a36Sopenharmony_ci goto free_buf; 46762306a36Sopenharmony_ci } 46862306a36Sopenharmony_ci 46962306a36Sopenharmony_ci fh = (struct fc_frame_header *) fc_frame_header_get(fp); 47062306a36Sopenharmony_ci /* Copy FC Frame header and payload into the frame */ 47162306a36Sopenharmony_ci memcpy(fh, buf, hdr_len + resp_len); 47262306a36Sopenharmony_ci 47362306a36Sopenharmony_ci opcode = fc_frame_payload_op(fp); 47462306a36Sopenharmony_ci if (opcode == ELS_LS_RJT) { 47562306a36Sopenharmony_ci BNX2FC_IO_DBG(rec_req, "opcode is RJT\n"); 47662306a36Sopenharmony_ci rjt = fc_frame_payload_get(fp, sizeof(*rjt)); 47762306a36Sopenharmony_ci if ((rjt->er_reason == ELS_RJT_LOGIC || 47862306a36Sopenharmony_ci rjt->er_reason == ELS_RJT_UNAB) && 47962306a36Sopenharmony_ci rjt->er_explan == ELS_EXPL_OXID_RXID) { 48062306a36Sopenharmony_ci BNX2FC_IO_DBG(rec_req, "handle CMD LOST case\n"); 48162306a36Sopenharmony_ci new_io_req = bnx2fc_cmd_alloc(tgt); 48262306a36Sopenharmony_ci if (!new_io_req) 48362306a36Sopenharmony_ci goto abort_io; 48462306a36Sopenharmony_ci new_io_req->sc_cmd = orig_io_req->sc_cmd; 48562306a36Sopenharmony_ci /* cleanup orig_io_req that is with the FW */ 48662306a36Sopenharmony_ci set_bit(BNX2FC_FLAG_CMD_LOST, 48762306a36Sopenharmony_ci &orig_io_req->req_flags); 48862306a36Sopenharmony_ci bnx2fc_initiate_cleanup(orig_io_req); 48962306a36Sopenharmony_ci /* Post a new IO req with the same sc_cmd */ 49062306a36Sopenharmony_ci BNX2FC_IO_DBG(rec_req, "Post IO request again\n"); 49162306a36Sopenharmony_ci rc = bnx2fc_post_io_req(tgt, new_io_req); 49262306a36Sopenharmony_ci if (!rc) 49362306a36Sopenharmony_ci goto free_frame; 49462306a36Sopenharmony_ci BNX2FC_IO_DBG(rec_req, "REC: io post err\n"); 49562306a36Sopenharmony_ci } 49662306a36Sopenharmony_ciabort_io: 49762306a36Sopenharmony_ci rc = bnx2fc_initiate_abts(orig_io_req); 49862306a36Sopenharmony_ci if (rc != SUCCESS) { 49962306a36Sopenharmony_ci BNX2FC_IO_DBG(rec_req, "rec_compl: initiate_abts " 50062306a36Sopenharmony_ci "failed. issue cleanup\n"); 50162306a36Sopenharmony_ci bnx2fc_initiate_cleanup(orig_io_req); 50262306a36Sopenharmony_ci } 50362306a36Sopenharmony_ci } else if (opcode == ELS_LS_ACC) { 50462306a36Sopenharmony_ci /* REVISIT: Check if the exchange is already aborted */ 50562306a36Sopenharmony_ci offset = ntohl(acc->reca_fc4value); 50662306a36Sopenharmony_ci e_stat = ntohl(acc->reca_e_stat); 50762306a36Sopenharmony_ci if (e_stat & ESB_ST_SEQ_INIT) { 50862306a36Sopenharmony_ci BNX2FC_IO_DBG(rec_req, "target has the seq init\n"); 50962306a36Sopenharmony_ci goto free_frame; 51062306a36Sopenharmony_ci } 51162306a36Sopenharmony_ci BNX2FC_IO_DBG(rec_req, "e_stat = 0x%x, offset = 0x%x\n", 51262306a36Sopenharmony_ci e_stat, offset); 51362306a36Sopenharmony_ci /* Seq initiative is with us */ 51462306a36Sopenharmony_ci err_entry = (struct fcoe_err_report_entry *) 51562306a36Sopenharmony_ci &orig_io_req->err_entry; 51662306a36Sopenharmony_ci sc_cmd = orig_io_req->sc_cmd; 51762306a36Sopenharmony_ci if (sc_cmd->sc_data_direction == DMA_TO_DEVICE) { 51862306a36Sopenharmony_ci /* SCSI WRITE command */ 51962306a36Sopenharmony_ci if (offset == orig_io_req->data_xfer_len) { 52062306a36Sopenharmony_ci BNX2FC_IO_DBG(rec_req, "WRITE - resp lost\n"); 52162306a36Sopenharmony_ci /* FCP_RSP lost */ 52262306a36Sopenharmony_ci r_ctl = FC_RCTL_DD_CMD_STATUS; 52362306a36Sopenharmony_ci offset = 0; 52462306a36Sopenharmony_ci } else { 52562306a36Sopenharmony_ci /* start transmitting from offset */ 52662306a36Sopenharmony_ci BNX2FC_IO_DBG(rec_req, "XFER_RDY/DATA lost\n"); 52762306a36Sopenharmony_ci send_seq_clnp = true; 52862306a36Sopenharmony_ci r_ctl = FC_RCTL_DD_DATA_DESC; 52962306a36Sopenharmony_ci if (bnx2fc_initiate_seq_cleanup(orig_io_req, 53062306a36Sopenharmony_ci offset, r_ctl)) 53162306a36Sopenharmony_ci abort_io = true; 53262306a36Sopenharmony_ci /* XFER_RDY */ 53362306a36Sopenharmony_ci } 53462306a36Sopenharmony_ci } else { 53562306a36Sopenharmony_ci /* SCSI READ command */ 53662306a36Sopenharmony_ci if (err_entry->data.rx_buf_off == 53762306a36Sopenharmony_ci orig_io_req->data_xfer_len) { 53862306a36Sopenharmony_ci /* FCP_RSP lost */ 53962306a36Sopenharmony_ci BNX2FC_IO_DBG(rec_req, "READ - resp lost\n"); 54062306a36Sopenharmony_ci r_ctl = FC_RCTL_DD_CMD_STATUS; 54162306a36Sopenharmony_ci offset = 0; 54262306a36Sopenharmony_ci } else { 54362306a36Sopenharmony_ci /* request retransmission from this offset */ 54462306a36Sopenharmony_ci send_seq_clnp = true; 54562306a36Sopenharmony_ci offset = err_entry->data.rx_buf_off; 54662306a36Sopenharmony_ci BNX2FC_IO_DBG(rec_req, "RD DATA lost\n"); 54762306a36Sopenharmony_ci /* FCP_DATA lost */ 54862306a36Sopenharmony_ci r_ctl = FC_RCTL_DD_SOL_DATA; 54962306a36Sopenharmony_ci if (bnx2fc_initiate_seq_cleanup(orig_io_req, 55062306a36Sopenharmony_ci offset, r_ctl)) 55162306a36Sopenharmony_ci abort_io = true; 55262306a36Sopenharmony_ci } 55362306a36Sopenharmony_ci } 55462306a36Sopenharmony_ci if (abort_io) { 55562306a36Sopenharmony_ci rc = bnx2fc_initiate_abts(orig_io_req); 55662306a36Sopenharmony_ci if (rc != SUCCESS) { 55762306a36Sopenharmony_ci BNX2FC_IO_DBG(rec_req, "rec_compl:initiate_abts" 55862306a36Sopenharmony_ci " failed. issue cleanup\n"); 55962306a36Sopenharmony_ci bnx2fc_initiate_cleanup(orig_io_req); 56062306a36Sopenharmony_ci } 56162306a36Sopenharmony_ci } else if (!send_seq_clnp) { 56262306a36Sopenharmony_ci BNX2FC_IO_DBG(rec_req, "Send SRR - FCP_RSP\n"); 56362306a36Sopenharmony_ci spin_unlock_bh(&tgt->tgt_lock); 56462306a36Sopenharmony_ci rc = bnx2fc_send_srr(orig_io_req, offset, r_ctl); 56562306a36Sopenharmony_ci spin_lock_bh(&tgt->tgt_lock); 56662306a36Sopenharmony_ci 56762306a36Sopenharmony_ci if (rc) { 56862306a36Sopenharmony_ci BNX2FC_IO_DBG(rec_req, "Unable to send SRR" 56962306a36Sopenharmony_ci " IO will abort\n"); 57062306a36Sopenharmony_ci } 57162306a36Sopenharmony_ci } 57262306a36Sopenharmony_ci } 57362306a36Sopenharmony_cifree_frame: 57462306a36Sopenharmony_ci fc_frame_free(fp); 57562306a36Sopenharmony_cifree_buf: 57662306a36Sopenharmony_ci kfree(buf); 57762306a36Sopenharmony_cirec_compl_done: 57862306a36Sopenharmony_ci kref_put(&orig_io_req->refcount, bnx2fc_cmd_release); 57962306a36Sopenharmony_ci kfree(cb_arg); 58062306a36Sopenharmony_ci} 58162306a36Sopenharmony_ci 58262306a36Sopenharmony_ciint bnx2fc_send_rec(struct bnx2fc_cmd *orig_io_req) 58362306a36Sopenharmony_ci{ 58462306a36Sopenharmony_ci struct fc_els_rec rec; 58562306a36Sopenharmony_ci struct bnx2fc_rport *tgt = orig_io_req->tgt; 58662306a36Sopenharmony_ci struct fc_lport *lport = tgt->rdata->local_port; 58762306a36Sopenharmony_ci struct bnx2fc_els_cb_arg *cb_arg = NULL; 58862306a36Sopenharmony_ci u32 sid = tgt->sid; 58962306a36Sopenharmony_ci u32 r_a_tov = lport->r_a_tov; 59062306a36Sopenharmony_ci int rc; 59162306a36Sopenharmony_ci 59262306a36Sopenharmony_ci BNX2FC_IO_DBG(orig_io_req, "Sending REC\n"); 59362306a36Sopenharmony_ci memset(&rec, 0, sizeof(rec)); 59462306a36Sopenharmony_ci 59562306a36Sopenharmony_ci cb_arg = kzalloc(sizeof(struct bnx2fc_els_cb_arg), GFP_ATOMIC); 59662306a36Sopenharmony_ci if (!cb_arg) { 59762306a36Sopenharmony_ci printk(KERN_ERR PFX "Unable to allocate cb_arg for REC\n"); 59862306a36Sopenharmony_ci rc = -ENOMEM; 59962306a36Sopenharmony_ci goto rec_err; 60062306a36Sopenharmony_ci } 60162306a36Sopenharmony_ci kref_get(&orig_io_req->refcount); 60262306a36Sopenharmony_ci 60362306a36Sopenharmony_ci cb_arg->aborted_io_req = orig_io_req; 60462306a36Sopenharmony_ci 60562306a36Sopenharmony_ci rec.rec_cmd = ELS_REC; 60662306a36Sopenharmony_ci hton24(rec.rec_s_id, sid); 60762306a36Sopenharmony_ci rec.rec_ox_id = htons(orig_io_req->xid); 60862306a36Sopenharmony_ci rec.rec_rx_id = htons(orig_io_req->task->rxwr_txrd.var_ctx.rx_id); 60962306a36Sopenharmony_ci 61062306a36Sopenharmony_ci rc = bnx2fc_initiate_els(tgt, ELS_REC, &rec, sizeof(rec), 61162306a36Sopenharmony_ci bnx2fc_rec_compl, cb_arg, 61262306a36Sopenharmony_ci r_a_tov); 61362306a36Sopenharmony_ci if (rc) { 61462306a36Sopenharmony_ci BNX2FC_IO_DBG(orig_io_req, "REC failed - release\n"); 61562306a36Sopenharmony_ci spin_lock_bh(&tgt->tgt_lock); 61662306a36Sopenharmony_ci kref_put(&orig_io_req->refcount, bnx2fc_cmd_release); 61762306a36Sopenharmony_ci spin_unlock_bh(&tgt->tgt_lock); 61862306a36Sopenharmony_ci kfree(cb_arg); 61962306a36Sopenharmony_ci } 62062306a36Sopenharmony_cirec_err: 62162306a36Sopenharmony_ci return rc; 62262306a36Sopenharmony_ci} 62362306a36Sopenharmony_ci 62462306a36Sopenharmony_ciint bnx2fc_send_srr(struct bnx2fc_cmd *orig_io_req, u32 offset, u8 r_ctl) 62562306a36Sopenharmony_ci{ 62662306a36Sopenharmony_ci struct fcp_srr srr; 62762306a36Sopenharmony_ci struct bnx2fc_rport *tgt = orig_io_req->tgt; 62862306a36Sopenharmony_ci struct fc_lport *lport = tgt->rdata->local_port; 62962306a36Sopenharmony_ci struct bnx2fc_els_cb_arg *cb_arg = NULL; 63062306a36Sopenharmony_ci u32 r_a_tov = lport->r_a_tov; 63162306a36Sopenharmony_ci int rc; 63262306a36Sopenharmony_ci 63362306a36Sopenharmony_ci BNX2FC_IO_DBG(orig_io_req, "Sending SRR\n"); 63462306a36Sopenharmony_ci memset(&srr, 0, sizeof(srr)); 63562306a36Sopenharmony_ci 63662306a36Sopenharmony_ci cb_arg = kzalloc(sizeof(struct bnx2fc_els_cb_arg), GFP_ATOMIC); 63762306a36Sopenharmony_ci if (!cb_arg) { 63862306a36Sopenharmony_ci printk(KERN_ERR PFX "Unable to allocate cb_arg for SRR\n"); 63962306a36Sopenharmony_ci rc = -ENOMEM; 64062306a36Sopenharmony_ci goto srr_err; 64162306a36Sopenharmony_ci } 64262306a36Sopenharmony_ci kref_get(&orig_io_req->refcount); 64362306a36Sopenharmony_ci 64462306a36Sopenharmony_ci cb_arg->aborted_io_req = orig_io_req; 64562306a36Sopenharmony_ci 64662306a36Sopenharmony_ci srr.srr_op = ELS_SRR; 64762306a36Sopenharmony_ci srr.srr_ox_id = htons(orig_io_req->xid); 64862306a36Sopenharmony_ci srr.srr_rx_id = htons(orig_io_req->task->rxwr_txrd.var_ctx.rx_id); 64962306a36Sopenharmony_ci srr.srr_rel_off = htonl(offset); 65062306a36Sopenharmony_ci srr.srr_r_ctl = r_ctl; 65162306a36Sopenharmony_ci orig_io_req->srr_offset = offset; 65262306a36Sopenharmony_ci orig_io_req->srr_rctl = r_ctl; 65362306a36Sopenharmony_ci 65462306a36Sopenharmony_ci rc = bnx2fc_initiate_els(tgt, ELS_SRR, &srr, sizeof(srr), 65562306a36Sopenharmony_ci bnx2fc_srr_compl, cb_arg, 65662306a36Sopenharmony_ci r_a_tov); 65762306a36Sopenharmony_ci if (rc) { 65862306a36Sopenharmony_ci BNX2FC_IO_DBG(orig_io_req, "SRR failed - release\n"); 65962306a36Sopenharmony_ci spin_lock_bh(&tgt->tgt_lock); 66062306a36Sopenharmony_ci kref_put(&orig_io_req->refcount, bnx2fc_cmd_release); 66162306a36Sopenharmony_ci spin_unlock_bh(&tgt->tgt_lock); 66262306a36Sopenharmony_ci kfree(cb_arg); 66362306a36Sopenharmony_ci } else 66462306a36Sopenharmony_ci set_bit(BNX2FC_FLAG_SRR_SENT, &orig_io_req->req_flags); 66562306a36Sopenharmony_ci 66662306a36Sopenharmony_cisrr_err: 66762306a36Sopenharmony_ci return rc; 66862306a36Sopenharmony_ci} 66962306a36Sopenharmony_ci 67062306a36Sopenharmony_cistatic int bnx2fc_initiate_els(struct bnx2fc_rport *tgt, unsigned int op, 67162306a36Sopenharmony_ci void *data, u32 data_len, 67262306a36Sopenharmony_ci void (*cb_func)(struct bnx2fc_els_cb_arg *cb_arg), 67362306a36Sopenharmony_ci struct bnx2fc_els_cb_arg *cb_arg, u32 timer_msec) 67462306a36Sopenharmony_ci{ 67562306a36Sopenharmony_ci struct fcoe_port *port = tgt->port; 67662306a36Sopenharmony_ci struct bnx2fc_interface *interface = port->priv; 67762306a36Sopenharmony_ci struct fc_rport *rport = tgt->rport; 67862306a36Sopenharmony_ci struct fc_lport *lport = port->lport; 67962306a36Sopenharmony_ci struct bnx2fc_cmd *els_req; 68062306a36Sopenharmony_ci struct bnx2fc_mp_req *mp_req; 68162306a36Sopenharmony_ci struct fc_frame_header *fc_hdr; 68262306a36Sopenharmony_ci struct fcoe_task_ctx_entry *task; 68362306a36Sopenharmony_ci struct fcoe_task_ctx_entry *task_page; 68462306a36Sopenharmony_ci int rc = 0; 68562306a36Sopenharmony_ci int task_idx, index; 68662306a36Sopenharmony_ci u32 did, sid; 68762306a36Sopenharmony_ci u16 xid; 68862306a36Sopenharmony_ci 68962306a36Sopenharmony_ci rc = fc_remote_port_chkready(rport); 69062306a36Sopenharmony_ci if (rc) { 69162306a36Sopenharmony_ci printk(KERN_ERR PFX "els 0x%x: rport not ready\n", op); 69262306a36Sopenharmony_ci rc = -EINVAL; 69362306a36Sopenharmony_ci goto els_err; 69462306a36Sopenharmony_ci } 69562306a36Sopenharmony_ci if (lport->state != LPORT_ST_READY || !(lport->link_up)) { 69662306a36Sopenharmony_ci printk(KERN_ERR PFX "els 0x%x: link is not ready\n", op); 69762306a36Sopenharmony_ci rc = -EINVAL; 69862306a36Sopenharmony_ci goto els_err; 69962306a36Sopenharmony_ci } 70062306a36Sopenharmony_ci if (!(test_bit(BNX2FC_FLAG_SESSION_READY, &tgt->flags))) { 70162306a36Sopenharmony_ci printk(KERN_ERR PFX "els 0x%x: tgt not ready\n", op); 70262306a36Sopenharmony_ci rc = -EINVAL; 70362306a36Sopenharmony_ci goto els_err; 70462306a36Sopenharmony_ci } 70562306a36Sopenharmony_ci els_req = bnx2fc_elstm_alloc(tgt, BNX2FC_ELS); 70662306a36Sopenharmony_ci if (!els_req) { 70762306a36Sopenharmony_ci rc = -ENOMEM; 70862306a36Sopenharmony_ci goto els_err; 70962306a36Sopenharmony_ci } 71062306a36Sopenharmony_ci 71162306a36Sopenharmony_ci els_req->sc_cmd = NULL; 71262306a36Sopenharmony_ci els_req->port = port; 71362306a36Sopenharmony_ci els_req->tgt = tgt; 71462306a36Sopenharmony_ci els_req->cb_func = cb_func; 71562306a36Sopenharmony_ci cb_arg->io_req = els_req; 71662306a36Sopenharmony_ci els_req->cb_arg = cb_arg; 71762306a36Sopenharmony_ci els_req->data_xfer_len = data_len; 71862306a36Sopenharmony_ci 71962306a36Sopenharmony_ci mp_req = (struct bnx2fc_mp_req *)&(els_req->mp_req); 72062306a36Sopenharmony_ci rc = bnx2fc_init_mp_req(els_req); 72162306a36Sopenharmony_ci if (rc == FAILED) { 72262306a36Sopenharmony_ci printk(KERN_ERR PFX "ELS MP request init failed\n"); 72362306a36Sopenharmony_ci spin_lock_bh(&tgt->tgt_lock); 72462306a36Sopenharmony_ci kref_put(&els_req->refcount, bnx2fc_cmd_release); 72562306a36Sopenharmony_ci spin_unlock_bh(&tgt->tgt_lock); 72662306a36Sopenharmony_ci rc = -ENOMEM; 72762306a36Sopenharmony_ci goto els_err; 72862306a36Sopenharmony_ci } else { 72962306a36Sopenharmony_ci /* rc SUCCESS */ 73062306a36Sopenharmony_ci rc = 0; 73162306a36Sopenharmony_ci } 73262306a36Sopenharmony_ci 73362306a36Sopenharmony_ci /* Set the data_xfer_len to the size of ELS payload */ 73462306a36Sopenharmony_ci mp_req->req_len = data_len; 73562306a36Sopenharmony_ci els_req->data_xfer_len = mp_req->req_len; 73662306a36Sopenharmony_ci 73762306a36Sopenharmony_ci /* Fill ELS Payload */ 73862306a36Sopenharmony_ci if ((op >= ELS_LS_RJT) && (op <= ELS_AUTH_ELS)) { 73962306a36Sopenharmony_ci memcpy(mp_req->req_buf, data, data_len); 74062306a36Sopenharmony_ci } else { 74162306a36Sopenharmony_ci printk(KERN_ERR PFX "Invalid ELS op 0x%x\n", op); 74262306a36Sopenharmony_ci els_req->cb_func = NULL; 74362306a36Sopenharmony_ci els_req->cb_arg = NULL; 74462306a36Sopenharmony_ci spin_lock_bh(&tgt->tgt_lock); 74562306a36Sopenharmony_ci kref_put(&els_req->refcount, bnx2fc_cmd_release); 74662306a36Sopenharmony_ci spin_unlock_bh(&tgt->tgt_lock); 74762306a36Sopenharmony_ci rc = -EINVAL; 74862306a36Sopenharmony_ci } 74962306a36Sopenharmony_ci 75062306a36Sopenharmony_ci if (rc) 75162306a36Sopenharmony_ci goto els_err; 75262306a36Sopenharmony_ci 75362306a36Sopenharmony_ci /* Fill FC header */ 75462306a36Sopenharmony_ci fc_hdr = &(mp_req->req_fc_hdr); 75562306a36Sopenharmony_ci 75662306a36Sopenharmony_ci did = tgt->rport->port_id; 75762306a36Sopenharmony_ci sid = tgt->sid; 75862306a36Sopenharmony_ci 75962306a36Sopenharmony_ci if (op == ELS_SRR) 76062306a36Sopenharmony_ci __fc_fill_fc_hdr(fc_hdr, FC_RCTL_ELS4_REQ, did, sid, 76162306a36Sopenharmony_ci FC_TYPE_FCP, FC_FC_FIRST_SEQ | 76262306a36Sopenharmony_ci FC_FC_END_SEQ | FC_FC_SEQ_INIT, 0); 76362306a36Sopenharmony_ci else 76462306a36Sopenharmony_ci __fc_fill_fc_hdr(fc_hdr, FC_RCTL_ELS_REQ, did, sid, 76562306a36Sopenharmony_ci FC_TYPE_ELS, FC_FC_FIRST_SEQ | 76662306a36Sopenharmony_ci FC_FC_END_SEQ | FC_FC_SEQ_INIT, 0); 76762306a36Sopenharmony_ci 76862306a36Sopenharmony_ci /* Obtain exchange id */ 76962306a36Sopenharmony_ci xid = els_req->xid; 77062306a36Sopenharmony_ci task_idx = xid/BNX2FC_TASKS_PER_PAGE; 77162306a36Sopenharmony_ci index = xid % BNX2FC_TASKS_PER_PAGE; 77262306a36Sopenharmony_ci 77362306a36Sopenharmony_ci /* Initialize task context for this IO request */ 77462306a36Sopenharmony_ci task_page = (struct fcoe_task_ctx_entry *) 77562306a36Sopenharmony_ci interface->hba->task_ctx[task_idx]; 77662306a36Sopenharmony_ci task = &(task_page[index]); 77762306a36Sopenharmony_ci bnx2fc_init_mp_task(els_req, task); 77862306a36Sopenharmony_ci 77962306a36Sopenharmony_ci spin_lock_bh(&tgt->tgt_lock); 78062306a36Sopenharmony_ci 78162306a36Sopenharmony_ci if (!test_bit(BNX2FC_FLAG_SESSION_READY, &tgt->flags)) { 78262306a36Sopenharmony_ci printk(KERN_ERR PFX "initiate_els.. session not ready\n"); 78362306a36Sopenharmony_ci els_req->cb_func = NULL; 78462306a36Sopenharmony_ci els_req->cb_arg = NULL; 78562306a36Sopenharmony_ci kref_put(&els_req->refcount, bnx2fc_cmd_release); 78662306a36Sopenharmony_ci spin_unlock_bh(&tgt->tgt_lock); 78762306a36Sopenharmony_ci return -EINVAL; 78862306a36Sopenharmony_ci } 78962306a36Sopenharmony_ci 79062306a36Sopenharmony_ci if (timer_msec) 79162306a36Sopenharmony_ci bnx2fc_cmd_timer_set(els_req, timer_msec); 79262306a36Sopenharmony_ci bnx2fc_add_2_sq(tgt, xid); 79362306a36Sopenharmony_ci 79462306a36Sopenharmony_ci els_req->on_active_queue = 1; 79562306a36Sopenharmony_ci list_add_tail(&els_req->link, &tgt->els_queue); 79662306a36Sopenharmony_ci 79762306a36Sopenharmony_ci /* Ring doorbell */ 79862306a36Sopenharmony_ci bnx2fc_ring_doorbell(tgt); 79962306a36Sopenharmony_ci spin_unlock_bh(&tgt->tgt_lock); 80062306a36Sopenharmony_ci 80162306a36Sopenharmony_ciels_err: 80262306a36Sopenharmony_ci return rc; 80362306a36Sopenharmony_ci} 80462306a36Sopenharmony_ci 80562306a36Sopenharmony_civoid bnx2fc_process_els_compl(struct bnx2fc_cmd *els_req, 80662306a36Sopenharmony_ci struct fcoe_task_ctx_entry *task, u8 num_rq) 80762306a36Sopenharmony_ci{ 80862306a36Sopenharmony_ci struct bnx2fc_mp_req *mp_req; 80962306a36Sopenharmony_ci struct fc_frame_header *fc_hdr; 81062306a36Sopenharmony_ci u64 *hdr; 81162306a36Sopenharmony_ci u64 *temp_hdr; 81262306a36Sopenharmony_ci 81362306a36Sopenharmony_ci BNX2FC_ELS_DBG("Entered process_els_compl xid = 0x%x" 81462306a36Sopenharmony_ci "cmd_type = %d\n", els_req->xid, els_req->cmd_type); 81562306a36Sopenharmony_ci 81662306a36Sopenharmony_ci if (test_and_set_bit(BNX2FC_FLAG_ELS_DONE, 81762306a36Sopenharmony_ci &els_req->req_flags)) { 81862306a36Sopenharmony_ci BNX2FC_ELS_DBG("Timer context finished processing this " 81962306a36Sopenharmony_ci "els - 0x%x\n", els_req->xid); 82062306a36Sopenharmony_ci /* This IO doesn't receive cleanup completion */ 82162306a36Sopenharmony_ci kref_put(&els_req->refcount, bnx2fc_cmd_release); 82262306a36Sopenharmony_ci return; 82362306a36Sopenharmony_ci } 82462306a36Sopenharmony_ci 82562306a36Sopenharmony_ci /* Cancel the timeout_work, as we received the response */ 82662306a36Sopenharmony_ci if (cancel_delayed_work(&els_req->timeout_work)) 82762306a36Sopenharmony_ci kref_put(&els_req->refcount, 82862306a36Sopenharmony_ci bnx2fc_cmd_release); /* drop timer hold */ 82962306a36Sopenharmony_ci 83062306a36Sopenharmony_ci if (els_req->on_active_queue) { 83162306a36Sopenharmony_ci list_del_init(&els_req->link); 83262306a36Sopenharmony_ci els_req->on_active_queue = 0; 83362306a36Sopenharmony_ci } 83462306a36Sopenharmony_ci 83562306a36Sopenharmony_ci mp_req = &(els_req->mp_req); 83662306a36Sopenharmony_ci fc_hdr = &(mp_req->resp_fc_hdr); 83762306a36Sopenharmony_ci 83862306a36Sopenharmony_ci hdr = (u64 *)fc_hdr; 83962306a36Sopenharmony_ci temp_hdr = (u64 *) 84062306a36Sopenharmony_ci &task->rxwr_only.union_ctx.comp_info.mp_rsp.fc_hdr; 84162306a36Sopenharmony_ci hdr[0] = cpu_to_be64(temp_hdr[0]); 84262306a36Sopenharmony_ci hdr[1] = cpu_to_be64(temp_hdr[1]); 84362306a36Sopenharmony_ci hdr[2] = cpu_to_be64(temp_hdr[2]); 84462306a36Sopenharmony_ci 84562306a36Sopenharmony_ci mp_req->resp_len = 84662306a36Sopenharmony_ci task->rxwr_only.union_ctx.comp_info.mp_rsp.mp_payload_len; 84762306a36Sopenharmony_ci 84862306a36Sopenharmony_ci /* Parse ELS response */ 84962306a36Sopenharmony_ci if ((els_req->cb_func) && (els_req->cb_arg)) { 85062306a36Sopenharmony_ci els_req->cb_func(els_req->cb_arg); 85162306a36Sopenharmony_ci els_req->cb_arg = NULL; 85262306a36Sopenharmony_ci } 85362306a36Sopenharmony_ci 85462306a36Sopenharmony_ci kref_put(&els_req->refcount, bnx2fc_cmd_release); 85562306a36Sopenharmony_ci} 85662306a36Sopenharmony_ci 85762306a36Sopenharmony_ci#define BNX2FC_FCOE_MAC_METHOD_GRANGED_MAC 1 85862306a36Sopenharmony_ci#define BNX2FC_FCOE_MAC_METHOD_FCF_MAP 2 85962306a36Sopenharmony_ci#define BNX2FC_FCOE_MAC_METHOD_FCOE_SET_MAC 3 86062306a36Sopenharmony_cistatic void bnx2fc_flogi_resp(struct fc_seq *seq, struct fc_frame *fp, 86162306a36Sopenharmony_ci void *arg) 86262306a36Sopenharmony_ci{ 86362306a36Sopenharmony_ci struct fcoe_ctlr *fip = arg; 86462306a36Sopenharmony_ci struct fc_exch *exch = fc_seq_exch(seq); 86562306a36Sopenharmony_ci struct fc_lport *lport = exch->lp; 86662306a36Sopenharmony_ci 86762306a36Sopenharmony_ci struct fc_frame_header *fh; 86862306a36Sopenharmony_ci u8 *granted_mac; 86962306a36Sopenharmony_ci u8 fcoe_mac[6]; 87062306a36Sopenharmony_ci u8 fc_map[3]; 87162306a36Sopenharmony_ci int method; 87262306a36Sopenharmony_ci 87362306a36Sopenharmony_ci if (IS_ERR(fp)) 87462306a36Sopenharmony_ci goto done; 87562306a36Sopenharmony_ci 87662306a36Sopenharmony_ci fh = fc_frame_header_get(fp); 87762306a36Sopenharmony_ci granted_mac = fr_cb(fp)->granted_mac; 87862306a36Sopenharmony_ci 87962306a36Sopenharmony_ci /* 88062306a36Sopenharmony_ci * We set the source MAC for FCoE traffic based on the Granted MAC 88162306a36Sopenharmony_ci * address from the switch. 88262306a36Sopenharmony_ci * 88362306a36Sopenharmony_ci * If granted_mac is non-zero, we use that. 88462306a36Sopenharmony_ci * If the granted_mac is zeroed out, create the FCoE MAC based on 88562306a36Sopenharmony_ci * the sel_fcf->fc_map and the d_id fo the FLOGI frame. 88662306a36Sopenharmony_ci * If sel_fcf->fc_map is 0, then we use the default FCF-MAC plus the 88762306a36Sopenharmony_ci * d_id of the FLOGI frame. 88862306a36Sopenharmony_ci */ 88962306a36Sopenharmony_ci if (!is_zero_ether_addr(granted_mac)) { 89062306a36Sopenharmony_ci ether_addr_copy(fcoe_mac, granted_mac); 89162306a36Sopenharmony_ci method = BNX2FC_FCOE_MAC_METHOD_GRANGED_MAC; 89262306a36Sopenharmony_ci } else if (fip->sel_fcf && fip->sel_fcf->fc_map != 0) { 89362306a36Sopenharmony_ci hton24(fc_map, fip->sel_fcf->fc_map); 89462306a36Sopenharmony_ci fcoe_mac[0] = fc_map[0]; 89562306a36Sopenharmony_ci fcoe_mac[1] = fc_map[1]; 89662306a36Sopenharmony_ci fcoe_mac[2] = fc_map[2]; 89762306a36Sopenharmony_ci fcoe_mac[3] = fh->fh_d_id[0]; 89862306a36Sopenharmony_ci fcoe_mac[4] = fh->fh_d_id[1]; 89962306a36Sopenharmony_ci fcoe_mac[5] = fh->fh_d_id[2]; 90062306a36Sopenharmony_ci method = BNX2FC_FCOE_MAC_METHOD_FCF_MAP; 90162306a36Sopenharmony_ci } else { 90262306a36Sopenharmony_ci fc_fcoe_set_mac(fcoe_mac, fh->fh_d_id); 90362306a36Sopenharmony_ci method = BNX2FC_FCOE_MAC_METHOD_FCOE_SET_MAC; 90462306a36Sopenharmony_ci } 90562306a36Sopenharmony_ci 90662306a36Sopenharmony_ci BNX2FC_HBA_DBG(lport, "fcoe_mac=%pM method=%d\n", fcoe_mac, method); 90762306a36Sopenharmony_ci fip->update_mac(lport, fcoe_mac); 90862306a36Sopenharmony_cidone: 90962306a36Sopenharmony_ci fc_lport_flogi_resp(seq, fp, lport); 91062306a36Sopenharmony_ci} 91162306a36Sopenharmony_ci 91262306a36Sopenharmony_cistatic void bnx2fc_logo_resp(struct fc_seq *seq, struct fc_frame *fp, 91362306a36Sopenharmony_ci void *arg) 91462306a36Sopenharmony_ci{ 91562306a36Sopenharmony_ci struct fcoe_ctlr *fip = arg; 91662306a36Sopenharmony_ci struct fc_exch *exch = fc_seq_exch(seq); 91762306a36Sopenharmony_ci struct fc_lport *lport = exch->lp; 91862306a36Sopenharmony_ci static u8 zero_mac[ETH_ALEN] = { 0 }; 91962306a36Sopenharmony_ci 92062306a36Sopenharmony_ci if (!IS_ERR(fp)) 92162306a36Sopenharmony_ci fip->update_mac(lport, zero_mac); 92262306a36Sopenharmony_ci fc_lport_logo_resp(seq, fp, lport); 92362306a36Sopenharmony_ci} 92462306a36Sopenharmony_ci 92562306a36Sopenharmony_cistruct fc_seq *bnx2fc_elsct_send(struct fc_lport *lport, u32 did, 92662306a36Sopenharmony_ci struct fc_frame *fp, unsigned int op, 92762306a36Sopenharmony_ci void (*resp)(struct fc_seq *, 92862306a36Sopenharmony_ci struct fc_frame *, 92962306a36Sopenharmony_ci void *), 93062306a36Sopenharmony_ci void *arg, u32 timeout) 93162306a36Sopenharmony_ci{ 93262306a36Sopenharmony_ci struct fcoe_port *port = lport_priv(lport); 93362306a36Sopenharmony_ci struct bnx2fc_interface *interface = port->priv; 93462306a36Sopenharmony_ci struct fcoe_ctlr *fip = bnx2fc_to_ctlr(interface); 93562306a36Sopenharmony_ci struct fc_frame_header *fh = fc_frame_header_get(fp); 93662306a36Sopenharmony_ci 93762306a36Sopenharmony_ci switch (op) { 93862306a36Sopenharmony_ci case ELS_FLOGI: 93962306a36Sopenharmony_ci case ELS_FDISC: 94062306a36Sopenharmony_ci return fc_elsct_send(lport, did, fp, op, bnx2fc_flogi_resp, 94162306a36Sopenharmony_ci fip, timeout); 94262306a36Sopenharmony_ci case ELS_LOGO: 94362306a36Sopenharmony_ci /* only hook onto fabric logouts, not port logouts */ 94462306a36Sopenharmony_ci if (ntoh24(fh->fh_d_id) != FC_FID_FLOGI) 94562306a36Sopenharmony_ci break; 94662306a36Sopenharmony_ci return fc_elsct_send(lport, did, fp, op, bnx2fc_logo_resp, 94762306a36Sopenharmony_ci fip, timeout); 94862306a36Sopenharmony_ci } 94962306a36Sopenharmony_ci return fc_elsct_send(lport, did, fp, op, resp, arg, timeout); 95062306a36Sopenharmony_ci} 951