This patch contains code that interfaces with 57712 firmware and ELS handling. Signed-off-by: Bhanu Prakash Gollapudi <bprakash@xxxxxxxxxxxx> --- drivers/scsi/bnx2fc/bnx2fc_els.c | 500 ++++++++++ drivers/scsi/bnx2fc/bnx2fc_hwi.c | 1939 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 2439 insertions(+), 0 deletions(-) create mode 100644 drivers/scsi/bnx2fc/bnx2fc_els.c create mode 100644 drivers/scsi/bnx2fc/bnx2fc_hwi.c diff --git a/drivers/scsi/bnx2fc/bnx2fc_els.c b/drivers/scsi/bnx2fc/bnx2fc_els.c new file mode 100644 index 0000000..af5a4f4 --- /dev/null +++ b/drivers/scsi/bnx2fc/bnx2fc_els.c @@ -0,0 +1,500 @@ +/* bnx2fc_els.c: Broadcom NetXtreme II Linux FCoE offload driver. + * + * Copyright (c) 2008 - 2010 Broadcom Corporation + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation. + * + * Written by: Bhanu Prakash Gollapudi (bprakash@xxxxxxxxxxxx) + */ + +#include "bnx2fc.h" + +static void bnx2fc_logo_resp(struct fc_seq *seq, struct fc_frame *fp, + void *arg); +static void bnx2fc_flogi_resp(struct fc_seq *seq, struct fc_frame *fp, + void *arg); +static int bnx2fc_initiate_els(struct bnx2fc_rport *tgt, unsigned int op, + void *data, u32 data_len, + void (*cb_func)(struct bnx2fc_els_cb_arg *cb_arg), + struct bnx2fc_els_cb_arg *cb_arg, u32 timer_msec); + +void bnx2fc_fill_fc_hdr(struct fc_frame_header *fc_hdr, enum fc_rctl r_ctl, + u32 sid, u32 did, enum fc_fh_type fh_type, + u32 f_ctl, u32 parm_offset) +{ + hton24(fc_hdr->fh_s_id, sid); + hton24(fc_hdr->fh_d_id, did); + fc_hdr->fh_r_ctl = r_ctl; + fc_hdr->fh_type = fh_type; + hton24(fc_hdr->fh_f_ctl, f_ctl); + fc_hdr->fh_cs_ctl = 0; + fc_hdr->fh_df_ctl = 0; + fc_hdr->fh_parm_offset = htonl(parm_offset); +} + +static void bnx2fc_rrq_compl(struct bnx2fc_els_cb_arg *cb_arg) +{ + struct bnx2fc_cmd *orig_io_req; + struct bnx2fc_cmd *rrq_req; + int rc = 0; + + BUG_ON(!cb_arg); + rrq_req = cb_arg->io_req; + bnx2fc_dbg(LOG_ELS, "Entered rrq_compl callback\n"); + orig_io_req = cb_arg->aborted_io_req; + BUG_ON(!orig_io_req); + bnx2fc_dbg(LOG_ELS, "rrq_compl: orig xid = 0x%x, rrq_xid = 0x%x " + "ref_cnt(o) = %d, ref_cnt = %d\n", + orig_io_req->xid, rrq_req->xid, orig_io_req->cmd_refcnt.counter, + rrq_req->cmd_refcnt.counter); + + bnx2fc_cmd_release(orig_io_req); + + if (test_and_clear_bit(BNX2FC_FLAG_ELS_TIMEOUT, &rrq_req->req_flags)) { + /* + * els req is timed out. cleanup the IO with FW and + * drop the completion. Remove from active_cmd_queue. + */ + bnx2fc_dbg(LOG_ELS, "rrq xid - 0x%x timed out, clean it up\n", + rrq_req->xid); + + if (rrq_req->on_active_queue) { + list_del_init(&rrq_req->link); + rrq_req->on_active_queue = 0; + rc = bnx2fc_initiate_cleanup(rrq_req); + BUG_ON(rc); + } + } + kfree(cb_arg); +} +int bnx2fc_send_rrq(struct bnx2fc_cmd *aborted_io_req) +{ + + struct fc_els_rrq rrq; + struct bnx2fc_rport *tgt = aborted_io_req->tgt; + struct fc_lport *lport = tgt->rdata->local_port; + struct bnx2fc_els_cb_arg *cb_arg = NULL; + u32 sid = tgt->sid; + u32 r_a_tov = lport->r_a_tov; + int rc; + + bnx2fc_dbg(LOG_ELS, "Sending RRQ orig_xid = 0x%x\n", + aborted_io_req->xid); + memset(&rrq, 0, sizeof(rrq)); + + cb_arg = kzalloc(sizeof(struct bnx2fc_els_cb_arg), GFP_ATOMIC); + if (!cb_arg) { + printk(KERN_ERR PFX "Unable to allocate cb_arg for RRQ\n"); + rc = -ENOMEM; + goto rrq_err; + } + + cb_arg->aborted_io_req = aborted_io_req; + + rrq.rrq_cmd = ELS_RRQ; + hton24(rrq.rrq_s_id, sid); + rrq.rrq_ox_id = htons(aborted_io_req->xid); + rrq.rrq_rx_id = htons(aborted_io_req->task->rx_wr_tx_rd.rx_id); + + rc = bnx2fc_initiate_els(tgt, ELS_RRQ, &rrq, sizeof(rrq), + bnx2fc_rrq_compl, cb_arg, + r_a_tov); +rrq_err: + if (rc) { + bnx2fc_dbg(LOG_ELS, "RRQ failed - release orig io req 0x%x\n", + aborted_io_req->xid); + kfree(cb_arg); + spin_lock_bh(&tgt->tgt_lock); + bnx2fc_cmd_release(aborted_io_req); + spin_unlock_bh(&tgt->tgt_lock); + } + return rc; +} + +static void bnx2fc_l2_els_compl(struct bnx2fc_els_cb_arg *cb_arg) +{ + struct bnx2fc_cmd *els_req; + struct bnx2fc_rport *tgt; + struct bnx2fc_mp_req *mp_req; + struct fc_frame_header *fc_hdr; + unsigned char *buf; + void *resp_buf; + u32 resp_len, hdr_len; + u16 l2_oxid; + int frame_len; + int rc = 0; + + l2_oxid = cb_arg->l2_oxid; + bnx2fc_dbg(LOG_ELS, "ELS COMPL - l2_oxid = 0x%x\n", l2_oxid); + + els_req = cb_arg->io_req; + if (test_and_clear_bit(BNX2FC_FLAG_ELS_TIMEOUT, &els_req->req_flags)) { + /* + * els req is timed out. cleanup the IO with FW and + * drop the completion. libfc will handle the els timeout + */ + if (els_req->on_active_queue) { + list_del_init(&els_req->link); + els_req->on_active_queue = 0; + rc = bnx2fc_initiate_cleanup(els_req); + BUG_ON(rc); + } + goto free_arg; + } + + tgt = els_req->tgt; + mp_req = &(els_req->mp_req); + fc_hdr = &(mp_req->resp_fc_hdr); + resp_len = mp_req->resp_len; + resp_buf = mp_req->resp_buf; + + buf = kzalloc(0x1000, GFP_ATOMIC); + if (!buf) { + printk(KERN_ERR PFX "Unable to alloc mp buf\n"); + return; + } + hdr_len = sizeof(*fc_hdr); + memcpy(buf, fc_hdr, hdr_len); + memcpy(buf + hdr_len, resp_buf, resp_len); + frame_len = hdr_len + resp_len; + + bnx2fc_process_l2_frame_compl(tgt, buf, frame_len, l2_oxid); + kfree(buf); +free_arg: + kfree(cb_arg); +} + +int bnx2fc_send_adisc(struct bnx2fc_rport *tgt, struct fc_frame *fp) +{ + struct fc_els_adisc *adisc; + struct fc_frame_header *fh; + struct bnx2fc_els_cb_arg *cb_arg; + struct fc_lport *lport = tgt->rdata->local_port; + u32 r_a_tov = lport->r_a_tov; + int rc; + + bnx2fc_dbg(LOG_ELS, "Sending ADISC\n"); + fh = fc_frame_header_get(fp); + cb_arg = kzalloc(sizeof(struct bnx2fc_els_cb_arg), GFP_ATOMIC); + if (!cb_arg) { + printk(KERN_ERR PFX "Unable to allocate cb_arg for ADISC\n"); + return -ENOMEM; + } + + cb_arg->l2_oxid = ntohs(fh->fh_ox_id); + + bnx2fc_dbg(LOG_ELS, "send ADISC: l2_oxid = 0x%x\n", cb_arg->l2_oxid); + adisc = fc_frame_payload_get(fp, sizeof(*adisc)); + /* adisc is initialized by libfc */ + rc = bnx2fc_initiate_els(tgt, ELS_ADISC, adisc, sizeof(*adisc), + bnx2fc_l2_els_compl, cb_arg, 2 * r_a_tov); + if (rc) + kfree(cb_arg); + return rc; +} + +int bnx2fc_send_logo(struct bnx2fc_rport *tgt, struct fc_frame *fp) +{ + struct fc_els_logo *logo; + struct fc_frame_header *fh; + struct bnx2fc_els_cb_arg *cb_arg; + struct fc_lport *lport = tgt->rdata->local_port; + u32 r_a_tov = lport->r_a_tov; + int rc; + + bnx2fc_dbg(LOG_ELS, "Sending LOGO\n"); + fh = fc_frame_header_get(fp); + cb_arg = kzalloc(sizeof(struct bnx2fc_els_cb_arg), GFP_ATOMIC); + if (!cb_arg) { + printk(KERN_ERR PFX "Unable to allocate cb_arg for LOGO\n"); + return -ENOMEM; + } + + cb_arg->l2_oxid = ntohs(fh->fh_ox_id); + + logo = fc_frame_payload_get(fp, sizeof(*logo)); + /* logo is initialized by libfc */ + rc = bnx2fc_initiate_els(tgt, ELS_LOGO, logo, sizeof(*logo), + bnx2fc_l2_els_compl, cb_arg, 2 * r_a_tov); + if (rc) + kfree(cb_arg); + return rc; +} + +static int bnx2fc_initiate_els(struct bnx2fc_rport *tgt, unsigned int op, + void *data, u32 data_len, + void (*cb_func)(struct bnx2fc_els_cb_arg *cb_arg), + struct bnx2fc_els_cb_arg *cb_arg, u32 timer_msec) +{ + struct bnx2fc_port *port = tgt->port; + struct bnx2fc_hba *hba = port->hba; + struct fc_rport *rport = tgt->rport; + struct fc_lport *lport = port->lport; + struct bnx2fc_cmd *els_req; + struct bnx2fc_mp_req *mp_req; + struct fc_frame_header *fc_hdr; + struct fcoe_task_ctx_entry *task; + struct fcoe_task_ctx_entry *task_page; + int rc = 0; + int task_idx, index; + u32 did, sid; + u16 xid; + unsigned long start = jiffies; + + bnx2fc_dbg(LOG_ELS, "Sending ELS\n"); + + rc = fc_remote_port_chkready(rport); + if (rc) { + printk(KERN_ALERT PFX "els 0x%x: rport not ready\n", op); + rc = -EINVAL; + goto els_err; + } + if (lport->state != LPORT_ST_READY || !(lport->link_up)) { + printk(KERN_ALERT PFX "els 0x%x: link is not ready\n", op); + rc = -EINVAL; + goto els_err; + } + if (!(test_bit(BNX2FC_FLAG_SESSION_READY, &tgt->flags)) || + (test_bit(BNX2FC_FLAG_EXPL_LOGO, &tgt->flags))) { + printk(KERN_ERR PFX "els 0x%x: tgt not ready\n", op); + rc = -EINVAL; + goto els_err; + } +retry_els: + els_req = bnx2fc_elstm_alloc(tgt, BNX2FC_ELS); + if (!els_req) { + if (time_after(jiffies, start + (10 * HZ))) { + bnx2fc_dbg(LOG_ELS, "els: Failed els 0x%x", op); + rc = FAILED; + goto els_err; + } + msleep(20); + goto retry_els; + } + + bnx2fc_dbg(LOG_ELS, "initiate_els els_req = 0x%p cb_arg = %p\n", + els_req, cb_arg); + els_req->sc_cmd = NULL; + els_req->port = port; + els_req->tgt = tgt; + els_req->cb_func = cb_func; + cb_arg->io_req = els_req; + els_req->cb_arg = cb_arg; + + mp_req = (struct bnx2fc_mp_req *)&(els_req->mp_req); + rc = bnx2fc_init_mp_req(els_req); + if (rc == FAILED) { + printk(KERN_ALERT PFX "ELS MP request init failed\n"); + spin_lock_bh(&tgt->tgt_lock); + bnx2fc_cmd_release(els_req); + spin_unlock_bh(&tgt->tgt_lock); + goto els_err; + } else { + /* rc SUCCESS */ + rc = 0; + } + + /* Fill ELS Payload */ + if ((op >= ELS_LS_RJT) && (op <= ELS_AUTH_ELS)) { + memcpy(mp_req->req_buf, data, data_len); + } else { + printk(KERN_ALERT PFX "Invalid ELS op 0x%x\n", op); + els_req->cb_func = NULL; + els_req->cb_arg = NULL; + spin_lock_bh(&tgt->tgt_lock); + bnx2fc_cmd_release(els_req); + spin_unlock_bh(&tgt->tgt_lock); + rc = -EINVAL; + } + + if (rc) + goto els_err; + + /* Fill FC header */ + fc_hdr = &(mp_req->req_fc_hdr); + + did = tgt->rport->port_id; + sid = tgt->sid; + + bnx2fc_fill_fc_hdr(fc_hdr, FC_RCTL_ELS_REQ, sid, did, + FC_TYPE_ELS, FC_FC_FIRST_SEQ | FC_FC_END_SEQ | + FC_FC_SEQ_INIT, 0); + + /* Obtain exchange id */ + xid = bnx2fc_get_xid(els_req, hba); + task_idx = xid/BNX2FC_TASKS_PER_PAGE; + index = xid % BNX2FC_TASKS_PER_PAGE; + + /* Initialize task context for this IO request */ + task_page = (struct fcoe_task_ctx_entry *) hba->task_ctx[task_idx]; + task = &(task_page[index]); + bnx2fc_init_mp_task(els_req, task); + + spin_lock_bh(&tgt->tgt_lock); + + if (!test_bit(BNX2FC_FLAG_SESSION_READY, &tgt->flags)) { + printk(KERN_ERR PFX "initiate_els.. session not ready\n"); + els_req->cb_func = NULL; + els_req->cb_arg = NULL; + bnx2fc_cmd_release(els_req); + spin_unlock_bh(&tgt->tgt_lock); + return -EINVAL; + } + + if (timer_msec) + bnx2fc_cmd_timer_set(els_req, timer_msec); + bnx2fc_add_2_sq(tgt, xid); + + els_req->on_active_queue = 1; + list_add_tail(&els_req->link, &tgt->els_queue); + + /* Ring doorbell */ + bnx2fc_dbg(LOG_ELS, "Ringing doorbell for ELS req\n"); + bnx2fc_ring_doorbell(tgt); + spin_unlock_bh(&tgt->tgt_lock); + +els_err: + return rc; +} + +void bnx2fc_process_els_compl(struct bnx2fc_cmd *els_req, + struct fcoe_task_ctx_entry *task, u8 num_rq) +{ + struct bnx2fc_mp_req *mp_req; + struct fc_frame_header *fc_hdr; + u64 *hdr; + u64 *temp_hdr; + + bnx2fc_dbg(LOG_ELS, "Entered process_els_compl xid = 0x%x\n" + "refcnt = %d, cmd_type = %d\n", + els_req->xid, els_req->cmd_refcnt.counter, + els_req->cmd_type); + + if (test_and_set_bit(BNX2FC_FLAG_ELS_DONE, + &els_req->req_flags)) { + bnx2fc_dbg(LOG_ELS, "Timer context finished processing this " + "els - 0x%x\n", els_req->xid); + /* This IO doesnt receive cleanup completion */ + bnx2fc_cmd_release(els_req); + return; + } + + /* Cancel the timeout_work, as we received the response */ + if (cancel_delayed_work(&els_req->timeout_work)) + bnx2fc_cmd_release(els_req); /* drop timer hold */ + + if (els_req->on_active_queue) { + list_del_init(&els_req->link); + els_req->on_active_queue = 0; + } + + mp_req = &(els_req->mp_req); + fc_hdr = &(mp_req->resp_fc_hdr); + + hdr = (u64 *)fc_hdr; + temp_hdr = (u64 *) + &task->cmn.general.cmd_info.mp_fc_frame.fc_hdr; + hdr[0] = cpu_to_be64(temp_hdr[0]); + hdr[1] = cpu_to_be64(temp_hdr[1]); + hdr[2] = cpu_to_be64(temp_hdr[2]); + + mp_req->resp_len = task->rx_wr_only.sgl_ctx.mul_sges.cur_sge_off; + bnx2fc_dbg(LOG_ELS, "els_compl: resp_len = %d\n", mp_req->resp_len); + + /* Parse ELS response */ + if ((els_req->cb_func) && (els_req->cb_arg)) { + els_req->cb_func(els_req->cb_arg); + els_req->cb_arg = NULL; + } + + bnx2fc_cmd_release(els_req); +} + +static void bnx2fc_flogi_resp(struct fc_seq *seq, struct fc_frame *fp, + void *arg) +{ + struct fcoe_ctlr *fip = arg; + struct fc_exch *exch = fc_seq_exch(seq); + struct fc_lport *lport = exch->lp; + u8 *mac; + int i; + struct fc_frame_header *fh; + u8 op; + + if (IS_ERR(fp)) + goto done; + + mac = fr_cb(fp)->granted_mac; + if (is_zero_ether_addr(mac)) { + fh = fc_frame_header_get(fp); + if (fh->fh_type != FC_TYPE_ELS) { + printk(KERN_ERR PFX "bnx2fc_flogi_resp:" + "fh_type != FC_TYPE_ELS\n"); + fc_frame_free(fp); + return; + } + op = fc_frame_payload_op(fp); + if (lport->vport) { + if (op == ELS_LS_RJT) { + printk(KERN_ERR PFX "bnx2fc_flogi_resp is LS_RJT\n"); + fc_vport_terminate(lport->vport); + fc_frame_free(fp); + return; + } + } + if (fcoe_ctlr_recv_flogi(fip, lport, fp)) { + fc_frame_free(fp); + return; + } + } + fip->update_mac(lport, mac); + bnx2fc_dbg(LOG_ELS, "flogi_resp - updated_mac = "); + for (i = 0; i < 6; i++) + bnx2fc_dbg(LOG_ELS, "%02x:", mac[i]); + bnx2fc_dbg(LOG_ELS, "\n"); + +done: + fc_lport_flogi_resp(seq, fp, lport); +} + +static void bnx2fc_logo_resp(struct fc_seq *seq, struct fc_frame *fp, + void *arg) +{ + struct fcoe_ctlr *fip = arg; + struct fc_exch *exch = fc_seq_exch(seq); + struct fc_lport *lport = exch->lp; + static u8 zero_mac[ETH_ALEN] = { 0 }; + + if (!IS_ERR(fp)) + fip->update_mac(lport, zero_mac); + fc_lport_logo_resp(seq, fp, lport); +} + +struct fc_seq *bnx2fc_elsct_send(struct fc_lport *lport, u32 did, + struct fc_frame *fp, unsigned int op, + void (*resp)(struct fc_seq *, + struct fc_frame *, + void *), + void *arg, u32 timeout) +{ + struct bnx2fc_port *port = lport_priv(lport); + struct bnx2fc_hba *hba = port->hba; + struct fcoe_ctlr *fip = &hba->ctlr; + struct fc_frame_header *fh = fc_frame_header_get(fp); + + switch (op) { + case ELS_FLOGI: + case ELS_FDISC: + return fc_elsct_send(lport, did, fp, op, bnx2fc_flogi_resp, + fip, timeout); + case ELS_LOGO: + /* only hook onto fabric logouts, not port logouts */ + if (ntoh24(fh->fh_d_id) != FC_FID_FLOGI) + break; + return fc_elsct_send(lport, did, fp, op, bnx2fc_logo_resp, + fip, timeout); + } + return fc_elsct_send(lport, did, fp, op, resp, arg, timeout); +} diff --git a/drivers/scsi/bnx2fc/bnx2fc_hwi.c b/drivers/scsi/bnx2fc/bnx2fc_hwi.c new file mode 100644 index 0000000..6452b0f --- /dev/null +++ b/drivers/scsi/bnx2fc/bnx2fc_hwi.c @@ -0,0 +1,1939 @@ +/* bnx2fc_hwi.c: Broadcom NetXtreme II Linux FCoE offload driver. + * + * Copyright (c) 2008 - 2010 Broadcom Corporation + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation. + * + * Written by: Bhanu Prakash Gollapudi (bprakash@xxxxxxxxxxxx) + */ + +#include "bnx2fc.h" + +static void bnx2fc_process_cq_compl(struct bnx2fc_rport *tgt, u16 wqe); +static void bnx2fc_fastpath_notification(struct bnx2fc_hba *hba, + struct fcoe_kcqe *new_cqe_kcqe); +static void bnx2fc_process_ofld_cmpl(struct bnx2fc_hba *hba, + struct fcoe_kcqe *ofld_kcqe); +static void bnx2fc_process_enable_conn_cmpl(struct bnx2fc_hba *hba, + struct fcoe_kcqe *ofld_kcqe); +static void bnx2fc_init_failure(struct bnx2fc_hba *hba, u32 err_code); +static void bnx2fc_process_fcoe_error(struct bnx2fc_hba *hba, + struct fcoe_kcqe *fcoe_err); +static void bnx2fc_process_conn_destroy_cmpl(struct bnx2fc_hba *hba, + struct fcoe_kcqe *conn_destroy); + +int bnx2fc_send_stat_req(struct bnx2fc_hba *hba) +{ + struct fcoe_kwqe_stat stat_req; + struct kwqe *kwqe_arr[2]; + int num_kwqes = 1; + int rc = 0; + + bnx2fc_dbg(LOG_INIT, "Entered bnx2fc_send_stat_req\n"); + + memset(&stat_req, 0x00, sizeof(struct fcoe_kwqe_stat)); + stat_req.hdr.op_code = FCOE_KWQE_OPCODE_STAT; + stat_req.hdr.flags = + (FCOE_KWQE_LAYER_CODE << FCOE_KWQE_HEADER_LAYER_CODE_SHIFT); + + stat_req.stat_params_addr_lo = (u32) hba->stats_buf_dma; + stat_req.stat_params_addr_hi = (u32) ((u64)hba->stats_buf_dma >> 32); + + kwqe_arr[0] = (struct kwqe *) &stat_req; + + if (hba->cnic && hba->cnic->submit_kwqes) + rc = hba->cnic->submit_kwqes(hba->cnic, kwqe_arr, num_kwqes); + + return rc; +} + +/** + * bnx2fc_send_fw_fcoe_init_msg - initiates initial handshake with FCoE f/w + * + * @hba: adapter structure pointer + * + * Send down FCoE firmware init KWQEs which initiates the initial handshake + * with the f/w. + * + */ +int bnx2fc_send_fw_fcoe_init_msg(struct bnx2fc_hba *hba) +{ + struct fcoe_kwqe_init1 fcoe_init1; + struct fcoe_kwqe_init2 fcoe_init2; + struct fcoe_kwqe_init3 fcoe_init3; + struct kwqe *kwqe_arr[3]; + int num_kwqes = 3; + int rc = 0; + + if (!hba->cnic) { + printk(KERN_ALERT PFX "hba->cnic NULL during fcoe fw init\n"); + return -ENODEV; + } + + /* fill init1 KWQE */ + memset(&fcoe_init1, 0x00, sizeof(struct fcoe_kwqe_init1)); + fcoe_init1.hdr.op_code = FCOE_KWQE_OPCODE_INIT1; + fcoe_init1.hdr.flags = (FCOE_KWQE_LAYER_CODE << + FCOE_KWQE_HEADER_LAYER_CODE_SHIFT); + + fcoe_init1.num_tasks = BNX2FC_MAX_TASKS; + fcoe_init1.sq_num_wqes = BNX2FC_SQ_WQES_MAX; + fcoe_init1.rq_num_wqes = BNX2FC_RQ_WQES_MAX; + fcoe_init1.rq_buffer_log_size = BNX2FC_RQ_BUF_LOG_SZ; + fcoe_init1.cq_num_wqes = BNX2FC_CQ_WQES_MAX; + fcoe_init1.dummy_buffer_addr_lo = (u32) hba->dummy_buf_dma; + fcoe_init1.dummy_buffer_addr_hi = (u32) ((u64)hba->dummy_buf_dma >> 32); + fcoe_init1.task_list_pbl_addr_lo = (u32) hba->task_ctx_bd_dma; + fcoe_init1.task_list_pbl_addr_hi = + (u32) ((u64) hba->task_ctx_bd_dma >> 32); + fcoe_init1.mtu = hba->netdev->mtu; + + fcoe_init1.flags = (PAGE_SHIFT << + FCOE_KWQE_INIT1_LOG_PAGE_SIZE_SHIFT); + fcoe_init1.flags |= (0 << + FCOE_KWQE_INIT1_LOG_CACHED_PBES_PER_FUNC_SHIFT); + + fcoe_init1.num_sessions_log = BNX2FC_NUM_MAX_SESS_LOG; + + /* fill init2 KWQE */ + memset(&fcoe_init2, 0x00, sizeof(struct fcoe_kwqe_init2)); + fcoe_init2.hdr.op_code = FCOE_KWQE_OPCODE_INIT2; + fcoe_init2.hdr.flags = (FCOE_KWQE_LAYER_CODE << + FCOE_KWQE_HEADER_LAYER_CODE_SHIFT); + + fcoe_init2.hash_tbl_pbl_addr_lo = (u32) hba->hash_tbl_pbl_dma; + fcoe_init2.hash_tbl_pbl_addr_hi = (u32) + ((u64) hba->hash_tbl_pbl_dma >> 32); + + fcoe_init2.t2_hash_tbl_addr_lo = (u32) hba->t2_hash_tbl_dma; + fcoe_init2.t2_hash_tbl_addr_hi = (u32) + ((u64) hba->t2_hash_tbl_dma >> 32); + + fcoe_init2.t2_ptr_hash_tbl_addr_lo = (u32) hba->t2_hash_tbl_ptr_dma; + fcoe_init2.t2_ptr_hash_tbl_addr_hi = (u32) + ((u64) hba->t2_hash_tbl_ptr_dma >> 32); + + fcoe_init2.free_list_count = BNX2FC_NUM_MAX_SESS; + + /* fill init3 KWQE */ + memset(&fcoe_init3, 0x00, sizeof(struct fcoe_kwqe_init3)); + fcoe_init3.hdr.op_code = FCOE_KWQE_OPCODE_INIT3; + fcoe_init3.hdr.flags = (FCOE_KWQE_LAYER_CODE << + FCOE_KWQE_HEADER_LAYER_CODE_SHIFT); + fcoe_init3.error_bit_map_lo = 0xffffffff; + fcoe_init3.error_bit_map_hi = 0xffffffff; + + + kwqe_arr[0] = (struct kwqe *) &fcoe_init1; + kwqe_arr[1] = (struct kwqe *) &fcoe_init2; + kwqe_arr[2] = (struct kwqe *) &fcoe_init3; + + if (hba->cnic && hba->cnic->submit_kwqes) + rc = hba->cnic->submit_kwqes(hba->cnic, kwqe_arr, num_kwqes); + + return rc; +} +int bnx2fc_send_fw_fcoe_destroy_msg(struct bnx2fc_hba *hba) +{ + struct fcoe_kwqe_destroy fcoe_destroy; + struct kwqe *kwqe_arr[2]; + int num_kwqes = 1; + int rc = -1; + + /* fill destroy KWQE */ + memset(&fcoe_destroy, 0x00, sizeof(struct fcoe_kwqe_destroy)); + fcoe_destroy.hdr.op_code = FCOE_KWQE_OPCODE_DESTROY; + fcoe_destroy.hdr.flags = (FCOE_KWQE_LAYER_CODE << + FCOE_KWQE_HEADER_LAYER_CODE_SHIFT); + kwqe_arr[0] = (struct kwqe *) &fcoe_destroy; + + bnx2fc_dbg(LOG_INIT, "Submitting destroy KWQE\n"); + if (hba->cnic && hba->cnic->submit_kwqes) + rc = hba->cnic->submit_kwqes(hba->cnic, kwqe_arr, num_kwqes); + bnx2fc_dbg(LOG_INIT, "destroy rc = %d\n", rc); + + return rc; +} + +/** + * bnx2fc_send_session_ofld_req - initiates FCoE Session offload process + * + * @port: port structure pointer + * @tgt: bnx2fc_rport structure pointer + */ +int bnx2fc_send_session_ofld_req(struct bnx2fc_port *port, + struct bnx2fc_rport *tgt) +{ + struct fc_lport *lport = port->lport; + struct bnx2fc_hba *hba = port->hba; + struct kwqe *kwqe_arr[4]; + struct fcoe_kwqe_conn_offload1 ofld_req1; + struct fcoe_kwqe_conn_offload2 ofld_req2; + struct fcoe_kwqe_conn_offload3 ofld_req3; + struct fcoe_kwqe_conn_offload4 ofld_req4; + struct fc_rport_priv *rdata = tgt->rdata; + struct fc_rport *rport = tgt->rport; + int num_kwqes = 4; + u32 port_id; + int rc = 0; + u16 conn_id; + + /* Initialize offload request 1 structure */ + memset(&ofld_req1, 0x00, sizeof(struct fcoe_kwqe_conn_offload1)); + + ofld_req1.hdr.op_code = FCOE_KWQE_OPCODE_OFFLOAD_CONN1; + ofld_req1.hdr.flags = + (FCOE_KWQE_LAYER_CODE << FCOE_KWQE_HEADER_LAYER_CODE_SHIFT); + + + conn_id = (u16)tgt->fcoe_conn_id; + ofld_req1.fcoe_conn_id = conn_id; + + + ofld_req1.sq_addr_lo = (u32) tgt->sq_dma; + ofld_req1.sq_addr_hi = (u32)((u64) tgt->sq_dma >> 32); + + ofld_req1.rq_pbl_addr_lo = (u32) tgt->rq_pbl_dma; + ofld_req1.rq_pbl_addr_hi = (u32)((u64) tgt->rq_pbl_dma >> 32); + + ofld_req1.rq_first_pbe_addr_lo = (u32) tgt->rq_dma; + ofld_req1.rq_first_pbe_addr_hi = + (u32)((u64) tgt->rq_dma >> 32); + + ofld_req1.rq_prod = 0x8000; + + /* Initialize offload request 2 structure */ + memset(&ofld_req2, 0x00, sizeof(struct fcoe_kwqe_conn_offload2)); + + ofld_req2.hdr.op_code = FCOE_KWQE_OPCODE_OFFLOAD_CONN2; + ofld_req2.hdr.flags = + (FCOE_KWQE_LAYER_CODE << FCOE_KWQE_HEADER_LAYER_CODE_SHIFT); + + ofld_req2.tx_max_fc_pay_len = rdata->maxframe_size; + + ofld_req2.cq_addr_lo = (u32) tgt->cq_dma; + ofld_req2.cq_addr_hi = (u32)((u64)tgt->cq_dma >> 32); + + ofld_req2.xferq_addr_lo = (u32) tgt->xferq_dma; + ofld_req2.xferq_addr_hi = (u32)((u64)tgt->xferq_dma >> 32); + + ofld_req2.conn_db_addr_lo = (u32)tgt->conn_db_dma; + ofld_req2.conn_db_addr_hi = (u32)((u64)tgt->conn_db_dma >> 32); + + /* Initialize offload request 3 structure */ + memset(&ofld_req3, 0x00, sizeof(struct fcoe_kwqe_conn_offload3)); + + ofld_req3.hdr.op_code = FCOE_KWQE_OPCODE_OFFLOAD_CONN3; + ofld_req3.hdr.flags = + (FCOE_KWQE_LAYER_CODE << FCOE_KWQE_HEADER_LAYER_CODE_SHIFT); + + ofld_req3.vlan_tag = hba->vlan_id << + FCOE_KWQE_CONN_OFFLOAD3_VLAN_ID_SHIFT; + ofld_req3.vlan_tag |= 0 << FCOE_KWQE_CONN_OFFLOAD3_CFI_SHIFT; + ofld_req3.vlan_tag |= 3 << FCOE_KWQE_CONN_OFFLOAD3_PRIORITY_SHIFT; + + port_id = fc_host_port_id(lport->host); + if (port_id == 0) { + bnx2fc_dbg(LOG_SESS, "ofld_req: port_id = 0, link down?\n"); + return -EINVAL; + } + + /* + * Store s_id of the initiator for further reference. This will + * be used during disable/destroy during linkdown processing as + * when the lport is reset, the port_id also is reset to 0 + */ + tgt->sid = port_id; + ofld_req3.s_id[0] = (port_id & 0x000000FF); + ofld_req3.s_id[1] = (port_id & 0x0000FF00) >> 8; + ofld_req3.s_id[2] = (port_id & 0x00FF0000) >> 16; + + port_id = rport->port_id; + ofld_req3.d_id[0] = (port_id & 0x000000FF); + ofld_req3.d_id[1] = (port_id & 0x0000FF00) >> 8; + ofld_req3.d_id[2] = (port_id & 0x00FF0000) >> 16; + + ofld_req3.tx_total_conc_seqs = rdata->max_seq; + + ofld_req3.tx_max_conc_seqs_c3 = rdata->max_seq; + ofld_req3.rx_max_fc_pay_len = lport->mfs; + + ofld_req3.rx_total_conc_seqs = BNX2FC_MAX_SEQS; + ofld_req3.rx_max_conc_seqs_c3 = BNX2FC_MAX_SEQS; + ofld_req3.rx_open_seqs_exch_c3 = 1; + + ofld_req3.confq_first_pbe_addr_lo = tgt->confq_dma; + ofld_req3.confq_first_pbe_addr_hi = (u32)((u64) tgt->confq_dma >> 32); + + /* set mul_n_port_ids supported flag to 0, until it is supported */ + ofld_req3.flags = 0; + /* + ofld_req3.flags |= (((lport->send_sp_features & FC_SP_FT_MNA) ? 1:0) << + FCOE_KWQE_CONN_OFFLOAD3_B_MUL_N_PORT_IDS_SHIFT); + */ + /* Info from PLOGI response */ + ofld_req3.flags |= (((rdata->sp_features & FC_SP_FT_EDTR) ? 1 : 0) << + FCOE_KWQE_CONN_OFFLOAD3_B_E_D_TOV_RES_SHIFT); + + ofld_req3.flags |= (((rdata->sp_features & FC_SP_FT_SEQC) ? 1 : 0) << + FCOE_KWQE_CONN_OFFLOAD3_B_CONT_INCR_SEQ_CNT_SHIFT); + + /* Info from PRLI response */ + /* Enable these when Seq Level Error Recovery is supported */ + /* + ofld_req3.flags |= (((rdata->flags & FC_RP_FLAGS_CONF_REQ) + ? 1 : 0) << + FCOE_KWQE_CONN_OFFLOAD3_B_CONF_REQ_SHIFT); + + ofld_req3.flags |= (((rdata->flags & FC_RP_FLAGS_REC_SUPPORTED) + ? 1 : 0) << + FCOE_KWQE_CONN_OFFLOAD3_B_REC_VALID_SHIFT); + */ + ofld_req3.flags |= 0 << FCOE_KWQE_CONN_OFFLOAD3_B_CONF_REQ_SHIFT; + ofld_req3.flags |= 0 << FCOE_KWQE_CONN_OFFLOAD3_B_REC_VALID_SHIFT; + + /* vlan flag */ + ofld_req3.flags |= (hba->vlan_enabled << + FCOE_KWQE_CONN_OFFLOAD3_B_VLAN_FLAG_SHIFT); + + /* C2_VALID and ACK flags are not set as they are not suppported */ + + + /* Initialize offload request 4 structure */ + memset(&ofld_req4, 0x00, sizeof(struct fcoe_kwqe_conn_offload4)); + ofld_req4.hdr.op_code = FCOE_KWQE_OPCODE_OFFLOAD_CONN4; + ofld_req4.hdr.flags = + (FCOE_KWQE_LAYER_CODE << FCOE_KWQE_HEADER_LAYER_CODE_SHIFT); + + ofld_req4.e_d_tov_timer_val = lport->e_d_tov / 20; + + + ofld_req4.src_mac_addr_lo32[0] = port->data_src_addr[5]; + /* local mac */ + ofld_req4.src_mac_addr_lo32[1] = port->data_src_addr[4]; + ofld_req4.src_mac_addr_lo32[2] = port->data_src_addr[3]; + ofld_req4.src_mac_addr_lo32[3] = port->data_src_addr[2]; + ofld_req4.src_mac_addr_hi16[0] = port->data_src_addr[1]; + ofld_req4.src_mac_addr_hi16[1] = port->data_src_addr[0]; + ofld_req4.dst_mac_addr_lo32[0] = hba->ctlr.dest_addr[5];/* fcf mac */ + ofld_req4.dst_mac_addr_lo32[1] = hba->ctlr.dest_addr[4]; + ofld_req4.dst_mac_addr_lo32[2] = hba->ctlr.dest_addr[3]; + ofld_req4.dst_mac_addr_lo32[3] = hba->ctlr.dest_addr[2]; + ofld_req4.dst_mac_addr_hi16[0] = hba->ctlr.dest_addr[1]; + ofld_req4.dst_mac_addr_hi16[1] = hba->ctlr.dest_addr[0]; + + ofld_req4.lcq_addr_lo = (u32) tgt->lcq_dma; + ofld_req4.lcq_addr_hi = (u32)((u64) tgt->lcq_dma >> 32); + + ofld_req4.confq_pbl_base_addr_lo = (u32) tgt->confq_pbl_dma; + ofld_req4.confq_pbl_base_addr_hi = + (u32)((u64) tgt->confq_pbl_dma >> 32); + + kwqe_arr[0] = (struct kwqe *) &ofld_req1; + kwqe_arr[1] = (struct kwqe *) &ofld_req2; + kwqe_arr[2] = (struct kwqe *) &ofld_req3; + kwqe_arr[3] = (struct kwqe *) &ofld_req4; + + if (hba->cnic && hba->cnic->submit_kwqes) + rc = hba->cnic->submit_kwqes(hba->cnic, kwqe_arr, num_kwqes); + + return rc; +} + +/** + * bnx2fc_send_session_enable_req - initiates FCoE Session enablement + * + * @port: port structure pointer + * @tgt: bnx2fc_rport structure pointer + */ +static int bnx2fc_send_session_enable_req(struct bnx2fc_port *port, + struct bnx2fc_rport *tgt) +{ + struct kwqe *kwqe_arr[2]; + struct bnx2fc_hba *hba = port->hba; + struct fcoe_kwqe_conn_enable_disable enbl_req; + struct fc_lport *lport = port->lport; + struct fc_rport *rport = tgt->rport; + int num_kwqes = 1; + int rc = 0; + u32 port_id; + + memset(&enbl_req, 0x00, + sizeof(struct fcoe_kwqe_conn_enable_disable)); + enbl_req.hdr.op_code = FCOE_KWQE_OPCODE_ENABLE_CONN; + enbl_req.hdr.flags = + (FCOE_KWQE_LAYER_CODE << FCOE_KWQE_HEADER_LAYER_CODE_SHIFT); + + enbl_req.src_mac_addr_lo32[0] = port->data_src_addr[5]; + /* local mac */ + enbl_req.src_mac_addr_lo32[1] = port->data_src_addr[4]; + enbl_req.src_mac_addr_lo32[2] = port->data_src_addr[3]; + enbl_req.src_mac_addr_lo32[3] = port->data_src_addr[2]; + enbl_req.src_mac_addr_hi16[0] = port->data_src_addr[1]; + enbl_req.src_mac_addr_hi16[1] = port->data_src_addr[0]; + + enbl_req.dst_mac_addr_lo32[0] = hba->ctlr.dest_addr[5];/* fcf mac */ + enbl_req.dst_mac_addr_lo32[1] = hba->ctlr.dest_addr[4]; + enbl_req.dst_mac_addr_lo32[2] = hba->ctlr.dest_addr[3]; + enbl_req.dst_mac_addr_lo32[3] = hba->ctlr.dest_addr[2]; + enbl_req.dst_mac_addr_hi16[0] = hba->ctlr.dest_addr[1]; + enbl_req.dst_mac_addr_hi16[1] = hba->ctlr.dest_addr[0]; + + port_id = fc_host_port_id(lport->host); + if (port_id != tgt->sid) { + printk(KERN_ERR PFX "WARN: enable_req port_id = 0x%x," + "sid = 0x%x\n", port_id, tgt->sid); + port_id = tgt->sid; + } + enbl_req.s_id[0] = (port_id & 0x000000FF); + enbl_req.s_id[1] = (port_id & 0x0000FF00) >> 8; + enbl_req.s_id[2] = (port_id & 0x00FF0000) >> 16; + + port_id = rport->port_id; + enbl_req.d_id[0] = (port_id & 0x000000FF); + enbl_req.d_id[1] = (port_id & 0x0000FF00) >> 8; + enbl_req.d_id[2] = (port_id & 0x00FF0000) >> 16; + enbl_req.vlan_tag = hba->vlan_id << + FCOE_KWQE_CONN_ENABLE_DISABLE_VLAN_ID_SHIFT; + enbl_req.vlan_tag |= 0 << FCOE_KWQE_CONN_ENABLE_DISABLE_CFI_SHIFT; + enbl_req.vlan_tag |= 3 << FCOE_KWQE_CONN_ENABLE_DISABLE_PRIORITY_SHIFT; + enbl_req.vlan_flag = hba->vlan_enabled; + enbl_req.context_id = tgt->context_id; + enbl_req.conn_id = tgt->fcoe_conn_id; + + kwqe_arr[0] = (struct kwqe *) &enbl_req; + + if (hba->cnic && hba->cnic->submit_kwqes) + rc = hba->cnic->submit_kwqes(hba->cnic, kwqe_arr, num_kwqes); + return rc; +} + +/** + * bnx2fc_send_session_disable_req - initiates FCoE Session disable + * + * @port: port structure pointer + * @tgt: bnx2fc_rport structure pointer + */ +int bnx2fc_send_session_disable_req(struct bnx2fc_port *port, + struct bnx2fc_rport *tgt) +{ + struct bnx2fc_hba *hba = port->hba; + struct fcoe_kwqe_conn_enable_disable disable_req; + struct kwqe *kwqe_arr[2]; + struct fc_rport *rport = tgt->rport; + int num_kwqes = 1; + int rc = 0; + u32 port_id; + + memset(&disable_req, 0x00, + sizeof(struct fcoe_kwqe_conn_enable_disable)); + disable_req.hdr.op_code = FCOE_KWQE_OPCODE_DISABLE_CONN; + disable_req.hdr.flags = + (FCOE_KWQE_LAYER_CODE << FCOE_KWQE_HEADER_LAYER_CODE_SHIFT); + + disable_req.src_mac_addr_lo32[0] = port->data_src_addr[5]; + disable_req.src_mac_addr_lo32[2] = port->data_src_addr[3]; + disable_req.src_mac_addr_lo32[3] = port->data_src_addr[2]; + disable_req.src_mac_addr_hi16[0] = port->data_src_addr[1]; + disable_req.src_mac_addr_hi16[1] = port->data_src_addr[0]; + + disable_req.dst_mac_addr_lo32[0] = hba->ctlr.dest_addr[5];/* fcf mac */ + disable_req.dst_mac_addr_lo32[1] = hba->ctlr.dest_addr[4]; + disable_req.dst_mac_addr_lo32[2] = hba->ctlr.dest_addr[3]; + disable_req.dst_mac_addr_lo32[3] = hba->ctlr.dest_addr[2]; + disable_req.dst_mac_addr_hi16[0] = hba->ctlr.dest_addr[1]; + disable_req.dst_mac_addr_hi16[1] = hba->ctlr.dest_addr[0]; + + port_id = tgt->sid; + bnx2fc_dbg(LOG_SESS, "Host Port id = 0x%x\n", port_id); + disable_req.s_id[0] = (port_id & 0x000000FF); + disable_req.s_id[1] = (port_id & 0x0000FF00) >> 8; + disable_req.s_id[2] = (port_id & 0x00FF0000) >> 16; + + + port_id = rport->port_id; + disable_req.d_id[0] = (port_id & 0x000000FF); + disable_req.d_id[1] = (port_id & 0x0000FF00) >> 8; + disable_req.d_id[2] = (port_id & 0x00FF0000) >> 16; + disable_req.context_id = tgt->context_id; + disable_req.conn_id = tgt->fcoe_conn_id; + disable_req.vlan_tag = hba->vlan_id << + FCOE_KWQE_CONN_ENABLE_DISABLE_VLAN_ID_SHIFT; + disable_req.vlan_tag |= 0 << FCOE_KWQE_CONN_ENABLE_DISABLE_CFI_SHIFT; + disable_req.vlan_tag |= + 3 << FCOE_KWQE_CONN_ENABLE_DISABLE_PRIORITY_SHIFT; + disable_req.vlan_flag = hba->vlan_enabled; + + kwqe_arr[0] = (struct kwqe *) &disable_req; + + if (hba->cnic && hba->cnic->submit_kwqes) + rc = hba->cnic->submit_kwqes(hba->cnic, kwqe_arr, num_kwqes); + + return rc; +} + +/** + * bnx2fc_send_session_destroy_req - initiates FCoE Session destroy + * + * @port: port structure pointer + * @tgt: bnx2fc_rport structure pointer + */ +int bnx2fc_send_session_destroy_req(struct bnx2fc_hba *hba, + struct bnx2fc_rport *tgt) +{ + struct fcoe_kwqe_conn_destroy destroy_req; + struct kwqe *kwqe_arr[2]; + int num_kwqes = 1; + int rc = 0; + + memset(&destroy_req, 0x00, sizeof(struct fcoe_kwqe_conn_destroy)); + destroy_req.hdr.op_code = FCOE_KWQE_OPCODE_DESTROY_CONN; + destroy_req.hdr.flags = + (FCOE_KWQE_LAYER_CODE << FCOE_KWQE_HEADER_LAYER_CODE_SHIFT); + + destroy_req.context_id = tgt->context_id; + destroy_req.conn_id = tgt->fcoe_conn_id; + + kwqe_arr[0] = (struct kwqe *) &destroy_req; + + if (hba->cnic && hba->cnic->submit_kwqes) + rc = hba->cnic->submit_kwqes(hba->cnic, kwqe_arr, num_kwqes); + + return rc; +} + +void bnx2fc_process_l2_frame_compl(struct bnx2fc_rport *tgt, + unsigned char *buf, + u32 frame_len, u16 l2_oxid) +{ + struct bnx2fc_port *port = tgt->port; + struct bnx2fc_hba *hba = port->hba; + struct fc_lport *lp = port->lport; + struct fc_frame *fp; + struct fc_frame_header *fh; + struct fcoe_hdr *hp; + struct fcoe_crc_eof *cp; + struct fcoe_rcv_info *fr; + struct bnx2fc_global_s *bg; + + struct sk_buff *skb; + unsigned int hlen; /* header length implies the version */ + unsigned int tlen; /* trailer length */ + unsigned int elen; /* eth header, may include vlan */ + u32 crc; + struct ethhdr *eh; + + u32 payload_len; + u16 oxid; + int idx; + u8 op; + + bnx2fc_dbg(LOG_FRAME, "l2_frame_compl l2_oxid = 0x%x, frame_len = %d\n", + l2_oxid, frame_len); + payload_len = frame_len - sizeof(struct fc_frame_header); + + fp = fc_frame_alloc(lp, payload_len); + if (!fp) { + printk(KERN_ERR PFX "fc_frame_alloc failure\n"); + return; + } + + fh = (struct fc_frame_header *) fc_frame_header_get(fp); + /* Copy FC Frame header and payload into the frame */ + memcpy(fh, buf, frame_len); + + if (l2_oxid != FC_XID_UNKNOWN) + fh->fh_ox_id = htons(l2_oxid); + + skb = fp_skb(fp); + + if ((fh->fh_r_ctl == FC_RCTL_ELS_REQ) || + (fh->fh_r_ctl == FC_RCTL_ELS_REP)) { + + if (fh->fh_type == FC_TYPE_ELS) { + op = fc_frame_payload_op(fp); + if ((op == ELS_TEST) || (op == ELS_ESTC) || + (op == ELS_FAN) || (op == ELS_CSU)) { + /* + * No need to reply for these + * ELS requests + */ + printk(KERN_ERR PFX "dropping ELS 0x%x\n", op); + kfree_skb(skb); + return; + } + } + /* Pass it on to libfc for further processing */ + elen = (hba->netdev->priv_flags & IFF_802_1Q_VLAN) ? + sizeof(struct vlan_ethhdr) : + sizeof(struct ethhdr); + hlen = sizeof(struct fcoe_hdr); + tlen = sizeof(struct fcoe_crc_eof); + skb->ip_summed = CHECKSUM_NONE; + crc = bnx2fc_crc(fp); + + if (skb_is_nonlinear(skb)) { + skb_frag_t *frag; + if (bnx2fc_get_paged_crc_eof(skb, tlen)) { + printk(KERN_ERR PFX "Frame crc error\n"); + kfree_skb(skb); + return; + } + idx = skb_shinfo(skb)->nr_frags - 1; + frag = &skb_shinfo(skb)->frags[idx]; + cp = kmap_atomic(frag->page, + KM_SKB_DATA_SOFTIRQ) + + frag->page_offset; + } else { + cp = (struct fcoe_crc_eof *)skb_put(skb, tlen); + } + memset(cp, 0, sizeof(*cp)); + /* + * We are supposed to receive single frame + * sequences from the firmware + */ + cp->fcoe_eof = FC_EOF_T; + cp->fcoe_crc32 = cpu_to_le32(~crc); + + if (skb_is_nonlinear(skb)) { + kunmap_atomic(cp, KM_SKB_DATA_SOFTIRQ); + cp = NULL; + } + + skb_push(skb, hlen + elen); + skb_reset_mac_header(skb); + skb_set_network_header(skb, elen); + skb_set_transport_header(skb, elen+hlen); + skb->mac_len = elen; + skb->protocol = htons(ETH_P_FCOE); + skb->dev = hba->netdev; + + /* adjust skb->data to point to fcoe_hdr */ + skb_pull(skb, elen); + + eh = eth_hdr(skb); + eh->h_proto = htons(ETH_P_FCOE); + + /* Should we be setting eh->h_dest and h_source? */ + memcpy(eh->h_dest, port->data_src_addr, ETH_ALEN); + + hp = (struct fcoe_hdr *)skb_network_header(skb); + memset(hp, 0, sizeof(*hp)); + if (FC_FCOE_VER) + FC_FCOE_ENCAPS_VER(hp, FC_FCOE_VER); + + /* needs to be less than 0x30 - FC_SOF_N3 */ + hp->fcoe_sof = FC_SOF_I3; + + fr = fcoe_dev_from_skb(skb); + fr->fr_dev = lp; + oxid = ntohs(fh->fh_ox_id); + + bg = &bnx2fc_global; + spin_lock_bh(&bg->fcoe_rx_list.lock); + + if (unlikely(!bg->l2_thread)) { + spin_unlock_bh(&bg->fcoe_rx_list.lock); + printk(KERN_ERR PFX "ERROR-thread is NULL\n"); + kfree_skb(skb); + return; + } + bnx2fc_dbg(LOG_FRAME, "l2 frame queued\n"); + __skb_queue_tail(&bg->fcoe_rx_list, skb); + if (bg->fcoe_rx_list.qlen == 1) + wake_up_process(bg->l2_thread); + spin_unlock_bh(&bg->fcoe_rx_list.lock); + } else { + bnx2fc_dbg(LOG_FRAME, "fh_r_ctl = 0x%x\n", fh->fh_r_ctl); + kfree_skb(skb); + return; + } +} + +static void bnx2fc_process_unsol_compl(struct bnx2fc_rport *tgt, u16 wqe) +{ + u8 num_rq; + struct fcoe_err_report_entry *err_entry; + unsigned char *rq_data; + unsigned char *buf = NULL, *buf1; + int i; + u16 xid; + u32 frame_len, len; + struct bnx2fc_cmd *io_req = NULL; + struct fcoe_task_ctx_entry *task, *task_page; + struct bnx2fc_hba *hba = tgt->port->hba; + int task_idx, index; + int rc = 0; + + + bnx2fc_dbg(LOG_IOERR, "Entered UNSOL COMPLETION wqe = 0x%x\n", wqe); + switch (wqe & FCOE_UNSOLICITED_CQE_SUBTYPE) { + case FCOE_UNSOLICITED_FRAME_CQE_TYPE: + frame_len = (wqe & FCOE_UNSOLICITED_CQE_PKT_LEN) >> + FCOE_UNSOLICITED_CQE_PKT_LEN_SHIFT; + + num_rq = (frame_len + BNX2FC_RQ_BUF_SZ - 1) / BNX2FC_RQ_BUF_SZ; + + rq_data = (unsigned char *)bnx2fc_get_next_rqe(tgt, num_rq); + if (rq_data) { + buf = rq_data; + } else { + buf1 = buf = kmalloc((num_rq * BNX2FC_RQ_BUF_SZ), + GFP_ATOMIC); + for (i = 0; i < num_rq; i++) { + rq_data = (unsigned char *) + bnx2fc_get_next_rqe(tgt, 1); + len = BNX2FC_RQ_BUF_SZ; + memcpy(buf1, rq_data, len); + buf1 += len; + } + } + bnx2fc_process_l2_frame_compl(tgt, buf, frame_len, + FC_XID_UNKNOWN); + + if (buf != rq_data) + kfree(buf); + bnx2fc_return_rqe(tgt, num_rq); + break; + + case FCOE_ERROR_DETECTION_CQE_TYPE: + /* + *In case of error reporting CQE a single RQ entry + * is consumes. + */ + spin_lock_bh(&tgt->tgt_lock); + num_rq = 1; + err_entry = (struct fcoe_err_report_entry *) + bnx2fc_get_next_rqe(tgt, 1); + xid = err_entry->fc_hdr.ox_id; + bnx2fc_dbg(LOG_IOERR, "Unsol Error Frame OX_ID = 0x%x\n", xid); + bnx2fc_dbg(LOG_IOERR, "err_warn_bitmap = %08x:%08x\n", + err_entry->err_warn_bitmap_hi, + err_entry->err_warn_bitmap_lo); + bnx2fc_dbg(LOG_IOERR, "buf_offsets - tx = 0x%x, rx = 0x%x\n", + err_entry->tx_buf_off, err_entry->rx_buf_off); + + task_idx = xid / BNX2FC_TASKS_PER_PAGE; + index = xid % BNX2FC_TASKS_PER_PAGE; + task_page = (struct fcoe_task_ctx_entry *) + hba->task_ctx[task_idx]; + task = &(task_page[index]); + + io_req = (struct bnx2fc_cmd *)hba->cmd_mgr->cmds[xid]; + if (!io_req) { + spin_unlock_bh(&tgt->tgt_lock); + break; + } + + if (io_req->cmd_type != BNX2FC_SCSI_CMD) { + printk(KERN_ERR PFX "err_warn: Not a SCSI cmd\n"); + spin_unlock_bh(&tgt->tgt_lock); + break; + } + + if (test_and_clear_bit(BNX2FC_FLAG_IO_CLEANUP, + &io_req->req_flags)) { + bnx2fc_dbg(LOG_IOERR, "unsol_err: cleanup in " + "progress.. ignore unsol err\n"); + spin_unlock_bh(&tgt->tgt_lock); + break; + } + + /* + * If ABTS is already in progress, and FW error is + * received after that, do not cancel the timeout_work + * and let the error recovery continue by explicitly + * logging out the target, when the ABTS eventually + * times out. + */ + if (!test_and_set_bit(BNX2FC_FLAG_ISSUE_ABTS, + &io_req->req_flags)) { + /* + * Cancel the timeout_work, as we received IO + * completion with FW error. + */ + if (cancel_delayed_work(&io_req->timeout_work)) + bnx2fc_cmd_release(io_req); /* timer hold */ + + rc = bnx2fc_initiate_abts(io_req); + if (rc != SUCCESS) { + bnx2fc_dbg(LOG_IOERR, "err_warn: initiate_abts " + "failed xid = 0x%x. issue cleanup\n", + io_req->xid); + rc = bnx2fc_initiate_cleanup(io_req); + BUG_ON(rc); + } + } else + printk(KERN_ERR PFX "err_warn: io_req (0x%x) already " + "in ABTS processing\n", xid); + spin_unlock_bh(&tgt->tgt_lock); + break; + + case FCOE_WARNING_DETECTION_CQE_TYPE: + /* + *In case of warning reporting CQE a single RQ entry + * is consumes. + */ + num_rq = 1; + err_entry = (struct fcoe_err_report_entry *) + bnx2fc_get_next_rqe(tgt, 1); + xid = cpu_to_be16(err_entry->fc_hdr.ox_id); + bnx2fc_dbg(LOG_IOERR, "Unsol Warning Frame OX_ID = 0x%x\n", + xid); + bnx2fc_dbg(LOG_IOERR, "err_warn_bitmap = %08x:%08x", + err_entry->err_warn_bitmap_hi, + err_entry->err_warn_bitmap_lo); + bnx2fc_dbg(LOG_IOERR, "buf_offsets - tx = 0x%x, rx = 0x%x", + err_entry->tx_buf_off, err_entry->rx_buf_off); + + break; + + default: + printk(KERN_ERR PFX "Unsol Compl: Invalid CQE Subtype\n"); + break; + } +} + +static void bnx2fc_process_cq_compl(struct bnx2fc_rport *tgt, u16 wqe) +{ + struct fcoe_task_ctx_entry *task; + struct fcoe_task_ctx_entry *task_page; + struct bnx2fc_port *port = tgt->port; + struct bnx2fc_hba *hba = port->hba; + struct bnx2fc_cmd *io_req; + int task_idx, index; + u16 xid; + u8 cmd_type; + u8 rx_state = 0; + u8 num_rq; + + spin_lock_bh(&tgt->tgt_lock); + xid = wqe & FCOE_PEND_WQ_CQE_TASK_ID; + if (xid >= BNX2FC_MAX_TASKS) { + printk(KERN_ALERT PFX "ERROR:xid out of range\n"); + spin_unlock_bh(&tgt->tgt_lock); + return; + } + task_idx = xid / BNX2FC_TASKS_PER_PAGE; + index = xid % BNX2FC_TASKS_PER_PAGE; + task_page = (struct fcoe_task_ctx_entry *)hba->task_ctx[task_idx]; + task = &(task_page[index]); + + num_rq = ((task->rx_wr_tx_rd.rx_flags & + FCOE_TASK_CTX_ENTRY_RXWR_TXRD_NUM_RQ_WQE) >> + FCOE_TASK_CTX_ENTRY_RXWR_TXRD_NUM_RQ_WQE_SHIFT); + + io_req = (struct bnx2fc_cmd *)hba->cmd_mgr->cmds[xid]; + + if (io_req == NULL) { + printk(KERN_ERR PFX "ERROR? cq_compl - io_req is NULL\n"); + spin_unlock_bh(&tgt->tgt_lock); + return; + } + + /* Timestamp IO completion time */ + cmd_type = io_req->cmd_type; + + /* optimized completion path */ + if (cmd_type == BNX2FC_SCSI_CMD) { + rx_state = ((task->rx_wr_tx_rd.rx_flags & + FCOE_TASK_CTX_ENTRY_RXWR_TXRD_RX_STATE) >> + FCOE_TASK_CTX_ENTRY_RXWR_TXRD_RX_STATE_SHIFT); + + if (rx_state == FCOE_TASK_RX_STATE_COMPLETED) { + bnx2fc_process_scsi_cmd_compl(io_req, task, num_rq); + spin_unlock_bh(&tgt->tgt_lock); + return; + } + } + + /* Process other IO completion types */ + switch (cmd_type) { + case BNX2FC_SCSI_CMD: + bnx2fc_dbg(LOG_IOERR, "SCSI_CMD(0x%x) "\ + "complete - rx_state = %d\n", io_req->xid, rx_state); + + if (rx_state == FCOE_TASK_RX_STATE_ABTS_COMPLETED) + bnx2fc_process_abts_compl(io_req, task, num_rq); + else if (rx_state == + FCOE_TASK_RX_STATE_EXCHANGE_CLEANUP_COMPLETED) + bnx2fc_process_cleanup_compl(io_req, task, num_rq); + else + printk(KERN_ERR PFX "Invalid rx state - %d\n", + rx_state); + break; + + case BNX2FC_TASK_MGMT_CMD: + bnx2fc_dbg(LOG_IOERR, "Processing TM complete (0x%x)\n", + io_req->xid); + bnx2fc_process_tm_compl(io_req, task, num_rq); + break; + + case BNX2FC_ABTS: + /* + * ABTS request received by firmware. ABTS response + * will be delivered to the task belonging to the IO + * that was aborted + */ + bnx2fc_dbg(LOG_IOERR, "cq_compl(0x%x) - ABTS sent out " + "by fw\n", io_req->xid); + bnx2fc_cmd_release(io_req); + break; + + case BNX2FC_ELS: + bnx2fc_dbg(LOG_ELS, "cq_compl(0x%x) - call" + " process_els_compl\n", io_req->xid); + bnx2fc_process_els_compl(io_req, task, num_rq); + break; + + case BNX2FC_CLEANUP: + bnx2fc_dbg(LOG_IOERR, "cq_compl(0x%x) - cleanup resp rcvd\n", + io_req->xid); + + bnx2fc_cmd_release(io_req); + break; + + default: + printk(KERN_ERR PFX "Invalid cmd_type %d\n", cmd_type); + break; + } + spin_unlock_bh(&tgt->tgt_lock); + return; +} + +int bnx2fc_process_new_cqes(struct bnx2fc_rport *tgt) +{ + struct fcoe_cqe *cq; + u32 cq_cons; + struct fcoe_cqe *cqe; + u16 wqe; + bool more_cqes_found = false; + + /* + * cq_lock is a low contention lock used to protect + * the CQ data structure from being freed up during + * the upload operation + */ + spin_lock_bh(&tgt->cq_lock); + + if (!tgt->cq) { + printk(KERN_ERR PFX "process_new_cqes: cq is NULL\n"); + spin_unlock_bh(&tgt->cq_lock); + return 0; + } + cq = tgt->cq; + cq_cons = tgt->cq_cons_idx; + cqe = &cq[cq_cons]; + + do { + more_cqes_found ^= true; + + while (((wqe = cqe->wqe) & FCOE_CQE_TOGGLE_BIT) == + (tgt->cq_curr_toggle_bit << + FCOE_CQE_TOGGLE_BIT_SHIFT)) { + + /* new entry on the cq */ + if (wqe & FCOE_CQE_CQE_TYPE) { + /* Unsolicited event notification */ + bnx2fc_process_unsol_compl(tgt, wqe); + } else { + /* Pending work request completion */ + bnx2fc_process_cq_compl(tgt, wqe); + } + cqe++; + tgt->cq_cons_idx++; + + if (tgt->cq_cons_idx == BNX2FC_CQ_WQES_MAX) { + tgt->cq_cons_idx = 0; + cqe = cq; + tgt->cq_curr_toggle_bit = + 1 - tgt->cq_curr_toggle_bit; + } + } + /* Re-arm CQ */ + if (more_cqes_found) { + tgt->conn_db->cq_arm.lo = -1; + wmb(); + } + } while (more_cqes_found); + + /* + * Commit tgt->cq_cons_idx change to the memory + * spin_lock implies full memory barrier, no need to smp_wmb + */ + + spin_unlock_bh(&tgt->cq_lock); + return 0; +} + +/** + * bnx2fc_fastpath_notification - process global event queue (KCQ) + * + * @hba: adapter structure pointer + * @new_cqe_kcqe: pointer to newly DMA'd KCQ entry + * + * Fast path event notification handler + */ +static void bnx2fc_fastpath_notification(struct bnx2fc_hba *hba, + struct fcoe_kcqe *new_cqe_kcqe) +{ + u32 conn_id = new_cqe_kcqe->fcoe_conn_id; + struct bnx2fc_rport *tgt = + (struct bnx2fc_rport *)hba->tgt_ofld_list[conn_id]; + + if (!tgt) { + printk(KERN_ALERT PFX "conn_id 0x%x not valid\n", conn_id); + return; + } + bnx2fc_process_new_cqes(tgt); +} + +/** + * bnx2fc_process_ofld_cmpl - process FCoE session offload completion + * + * @hba: adapter structure pointer + * @ofld_kcqe: connection offload kcqe pointer + * + * handle session offload completion, enable the session if offload is + * successful. + */ +static void bnx2fc_process_ofld_cmpl(struct bnx2fc_hba *hba, + struct fcoe_kcqe *ofld_kcqe) +{ + struct bnx2fc_rport *tgt; + struct bnx2fc_port *port; + u32 conn_id; + u32 context_id; + int rc; + + conn_id = ofld_kcqe->fcoe_conn_id; + context_id = ofld_kcqe->fcoe_conn_context_id; + bnx2fc_dbg(LOG_SESS, "Entered ofld compl - context_id = 0x%x\n", + ofld_kcqe->fcoe_conn_context_id); + tgt = (struct bnx2fc_rport *)hba->tgt_ofld_list[conn_id]; + if (!tgt) { + printk(KERN_ALERT PFX "ERROR:ofld_cmpl: No pending ofld req\n"); + return; + } + port = tgt->port; + if (hba != tgt->port->hba) { + printk(KERN_ALERT PFX "ERROR:ofld_cmpl: HBA mis-match\n"); + goto ofld_cmpl_err; + } + /* + * cnic has allocated a context_id for this session; use this + * while enabling the session. + */ + tgt->context_id = context_id; + if (ofld_kcqe->completion_status) { + if (ofld_kcqe->completion_status == + FCOE_KCQE_COMPLETION_STATUS_CTX_ALLOC_FAILURE) { + printk(KERN_ERR PFX "unable to allocate FCoE context " + "resources\n"); + set_bit(BNX2FC_FLAG_CTX_ALLOC_FAILURE, &tgt->flags); + } + goto ofld_cmpl_err; + } else { + + /* now enable the session */ + rc = bnx2fc_send_session_enable_req(port, tgt); + if (rc) { + printk(KERN_ALERT PFX "enable session failed\n"); + goto ofld_cmpl_err; + } + } + return; +ofld_cmpl_err: + set_bit(BNX2FC_FLAG_OFLD_REQ_CMPL, &tgt->flags); + wake_up_interruptible(&tgt->ofld_wait); + return; +} + +/** + * bnx2fc_process_enable_conn_cmpl - process FCoE session enable completion + * + * @hba: adapter structure pointer + * @ofld_kcqe: connection offload kcqe pointer + * + * handle session enable completion, mark the rport as ready + */ + +static void bnx2fc_process_enable_conn_cmpl(struct bnx2fc_hba *hba, + struct fcoe_kcqe *ofld_kcqe) +{ + struct bnx2fc_rport *tgt; + u32 conn_id; + u32 context_id; + + context_id = ofld_kcqe->fcoe_conn_context_id; + conn_id = ofld_kcqe->fcoe_conn_id; + tgt = (struct bnx2fc_rport *)hba->tgt_ofld_list[conn_id]; + + bnx2fc_dbg(LOG_SESS, "Enable compl - context_id = 0x%x\n", + ofld_kcqe->fcoe_conn_context_id); + if (!tgt) { + printk(KERN_ALERT PFX "ERROR:enbl_cmpl: No pending ofld req\n"); + return; + } + /* + * context_id should be the same for this target during offload + * and enable + */ + if (tgt->context_id != context_id) { + printk(KERN_ALERT PFX "context id mis-match\n"); + return; + } + if (hba != tgt->port->hba) { + printk(KERN_ALERT PFX "bnx2fc-enbl_cmpl: HBA mis-match\n"); + goto enbl_cmpl_err; + } + if (ofld_kcqe->completion_status) { + goto enbl_cmpl_err; + } else { + /* enable successful - rport ready for issuing IOs */ + set_bit(BNX2FC_FLAG_OFFLOADED, &tgt->flags); + set_bit(BNX2FC_FLAG_OFLD_REQ_CMPL, &tgt->flags); + wake_up_interruptible(&tgt->ofld_wait); + } + return; + +enbl_cmpl_err: + set_bit(BNX2FC_FLAG_OFLD_REQ_CMPL, &tgt->flags); + wake_up_interruptible(&tgt->ofld_wait); + return; +} + +static void bnx2fc_process_conn_disable_cmpl(struct bnx2fc_hba *hba, + struct fcoe_kcqe *disable_kcqe) +{ + + struct bnx2fc_rport *tgt; + u32 conn_id; + + conn_id = disable_kcqe->fcoe_conn_id; + tgt = (struct bnx2fc_rport *)hba->tgt_ofld_list[conn_id]; + bnx2fc_dbg(LOG_SESS, PFX "disable_cmpl: conn_id %d tgt 0x%p\n", + conn_id, tgt); + if (!tgt) { + printk(KERN_ALERT PFX "ERROR: disable_cmpl: No disable req\n"); + return; + } + + if (disable_kcqe->completion_status) { + printk(KERN_ALERT PFX "ERROR: Disable failed with cmpl status %d\n", + disable_kcqe->completion_status); + return; + } else { + /* disable successful */ + bnx2fc_dbg(LOG_SESS, "disable successful\n"); + clear_bit(BNX2FC_FLAG_OFFLOADED, &tgt->flags); + set_bit(BNX2FC_FLAG_DISABLED, &tgt->flags); + set_bit(BNX2FC_FLAG_UPLD_REQ_COMPL, &tgt->flags); + wake_up_interruptible(&tgt->upld_wait); + } + return; +} + +static void bnx2fc_process_conn_destroy_cmpl(struct bnx2fc_hba *hba, + struct fcoe_kcqe *destroy_kcqe) +{ + struct bnx2fc_rport *tgt; + u32 conn_id; + + conn_id = destroy_kcqe->fcoe_conn_id; + tgt = (struct bnx2fc_rport *)hba->tgt_ofld_list[conn_id]; + bnx2fc_dbg(LOG_SESS, "destroy_cmpl: conn_id %d tgt 0x%p\n", + conn_id, tgt); + if (!tgt) { + printk(KERN_ALERT PFX "destroy_cmpl: No destroy req\n"); + return; + } + + if (destroy_kcqe->completion_status) { + printk(KERN_ALERT PFX "Destroy conn failed, cmpl status %d\n", + destroy_kcqe->completion_status); + return; + } else { + /* destroy successful */ + bnx2fc_dbg(LOG_SESS, "upload successful\n"); + clear_bit(BNX2FC_FLAG_DISABLED, &tgt->flags); + set_bit(BNX2FC_FLAG_DESTROYED, &tgt->flags); + set_bit(BNX2FC_FLAG_UPLD_REQ_COMPL, &tgt->flags); + wake_up_interruptible(&tgt->upld_wait); + } + return; +} + +static void bnx2fc_init_failure(struct bnx2fc_hba *hba, u32 err_code) +{ + switch (err_code) { + case FCOE_KCQE_COMPLETION_STATUS_INVALID_OPCODE: + printk(KERN_ERR PFX "init_failure due to invalid opcode\n"); + break; + + case FCOE_KCQE_COMPLETION_STATUS_CTX_ALLOC_FAILURE: + printk(KERN_ERR PFX "init failed due to ctx alloc failure\n"); + break; + + case FCOE_KCQE_COMPLETION_STATUS_NIC_ERROR: + printk(KERN_ERR PFX "init_failure due to NIC error\n"); + break; + + default: + printk(KERN_ERR PFX "Unknown Error code %d\n", err_code); + } +} + +/** + * bnx2fc_indicae_kcqe - process KCQE + * + * @hba: adapter structure pointer + * @kcqe: kcqe pointer + * @num_cqe: Number of completion queue elements + * + * Generic KCQ event handler + */ +void bnx2fc_indicate_kcqe(void *context, struct kcqe *kcq[], + u32 num_cqe) +{ + struct bnx2fc_hba *hba = (struct bnx2fc_hba *)context; + int i = 0; + struct fcoe_kcqe *kcqe = NULL; + + while (i < num_cqe) { + kcqe = (struct fcoe_kcqe *) kcq[i++]; + + switch (kcqe->op_code) { + case FCOE_KCQE_OPCODE_CQ_EVENT_NOTIFICATION: + bnx2fc_fastpath_notification(hba, kcqe); + break; + + case FCOE_KCQE_OPCODE_OFFLOAD_CONN: + bnx2fc_process_ofld_cmpl(hba, kcqe); + break; + + case FCOE_KCQE_OPCODE_ENABLE_CONN: + bnx2fc_process_enable_conn_cmpl(hba, kcqe); + break; + + case FCOE_KCQE_OPCODE_INIT_FUNC: + if (kcqe->completion_status != + FCOE_KCQE_COMPLETION_STATUS_SUCCESS) { + bnx2fc_init_failure(hba, + kcqe->completion_status); + } else { + set_bit(ADAPTER_STATE_UP, &hba->adapter_state); + bnx2fc_get_link_state(hba); + printk(KERN_INFO PFX "[%.2x]: FCOE_INIT passed\n", + (u8)hba->pcidev->bus->number); + } + break; + + case FCOE_KCQE_OPCODE_DESTROY_FUNC: + if (kcqe->completion_status != + FCOE_KCQE_COMPLETION_STATUS_SUCCESS) { + + printk(KERN_ERR PFX "DESTROY failed\n"); + } else { + printk(KERN_ERR PFX "DESTROY success\n"); + } + hba->flags |= BNX2FC_FLAG_DESTROY_CMPL; + wake_up_interruptible(&hba->destroy_wait); + break; + + case FCOE_KCQE_OPCODE_DISABLE_CONN: + bnx2fc_process_conn_disable_cmpl(hba, kcqe); + break; + + case FCOE_KCQE_OPCODE_DESTROY_CONN: + bnx2fc_process_conn_destroy_cmpl(hba, kcqe); + break; + + case FCOE_KCQE_OPCODE_STAT_FUNC: + if (kcqe->completion_status != + FCOE_KCQE_COMPLETION_STATUS_SUCCESS) + printk(KERN_ERR PFX "STAT failed\n"); + else + bnx2fc_dbg(LOG_INIT, "STAT Success\n"); + + complete(&hba->stat_req_done); + break; + + case FCOE_KCQE_OPCODE_FCOE_ERROR: + bnx2fc_process_fcoe_error(hba, kcqe); + break; + + default: + printk(KERN_ALERT PFX "unknown opcode 0x%x\n", + kcqe->op_code); + } + } +} + +static void bnx2fc_process_fcoe_error(struct bnx2fc_hba *hba, + struct fcoe_kcqe *fcoe_err) +{ + + +} + +void bnx2fc_add_2_sq(struct bnx2fc_rport *tgt, u16 xid) +{ + struct fcoe_sqe *sqe; + + sqe = (struct fcoe_sqe *) &tgt->sq[tgt->sq_prod_idx]; + + /* Fill SQ WQE */ + sqe->wqe = xid << FCOE_SQE_TASK_ID_SHIFT; + sqe->wqe |= tgt->sq_curr_toggle_bit << FCOE_SQE_TOGGLE_BIT_SHIFT; + + /* Advance SQ Prod Idx */ + if (++tgt->sq_prod_idx == BNX2FC_SQ_WQES_MAX) { + tgt->sq_prod_idx = 0; + tgt->sq_curr_toggle_bit = 1 - tgt->sq_curr_toggle_bit; + } +} + +void bnx2fc_ring_doorbell(struct bnx2fc_rport *tgt) +{ + struct b577xx_doorbell_set_prod ev_doorbell; + u32 msg; + + wmb(); + + memset(&ev_doorbell, 0, sizeof(struct b577xx_doorbell_set_prod)); + ev_doorbell.header.header = B577XX_DOORBELL_HDR_DB_TYPE; + + ev_doorbell.prod = tgt->sq_prod_idx | + (tgt->sq_curr_toggle_bit << 15); + ev_doorbell.header.header |= B577XX_FCOE_CONNECTION_TYPE << + B577XX_DOORBELL_HDR_CONN_TYPE_SHIFT; + msg = *((u32 *)&ev_doorbell); + writel(cpu_to_le32(msg), tgt->ctx_base); + + mmiowb(); + +} + +void bnx2fc_map_doorbell(struct bnx2fc_rport *tgt) +{ + u32 context_id = tgt->context_id; + struct bnx2fc_port *port = tgt->port; + u32 reg_off; + resource_size_t reg_base; + + reg_base = pci_resource_start(port->hba->pcidev, + BNX2X_DOORBELL_PCI_BAR); + reg_off = BNX2FC_5771X_DB_PAGE_SIZE * + (context_id & 0x1FFFF) + DPM_TRIGER_TYPE; + tgt->ctx_base = ioremap_nocache(reg_base + reg_off, 4); +} + +char *bnx2fc_get_next_rqe(struct bnx2fc_rport *tgt, u8 num_items) +{ + char *buf = (char *)tgt->rq + (tgt->rq_cons_idx * BNX2FC_RQ_BUF_SZ); + + if (tgt->rq_cons_idx + num_items > BNX2FC_RQ_WQES_MAX) + return NULL; + + tgt->rq_cons_idx += num_items; + + if (tgt->rq_cons_idx >= BNX2FC_RQ_WQES_MAX) + tgt->rq_cons_idx -= BNX2FC_RQ_WQES_MAX; + + return buf; +} + +void bnx2fc_return_rqe(struct bnx2fc_rport *tgt, u8 num_items) +{ + /* return the rq buffer */ + u32 next_prod_idx = tgt->rq_prod_idx + num_items; + if ((next_prod_idx & 0x7fff) == BNX2FC_RQ_WQES_MAX) { + /* Wrap around RQ */ + next_prod_idx += 0x8000 - BNX2FC_RQ_WQES_MAX; + } + tgt->rq_prod_idx = next_prod_idx; + tgt->conn_db->rq_prod = tgt->rq_prod_idx; +} + +void bnx2fc_init_cleanup_task(struct bnx2fc_cmd *io_req, + struct fcoe_task_ctx_entry *task, + u16 orig_xid) +{ + u8 task_type = FCOE_TASK_TYPE_EXCHANGE_CLEANUP; + struct bnx2fc_rport *tgt = io_req->tgt; + u32 context_id = tgt->context_id; + + memset(task, 0, sizeof(struct fcoe_task_ctx_entry)); + + /* Tx Write Rx Read */ + task->tx_wr_rx_rd.tx_flags = FCOE_TASK_TX_STATE_EXCHANGE_CLEANUP << + FCOE_TASK_CTX_ENTRY_TXWR_RXRD_TX_STATE_SHIFT; + task->tx_wr_rx_rd.init_flags = task_type << + FCOE_TASK_CTX_ENTRY_TXWR_RXRD_TASK_TYPE_SHIFT; + task->tx_wr_rx_rd.init_flags |= FCOE_TASK_CLASS_TYPE_3 << + FCOE_TASK_CTX_ENTRY_TXWR_RXRD_CLASS_TYPE_SHIFT; + /* Common */ + task->cmn.common_flags = context_id << + FCOE_TASK_CTX_ENTRY_TX_RX_CMN_CID_SHIFT; + task->cmn.general.cleanup_info.task_id = orig_xid; + + +} + +void bnx2fc_init_mp_task(struct bnx2fc_cmd *io_req, + struct fcoe_task_ctx_entry *task) +{ + struct bnx2fc_mp_req *mp_req = &(io_req->mp_req); + struct bnx2fc_rport *tgt = io_req->tgt; + struct fc_frame_header *fc_hdr; + u8 task_type = 0; + u64 *hdr; + u64 temp_hdr[3]; + u32 context_id; + + bnx2fc_dbg(LOG_IOERR, "Initializing MP task for cmd_type = %d\n", + io_req->cmd_type); + + /* Obtain task_type */ + if ((io_req->cmd_type == BNX2FC_TASK_MGMT_CMD) || + (io_req->cmd_type == BNX2FC_ELS)) { + task_type = FCOE_TASK_TYPE_MIDPATH; + } else if (io_req->cmd_type == BNX2FC_ABTS) { + task_type = FCOE_TASK_TYPE_ABTS; + } + + memset(task, 0, sizeof(struct fcoe_task_ctx_entry)); + + /* Setup the task from io_req for easy reference */ + io_req->task = task; + + bnx2fc_dbg(LOG_IOERR, "task type = %d\n", task_type); + + /* Tx only */ + if ((task_type == FCOE_TASK_TYPE_MIDPATH) || + (task_type == FCOE_TASK_TYPE_UNSOLICITED)) { + task->tx_wr_only.sgl_ctx.mul_sges.cur_sge_addr.lo = + (u32)mp_req->mp_req_bd_dma; + task->tx_wr_only.sgl_ctx.mul_sges.cur_sge_addr.hi = + (u32)((u64)mp_req->mp_req_bd_dma >> 32); + task->tx_wr_only.sgl_ctx.mul_sges.sgl_size = 1; + bnx2fc_dbg(LOG_IOERR, "init_mp_task - bd_dma = 0x%llx\n", + mp_req->mp_req_bd_dma); + } + + /* Tx Write Rx Read */ + task->tx_wr_rx_rd.tx_flags = FCOE_TASK_TX_STATE_INIT << + FCOE_TASK_CTX_ENTRY_TXWR_RXRD_TX_STATE_SHIFT; + task->tx_wr_rx_rd.init_flags = task_type << + FCOE_TASK_CTX_ENTRY_TXWR_RXRD_TASK_TYPE_SHIFT; + task->tx_wr_rx_rd.init_flags |= FCOE_TASK_DEV_TYPE_DISK << + FCOE_TASK_CTX_ENTRY_TXWR_RXRD_DEV_TYPE_SHIFT; + task->tx_wr_rx_rd.init_flags |= FCOE_TASK_CLASS_TYPE_3 << + FCOE_TASK_CTX_ENTRY_TXWR_RXRD_CLASS_TYPE_SHIFT; + + /* Common */ + task->cmn.data_2_trns = io_req->data_xfer_len; + context_id = tgt->context_id; + task->cmn.common_flags = context_id << + FCOE_TASK_CTX_ENTRY_TX_RX_CMN_CID_SHIFT; + task->cmn.common_flags |= 1 << + FCOE_TASK_CTX_ENTRY_TX_RX_CMN_VALID_SHIFT; + task->cmn.common_flags |= 1 << + FCOE_TASK_CTX_ENTRY_TX_RX_CMN_EXP_FIRST_FRAME_SHIFT; + + /* Rx Write Tx Read */ + fc_hdr = &(mp_req->req_fc_hdr); + if (task_type == FCOE_TASK_TYPE_MIDPATH) { + fc_hdr->fh_ox_id = cpu_to_be16(io_req->xid); + fc_hdr->fh_rx_id = htons(0xffff); + task->rx_wr_tx_rd.rx_id = 0xffff; + } else if (task_type == FCOE_TASK_TYPE_UNSOLICITED) { + fc_hdr->fh_rx_id = cpu_to_be16(io_req->xid); + } + + /* Fill FC Header into middle path buffer */ + hdr = (u64 *) &task->cmn.general.cmd_info.mp_fc_frame.fc_hdr; + memcpy(temp_hdr, fc_hdr, sizeof(temp_hdr)); + hdr[0] = cpu_to_be64(temp_hdr[0]); + hdr[1] = cpu_to_be64(temp_hdr[1]); + hdr[2] = cpu_to_be64(temp_hdr[2]); + + /* Rx Only */ + if (task_type == FCOE_TASK_TYPE_MIDPATH) { + + task->rx_wr_only.sgl_ctx.mul_sges.cur_sge_addr.lo = + (u32)mp_req->mp_resp_bd_dma; + task->rx_wr_only.sgl_ctx.mul_sges.cur_sge_addr.hi = + (u32)((u64)mp_req->mp_resp_bd_dma >> 32); + task->rx_wr_only.sgl_ctx.mul_sges.sgl_size = 1; + } +} + +void bnx2fc_init_task(struct bnx2fc_cmd *io_req, + struct fcoe_task_ctx_entry *task) +{ + u8 task_type; + struct scsi_cmnd *sc_cmd = io_req->sc_cmd; + struct io_bdt *bd_tbl = io_req->bd_tbl; + struct bnx2fc_rport *tgt = io_req->tgt; + u64 *fcp_cmnd; + u64 tmp_fcp_cmnd[4]; + u32 context_id; + int cnt, i; + int bd_count; + + memset(task, 0, sizeof(struct fcoe_task_ctx_entry)); + + /* Setup the task from io_req for easy reference */ + io_req->task = task; + + if (sc_cmd->sc_data_direction == DMA_TO_DEVICE) + task_type = FCOE_TASK_TYPE_WRITE; + else + task_type = FCOE_TASK_TYPE_READ; + + /* Tx only */ + if (task_type == FCOE_TASK_TYPE_WRITE) { + task->tx_wr_only.sgl_ctx.mul_sges.cur_sge_addr.lo = + (u32)bd_tbl->bd_tbl_dma; + task->tx_wr_only.sgl_ctx.mul_sges.cur_sge_addr.hi = + (u32)((u64)bd_tbl->bd_tbl_dma >> 32); + task->tx_wr_only.sgl_ctx.mul_sges.sgl_size = + bd_tbl->bd_valid; + } + + /*Tx Write Rx Read */ + /* Init state to NORMAL */ + task->tx_wr_rx_rd.tx_flags = FCOE_TASK_TX_STATE_NORMAL << + FCOE_TASK_CTX_ENTRY_TXWR_RXRD_TX_STATE_SHIFT; + task->tx_wr_rx_rd.init_flags = task_type << + FCOE_TASK_CTX_ENTRY_TXWR_RXRD_TASK_TYPE_SHIFT; + task->tx_wr_rx_rd.init_flags |= FCOE_TASK_DEV_TYPE_DISK << + FCOE_TASK_CTX_ENTRY_TXWR_RXRD_DEV_TYPE_SHIFT; + task->tx_wr_rx_rd.init_flags |= FCOE_TASK_CLASS_TYPE_3 << + FCOE_TASK_CTX_ENTRY_TXWR_RXRD_CLASS_TYPE_SHIFT; + + /* Common */ + task->cmn.data_2_trns = io_req->data_xfer_len; + context_id = tgt->context_id; + task->cmn.common_flags = context_id << + FCOE_TASK_CTX_ENTRY_TX_RX_CMN_CID_SHIFT; + task->cmn.common_flags |= 1 << + FCOE_TASK_CTX_ENTRY_TX_RX_CMN_VALID_SHIFT; + task->cmn.common_flags |= 1 << + FCOE_TASK_CTX_ENTRY_TX_RX_CMN_EXP_FIRST_FRAME_SHIFT; + + /* Set initiative ownership */ + task->cmn.common_flags |= FCOE_TASK_CTX_ENTRY_TX_RX_CMN_SEQ_INIT; + + /* Set initial seq counter */ + task->cmn.tx_low_seq_cnt = 1; + + /* Set state to "waiting for the first packet" */ + task->cmn.common_flags |= FCOE_TASK_CTX_ENTRY_TX_RX_CMN_EXP_FIRST_FRAME; + + /* Fill FCP_CMND IU */ + fcp_cmnd = (u64 *) + task->cmn.general.cmd_info.fcp_cmd_payload.opaque; + bnx2fc_build_fcp_cmnd(io_req, (struct fcp_cmnd *)&tmp_fcp_cmnd); + + /* swap fcp_cmnd */ + cnt = sizeof(struct fcp_cmnd) / sizeof(u64); + + for (i = 0; i < cnt; i++) { + *fcp_cmnd = cpu_to_be64(tmp_fcp_cmnd[i]); + fcp_cmnd++; + } + + /* Rx Write Tx Read */ + task->rx_wr_tx_rd.rx_id = 0xffff; + + /* Rx Only */ + if (task_type == FCOE_TASK_TYPE_READ) { + + bd_count = bd_tbl->bd_valid; + if (bd_count == 1) { + + struct fcoe_bd_ctx *fcoe_bd_tbl = bd_tbl->bd_tbl; + + task->rx_wr_only.sgl_ctx.single_sge.cur_buf_addr.lo = + fcoe_bd_tbl->buf_addr_lo; + task->rx_wr_only.sgl_ctx.single_sge.cur_buf_addr.hi = + fcoe_bd_tbl->buf_addr_hi; + task->rx_wr_only.sgl_ctx.single_sge.cur_buf_rem = + fcoe_bd_tbl->buf_len; + task->tx_wr_rx_rd.init_flags |= 1 << + FCOE_TASK_CTX_ENTRY_TXWR_RXRD_SINGLE_SGE_SHIFT; + } else { + + task->rx_wr_only.sgl_ctx.mul_sges.cur_sge_addr.lo = + (u32)bd_tbl->bd_tbl_dma; + task->rx_wr_only.sgl_ctx.mul_sges.cur_sge_addr.hi = + (u32)((u64)bd_tbl->bd_tbl_dma >> 32); + task->rx_wr_only.sgl_ctx.mul_sges.sgl_size = + bd_tbl->bd_valid; + } + } +} + +/** + * bnx2fc_setup_task_ctx - allocate and map task context + * + * @hba: pointer to adapter structure + * + * allocate memory for task context, and associated BD table to be used + * by firmware + * + */ +int bnx2fc_setup_task_ctx(struct bnx2fc_hba *hba) +{ + int rc = 0; + struct regpair *task_ctx_bdt; + dma_addr_t addr; + int i; + + /* + * Allocate task context bd table. A page size of bd table + * can map 256 buffers. Each buffer contains 32 task context + * entries. Hence the limit with one page is 8192 task context + * entries. + */ + hba->task_ctx_bd_tbl = dma_alloc_coherent(&hba->pcidev->dev, + PAGE_SIZE, + &hba->task_ctx_bd_dma, + GFP_KERNEL); + if (!hba->task_ctx_bd_tbl) { + printk(KERN_ERR PFX "unable to allocate task context BDT\n"); + rc = -1; + goto out; + } + memset(hba->task_ctx_bd_tbl, 0, PAGE_SIZE); + + /* + * Allocate task_ctx which is an array of pointers pointing to + * a page containing 32 task contexts + */ + hba->task_ctx = kmalloc((BNX2FC_TASK_CTX_ARR_SZ * sizeof(void *)), + GFP_KERNEL); + if (!hba->task_ctx) { + printk(KERN_ERR PFX "unable to allocate task context array\n"); + rc = -1; + goto out1; + } + memset(hba->task_ctx, 0, BNX2FC_TASK_CTX_ARR_SZ * sizeof(void *)); + + /* + * Allocate task_ctx_dma which is an array of dma addresses + */ + hba->task_ctx_dma = kmalloc((BNX2FC_TASK_CTX_ARR_SZ * + sizeof(dma_addr_t)), GFP_KERNEL); + if (!hba->task_ctx_dma) { + printk(KERN_ERR PFX "unable to alloc context mapping array\n"); + rc = -1; + goto out2; + } + + task_ctx_bdt = (struct regpair *)hba->task_ctx_bd_tbl; + for (i = 0; i < BNX2FC_TASK_CTX_ARR_SZ; i++) { + + hba->task_ctx[i] = dma_alloc_coherent(&hba->pcidev->dev, + PAGE_SIZE, + &hba->task_ctx_dma[i], + GFP_KERNEL); + if (!hba->task_ctx[i]) { + printk(KERN_ERR PFX "unable to alloc task context\n"); + rc = -1; + goto out3; + } + memset(hba->task_ctx[i], 0, PAGE_SIZE); + addr = (u64)hba->task_ctx_dma[i]; + task_ctx_bdt->hi = (u64) addr >> 32; + task_ctx_bdt->lo = (u32) addr; + task_ctx_bdt++; + } + return 0; + +out3: + for (i = 0; i < BNX2FC_TASK_CTX_ARR_SZ; i++) { + if (hba->task_ctx[i]) { + + dma_free_coherent(&hba->pcidev->dev, PAGE_SIZE, + hba->task_ctx[i], hba->task_ctx_dma[i]); + hba->task_ctx[i] = NULL; + } + } + + kfree(hba->task_ctx_dma); + hba->task_ctx_dma = NULL; +out2: + kfree(hba->task_ctx); + hba->task_ctx = NULL; +out1: + dma_free_coherent(&hba->pcidev->dev, PAGE_SIZE, + hba->task_ctx_bd_tbl, hba->task_ctx_bd_dma); + hba->task_ctx_bd_tbl = NULL; +out: + return rc; +} + +void bnx2fc_free_task_ctx(struct bnx2fc_hba *hba) +{ + int i; + + if (hba->task_ctx_bd_tbl) { + dma_free_coherent(&hba->pcidev->dev, PAGE_SIZE, + hba->task_ctx_bd_tbl, + hba->task_ctx_bd_dma); + hba->task_ctx_bd_tbl = NULL; + } + + if (hba->task_ctx) { + for (i = 0; i < BNX2FC_TASK_CTX_ARR_SZ; i++) { + if (hba->task_ctx[i]) { + dma_free_coherent(&hba->pcidev->dev, PAGE_SIZE, + hba->task_ctx[i], + hba->task_ctx_dma[i]); + hba->task_ctx[i] = NULL; + } + } + kfree(hba->task_ctx); + hba->task_ctx = NULL; + } + + kfree(hba->task_ctx_dma); + hba->task_ctx_dma = NULL; +} + +static void bnx2fc_free_hash_table(struct bnx2fc_hba *hba) +{ + int i; + int segment_count; + int hash_table_size; + u32 *pbl; + + segment_count = hba->hash_tbl_segment_count; + hash_table_size = BNX2FC_NUM_MAX_SESS * BNX2FC_MAX_ROWS_IN_HASH_TBL * + sizeof(struct fcoe_hash_table_entry); + + pbl = hba->hash_tbl_pbl; + for (i = 0; i < segment_count; ++i) { + dma_addr_t dma_address; + + dma_address = *pbl; + ++pbl; + dma_address += (u64) *pbl << 32; + ++pbl; + dma_free_coherent(&hba->pcidev->dev, + BNX2FC_HASH_TBL_CHUNK_SIZE, + hba->hash_tbl_segments[i], + dma_address); + + } + + if (hba->hash_tbl_pbl) { + dma_free_coherent(&hba->pcidev->dev, PAGE_SIZE, + hba->hash_tbl_pbl, + hba->hash_tbl_pbl_dma); + hba->hash_tbl_pbl = NULL; + } +} + +static int bnx2fc_allocate_hash_table(struct bnx2fc_hba *hba) +{ + int i; + int hash_table_size; + int segment_count; + int segment_array_size; + int dma_segment_array_size; + dma_addr_t *dma_segment_array; + u32 *pbl; + + hash_table_size = BNX2FC_NUM_MAX_SESS * BNX2FC_MAX_ROWS_IN_HASH_TBL * + sizeof(struct fcoe_hash_table_entry); + + segment_count = hash_table_size + BNX2FC_HASH_TBL_CHUNK_SIZE - 1; + segment_count /= BNX2FC_HASH_TBL_CHUNK_SIZE; + hba->hash_tbl_segment_count = segment_count; + + segment_array_size = segment_count * sizeof(*hba->hash_tbl_segments); + hba->hash_tbl_segments = kzalloc(segment_array_size, GFP_KERNEL); + if (!hba->hash_tbl_segments) { + printk(KERN_ERR PFX "hash table pointers alloc failed\n"); + return -ENOMEM; + } + dma_segment_array_size = segment_count * sizeof(*dma_segment_array); + dma_segment_array = kzalloc(dma_segment_array_size, GFP_KERNEL); + if (!dma_segment_array) { + printk(KERN_ERR PFX "hash table pointers (dma) alloc failed\n"); + return -ENOMEM; + } + + for (i = 0; i < segment_count; ++i) { + hba->hash_tbl_segments[i] = + dma_alloc_coherent(&hba->pcidev->dev, + BNX2FC_HASH_TBL_CHUNK_SIZE, + &dma_segment_array[i], + GFP_KERNEL); + bnx2fc_dbg(LOG_INIT, "hash table segment[%i]: %p: %llx\n", + i, hba->hash_tbl_segments[i], + dma_segment_array[i]); + if (!hba->hash_tbl_segments[i]) { + printk(KERN_ERR PFX "hash segment alloc failed\n"); + while (--i >= 0) { + dma_free_coherent(&hba->pcidev->dev, + BNX2FC_HASH_TBL_CHUNK_SIZE, + hba->hash_tbl_segments[i], + dma_segment_array[i]); + hba->hash_tbl_segments[i] = NULL; + } + kfree(dma_segment_array); + return -ENOMEM; + } + memset(hba->hash_tbl_segments[i], 0, + BNX2FC_HASH_TBL_CHUNK_SIZE); + } + + hba->hash_tbl_pbl = dma_alloc_coherent(&hba->pcidev->dev, + PAGE_SIZE, + &hba->hash_tbl_pbl_dma, + GFP_KERNEL); + bnx2fc_dbg(LOG_INIT, "hash table pbl: %p: %llx\n", + hba->hash_tbl_pbl, hba->hash_tbl_pbl_dma); + if (!hba->hash_tbl_pbl) { + printk(KERN_ERR PFX "hash table pbl alloc failed\n"); + kfree(dma_segment_array); + return -ENOMEM; + } + memset(hba->hash_tbl_pbl, 0, PAGE_SIZE); + + pbl = hba->hash_tbl_pbl; + for (i = 0; i < segment_count; ++i) { + u64 paddr = dma_segment_array[i]; + *pbl = (u32) paddr; + ++pbl; + *pbl = (u32) (paddr >> 32); + ++pbl; + } + pbl = hba->hash_tbl_pbl; + i = 0; + while (*pbl && *(pbl + 1)) { + u32 lo; + u32 hi; + lo = *pbl; + ++pbl; + hi = *pbl; + ++pbl; + bnx2fc_dbg(LOG_INIT, "hash table[%d]: 0x%08x%08x\n", i, hi, lo); + ++i; + } + kfree(dma_segment_array); + return 0; +} + +/** + * bnx2fc_setup_fw_resc - Allocate and map hash table and dummy buffer + * + * @hba: Pointer to adapter structure + * + */ +int bnx2fc_setup_fw_resc(struct bnx2fc_hba *hba) +{ + u64 addr; + u32 mem_size; + int i; + + if (bnx2fc_allocate_hash_table(hba)) + return -ENOMEM; + + mem_size = BNX2FC_NUM_MAX_SESS * sizeof(struct regpair); + hba->t2_hash_tbl_ptr = dma_alloc_coherent(&hba->pcidev->dev, mem_size, + &hba->t2_hash_tbl_ptr_dma, + GFP_KERNEL); + if (!hba->t2_hash_tbl_ptr) { + printk(KERN_ERR PFX "unable to allocate t2 hash table ptr\n"); + bnx2fc_free_fw_resc(hba); + return -ENOMEM; + } + memset(hba->t2_hash_tbl_ptr, 0x00, mem_size); + bnx2fc_dbg(LOG_INIT, "t2_hash_tbl_ptr = 0x%p, DMA = 0x%llx\n", + hba->t2_hash_tbl_ptr, hba->t2_hash_tbl_ptr_dma); + + mem_size = BNX2FC_NUM_MAX_SESS * + sizeof(struct fcoe_t2_hash_table_entry); + hba->t2_hash_tbl = dma_alloc_coherent(&hba->pcidev->dev, mem_size, + &hba->t2_hash_tbl_dma, + GFP_KERNEL); + if (!hba->t2_hash_tbl) { + printk(KERN_ERR PFX "unable to allocate t2 hash table\n"); + bnx2fc_free_fw_resc(hba); + return -ENOMEM; + } + memset(hba->t2_hash_tbl, 0x00, mem_size); + for (i = 0; i < BNX2FC_NUM_MAX_SESS; i++) { + addr = (unsigned long) hba->t2_hash_tbl_dma + + ((i+1) * sizeof(struct fcoe_t2_hash_table_entry)); + hba->t2_hash_tbl[i].next.lo = addr & 0xffffffff; + hba->t2_hash_tbl[i].next.hi = addr >> 32; + } + bnx2fc_dbg(LOG_INIT, "t2_hash_tbl = 0x%p, DMA = 0x%llx\n", + hba->t2_hash_tbl, hba->t2_hash_tbl_dma); + + hba->dummy_buffer = dma_alloc_coherent(&hba->pcidev->dev, + PAGE_SIZE, &hba->dummy_buf_dma, + GFP_KERNEL); + if (!hba->dummy_buffer) { + printk(KERN_ERR PFX "unable to alloc MP Dummy Buffer\n"); + bnx2fc_free_fw_resc(hba); + return -ENOMEM; + } + + hba->stats_buffer = dma_alloc_coherent(&hba->pcidev->dev, + PAGE_SIZE, + &hba->stats_buf_dma, + GFP_KERNEL); + if (!hba->stats_buffer) { + printk(KERN_ERR PFX "unable to alloc Stats Buffer\n"); + bnx2fc_free_fw_resc(hba); + return -ENOMEM; + } + memset(hba->stats_buffer, 0x00, PAGE_SIZE); + + return 0; +} + +void bnx2fc_free_fw_resc(struct bnx2fc_hba *hba) +{ + u32 mem_size; + + if (hba->stats_buffer) { + dma_free_coherent(&hba->pcidev->dev, PAGE_SIZE, + hba->stats_buffer, hba->stats_buf_dma); + hba->stats_buffer = NULL; + } + + if (hba->dummy_buffer) { + dma_free_coherent(&hba->pcidev->dev, PAGE_SIZE, + hba->dummy_buffer, hba->dummy_buf_dma); + hba->dummy_buffer = NULL; + } + + if (hba->t2_hash_tbl_ptr) { + mem_size = BNX2FC_NUM_MAX_SESS * sizeof(struct regpair); + dma_free_coherent(&hba->pcidev->dev, mem_size, + hba->t2_hash_tbl_ptr, + hba->t2_hash_tbl_ptr_dma); + hba->t2_hash_tbl_ptr = NULL; + } + + if (hba->t2_hash_tbl) { + mem_size = BNX2FC_NUM_MAX_SESS * + sizeof(struct fcoe_t2_hash_table_entry); + dma_free_coherent(&hba->pcidev->dev, mem_size, + hba->t2_hash_tbl, hba->t2_hash_tbl_dma); + hba->t2_hash_tbl = NULL; + } + bnx2fc_free_hash_table(hba); +} -- 1.7.0.6 -- To unsubscribe from this list: send the line "unsubscribe linux-scsi" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html