/*
* 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 Interrupt and Event Processing Routines
*
* Implements all the routines necessary for allocating, freeing, and
* handling all of the various event types that the Hermon hardware can
* generate.
* These routines include the main Hermon interrupt service routine
* (hermon_isr()) as well as all the code necessary to setup and handle
* events from each of the many event queues used by the Hermon device.
*/
/*
* hermon_eq_init_all
* Context: Only called from attach() path context
*/
int
{
/* initialize the FMA retry loop */
/*
* For now, all Event Queues default to the same size (pulled from
* the current configuration profile) and are all assigned to the
* same interrupt or MSI. In the future we may support assigning
* EQs to specific interrupts or MSIs XXX
*/
/*
* Total number of supported EQs is fixed. Hermon hardware
* supports up to 512 EQs, though in theory they will one day be
* alloc'd to virtual HCA's. We are currently using only 47 of them
* - that is, in Arbel and Tavor, before HERMON, where
* we had set aside the first 32 for use with Completion Queues (CQ)
* and reserved a few of the other 32 for each specific class of event
*
* However, with the coming of vitualization, we'll have only 4 per
* potential guest - so, we'll try alloc'ing them differntly
* (see below for more details).
*/
/*
* If MSI is to be used, then set intr_num to the MSI number.
* Otherwise, for fixed (i.e. 'legacy') interrupts,
* it is what the card tells us in 'inta_pin'.
*/
num_extra = 0;
} else {
/* If we have more than one MSI-X vector, init them. */
if (status != DDI_SUCCESS) {
while (--i >= 0) {
(void) hermon_eq_handler_fini(state,
eq[i]);
}
return (DDI_FAILURE);
}
}
intr_num = i;
num_extra = i;
}
/*
* Allocate and initialize the rest of the Event Queues to be used.
* If any of these EQ allocations fail then jump to the end, cleanup
* what had been successfully initialized, and return an error.
*/
for (i = 0; i < num_eq; i++) {
if (status != DDI_SUCCESS) {
num_eq_init = i;
goto all_eq_init_fail;
}
}
/*
* The "num_eq_unmap" variable is used in any possible failure
* cleanup (below) to indicate which events queues might require
* possible event class unmapping.
*/
num_eq_unmap = 0;
/*
* Setup EQ0 (first avail) for use with Completion Queues. Note: We can
* cast the return value to void here because, when we use the
* HERMON_EVT_NO_MASK flag, it is not possible for
* hermon_eq_handler_init() to return an error.
*/
num_eq_unmap++;
/*
* Setup EQ1 for handling Completion Queue Error Events.
*
* These events include things like CQ overflow or CQ access
* violation errors. If this setup fails for any reason (which, in
* general, it really never should), then jump to the end, cleanup
* everything that has been successfully initialized, and return an
* error.
*/
if (status != DDI_SUCCESS) {
goto all_eq_init_fail;
}
num_eq_unmap++;
/*
* Setup EQ2 for handling most other things including:
*
* Port State Change Events
* These events include things like Port Up and Port Down events.
*
* Communication Established Events
* These events correspond to the IB affiliated asynchronous events
* that are used for connection management
*
* Path Migration Succeeded Events
* These evens corresponid to the IB affiliated asynchronous events
* that are used to indicate successful completion of a
* Path Migration.
*
* Command Completion Events
* These events correspond to the Arbel generated events that are used
* to indicate Arbel firmware command completion.
*
* Local WQ Catastrophic Error Events
* Invalid Req Local WQ Error Events
* Local Access Violation WQ Error Events
* SRQ Catastrophic Error Events
* SRQ Last WQE Reached Events
* ECC error detection events
* These events also correspond to the similarly-named IB affiliated
* asynchronous error type.
*
* Send Queue Drained Events
* These events correspond to the IB affiliated asynchronous events
* that are used to indicate completion of a Send Queue Drained QP
* state transition.
*
* Path Migration Failed Events
* These events correspond to the IB affiliated asynchronous events
* that are used to indicate that path migration was not successful.
*
* Fibre Channel Error Event
* This event is affiliated with an Fexch QP.
*
* NOTE: When an event fires on this EQ, it will demux the type and
* send it to the right specific handler routine
*
*/
if (status != DDI_SUCCESS) {
goto all_eq_init_fail;
}
num_eq_unmap++;
/*
* Setup EQ3 to catch all other types of events. Specifically, we
* do not catch the "Local EEC Catastrophic Error Event" because we
* should have no EEC (the Arbel driver does not support RD). We also
* choose not to handle any of the address translation page fault
* event types. Since we are not doing any page fault handling (and
* since the Arbel firmware does not currently support any such
* handling), we allow these events to go to the catch-all handler.
*/
if (status != DDI_SUCCESS) {
goto all_eq_init_fail;
}
num_eq_unmap++;
/* the FMA retry loop starts. */
/*
* Run through and initialize the Consumer Index for each EQC.
*/
}
/* the FMA retry loop ends. */
return (DDI_SUCCESS);
/* Unmap any of the partially mapped EQs from above */
for (i = 0; i < num_eq_unmap + num_extra; i++) {
}
/* Free up any of the partially allocated EQs from above */
for (i = 0; i < num_eq_init + num_extra; i++) {
}
/* If a HW error happen during ddi_pio, return DDI_FAILURE */
if (fm_status == HCA_PIO_PERSISTENT) {
}
return (status);
}
/*
* hermon_eq_fini_all
*/
int
{
int status, i;
/*
* Grab the total number of supported EQs again. This is the same
* hardcoded value that was used above (during the event queue
* initialization.)
*/
/*
* For each of the event queues that we initialized and mapped
* earlier, attempt to unmap the events from the EQ.
*/
for (i = 0; i < num_eq; i++) {
if (status != DDI_SUCCESS) {
return (DDI_FAILURE);
}
}
/*
* Teardown and free up all the Event Queues that were allocated
* earlier.
*/
for (i = 0; i < num_eq; i++) {
if (status != DDI_SUCCESS) {
return (DDI_FAILURE);
}
}
return (DDI_SUCCESS);
}
/*
* hermon_eq_reset_uar_baseaddr
* Context: Only called from attach()
*/
void
{
int i, num_eq;
for (i = 0; i < num_eq; i++) {
}
}
/*
* hermon_eq_arm_all
*/
int
{
int i;
/* initialize the FMA retry loop */
/* the FMA retry loop starts. */
fm_test);
for (i = 0; i < num_eq; i++) {
}
/* the FMA retry loop ends. */
fm_test);
return (DDI_SUCCESS);
return (DDI_FAILURE);
}
/*
* hermon_isr()
* Context: Only called from interrupt context (and during panic)
*/
{
int i, r;
int intr;
/*
* Grab the Hermon softstate pointer from the input parameter
*/
/* Get the interrupt number */
/*
* Clear the interrupt. Note: This is only needed for
* fixed interrupts as the framework does what is needed for
* MSI-X interrupts.
*/
/* initialize the FMA retry loop */
/* the FMA retry loop starts. */
/* the FMA retry loop ends. */
fm_test);
}
/*
* Loop through all the EQs looking for ones that have "fired".
* To determine if an EQ is fired, the ownership will be the SW
* (the HW will set the owner appropriately). Update the Consumer Index
* of the Event Queue Entry (EQE) and pass it to HW by writing it
* to the respective Set CI DB Register.
*
* The "else" case handles the extra EQs used only for completion
* events, whereas the "if" case deals with the required interrupt
* vector that is used for all classes of events.
*/
r = state->hs_rsvd_eqs;
for (i = 0; i < HERMON_NUM_EQ_USED; i++) {
}
} else { /* only poll the one EQ */
}
return (DDI_INTR_CLAIMED);
return (DDI_INTR_UNCLAIMED);
}
/*
* hermon_eq_poll
* Context: Only called from interrupt context (and during panic)
*/
static void
{
int polled_some;
/* initialize the FMA retry loop */
/* Get the consumer pointer index */
/*
* Calculate the wrap around mask. Note: This operation only works
* because all Hermon event queues have power-of-2 sizes
*/
/* Calculate the pointer to the first EQ entry */
/*
* Pull the handler function for this EQ from the Hermon Event Queue
* handle
*/
for (;;) {
polled_some = 0;
/*
* Call the EQ handler function. But only call if we
* are not in polled I/O mode (i.e. not processing
* because of a system panic). Note: We don't call
* the EQ handling functions from a system panic
* because we are primarily concerned only with
* ensuring that the event queues do not overflow (or,
* more specifically, the event queue associated with
* Also, we don't want to make any upcalls (to the
* calls would ever return. And, if we're in panic,
* then we reached here through a PollCQ() call (from
* hermon_cq_poll()), and we need to ensure that we
* successfully return any work completions to the
* caller.
*/
if (ddi_in_panic() == 0) {
}
/* Reset to hardware ownership is implicit */
/* Increment the consumer index */
cons_indx++;
/* Update the pointer to the next EQ entry */
polled_some = 1;
}
/*
* write consumer index via EQ set CI Doorbell, to keep overflow
* from occuring during poll
*/
/* the FMA retry loop starts. */
/* the FMA retry loop starts. */
if (polled_some == 0)
break;
};
return;
}
/*
* hermon_eq_catastrophic
* Context: Only called from interrupt context (and during panic)
*/
static void
{
int i;
/* initialize the FMA retry loop */
/* the FMA retry loop starts. */
fm_test);
/* the FMA retry loop ends. */
fm_test);
switch (err_type) {
err_type);
break;
err_type);
break;
err_type);
break;
err_type);
break;
default:
/* Unknown type of Catastrophic error */
err_type);
break;
}
/* the FMA retry loop starts. */
fm_test);
/*
* Read in the catastrophic error buffer from the hardware.
*/
for (i = 0; i < buf_size; i++) {
}
/* the FMA retry loop ends. */
fm_test);
/*
* We also call the IBTF here to inform it of the catastrophic error.
* Note: Since no event information (i.e. QP handles, CQ handles,
* etc.) is necessary, we pass a NULL pointer instead of a pointer to
* an empty ibc_async_event_t struct.
*
* But we also 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.
*/
}
/* ignore these errors but log them because they're harmless. */
}
/*
* hermon_eq_alloc()
* Context: Only called from attach() path context
*/
static int
{
int status;
/* Use the internal protection domain (PD) for setting up EQs */
/* Increment the reference count on the protection domain (PD) */
/*
* Allocate an EQ context entry. This will be filled in with all
* the necessary parameters to define the Event 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 eqalloc_fail1;
}
/*
* Allocate the software structure for tracking the event queue (i.e.
* the Hermon Event Queue handle). If we fail here, we must undo the
* protection domain reference count and the previous resource
* allocation.
*/
if (status != DDI_SUCCESS) {
goto eqalloc_fail2;
}
/*
* Allocate the memory for Event Queue.
*/
if (status != DDI_SUCCESS) {
goto eqalloc_fail3;
}
/*
* Initializing each of the Event Queue Entries (EQE) by setting their
* ownership to hardware ("owner" bit set to HW) is now done by HW
* when the transfer of ownership (below) of the
* EQ context itself is done.
*/
/*
* Register the memory for the EQ.
*
* Because we are in the attach path we use NOSLEEP here so that we
* SPIN in the HCR since the event queues are not setup yet, and we
* cannot NOSPIN at this point in time.
*/
op.mro_bind_override_addr = 0;
if (status != DDI_SUCCESS) {
goto eqalloc_fail4;
}
/*
* Fill in the EQC entry. This is the final step before passing
* ownership of the EQC entry to the Hermon hardware. We use all of
* the information collected/calculated above to fill in the
* requisite portions of the EQC. Note: We create all EQs in the
* "fired" state. We will arm them later (after our interrupt
* routine had been registered.)
*/
/*
* Write the EQC entry to hardware. Lastly, we pass ownership of
* the entry to the hardware (using the Hermon SW2HW_EQ 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) {
if (status == HERMON_CMD_INVALID_STATUS) {
}
status = ibc_get_ci_failure(0);
goto eqalloc_fail5;
}
/*
* Fill in the rest of the Hermon Event Queue handle. Having
* successfully transferred ownership of the EQC, we can update the
* following fields for use in further operations on the EQ.
*/
eq->eq_consindx = 0;
return (DDI_SUCCESS);
/*
* The following is cleanup for all possible failure cases in this routine
*/
HERMON_NOSLEEP) != DDI_SUCCESS) {
}
return (status);
}
/*
* hermon_eq_free()
*/
static int
{
int status;
/*
* Pull all the necessary information from the Hermon Event Queue
* handle. This is necessary here because the resource for the
* EQ handle is going to be freed up as part of this operation.
*/
/*
* Reclaim EQC entry from hardware (using the Hermon HW2SW_EQ
* 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);
return (DDI_FAILURE);
}
/*
* Deregister the memory for the Event 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 continue.
*/
if (status != DDI_SUCCESS) {
}
/* Free the memory for the EQ */
/* Free the Hermon Event Queue handle */
/* Free up the EQC entry resource */
/* Decrement the reference count on the protection domain (PD) */
/* Set the eqhdl pointer to NULL and return success */
return (DDI_SUCCESS);
}
/*
* hermon_eq_handler_init
* Context: Only called from attach() path context
*/
static int
{
int status;
/*
* Save away the EQ handler function and the event type mask. These
* will be used later during interrupt and event queue processing.
*/
/*
* Map the EQ to a specific class of event (or events) depending
* on the mask value passed in. The HERMON_EVT_NO_MASK means not
* to attempt associating the EQ with any specific class of event.
* This is particularly useful when initializing the events queues
* used for CQ events. The mapping is done using the Hermon MAP_EQ
* firmware command. Note: This command should not, in general, fail.
* If it does, then something (probably HW related) has gone seriously
* wrong.
*/
if (evt_type_mask != HERMON_EVT_NO_MASK) {
if (status != HERMON_CMD_SUCCESS) {
return (DDI_FAILURE);
}
}
return (DDI_SUCCESS);
}
/*
* hermon_eq_handler_fini
*/
static int
{
int status;
/*
* Unmap the EQ from the event class to which it had been previously
* mapped. The unmapping is done using the Hermon MAP_EQ (in much
* the same way that the initial mapping was done). The difference,
* however, is in the HERMON_EQ_EVT_UNMAP flag that is passed to the
* MAP_EQ firmware command. The HERMON_EVT_NO_MASK (which may have
* been passed in at init time) still means that no association has
* been made between the EQ and any specific class of event (and,
* hence, no unmapping is necessary). Note: This command should not,
* in general, fail. If it does, then something (probably HW related)
* has gone seriously wrong.
*/
if (status != HERMON_CMD_SUCCESS) {
return (DDI_FAILURE);
}
}
return (DDI_SUCCESS);
}
/*
* hermon_eq_demux()
* Context: Called only from interrupt context
* Usage: to demux the various type reported on one EQ
*/
static int
{
switch (eqe_evttype) {
break;
break;
break;
break;
break;
break;
break;
case HERMON_EVT_PATH_MIGRATED:
break;
break;
break;
break;
case HERMON_EVT_FEXCH_ERROR:
break;
default:
break;
}
return (status);
}
/*
* hermon_port_state_change_handler()
* Context: Only called from interrupt context
*/
/* ARGSUSED */
static int
{
/*
* Depending on the type of Port State Change event, pass the
* appropriate asynch event to the IBTF.
*/
/* Check for valid port number in event */
"change event");
return (DDI_FAILURE);
}
if (subtype == HERMON_PORT_LINK_ACTIVE) {
} else if (subtype == HERMON_PORT_LINK_DOWN) {
} else {
"event");
return (DDI_FAILURE);
}
/*
* Deliver the event to the IBTF. Note: If "hs_ibtfpriv" is NULL,
* then we 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_comm_estbl_handler()
* Context: Only called from interrupt context
*/
/* ARGSUSED */
static int
{
/* Get the QP handle from QP number in event descriptor */
/*
* If the QP handle is NULL, this is probably an indication
* that the QP has been freed already. In which case, we
* should not deliver this event.
*
* We also check that the QP number in the handle is the
* same as the QP number in the event queue entry. This
* extra check allows us to handle the case where a QP 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 QP number every time
* a new QP is allocated, we mitigate (somewhat) the chance
* that a stale event could be passed to the client's QP
* 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_local_wq_cat_err_handler()
* Context: Only called from interrupt context
*/
/* ARGSUSED */
static int
{
/* Get the QP handle from QP number in event descriptor */
/*
* If the QP handle is NULL, this is probably an indication
* that the QP has been freed already. In which case, we
* should not deliver this event.
*
* We also check that the QP number in the handle is the
* same as the QP number in the event queue entry. This
* extra check allows us to handle the case where a QP 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 QP number every time
* a new QP is allocated, we mitigate (somewhat) the chance
* that a stale event could be passed to the client's QP
* 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_invreq_local_wq_err_handler()
* Context: Only called from interrupt context
*/
/* ARGSUSED */
static int
{
/* Get the QP handle from QP number in event descriptor */
/*
* If the QP handle is NULL, this is probably an indication
* that the QP has been freed already. In which case, we
* should not deliver this event.
*
* We also check that the QP number in the handle is the
* same as the QP number in the event queue entry. This
* extra check allows us to handle the case where a QP 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 QP number every time
* a new QP is allocated, we mitigate (somewhat) the chance
* that a stale event could be passed to the client's QP
* 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_local_acc_vio_wq_err_handler()
* Context: Only called from interrupt context
*/
/* ARGSUSED */
static int
{
/* Get the QP handle from QP number in event descriptor */
/*
* If the QP handle is NULL, this is probably an indication
* that the QP has been freed already. In which case, we
* should not deliver this event.
*
* We also check that the QP number in the handle is the
* same as the QP number in the event queue entry. This
* extra check allows us to handle the case where a QP 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 QP number every time
* a new QP is allocated, we mitigate (somewhat) the chance
* that a stale event could be passed to the client's QP
* 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_sendq_drained_handler()
* Context: Only called from interrupt context
*/
/* ARGSUSED */
static int
{
/* Get the QP handle from QP number in event descriptor */
/*
* If the QP handle is NULL, this is probably an indication
* that the QP has been freed already. In which case, we
* should not deliver this event.
*
* We also check that the QP number in the handle is the
* same as the QP number in the event queue entry. This
* extra check allows us to handle the case where a QP 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 QP number every time
* a new QP is allocated, we mitigate (somewhat) the chance
* that a stale event could be passed to the client's QP
* 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.
*/
/*
* Grab the QP lock and update the QP state to reflect that
* the Send Queue Drained event has arrived. Also determine
* whether the event is intended to be forwarded on to the
* consumer or not. This information is used below in
* determining whether or not to call the IBTF.
*/
qp->qp_forward_sqd_event = 0;
qp->qp_sqd_still_draining = 0;
if (forward_sqd_event != 0) {
}
}
return (DDI_SUCCESS);
}
/*
* hermon_path_mig_handler()
* Context: Only called from interrupt context
*/
/* ARGSUSED */
static int
{
/* Get the QP handle from QP number in event descriptor */
/*
* If the QP handle is NULL, this is probably an indication
* that the QP has been freed already. In which case, we
* should not deliver this event.
*
* We also check that the QP number in the handle is the
* same as the QP number in the event queue entry. This
* extra check allows us to handle the case where a QP 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 QP number every time
* a new QP is allocated, we mitigate (somewhat) the chance
* that a stale event could be passed to the client's QP
* 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_path_mig_err_handler()
* Context: Only called from interrupt context
*/
/* ARGSUSED */
static int
{
/* Get the QP handle from QP number in event descriptor */
/*
* If the QP handle is NULL, this is probably an indication
* that the QP has been freed already. In which case, we
* should not deliver this event.
*
* We also check that the QP number in the handle is the
* same as the QP number in the event queue entry. This
* extra check allows us to handle the case where a QP 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 QP number every time
* a new QP is allocated, we mitigate (somewhat) the chance
* that a stale event could be passed to the client's QP
* 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_catastrophic_handler()
* Context: Only called from interrupt context
*/
/* ARGSUSED */
static int
{
return (DDI_SUCCESS);
}
/* Get the QP handle from QP number in event descriptor */
/*
* If the QP handle is NULL, this is probably an indication
* that the QP has been freed already. In which case, we
* should not deliver this event.
*
* We also check that the QP number in the handle is the
* same as the QP number in the event queue entry. This
* extra check allows us to handle the case where a QP 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 QP number every time
* a new QP is allocated, we mitigate (somewhat) the chance
* that a stale event could be passed to the client's QP
* 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_srq_last_wqe_reached_handler()
* Context: Only called from interrupt context
*/
/* ARGSUSED */
static int
{
/* Get the QP handle from QP number in event descriptor */
/*
* If the QP handle is NULL, this is probably an indication
* that the QP has been freed already. In which case, we
* should not deliver this event.
*
* We also check that the QP number in the handle is the
* same as the QP number in the event queue entry. This
* extra check allows us to handle the case where a QP 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 QP number every time
* a new QP is allocated, we mitigate (somewhat) the chance
* that a stale event could be passed to the client's QP
* 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);
}
/* ARGSUSED */
{
/* Get the QP handle from QP number in event descriptor */
/*
* If the QP handle is NULL, this is probably an indication
* that the QP has been freed already. In which case, we
* should not deliver this event.
*
* We also check that the QP number in the handle is the
* same as the QP number in the event queue entry. This
* extra check allows us to handle the case where a QP 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 QP number every time
* a new QP is allocated, we mitigate (somewhat) the chance
* that a stale event could be passed to the client's QP
* 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_no_eqhandler
* Context: Only called from interrupt context
*/
/* ARGSUSED */
static int
{
int i;
/*
* This "unexpected event" handler (or "catch-all" handler) will
* receive all events for which no other handler has been registered.
* If we end up here, then something has probably gone seriously wrong
* with the Hermon hardware (or, perhaps, with the software... though
* it's unlikely in this case). The EQE provides all the information
* about the event. So we print a warning message here along with
* the contents of the EQE.
*/
for (i = 0; i < sizeof (hermon_hw_eqe_t) >> 2; i++) {
}
return (DDI_SUCCESS);
}