/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
*/
/*
* Hermon Completion Queue Processing Routines
*
* Implements all the routines necessary for allocating, freeing, resizing,
* and handling the completion type events that the Hermon hardware can
* generate.
*/
#include <sys/sysmacros.h>
/* Build the doorbell record data (low 24 bits only) */ \
#pragma inline(hermon_cq_arm_doorbell)
#pragma inline(hermon_arm_cq_dbr_init)
/*
* hermon_cq_alloc()
* Context: Can be called only from user or kernel context.
*/
int
{
/*
* Determine whether CQ is being allocated for userland access or
* whether it is being allocated for kernel access. If the CQ is
* being allocated for userland access, then lookup the UAR
* page number for the current process. Note: If this is not found
* (e.g. if the process has not previously open()'d the Hermon driver),
* then an error is returned.
*/
if (cq_is_umap) {
if (status != DDI_SUCCESS) {
goto cqalloc_fail;
}
} else {
}
/* Use the internal protection domain (PD) for setting up CQs */
/* Increment the reference count on the protection domain (PD) */
/*
* Allocate an CQ context entry. This will be filled in with all
* the necessary parameters to define the Completion Queue. And then
* ownership will be passed to the hardware in the final step
* below. If we fail here, we must undo the protection domain
* reference count.
*/
if (status != DDI_SUCCESS) {
goto cqalloc_fail1;
}
/*
* Allocate the software structure for tracking the completion queue
* (i.e. the Hermon Completion Queue handle). If we fail here, we must
* undo the protection domain reference count and the previous
* resource allocation.
*/
if (status != DDI_SUCCESS) {
goto cqalloc_fail2;
}
cq->cq_intmod_count = 0;
cq->cq_intmod_usec = 0;
/*
* If this will be a user-mappable CQ, then allocate an entry for
* the "userland resources database". This will later be added to
* the database (after all further CQ operations are successful).
* If we fail here, we must undo the reference counts and the
* previous resource allocation.
*/
if (cq->cq_is_umap) {
goto cqalloc_fail3;
}
}
/*
* Allocate the doorbell record. We'll need one for the CQ, handling
* both consumer index (SET CI) and the CQ state (CQ ARM).
*/
if (status != DDI_SUCCESS) {
goto cqalloc_fail4;
}
/*
* Calculate the appropriate size for the completion queue.
* Note: All Hermon CQs must be a power-of-2 minus 1 in size. Also
* they may not be any smaller than HERMON_CQ_MIN_SIZE. This step is
* to round the requested size up to the next highest power-of-2
*/
/*
* Next we verify that the rounded-up size is valid (i.e. consistent
*/
goto cqalloc_fail4a;
}
/*
* Allocate the memory for Completion Queue.
*
* Note: Although we use the common queue allocation routine, we
* always specify HERMON_QUEUE_LOCATION_NORMAL (i.e. CQ located in
* kernel system memory) for kernel CQs because it would be
* inefficient to have CQs located in DDR memory. This is primarily
* because CQs are read from (by software) more than they are written
* to. (We always specify HERMON_QUEUE_LOCATION_USERLAND for all
* user-mappable CQs for a similar reason.)
* It is also worth noting that, unlike Hermon QP work queues,
* completion queues do not have the same strict alignment
* requirements. It is sufficient for the CQ memory to be both
* aligned to and bound to addresses which are a multiple of CQE size.
*/
if (cq->cq_is_umap) {
} else {
}
if (status != DDI_SUCCESS) {
goto cqalloc_fail4;
}
/*
* The ownership bit of the CQE's is set by the HW during the process
* of transferrring ownership of the CQ (PRM 09.35c, 14.2.1, note D1
*
*/
/*
* Register the memory for the CQ. The memory for the CQ must
* be registered in the Hermon TPT tables. This gives us the LKey
* to specify in the CQ context below. Note: If this is a user-
* mappable CQ, then we will force DDI_DMA_CONSISTENT mapping.
*/
op.mro_bind_override_addr = 0;
if (status != DDI_SUCCESS) {
goto cqalloc_fail5;
}
goto cqalloc_fail5;
}
} else {
int i;
for (i = 0; i < state->hs_cq_sched_array_size; i++)
break; /* found it */
if (i >= state->hs_cq_sched_array_size) {
"ignored\n");
}
}
}
/*
* Fill in the CQC entry. This is the final step before passing
* ownership of the CQC entry to the Hermon hardware. We use all of
* the information collected/calculated above to fill in the
* requisite portions of the CQC. Note: If this CQ is going to be
* used for userland access, then we need to set the UAR page number
* appropriately (otherwise it's a "don't care")
*/
/*
* Write the CQC entry to hardware - we pass ownership of
* the entry to the hardware (using the Hermon SW2HW_CQ firmware
* command). Note: In general, this operation shouldn't fail. But
* if it does, we have to undo everything we've done above before
* returning error.
*/
if (status != HERMON_CMD_SUCCESS) {
status);
if (status == HERMON_CMD_INVALID_STATUS) {
}
status = ibc_get_ci_failure(0);
goto cqalloc_fail6;
}
/*
* Fill in the rest of the Hermon Completion Queue handle. Having
* successfully transferred ownership of the CQC, we can update the
* following fields for use in further operations on the CQ.
*/
cq->cq_resize_hdl = 0;
cq->cq_consindx = 0;
/* least restrictive */
cq->cq_is_special = 0;
sizeof (struct hermon_workq_avl_s),
/*
* Put CQ handle in Hermon CQNum-to-CQHdl list. Then fill in the
* "actual_size" and "cqhdl" and return success
*/
/*
* If this is a user-mappable CQ, then we need to insert the previously
* allocated entry into the "userland resources database". This will
* allow for later lookup during devmap() (i.e. mmap()) calls.
*/
if (cq->cq_is_umap) {
}
/*
* Fill in the return arguments (if necessary). This includes the
* real completion queue size.
*/
if (actual_size != NULL) {
}
return (DDI_SUCCESS);
/*
* The following is cleanup for all possible failure cases in this routine
*/
sleepflag) != DDI_SUCCESS) {
}
if (cq_is_umap) {
}
return (status);
}
/*
* hermon_cq_free()
* Context: Can be called only from user or kernel context.
*/
/* ARGSUSED */
int
{
int status;
/*
* Pull all the necessary information from the Hermon Completion Queue
* handle. This is necessary here because the resource for the
* CQ handle is going to be freed up as part of this operation.
*/
/*
* If there are work queues still associated with the CQ, then return
* an error. Otherwise, we will be holding the CQ lock.
*/
return (IBT_CQ_BUSY);
}
/*
* If this was a user-mappable CQ, then we need to remove its entry
* from the "userland resources database". If it is also currently
* mmap()'d out to a user process, then we need to call
* devmap_devmem_remap() to remap the CQ memory to an invalid mapping.
* We also need to invalidate the CQ tracking information for the
* user mapping.
*/
if (cq->cq_is_umap) {
&umapdb);
if (status != DDI_SUCCESS) {
return (ibc_get_ci_failure(0));
}
if (status != DDI_SUCCESS) {
"devmap_devmem_remap()");
return (ibc_get_ci_failure(0));
}
}
}
/*
* Put NULL into the Arbel CQNum-to-CQHdl list. This will allow any
* in-progress events to detect that the CQ corresponding to this
* number has been freed.
*/
/*
* Reclaim CQC entry from hardware (using the Hermon HW2SW_CQ
* firmware command). If the ownership transfer fails for any reason,
* then it is an indication that something (either in HW or SW) has
* gone seriously wrong.
*/
if (status != HERMON_CMD_SUCCESS) {
status);
if (status == HERMON_CMD_INVALID_STATUS) {
}
return (ibc_get_ci_failure(0));
}
/*
* From here on, we start reliquishing resources - but check to see
* if a resize was in progress - if so, we need to relinquish those
* resources as well
*/
/*
* Deregister the memory for the Completion Queue. If this fails
* for any reason, then it is an indication that something (either
* in HW or SW) has gone seriously wrong. So we print a warning
* message and return.
*/
if (status != DDI_SUCCESS) {
return (ibc_get_ci_failure(0));
}
if (resize) { /* there was a pointer to a handle */
if (status != DDI_SUCCESS) {
"memory");
return (ibc_get_ci_failure(0));
}
}
/* Free the memory for the CQ */
if (resize) {
/* and the temporary handle */
}
/* everything else does not matter for the resize in progress */
/* Free the dbr */
/* Free the Hermon Completion Queue handle */
/* Free up the CQC entry resource */
/* Decrement the reference count on the protection domain (PD) */
/* Set the cqhdl pointer to NULL and return success */
return (DDI_SUCCESS);
}
/*
* hermon_cq_resize()
* Context: Can be called only from user or kernel context.
*/
int
{
goto cqresize_fail;
}
/* Use the internal protection domain (PD) for CQs */
/*
* Calculate the appropriate size for the new resized completion queue.
* Note: All Hermon CQs must be a power-of-2 minus 1 in size. Also
* they may not be any smaller than HERMON_CQ_MIN_SIZE. This step is
* to round the requested size up to the next highest power-of-2
*/
/*
* Next we verify that the rounded-up size is valid (i.e. consistent
*/
goto cqresize_fail;
}
/*
* Allocate the memory for newly resized Completion Queue.
*
* Note: Although we use the common queue allocation routine, we
* always specify HERMON_QUEUE_LOCATION_NORMAL (i.e. CQ located in
* kernel system memory) for kernel CQs because it would be
* inefficient to have CQs located in DDR memory. This is the same
* as we do when we first allocate completion queues primarily
* because CQs are read from (by software) more than they are written
* to. (We always specify HERMON_QUEUE_LOCATION_USERLAND for all
* user-mappable CQs for a similar reason.)
* It is also worth noting that, unlike Hermon QP work queues,
* completion queues do not have the same strict alignment
* requirements. It is sufficient for the CQ memory to be both
* aligned to and bound to addresses which are a multiple of CQE size.
*/
/* first, alloc the resize_handle */
if (cq->cq_is_umap) {
} else {
}
if (status != DDI_SUCCESS) {
/* free the resize handle */
goto cqresize_fail;
}
/*
* No initialization of the cq is needed - the command will do it
*/
/*
* Register the memory for the CQ. The memory for the CQ must
* be registered in the Hermon TPT tables. This gives us the LKey
* to specify in the CQ context below.
*/
op.mro_bind_override_addr = 0;
if (status != DDI_SUCCESS) {
/* free the resize handle */
goto cqresize_fail;
}
/*
* Now we grab the CQ lock. Since we will be updating the actual
* the lock.
*
* We do a ARBEL_NOSLEEP here (and below), though, because we are
* holding the "cq_lock" and if we got raised to interrupt level
* by priority inversion, we would not want to block in this routine
* waiting for success.
*/
/*
* Fill in the CQC entry. For the resize operation this is the
* final step before attempting the resize operation on the CQC entry.
* We use all of the information collected/calculated above to fill
* in the requisite portions of the CQC.
*/
/*
* Write the CQC entry to hardware. Lastly, we pass ownership of
* the entry to the hardware (using the Hermon RESIZE_CQ firmware
* command). Note: In general, this operation shouldn't fail. But
* if it does, we have to undo everything we've done above before
* returning error. Also note that the status returned may indicate
* the code to return to the IBTF.
*/
if (status != HERMON_CMD_SUCCESS) {
/* Resize attempt has failed, drop CQ lock and cleanup */
sleepflag) != DDI_SUCCESS) {
}
if (status == HERMON_CMD_BAD_SIZE) {
return (IBT_CQ_SZ_INSUFFICIENT);
} else {
"%08x\n", status);
if (status == HERMON_CMD_INVALID_STATUS) {
}
return (ibc_get_ci_failure(0));
}
}
/*
* For Hermon, we've alloc'd another handle structure and save off the
* important things in it. Then, in polling we check to see if there's
* a "resizing handle" and if so we look for the "special CQE", opcode
* 0x16, that indicates the transition to the new buffer.
*
* At that point, we'll adjust everything - including dereg and
* freeing of the original buffer, updating all the necessary fields
* in the cq_hdl, and setting up for the next cqe polling
*/
sizeof (struct hermon_qalloc_info_s));
/* now, save the address in the cq_handle */
/*
* Drop the CQ lock now.
*/
/*
* Fill in the return arguments (if necessary). This includes the
* real new completion queue size.
*/
if (actual_size != NULL) {
}
return (DDI_SUCCESS);
return (status);
}
/*
* hermon_cq_modify()
* Context: Can be called base context.
*/
/* ARGSUSED */
int
{
int status;
if (status != HERMON_CMD_SUCCESS) {
"command failed: %08x\n", status);
if (status == HERMON_CMD_INVALID_STATUS) {
}
return (ibc_get_ci_failure(0));
}
}
if (status != HERMON_CMD_SUCCESS) {
"%08x\n", status);
if (status == HERMON_CMD_INVALID_STATUS) {
}
return (ibc_get_ci_failure(0));
}
}
return (DDI_SUCCESS);
}
/*
* hermon_cq_notify()
* Context: Can be called from interrupt or base context.
*/
int
{
/* Validate IBT flags and call doorbell routine. */
if (flags == IBT_NEXT_COMPLETION) {
} else if (flags == IBT_NEXT_SOLICITED) {
} else {
return (IBT_CQ_NOTIFY_TYPE_INVALID);
}
return (status);
}
/*
* hermon_cq_poll()
* Context: Can be called from interrupt or base context.
*/
int
{
int status;
/*
* Check for user-mappable CQ memory. Note: We do not allow kernel
* clients to poll CQ memory that is accessible directly by the user.
* If the CQ memory is user accessible, then return an error.
*/
if (cq->cq_is_umap) {
return (IBT_CQ_HDL_INVALID);
}
/* Get the consumer index */
/*
* Calculate the wrap around mask. Note: This operation only works
* because all Hermon completion queues have power-of-2 sizes
*/
/* Calculate the pointer to the first CQ entry */
/*
* Keep pulling entries from the CQ until we find an entry owned by
* the hardware. As long as there the CQE's owned by SW, process
* each entry by calling hermon_cq_cqe_consume() and updating the CQ
* consumer index. Note: We only update the consumer index if
* hermon_cq_cqe_consume() returns HERMON_CQ_SYNC_AND_DB. Otherwise,
* it indicates that we are going to "recycle" the CQE (probably
* because it is a error CQE and corresponds to more than one
* completion).
*/
polled_cnt = 0;
/* peek at the opcode */
if (opcode == HERMON_CQE_RCV_RESIZE_CODE) {
/* Increment the consumer index */
/* Update the pointer to the next CQ entry */
continue;
}
} /* in resizing CQ */
/*
* either resizing and not the special opcode, or
* not resizing at all
*/
/* Increment the consumer index */
/* Update the pointer to the next CQ entry */
/*
* If we have run out of space to store work completions,
* then stop and return the ones we have pulled of the CQ.
*/
if (polled_cnt >= num_wc) {
break;
}
}
/*
* Now we only ring the doorbell (to update the consumer index) if
* we've actually consumed a CQ entry.
*/
/*
* Update the consumer index in both the CQ handle and the
* doorbell record.
*/
} else if (polled_cnt == 0) {
if (spec_op != 0) {
/* if we got the special opcode, update the consindx */
}
}
/* Set "num_polled" (if necessary) */
if (num_polled != NULL) {
*num_polled = polled_cnt;
}
/* Set CQ_EMPTY condition if needed, otherwise return success */
if (polled_cnt == 0) {
} else {
}
/*
* Check if the system is currently panicking. If it is, then call
* the Hermon interrupt service routine. This step is necessary here
* because we might be in a polled I/O mode and without the call to
* hermon_isr() - and its subsequent calls to poll and rearm each
* event queue - we might overflow our EQs and render the system
*/
if (ddi_in_panic() != 0) {
}
return (status);
}
/*
* cmd_sn must be initialized to 1 to enable proper reenabling
* by hermon_arm_cq_dbr_update().
*/
static void
{
}
/*
* User cmd_sn needs help from this kernel function to know
* when it should be incremented (modulo 4). We do an atomic
* update of the arm_cq dbr to communicate this fact. We retry
* in the case that user library is racing with us. We zero
* out the cmd field so that the user library can use the cmd
* field to track the last command it issued (solicited verses any).
*/
static void
{
int retries = 0;
(0x3 << HERMON_CQDB_CMDSN_SHIFT);
if (++retries > 100000) {
retries = 0;
}
goto retry;
}
}
/*
* hermon_cq_handler()
* Context: Only called from interrupt context
*/
/* ARGSUSED */
int
{
/* Get the CQ handle from CQ number in event descriptor */
/*
* If the CQ handle is NULL, this is probably an indication
* that the CQ has been freed already. In which case, we
* should not deliver this event.
*
* We also check that the CQ number in the handle is the
* same as the CQ number in the event queue entry. This
* extra check allows us to handle the case where a CQ was
* freed and then allocated again in the time it took to
* handle the event queue processing. By constantly incrementing
* the non-constrained portion of the CQ number every time
* a new CQ is allocated, we mitigate (somewhat) the chance
* that a stale event could be passed to the client's CQ
* handler.
*
* Lastly, we check if "hs_ibtfpriv" is NULL. If it is then it
* means that we've have either received this event before we
* finished attaching to the IBTF or we've received it while we
* are in the process of detaching.
*/
}
return (DDI_SUCCESS);
}
/*
* hermon_cq_err_handler()
* Context: Only called from interrupt context
*/
/* ARGSUSED */
int
{
/* Get the CQ handle from CQ number in event descriptor */
/*
* If the CQ handle is NULL, this is probably an indication
* that the CQ has been freed already. In which case, we
* should not deliver this event.
*
* We also check that the CQ number in the handle is the
* same as the CQ number in the event queue entry. This
* extra check allows us to handle the case where a CQ was
* freed and then allocated again in the time it took to
* handle the event queue processing. By constantly incrementing
* the non-constrained portion of the CQ number every time
* a new CQ is allocated, we mitigate (somewhat) the chance
* that a stale event could be passed to the client's CQ
* handler.
*
* And then we check if "hs_ibtfpriv" is NULL. If it is then it
* means that we've have either received this event before we
* finished attaching to the IBTF or we've received it while we
* are in the process of detaching.
*/
type = IBT_ERROR_CQ;
}
return (DDI_SUCCESS);
}
/*
* hermon_cq_refcnt_inc()
* Context: Can be called from interrupt or base context.
*/
int
{
/*
* Increment the completion queue's reference count. Note: In order
* to ensure compliance with IBA C11-15, we must ensure that a given
* This is accomplished here by keeping track of how the referenced
* CQ is being used.
*/
} else {
return (DDI_FAILURE);
}
}
return (DDI_SUCCESS);
}
/*
* hermon_cq_refcnt_dec()
* Context: Can be called from interrupt or base context.
*/
void
{
/* Decrement the completion queue's reference count */
}
/*
* hermon_cq_arm_doorbell()
* Context: Can be called from interrupt or base context.
*/
static int
{
/* initialize the FMA retry loop */
/* the FMA retry loop starts for Hermon doorbell register. */
if (cq_cmd == HERMON_CQDB_NOTIFY_CQ) {
if (old_cmd != HERMON_CQDB_NOTIFY_CQ) {
cmd_sn |= (HERMON_CQDB_NOTIFY_CQ <<
goto retry;
} /* else it's already armed */
} else {
if (old_cmd != HERMON_CQDB_NOTIFY_CQ &&
goto retry;
} /* else it's already armed */
}
/* the FMA retry loop ends. */
return (IBT_SUCCESS);
return (ibc_get_ci_failure(0));
}
/*
* hermon_cqhdl_from_cqnum()
* Context: Can be called from interrupt or base context.
*
* This routine is important because changing the unconstrained
* portion of the CQ number is critical to the detection of a
* potential race condition in the CQ handler code (i.e. the case
* where a CQ is freed and alloc'd again before an event for the
* "old" CQ can be handled).
*
* While this is not a perfect solution (not sure that one exists)
* it does help to mitigate the chance that this race condition will
* cause us to deliver a "stale" event to the new CQ owner. Note:
* this solution does not scale well because the number of constrained
* bits increases (and, hence, the number of unconstrained bits
* decreases) as the number of supported CQs grows. For small and
* intermediate values, it should hopefully provide sufficient
* protection.
*/
{
/* Calculate the CQ table index from the cqnum */
}
/*
* hermon_cq_cqe_consume()
* Context: Can be called from interrupt or base context.
*/
static void
{
/*
* Determine if this is an "error" CQE by examining "opcode". If it
* is an error CQE, then call hermon_cq_errcqe_consume() and return
* whatever status it returns. Otherwise, this is a successful
* completion.
*/
if ((opcode == HERMON_CQE_SEND_ERR_OPCODE) ||
(opcode == HERMON_CQE_RECV_ERR_OPCODE)) {
return;
}
/*
* Fetch the Work Request ID using the information in the CQE.
* See hermon_wr.c for more details.
*/
/*
* Parse the CQE opcode to determine completion type. This will set
* not only the type of the completion, but also any flags that might
* be associated with it (e.g. whether immediate data is present).
*/
/* Send CQE */
switch (opcode) {
case HERMON_CQE_SND_RDMAWR:
break;
case HERMON_CQE_SND_SEND_INV:
case HERMON_CQE_SND_SEND_IMM:
case HERMON_CQE_SND_SEND:
type = IBT_WRC_SEND;
break;
case HERMON_CQE_SND_LSO:
break;
case HERMON_CQE_SND_RDMARD:
break;
case HERMON_CQE_SND_ATOMIC_CS:
break;
case HERMON_CQE_SND_ATOMIC_FA:
type = IBT_WRC_FADD;
break;
case HERMON_CQE_SND_BIND_MW:
type = IBT_WRC_BIND;
break;
case HERMON_CQE_SND_FRWR:
break;
case HERMON_CQE_SND_LCL_INV:
break;
default:
return;
}
type = IBT_WRC_RECV;
} else {
/*
* Parse the remaining contents of the CQE into the work
* completion. This means filling in SL, QP number, SLID,
* immediate data, etc.
*
* Note: Not all of these fields are valid in a given
* completion. Many of them depend on the actual type of
* completion. So we fill in all of the fields and leave
* it up to the IBTF and consumer to sort out which are
* valid based on their context.
*/
wc->wc_immed_data =
/*
* Fill in "bytes transferred" as appropriate. Also,
* if necessary, fill in the "path bits" field.
*/
/*
* Check for GRH, update the flags, then fill in "wc_flags"
* field in the work completion
*/
}
/* Receive CQE */
switch (opcode) {
case HERMON_CQE_RCV_SEND_IMM:
/*
* Note: According to the PRM, all QP1 recv
* completions look like the result of a Send with
* Immediate. They are not, however, (MADs are Send
* Only) so we need to check the QP number and set
* the flag only if it is non-QP1.
*/
}
/* FALLTHROUGH */
case HERMON_CQE_RCV_SEND:
type = IBT_WRC_RECV;
flags |= IBT_WC_CKSUM_OK;
}
break;
case HERMON_CQE_RCV_SEND_INV:
type = IBT_WRC_RECV;
break;
break;
default:
return;
}
}
}
/*
* hermon_cq_errcqe_consume()
* Context: Can be called from interrupt or base context.
*/
static void
{
/*
* Fetch the Work Request ID using the information in the CQE.
* See hermon_wr.c for more details.
*/
/*
* Parse the CQE opcode to determine completion type. We know that
* the CQE is an error completion, so we extract only the completion
*/
if (status != HERMON_CQE_WR_FLUSHED_ERR)
switch (status) {
case HERMON_CQE_LOC_LEN_ERR:
break;
case HERMON_CQE_LOC_OP_ERR:
break;
case HERMON_CQE_LOC_PROT_ERR:
if (hermon_should_panic) {
"Local Protection Error\n");
}
break;
break;
case HERMON_CQE_MW_BIND_ERR:
break;
break;
break;
break;
case HERMON_CQE_REM_ACC_ERR:
break;
case HERMON_CQE_REM_OP_ERR:
break;
case HERMON_CQE_TRANS_TO_ERR:
break;
case HERMON_CQE_RNRNAK_TO_ERR:
break;
/*
* The following error codes are not supported in the Hermon driver
* as they relate only to Reliable Datagram completion statuses:
* case HERMON_CQE_LOCAL_RDD_VIO_ERR:
* case HERMON_CQE_REM_INV_RD_REQ_ERR:
* case HERMON_CQE_EEC_REM_ABORTED_ERR:
* case HERMON_CQE_INV_EEC_NUM_ERR:
* case HERMON_CQE_INV_EEC_STATE_ERR:
* case HERMON_CQE_LOC_EEC_ERR:
*/
default:
break;
}
}
/*
* hermon_cq_resize_helper()
* Context: Can be called only from user or kernel context.
*/
void
{
int status;
/*
* we're here because we found the special cqe opcode, so we have
* to update the cq_handle, release the old resources, clear the
* flag in the cq_hdl, and release the resize_hdl. When we return
* above, it will take care of the rest
*/
/*
* Deregister the memory for the old Completion Queue. Note: We
* really can't return error here because we have no good way to
* cleanup. Plus, the deregistration really shouldn't ever happen.
* So, if it does, it is an indication that something has gone
* seriously wrong. So we print a warning message and return error
* (knowing, of course, that the "old" CQ memory will be leaked)
*/
if (status != DDI_SUCCESS) {
}
/* Next, free the memory from the old CQ buffer */
/* now we can update the cq_hdl with the new things saved */
cq->cq_resize_hdl = 0;
sizeof (struct hermon_qalloc_info_s));
/* finally, release the resizing handle */
}
/*
* hermon_cq_entries_flush()
* Context: Can be called from interrupt or base context.
*/
/* ARGSUSED */
void
{
int outstanding_cqes;
else
}
do_send_cq: /* loop back to here if send_cq is not the same as recv_cq */
return;
/* Calculate the pointer to the first CQ entry */
/*
* Loop through the CQ looking for entries owned by software. If an
* entry is owned by software then we increment an 'outstanding_cqes'
* count to know how many entries total we have on our CQ. We use this
* value further down to know how many entries to loop through looking
* for our same QP number.
*/
outstanding_cqes = 0;
/* increment total cqes count */
/* increment the consumer index */
/* update the pointer to the next cq entry */
}
/*
* Using the 'tail_cons_indx' that was just set, we now know how many
* total CQEs possible there are. Set the 'check_indx' and the
* 'new_indx' to the last entry identified by 'tail_cons_indx'
*/
while (--outstanding_cqes >= 0) {
/*
* If the QP number is the same in the CQE as the QP, then
* we must "consume" it. If it is for an SRQ wqe, then we
* also must free the wqe back onto the free list of the SRQ.
*/
int indx;
/* Add wqe back to SRQ free list */
}
} else { /* CQEs for other QPNs need to remain */
if (check_indx != new_indx) {
next_cqe =
/* Copy the CQE into the "next_cqe" pointer. */
}
new_indx--; /* move index to next CQE to fill */
}
check_indx--; /* move index to next CQE to check */
}
/*
* Update consumer index to be the 'new_indx'. This moves it past all
* removed entries. Because 'new_indx' is pointing to the last
* previously valid SW owned entry, we add 1 to point the cons_indx to
* the first HW owned entry.
*/
/*
* Now we only ring the doorbell (to update the consumer index) if
* we've actually consumed a CQ entry. If we found no QP number
* matches above, then we would not have removed anything. So only if
* something was removed do we ring the doorbell.
*/
/*
* Update the consumer index in both the CQ handle and the
* doorbell record.
*/
}
goto do_send_cq;
}
}
/*
* hermon_get_cq_sched_list()
* Context: Only called from attach() path context
*
* Read properties, creating entries in hs_cq_sched_list with
* information about the requested "expected" and "minimum"
* number of MSI-X interrupt vectors per list entry.
*/
static int
{
int *data;
return (0);
sizeof (hermon_cq_sched_t), KM_SLEEP);
for (i = 0; i < nlist; i++) {
goto game_over;
}
for (j = 0; j < i; j++) {
goto game_over;
}
}
ulp_prop[0] = 'c';
goto game_over;
}
if (ndata != 2) {
"have 2 integers\n", ulp_prop);
goto game_over_free_data;
}
cq_schedp[i].cqs_refcnt = 0;
}
goto game_over;
}
if (ndata != 2) {
"have 2 integers\n");
goto game_over_free_data;
}
cq_schedp->cqs_refcnt = 0;
return (1); /* game on */
state->hs_cq_sched_array_size = 0;
return (0);
}
/*
* hermon_cq_sched_init()
* Context: Only called from attach() path context
*
* Read the hermon.conf properties looking for cq_sched info,
* creating reserved pools of MSI-X interrupt ranges for the
* specified ULPs.
*/
int
{
/* initialize cq_sched_default */
/* Read properties to determine which ULPs use cq_sched */
if (hermon_get_cq_sched_list(state) == 0)
goto done;
/* Determine if we have enough vectors, or if we have to scale down */
if (desired <= 0)
goto done; /* all interrupts in the default pool */
for (i = 0; i < array_size; i++)
"the #interrupts desired (%d)\n",
goto done; /* all interrupts in the default pool */
}
/* Game on. For each cq_sched group, reserve the MSI-X range */
for (i = 0; i < array_size; i++) {
}
/* reset default's start allocation seed */
done:
return (IBT_SUCCESS);
}
void
{
if (state->hs_cq_sched_array_size) {
state->hs_cq_sched_array_size = 0;
}
}
int
{
int i;
char *name;
*cq_sched_pp = NULL;
return (IBT_SUCCESS);
}
cq_schedp->cqs_refcnt++;
break; /* found it */
}
}
return (IBT_CQ_NO_SCHED_GROUP);
else
return (IBT_SUCCESS);
}
int
{
/* Just decrement refcnt */
if (cq_schedp->cqs_refcnt == 0)
else
cq_schedp->cqs_refcnt--;
}
return (IBT_SUCCESS);
}