/* * Broadcom NetXtreme-E RoCE driver. * * Copyright (c) 2016 - 2017, Broadcom. All rights reserved. The term * Broadcom refers to Broadcom Limited and/or its subsidiaries. * * This software is available to you under a choice of one of two * licenses. You may choose to be licensed under the terms of the GNU * General Public License (GPL) Version 2, available from the file * COPYING in the main directory of this source tree, or the * BSD license below: * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * Description: RDMA Controller HW interface
*/
/** * bnxt_qplib_map_rc - map return type based on opcode * @opcode: roce slow path opcode * * case #1 * Firmware initiated error recovery is a safe state machine and * driver can consider all the underlying rdma resources are free. * In this state, it is safe to return success for opcodes related to * destroying rdma resources (like destroy qp, destroy cq etc.). * * case #2 * If driver detect potential firmware stall, it is not safe state machine * and the driver can not consider all the underlying rdma resources are * freed. * In this state, it is not safe to return success for opcodes related to * destroying rdma resources (like destroy qp, destroy cq etc.). * * Scope of this helper function is only for case #1. * * Returns: * 0 to communicate success to caller. * Non zero error code to communicate failure to caller.
*/ staticint bnxt_qplib_map_rc(u8 opcode)
{ switch (opcode) { case CMDQ_BASE_OPCODE_DESTROY_QP: case CMDQ_BASE_OPCODE_DESTROY_SRQ: case CMDQ_BASE_OPCODE_DESTROY_CQ: case CMDQ_BASE_OPCODE_DEALLOCATE_KEY: case CMDQ_BASE_OPCODE_DEREGISTER_MR: case CMDQ_BASE_OPCODE_DELETE_GID: case CMDQ_BASE_OPCODE_DESTROY_QP1: case CMDQ_BASE_OPCODE_DESTROY_AH: case CMDQ_BASE_OPCODE_DEINITIALIZE_FW: case CMDQ_BASE_OPCODE_MODIFY_ROCE_CC: case CMDQ_BASE_OPCODE_SET_LINK_AGGR_MODE: return 0; default: return -ETIMEDOUT;
}
}
/** * bnxt_re_is_fw_stalled - Check firmware health * @rcfw: rcfw channel instance of rdev * @cookie: cookie to track the command * * If firmware has not responded any rcfw command within * rcfw->max_timeout, consider firmware as stalled. * * Returns: * 0 if firmware is responding * -ENODEV if firmware is not responding
*/ staticint bnxt_re_is_fw_stalled(struct bnxt_qplib_rcfw *rcfw,
u16 cookie)
{ struct bnxt_qplib_cmdq_ctx *cmdq; struct bnxt_qplib_crsqe *crsqe;
/** * __wait_for_resp - Don't hold the cpu context and wait for response * @rcfw: rcfw channel instance of rdev * @cookie: cookie to track the command * * Wait for command completion in sleepable context. * * Returns: * 0 if command is completed by firmware. * Non zero error code for rest of the case.
*/ staticint __wait_for_resp(struct bnxt_qplib_rcfw *rcfw, u16 cookie)
{ struct bnxt_qplib_cmdq_ctx *cmdq; struct bnxt_qplib_crsqe *crsqe; int ret;
do { if (test_bit(ERR_DEVICE_DETACHED, &cmdq->flags)) return bnxt_qplib_map_rc(crsqe->opcode); if (test_bit(FIRMWARE_STALL_DETECTED, &cmdq->flags)) return -ETIMEDOUT;
ret = bnxt_re_is_fw_stalled(rcfw, cookie); if (ret) return ret;
} while (true);
};
/** * __block_for_resp - hold the cpu context and wait for response * @rcfw: rcfw channel instance of rdev * @cookie: cookie to track the command * * This function will hold the cpu (non-sleepable context) and * wait for command completion. Maximum holding interval is 8 second. * * Returns: * -ETIMEOUT if command is not completed in specific time interval. * 0 if command is completed by firmware.
*/ staticint __block_for_resp(struct bnxt_qplib_rcfw *rcfw, u16 cookie)
{ struct bnxt_qplib_cmdq_ctx *cmdq = &rcfw->cmdq; struct bnxt_qplib_crsqe *crsqe; unsignedlong issue_time = 0;
do { if (test_bit(ERR_DEVICE_DETACHED, &cmdq->flags)) return bnxt_qplib_map_rc(crsqe->opcode); if (test_bit(FIRMWARE_STALL_DETECTED, &cmdq->flags)) return -ETIMEDOUT;
udelay(1);
bnxt_qplib_service_creq(&rcfw->creq.creq_tasklet); if (!crsqe->is_in_used) return 0;
} while (time_before(jiffies, issue_time + (8 * HZ)));
return -ETIMEDOUT;
};
/* __send_message_no_waiter - get cookie and post the message. * @rcfw: rcfw channel instance of rdev * @msg: qplib message internal * * This function will just post and don't bother about completion. * Current design of this function is - * user must hold the completion queue hwq->lock. * user must have used existing completion and free the resources. * this function will not check queue full condition. * this function will explicitly set is_waiter_alive=false. * current use case is - send destroy_ah if create_ah is return * after waiter of create_ah is lost. It can be extended for other * use case as well. * * Returns: Nothing *
*/ staticvoid __send_message_no_waiter(struct bnxt_qplib_rcfw *rcfw, struct bnxt_qplib_cmdqmsg *msg)
{ struct bnxt_qplib_cmdq_ctx *cmdq = &rcfw->cmdq; struct bnxt_qplib_hwq *hwq = &cmdq->hwq; struct bnxt_qplib_crsqe *crsqe; struct bnxt_qplib_cmdqe *cmdqe;
u32 sw_prod, cmdq_prod;
u16 cookie;
u32 bsize;
u8 *preq;
/* Set cmd_size in terms of 16B slots in req. */
bsize = bnxt_qplib_set_cmd_slots(msg->req); /* GET_CMD_SIZE would return number of slots in either case of tlv * and non-tlv commands after call to bnxt_qplib_set_cmd_slots()
*/
crsqe->is_internal_cmd = true;
crsqe->is_waiter_alive = false;
crsqe->is_in_used = true;
crsqe->req_size = __get_cmdq_base_cmd_size(msg->req, msg->req_sz);
preq = (u8 *)msg->req; do { /* Locate the next cmdq slot */
sw_prod = HWQ_CMP(hwq->prod, hwq);
cmdqe = bnxt_qplib_get_qe(hwq, sw_prod, NULL); /* Copy a segment of the req cmd to the cmdq */
memset(cmdqe, 0, sizeof(*cmdqe));
memcpy(cmdqe, preq, min_t(u32, bsize, sizeof(*cmdqe)));
preq += min_t(u32, bsize, sizeof(*cmdqe));
bsize -= min_t(u32, bsize, sizeof(*cmdqe));
hwq->prod++;
} while (bsize > 0);
cmdq->seq_num++;
cmdq_prod = hwq->prod;
atomic_inc(&rcfw->timeout_send); /* ring CMDQ DB */
wmb();
writel(cmdq_prod, cmdq->cmdq_mbox.prod);
writel(RCFW_CMDQ_TRIG_VAL, cmdq->cmdq_mbox.db);
}
preq = (u8 *)msg->req; do { /* Locate the next cmdq slot */
sw_prod = HWQ_CMP(hwq->prod, hwq);
cmdqe = bnxt_qplib_get_qe(hwq, sw_prod, NULL); /* Copy a segment of the req cmd to the cmdq */
memset(cmdqe, 0, sizeof(*cmdqe));
memcpy(cmdqe, preq, min_t(u32, bsize, sizeof(*cmdqe)));
preq += min_t(u32, bsize, sizeof(*cmdqe));
bsize -= min_t(u32, bsize, sizeof(*cmdqe));
hwq->prod++;
} while (bsize > 0);
cmdq->seq_num++;
cmdq_prod = hwq->prod & 0xFFFF; if (test_bit(FIRMWARE_FIRST_FLAG, &cmdq->flags)) { /* The very first doorbell write * is required to set this flag * which prompts the FW to reset * its internal pointers
*/
cmdq_prod |= BIT(FIRMWARE_FIRST_FLAG);
clear_bit(FIRMWARE_FIRST_FLAG, &cmdq->flags);
} /* ring CMDQ DB */
wmb();
writel(cmdq_prod, cmdq->cmdq_mbox.prod);
writel(RCFW_CMDQ_TRIG_VAL, cmdq->cmdq_mbox.db);
spin_unlock_bh(&hwq->lock); /* Return the CREQ response pointer */ return 0;
}
/** * __poll_for_resp - self poll completion for rcfw command * @rcfw: rcfw channel instance of rdev * @cookie: cookie to track the command * * It works same as __wait_for_resp except this function will * do self polling in sort interval since interrupt is disabled. * This function can not be called from non-sleepable context. * * Returns: * -ETIMEOUT if command is not completed in specific time interval. * 0 if command is completed by firmware.
*/ staticint __poll_for_resp(struct bnxt_qplib_rcfw *rcfw, u16 cookie)
{ struct bnxt_qplib_cmdq_ctx *cmdq = &rcfw->cmdq; struct bnxt_qplib_crsqe *crsqe; unsignedlong issue_time; int ret;
do { if (test_bit(ERR_DEVICE_DETACHED, &cmdq->flags)) return bnxt_qplib_map_rc(crsqe->opcode); if (test_bit(FIRMWARE_STALL_DETECTED, &cmdq->flags)) return -ETIMEDOUT;
usleep_range(1000, 1001);
bnxt_qplib_service_creq(&rcfw->creq.creq_tasklet); if (!crsqe->is_in_used) return 0; if (jiffies_to_msecs(jiffies - issue_time) >
(rcfw->max_timeout * 1000)) {
ret = bnxt_re_is_fw_stalled(rcfw, cookie); if (ret) return ret;
}
} while (true);
};
/* This function will just post and do not bother about completion */ staticvoid __destroy_timedout_ah(struct bnxt_qplib_rcfw *rcfw, struct creq_create_ah_resp *create_ah_resp)
{ struct bnxt_qplib_cmdqmsg msg = {}; struct cmdq_destroy_ah req = {};
/** * __bnxt_qplib_rcfw_send_message - qplib interface to send * and complete rcfw command. * @rcfw: rcfw channel instance of rdev * @msg: qplib message internal * * This function does not account shadow queue depth. It will send * all the command unconditionally as long as send queue is not full. * * Returns: * 0 if command completed by firmware. * Non zero if the command is not completed by firmware.
*/ staticint __bnxt_qplib_rcfw_send_message(struct bnxt_qplib_rcfw *rcfw, struct bnxt_qplib_cmdqmsg *msg)
{ struct creq_qp_event *evnt = (struct creq_qp_event *)msg->resp; struct bnxt_qplib_crsqe *crsqe;
u16 cookie; int rc;
u8 opcode;
if (rc) {
spin_lock_bh(&rcfw->cmdq.hwq.lock);
crsqe = &rcfw->crsqe_tbl[cookie];
crsqe->is_waiter_alive = false; if (rc == -ENODEV)
set_bit(FIRMWARE_STALL_DETECTED, &rcfw->cmdq.flags);
spin_unlock_bh(&rcfw->cmdq.hwq.lock); return -ETIMEDOUT;
}
if (evnt->status) { /* failed with status */
dev_err(&rcfw->pdev->dev, "cmdq[%#x]=%#x status %#x\n",
cookie, opcode, evnt->status);
rc = -EIO;
}
return rc;
}
/** * bnxt_qplib_rcfw_send_message - qplib interface to send * and complete rcfw command. * @rcfw: rcfw channel instance of rdev * @msg: qplib message internal * * Driver interact with Firmware through rcfw channel/slow path in two ways. * a. Blocking rcfw command send. In this path, driver cannot hold * the context for longer period since it is holding cpu until * command is not completed. * b. Non-blocking rcfw command send. In this path, driver can hold the * context for longer period. There may be many pending command waiting * for completion because of non-blocking nature. * * Driver will use shadow queue depth. Current queue depth of 8K * (due to size of rcfw message there can be actual ~4K rcfw outstanding) * is not optimal for rcfw command processing in firmware. * * Restrict at max #RCFW_CMD_NON_BLOCKING_SHADOW_QD Non-Blocking rcfw commands. * Allow all blocking commands until there is no queue full. * * Returns: * 0 if command completed by firmware. * Non zero if the command is not completed by firmware.
*/ int bnxt_qplib_rcfw_send_message(struct bnxt_qplib_rcfw *rcfw, struct bnxt_qplib_cmdqmsg *msg)
{ int ret;
if (!msg->block) {
down(&rcfw->rcfw_inflight);
ret = __bnxt_qplib_rcfw_send_message(rcfw, msg);
up(&rcfw->rcfw_inflight);
} else {
ret = __bnxt_qplib_rcfw_send_message(rcfw, msg);
}
switch (func_event->event) { case CREQ_FUNC_EVENT_EVENT_TX_WQE_ERROR: break; case CREQ_FUNC_EVENT_EVENT_TX_DATA_ERROR: break; case CREQ_FUNC_EVENT_EVENT_RX_WQE_ERROR: break; case CREQ_FUNC_EVENT_EVENT_RX_DATA_ERROR: break; case CREQ_FUNC_EVENT_EVENT_CQ_ERROR: break; case CREQ_FUNC_EVENT_EVENT_TQM_ERROR: break; case CREQ_FUNC_EVENT_EVENT_CFCQ_ERROR: break; case CREQ_FUNC_EVENT_EVENT_CFCS_ERROR: /* SRQ ctx error, call srq_handler?? * But there's no SRQ handle!
*/ break; case CREQ_FUNC_EVENT_EVENT_CFCC_ERROR: break; case CREQ_FUNC_EVENT_EVENT_CFCM_ERROR: break; case CREQ_FUNC_EVENT_EVENT_TIM_ERROR: break; case CREQ_FUNC_EVENT_EVENT_VF_COMM_REQUEST: break; case CREQ_FUNC_EVENT_EVENT_RESOURCE_EXHAUSTED: break; default: return -EINVAL;
}
if (crsqe->is_internal_cmd && !qp_event->status)
atomic_dec(&rcfw->timeout_send);
if (crsqe->is_waiter_alive) { if (crsqe->resp) {
memcpy(crsqe->resp, qp_event, sizeof(*qp_event)); /* Insert write memory barrier to ensure that * response data is copied before clearing the * flags
*/
smp_wmb();
} if (!blocked)
wait_cmds++;
}
crsqe->req_size = 0; if (!is_waiter_alive)
crsqe->resp = NULL;
crsqe->is_in_used = false;
hwq->cons += req_size;
/* This is a case to handle below scenario - * Create AH is completed successfully by firmware, * but completion took more time and driver already lost * the context of create_ah from caller. * We have already return failure for create_ah verbs, * so let's destroy the same address vector since it is * no more used in stack. We don't care about completion * in __send_message_no_waiter. * If destroy_ah is failued by firmware, there will be AH * resource leak and relatively not critical + unlikely * scenario. Current design is not to handle such case.
*/ if (!is_waiter_alive && !qp_event->status &&
qp_event->event == CREQ_QP_EVENT_EVENT_CREATE_AH)
__destroy_timedout_ah(rcfw,
(struct creq_create_ah_resp *)
qp_event);
spin_unlock(&hwq->lock);
}
*num_wait += wait_cmds; return rc;
}
/* Service the CREQ until budget is over */
spin_lock_bh(&hwq->lock); while (budget > 0) {
creqe = bnxt_qplib_get_qe(hwq, hwq->cons, NULL); if (!CREQ_CMP_VALID(creqe, creq->creq_db.dbinfo.flags)) break; /* The valid test of the entry must be done first before * reading any further.
*/
dma_rmb();
rcfw->cmdq.last_seen = jiffies;
type = creqe->type & CREQ_BASE_TYPE_MASK; switch (type) { case CREQ_BASE_TYPE_QP_EVENT:
bnxt_qplib_process_qp_event
(rcfw, (struct creq_qp_event *)creqe,
&num_wakeup);
creq->stats.creq_qp_event_processed++; break; case CREQ_BASE_TYPE_FUNC_EVENT: if (!bnxt_qplib_process_func_event
(rcfw, (struct creq_func_event *)creqe))
creq->stats.creq_func_event_processed++; else
dev_warn(&rcfw->pdev->dev, "aeqe:%#x Not handled\n", type); break; default: if (type != ASYNC_EVENT_CMPL_TYPE_HWRM_ASYNC_EVENT)
dev_warn(&rcfw->pdev->dev, "creqe with event 0x%x not handled\n",
type); break;
}
budget--;
hw_polled++;
bnxt_qplib_hwq_incr_cons(hwq->max_elements, &hwq->cons,
1, &creq->creq_db.dbinfo.flags);
}
if (hw_polled)
bnxt_qplib_ring_nq_db(&creq->creq_db.dbinfo,
rcfw->res->cctx, true);
spin_unlock_bh(&hwq->lock); if (num_wakeup)
wake_up_nr(&rcfw->cmdq.waitq, num_wakeup);
}
bnxt_qplib_rcfw_cmd_prep((struct cmdq_base *)&req,
CMDQ_BASE_OPCODE_INITIALIZE_FW, sizeof(req)); /* Supply (log-base-2-of-host-page-size - base-page-shift) * to bono to adjust the doorbell page sizes.
*/
req.log2_dbr_pg_size = cpu_to_le16(PAGE_SHIFT -
RCFW_DBR_BASE_PAGE_SHIFT); /* * Gen P5 devices doesn't require this allocation * as the L2 driver does the same for RoCE also. * Also, VFs need not setup the HW context area, PF * shall setup this area for VF. Skipping the * HW programming
*/ if (is_virtfn || bnxt_qplib_is_chip_gen_p5_p7(rcfw->res->cctx)) goto skip_ctx_setup;
int bnxt_qplib_enable_rcfw_channel(struct bnxt_qplib_rcfw *rcfw, int msix_vector, int cp_bar_reg_off,
aeq_handler_t aeq_handler)
{ struct bnxt_qplib_cmdq_ctx *cmdq; struct bnxt_qplib_creq_ctx *creq; int rc;
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.