/*
* 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 (InfiniBand) HCA Driver Fault Management Routines
*
* [Hermon FM Implementation]
*
* HW error by calling the FMA acc handle check functions. (calling
* ddi_fm_acc_err_get()) If a HW error is detected when either
* ddi_fm_acc_err_get() is called, to determine whether or not the error is
* transient, the I/O operation causing the error will retry up to three times.
*
* (Basic HW error recovery)
*
* |
* .---->*
* | |
* | issue an I/O request via PIO
* | |
* | |
* | check acc handle
* | |
* | |
* `--< a HW error detected && retry count < 3 >
* |
* v
*
* When a HW error is detected, to provide the error information for users to
* isolate the faulted HW, Hermon FM issues Solaris FMA ereports as follows.
*
* * PIO transient error
* invalid_state => unaffected
*
* * PIO persistent error
* invalid_state => lost
*
* * PIO fatal error
* invalid_state => lost => panic
*
* * Hermon HCA firmware error
* invalid_state => degraded
*
* * Other Hermon HCA specific errors
* uncorrect => unaffected
* or
* correct => unaffected
*
* (Restrictions)
*
* The current implementation has the following restrictions.
* * No runtime check/protection
* * No detach time check/protection
* * No DMA check/protection
*
* See the Hermon FMA portfolio in detail.
*/
#include <sys/sysmacros.h>
/*
* Hermon driver has to disable its FM functionality
* if this "fm_capable" variable is defined or has a value
* in /kernel/drv/hermon.conf.
*/
static void i_hca_fm_ereport(dev_info_t *, int, char *);
static void i_hca_fm_init(struct i_hca_fm *);
static void i_hca_fm_fini(struct i_hca_fm *);
ddi_acc_handle_t *);
hermon_test_t *);
hermon_test_t *);
/* forward declaration for hermon_fm_{init, fini}() */
#ifdef FMA_TEST
#endif /* FMA_TEST */
/*
* Hermon FM Functions
*
* These functions are based on the HCA FM common interface
* defined below, but specific to the Hermon HCA FM capabilities.
*/
/*
* void
* hermon_hca_fm_init(hermon_state_t *state, hermon_hca_fm_t *hca)
*
* Overview
* hermon_hca_fm_init() initializes the Hermon FM resources.
*
* Argument
* state: pointer to Hermon state structure
* hca: pointer to Hermon FM structure
*
* Return value
* Nothing
*
* Caller's context
* hermon_hca_fm_init() can be called in user or kernel context only.
*/
static void
{
}
/*
* void
* hermon_hca_fm_fini(hermon_state_t *state)
*
* Overview
* hermon_hca_fm_fini() releases the Hermon FM resources.
*
* Argument
* state: pointer to Hermon state structure
*
* Return value
* Nothing
*
* Caller's context
* hermon_hca_fm_fini() can be called in user or kernel context only.
*/
static void
{
}
/*
* void
* hermon_clr_state_nolock(hermon_state_t *state, int fm_state)
*
* Overview
* hermon_clr_state() drops the specified state from Hermon FM state
* without the mutex locks.
*
* Argument
* state: pointer to Hermon state structure
* fm_state: Hermon FM state, which is composed of:
* HCA_NO_FM Hermom FM is not supported
* HCA_PIO_FM PIO is fma-protected
* HCA_DMA_FM DMA is fma-protected
* HCA_EREPORT_FM FMA ereport is available
* HCA_ERRCB_FM FMA error callback is supported
* HCA_ATTCH_FM HCA FM attach mode
* HCA_RUNTM_FM HCA FM runtime mode
*
* Return value
* Nothing
*
* Caller's context
* hermon_clr_state() can be called in user, kernel, interrupt context
* or high interrupt context.
*/
void
{
extern void membar_sync(void);
membar_sync();
}
/*
* void
* hermon_clr_state(hermon_state_t *state, int fm_state)
*
* Overview
* hermon_clr_state() drops the specified state from Hermon FM state.
*
* Argument
* state: pointer to Hermon state structure
* fm_state: Hermon FM state, which is composed of:
* HCA_NO_FM Hermom FM is not supported
* HCA_PIO_FM PIO is fma-protected
* HCA_DMA_FM DMA is fma-protected
* HCA_EREPORT_FM FMA ereport is available
* HCA_ERRCB_FM FMA error callback is supported
* HCA_ATTCH_FM HCA FM attach mode
* HCA_RUNTM_FM HCA FM runtime mode
*
* Return value
* Nothing
*
* Caller's context
* hermon_clr_state() can be called in user, kernel or interrupt context.
*/
static void
{
}
/*
* void
* hermon_set_state(hermon_state_t *state, int fm_state)
*
* Overview
* hermon_set_state() sets Hermon FM state.
*
* Argument
* state: pointer to Hermon state structure
* fm_state: Hermon FM state, which is composed of:
* HCA_NO_FM Hermom FM is not supported
* HCA_PIO_FM PIO is fma-protected
* HCA_DMA_FM DMA is fma-protected
* HCA_EREPORT_FM FMA ereport is available
* HCA_ERRCB_FM FMA error callback is supported
* HCA_ATTCH_FM HCA FM attach mode
* HCA_RUNTM_FM HCA FM runtime mode
*
* Return value
* Nothing
*
* Caller's context
* hermon_set_state() can be called in user, kernel or interrupt context.
*/
static void
{
extern void membar_sync(void);
} else {
}
membar_sync();
}
/*
* int
* hermon_get_state(hermon_state_t *state)
*
* Overview
* hermon_get_state() returns the current Hermon FM state.
*
* Argument
* state: pointer to Hermon state structure
*
* Return value
* fm_state: Hermon FM state, which is composed of:
* HCA_NO_FM Hermom FM is not supported
* HCA_PIO_FM PIO is fma-protected
* HCA_DMA_FM DMA is fma-protected
* HCA_EREPORT_FM FMA ereport is available
* HCA_ERRCB_FM FMA error callback is supported
* HCA_ATTCH_FM HCA FM attach mode
* HCA_RUNTM_FM HCA FM runtime mode
*
* Caller's context
* hermon_get_state() can be called in user, kernel or interrupt context.
*/
int
{
return (state->hs_fm_state);
}
/*
* void
* hermon_fm_init(hermon_state_t *state)
*
* Overview
* hermon_fm_init() is a Hermon FM initialization function which registers
* some FMA functions such as the ereport and the acc check capabilities
* for Hermon. If the "fm_disable" property in /kernel/drv/hermon.conf is
* drop, and only the default capabilities (the ereport and error callback
* capabilities) are available (and the action against HW errors is
* issuing an ereport then panicking the system).
*
* Argument
* state: pointer to Hermon state structure
*
* Return value
* Nothing
*
* Caller's context
* hermon_fm_init() can be called in user or kernel context only.
*/
void
{
/*
* Check the "fm_disable" property. If it's defined,
* use the Solaris FMA default action for Hermon.
*/
"fm_disable", 0) != 0) {
}
/* If hs_fm_diable is set, then skip the rest */
if (state->hs_fm_disable) {
return;
}
/* Set the Hermon FM attach mode */
/* Initialize the Solaris FMA capabilities for the Hermon FM support */
/*
* The Hermon FM uses the ereport and acc check capabilites only,
* but both of them should be available. If either is not, turn
* hs_fm_disable on and behave in the same way as the "fm_diable"
* property is set.
*/
if (state->hs_fm_capabilities !=
"Hermon FM capability fails");
return;
}
/* Initialize the HCA FM resources */
/* Initialize the fm state lock */
/* Register the capabilities with the IO fault services */
/* Set up the pci ereport capabilities if the ereport is capable */
}
/* Set the Hermon FM state */
#ifdef FMA_TEST
#endif /* FMA_TEST */
}
/*
* void
* hermon_fm_fini(hermon_state_t *state)
*
* Overview
* hermon_fm_fini() is a Hermon FM finalization function which de-registers
* Solaris FMA functions set to Hermon.
*
* Argument
* state: pointer to Hermon state structure
*
* Return value
* Nothing
*
* Caller's context
* hermon_fm_fini() can be called in user or kernel context only.
*/
void
{
/*
* If hermon_fm_diable is set or there is no FM service provided,
* then skip the rest.
*/
return;
}
#ifdef FMA_TEST
#endif /* FMA_TEST */
/* Set the Hermon FM state to no support */
/* Release HCA FM resources */
/*
* Release any resources allocated by pci_ereport_setup()
*/
}
/* De-register the Hermon FM from the IO fault services */
}
/*
* int
* hermon_fm_ereport_init(hermon_state_t *state)
*
* Overview
* hermon_fm_ereport_init() changes the Hermon FM state to the ereport
* only mode during the driver attach.
*
* Argument
* state: pointer to Hermon state structure
*
* Return value
* DDI_SUCCESS
* DDI_FAILURE
*
* Caller's context
* hermon_fm_ereport_init() can be called in user or kernel context only.
*/
int
{
};
char *rsrc_name;
extern void membar_sync(void);
/* Stop the poll thread while the FM state is being changed */
membar_sync();
/*
* Disable the Hermon interrupt after the interrupt capability flag
* is checked.
*/
return (DDI_FAILURE);
}
} else {
if (ddi_intr_disable
return (DDI_FAILURE);
}
}
/*
* Release any resources allocated by pci_ereport_setup()
*/
}
/* De-register the Hermon FM from the IO fault services */
/* Re-initialize fm ereport with the ereport only */
/*
* Now that the Hermon FM uses the ereport capability only,
* If it's not set, turn hs_fm_disable on and behave in the
* same way as the "fm_diable" property is set.
*/
"Hermon FM ereport fails (ereport mode)");
goto error;
}
/* Re-register the ereport capability with the IO fault services */
/* Initialize the pci ereport capabilities if the ereport is capable */
}
DDI_SUCCESS) {
"PCI config mapping fails (ereport mode)");
goto error;
}
/* Allocate the regular access handle for MSI-X tables */
"MSI-X Table mapping fails (ereport mode)");
goto error;
}
/* Allocate the regular access handle for MSI-X PBA */
"MSI-X PBA mapping fails (ereport mode)");
goto error;
}
/* Allocate the regular access handle for Hermon CMD I/O space */
"CMD_BAR mapping fails (ereport mode)");
goto error;
}
/* Reset the host command register */
/* Reset the software reset register */
/* Reset the software reset register semaphore */
/* Calculate the clear interrupt register offset */
/* Reset the clear interrupt address */
/* Reset the internal error buffer address */
/* Check if the blue flame is enabled, and set the offset value */
} else {
offset = 0;
}
/* Allocate the regular access handle for Hermon UAR I/O space */
"UAR BAR mapping fails (ereport mode)");
goto error;
}
/* Drop the Hermon FM Attach Mode */
/* Set the Hermon FM Runtime Mode */
/* Free up Hermon UAR page #1 */
/* Free up the UAR pool */
/* Re-allocate the UAR pool */
goto error;
}
/* Re-allocate the kernel UAR page */
goto error;
}
/* Setup pointer to kernel UAR page */
/* Now drop the the Hermon PIO FM */
/* Release the MSI-X Table access handle */
if (state->hs_fm_msix_tblhdl) {
}
/* Release the MSI-X PBA access handle */
if (state->hs_fm_msix_pbahdl) {
}
/* Release the pci config space access handle */
if (state->hs_fm_pcihdl) {
}
/* Release the cmd protected access handle */
if (state->hs_fm_cmdhdl) {
}
/* Release the uar fma-protected access handle */
if (state->hs_fm_uarhdl) {
}
/* Enable the Hermon interrupt again */
return (DDI_FAILURE);
}
} else {
if (ddi_intr_enable
return (DDI_FAILURE);
}
}
/* Restart the poll thread */
return (DDI_SUCCESS);
/* Enable the Hermon interrupt again */
} else {
}
return (DDI_FAILURE);
}
/*
* int
* hermon_regs_map_setup(hermon_state_t *state, uint_t rnumber, caddr_t *addrp,
* offset_t offset, offset_t len, ddi_device_acc_attr_t *accattrp,
* ddi_acc_handle_t *handle)
*
* Overview
* This is a wrapper function of i_hca_regs_map_setup() for Hermon FM so
* that it calls i_hca_regs_map_setup() inside after it checks the
* "fm_disable" configuration property. If the "fm_disable" is described
* in /kernel/drv/hermon.conf, the function calls ddi_regs_map_setup()
* directly instead.
* See i_hca_regs_map_setup() in detail.
*
* Argument
* state: pointer to Hermon state structure
* rnumber: index number to the register address space set
* addrp: platform-dependent value (same as ddi_regs_map_setup())
* offset: offset into the register address space
* len: address space length to be mapped
* accattrp: pointer to device access attribute structure
* handle: pointer to ddi_acc_handle_t used for HCA FM
*
* Return value
* ddi function status value which are:
* DDI_SUCCESS
* DDI_FAILURE
* DDI_ME_RNUMBER_RNGE
* DDI_REGS_ACC_CONFLICT
*
* Caller's context
* hermon_regs_map_setup() can be called in user or kernel context only.
*/
int
{
if (state->hs_fm_disable) {
} else {
}
}
/*
* void
* hermon_regs_map_free(hermon_state_t *state, ddi_acc_handle_t *handlep)
*
* Overview
* This is a wrapper function of i_hca_regs_map_free() for Hermon FM so
* that it calls i_hca_regs_map_free() inside after it checks the
* "fm_disable" configuration property. If the "fm_disable" is described
* in /kernel/drv/hermon.conf, the function calls ddi_regs_map_fre()
* directly instead. See i_hca_regs_map_free() in detail.
*
* Argument
* state: pointer to Hermon state structure
* handle: pointer to ddi_acc_handle_t used for HCA FM
*
* Return value
* Nothing
*
* Caller's context
* hermon_regs_map_free() can be called in user or kernel context only.
*
* Note that the handle passed to hermon_regs_map_free() is NULL-cleared
* after this function is called.
*/
void
{
if (state->hs_fm_disable) {
} else {
}
}
/*
* int
* hermon_pci_config_setup(hermon_state_t *state, ddi_acc_handle_t *handle)
*
* Overview
* This is a wrapper function of i_hca_pci_config_setup() for Hermon FM so
* that it calls i_hca_pci_config_setup() inside after it checks the
* "fm-disable" configuration property. If the "fm_disable" is described
* in /kernel/drv/hermon.conf, the function calls pci_config_setup()
* directly instead. See i_hca_pci_config_setup() in detail.
*
* Argument
* state: pointer to Hermon state structure
* handle: pointer to ddi_acc_handle_t used for HCA FM
*
* Return value
* ddi function status value which are:
* DDI_SUCCESS
* DDI_FAILURE
*
* Caller's context
* hermon_pci_config_setup() can be called in user or kernel context only.
*/
int
{
if (state->hs_fm_disable) {
} else {
/* Check Hermon FM and Solaris FMA capability flags */
}
}
/*
* void
* hermon_pci_config_teardown(hermon_state_t *state, ddi_acc_handle_t *handle)
*
* Overview
* This is a wrapper function of i_hca_pci_config_teardown() for Hermon
* FM so that it calls i_hca_pci_config_teardown() inside after it checks
* the "fm-disable" configuration property. If the "fm_disable" is
* described in /kernel/drv/hermon.conf, the function calls
* pci_config_teardown() directly instead.
* See i_hca_pci_config_teardown() in detail.
*
* Argument
* state: pointer to Hermon state structure
* handle: pointer to ddi_acc_handle_t used for HCA FM
*
* Return value
* Nothing
*
* Caller's context
* hermon_pci_config_teardown() can be called in user or kernel context
* only.
*/
void
{
if (state->hs_fm_disable) {
} else {
}
}
/*
* boolean_t
* hermon_init_failure(hermon_state_t *state)
*
* Overview
* hermon_init_failure() tells if HW errors are detected in
* the Hermon driver attach.
*
* Argument
* state: pointer to Hermon state structure
*
* Return value
* B_TRUE HW errors detected during attach
* B_FALSE No HW errors during attach
*
* Caller's context
* hermon_init_failure() can be called in user, kernel, interrupt
* context or high interrupt context.
*/
{
return (B_FALSE);
/* check if fatal errors occur during attach */
if (state->hs_fm_async_fatal)
return (B_TRUE);
/* Get the PIO error against UAR I/O space */
return (B_TRUE);
}
/* Get the PIO error againsts CMD I/O space */
return (B_TRUE);
}
return (B_FALSE);
}
/*
* void
* hermon_fm_ereport(hermon_state_t *state, int type, int detail)
*
* Overview
* hermon_fm_ereport() is a Hermon FM ereport function used
* to issue a Solaris FMA ereport. See Hermon FM comments at the
* beginning of this file in detail.
*
* Argument
* state: pointer to Hermon state structure
* type: error type
* HCA_SYS_ERR FMA reporting HW error
* HCA_IBA_ERR HCA specific HW error
* detail: HW error hint implying which ereport is issued
* HCA_ERR_TRANSIENT HW transienet error
* HCA_ERR_NON_FATAL HW persistent error
* HCA_ERR_FATAL HW fatal error
* HCA_ERR_SRV_LOST IB service lost due to HW error
* due to HW error
* HCA_ERR_IOCTL HW error detected in user conetxt
* (especially in ioctl())
*
* Return value
* Nothing
*
* Caller's context
* hermon_fm_ereport() can be called in user, kernel, interrupt context
* or high interrupt context.
*/
void
{
/*
* If hermon_fm_diable is set or there is no FM ereport service
* provided, then skip the rest.
*/
if (state->hs_fm_disable ||
return;
}
switch (type) {
case HCA_SYS_ERR:
switch (detail) {
case HCA_ERR_TRANSIENT:
case HCA_ERR_IOCTL:
break;
case HCA_ERR_NON_FATAL:
/* Nothing */
break;
case HCA_ERR_SRV_LOST:
break;
case HCA_ERR_DEGRADED:
switch (state->hs_fm_degraded_reason) {
case HCA_FW_CORRUPT:
break;
case HCA_FW_MISMATCH:
break;
case HCA_FW_MISC:
default:
break;
}
break;
case HCA_ERR_FATAL:
break;
default:
}
break;
case HCA_IBA_ERR:
switch (detail) {
case HCA_ERR_TRANSIENT:
break;
case HCA_ERR_SRV_LOST:
break;
case HCA_ERR_DEGRADED:
switch (state->hs_fm_degraded_reason) {
case HCA_FW_CORRUPT:
break;
case HCA_FW_MISMATCH:
break;
case HCA_FW_MISC:
default:
break;
}
break;
case HCA_ERR_IOCTL:
case HCA_ERR_NON_FATAL:
break;
case HCA_ERR_FATAL:
if (servicing_interrupt()) {
} else {
}
} else {
"Hermon Fatal Internal Error. "
"Hermon state=0x%p", (void *)state);
}
break;
default:
}
break;
default:
break;
}
}
/*
* uchar_t
* hermon_devacc_attr_version(hermon_state_t *)
*
* Overview
* hermon_devacc_attr_version() returns the ddi device attribute
* version.
*
* Argument
* state: pointer to Hermon state structure
*
* Return value
* dev_acc_attr_version value
* DDI_DEVICE_ATTR_V0 Hermon FM disabled
* DDI_DEVICE_ATTR_V1 Hermon FM enabled
*
* Caller's context
* hermon_devacc_attr_version() can be called in user, kernel, interrupt
* context or high interrupt context.
*/
{
if (state->hs_fm_disable) {
return (DDI_DEVICE_ATTR_V0);
} else {
return (DDI_DEVICE_ATTR_V1);
}
}
/*
* uchar_t
* hermon_devacc_attr_access(hermon_state_t *)
*
* Overview
* hermon_devacc_attr_access() returns devacc_attr_access error
* protection types.
*
* Argument
* state: pointer to Hermon state structure
*
* Return value
* dev_acc_attr_access error protection type
* DDI_DEFAULT_ACC Hermon FM disabled for PIO
* DDI_FLAGERR_ACC Hermon FM enabled for PIO
*
* Caller's context
* hermon_devacc_attr_access() can be called in user, kernel, interrupt
* context or high interrupt context.
*/
{
if (state->hs_fm_disable) {
return (DDI_DEFAULT_ACC);
} else {
return (DDI_FLAGERR_ACC);
}
}
/*
* int
* hermon_PIO_start(hermon_state_t *state, ddi_acc_handle_t handle,
* hermon_test_t *tst)
*
* Overview
* hermon_PIO_start() should be called before Hermon driver issues PIOs
* against I/O space. If Hermon FM is disabled, this function returns
* HCA_PIO_OK always. See i_hca_pio_start() in detail.
*
* Argument
* state: pointer to Hermon state structure
* handle: pointer to ddi_acc_handle_t used for HCA FM
* tst: pointer to HCA FM function test structure. If the structure
* is not used, the NULL value must be passed instead.
*
* Return value
* error status showing whether or not this error can retry
* HCA_PIO_OK No HW errors
* HCA_PIO_TRANSIENT This error could be transient
* HCA_PIO_PERSISTENT This error is persistent
*
* Caller's context
* hermon_PIO_start() can be called in user, kernel or interrupt context.
*/
int
{
if (state->hs_fm_disable) {
return (HCA_PIO_OK);
} else {
}
}
/*
* int
* hermon_PIO_end(hermon_state_t *state, ddi_acc_handle_t handle, int *cnt,
* hermon_test_t *tst)
*
* Overview
* hermon_PIO_end() should be called after Hermon driver issues PIOs
* against I/O space. If Hermon FM is disabled, this function returns
* HCA_PIO_OK always. See i_hca_pio_end() in detail.
*
* Argument
* state: pointer to Hermon state structure
* handle: pointer to ddi_acc_handle_t used for HCA FM
* cnt: pointer to the counter variable which holds the nubmer of retry
* (HCA_PIO_RETRY_CNT) when a HW error is detected.
* tst: pointer to HCA FM function test structure. If the structure
* is not used, the NULL value must be passed instead.
*
* Return value
* error status showing whether or not this error can retry
* HCA_PIO_OK No HW errors
* HCA_PIO_TRANSIENT This error could be transient
* HCA_PIO_PERSISTENT This error is persistent
*
* Caller's context
* hermon_PIO_end() can be called in user, kernel or interrupt context.
*/
int
{
if (state->hs_fm_disable) {
return (HCA_PIO_OK);
} else {
}
}
/*
* ddi_acc_handle_t
* hermon_get_cmdhdl(hermon_state_t *state)
*
* Overview
* hermon_get_cmdhdl() returns either the fma-protected access handle or
* the regular ddi-access handle depending on the Hermon FM state for
* Hermon command I/O space.
*
* Argument
* state: pointer to Hermon state structure
*
* Return value
* the access handle for pio requests
*
* Caller's context
* hermon_get_cmdhdl() can be called in user, kernel, interrupt context
* or high interrupt context.
*/
{
}
/*
* ddi_acc_handle_t
* hermon_get_uarhdl(hermon_state_t *state)
*
* Overview
* hermon_get_uarhdl() returns either the fma-protected access handle or
* the regular ddi-access handle depending on the Hermon FM state for
* Hermon UAR I/O space.
*
* Argument
* state: pointer to Hermon state structure
*
* Return value
* the access handle for pio requests
*
* Caller's context
* hermon_get_uarhdl() can be called in user, kernel, interrupt context
* or high interrupt context.
*/
{
}
/*
* ddi_acc_handle_t
* hermon_rsrc_alloc_uarhdl(hermon_state_t *state)
*
* Overview
* hermon_rsrc_alloc_uarhdl() returns either the fma-protected access
* handle or the regular ddi-access handle depending on the Hermon FM
* state for Hermon UAR I/O space as well as hermon_get_uarhdl(), but
* this function is dedicated to the UAR resource allocator.
*
* Argument
* state: pointer to Hermon state structure
*
* Return value
* the access handle for pio requests
*
* Caller's context
* hermon_rsrc_alloc_uarhdl() can be called in user, kernel, interrupt
* or high interrupt context.
*/
{
}
/*
* ddi_acc_handle_t
* hermon_get_pcihdl(hermon_state_t *state)
*
* Overview
* hermon_get_pcihdl() returns either the fma-protected access
* handle or the regular ddi-access handle to access the PCI config
* space. Whether or not which handle is returned at the moment depends
* on the Hermon FM state.
*
* Argument
* state: pointer to Hermon state structure
*
* Return value
* the access handle to PCI config space
*
* Caller's context
* hermon_get_pcihdl() can be called in user, kernel, interrupt
* or high interrupt context.
*/
{
}
/*
* ddi_acc_handle_t
* hermon_get_msix_tblhdl(hermon_state_t *state)
*
* Overview
* hermon_get_msix_tblhdl() returns either the fma-protected access
* handle or the regular ddi-access handle to access the MSI-X tables.
* Whether or not which handle is returned at the moment depends on
* the Hermon FM state.
*
* Argument
* state: pointer to Hermon state structure
*
* Return value
* the access handle to MSI-X tables
*
* Caller's context
* hermon_get_msix_tblhdl() can be called in user, kernel, interrupt
* context or high interrupt context.
*/
{
}
/*
* ddi_acc_handle_t
* hermon_get_msix_pbahdl(hermon_state_t *state)
*
* Overview
* hermon_get_msix_pbahdl() returns either the fma-protected access
* handle or the regular ddi-access handle to access the MSI-X PBA.
* Whether or not which handle is returned at the moment depends on
* the Hermon FM state.
*
* Argument
* state: pointer to Hermon state structure
*
* Return value
* the access handle to MSI-X PBA
*
* Caller's context
* hermon_get_msix_pbahdl() can be called in user, kernel, interrupt
* context or high interrupt context.
*/
{
}
/*
* void
* hermon_inter_err_chk(void *arg)
*
* Overview
* hermon_inter_err_chk() periodically checks the internal error buffer
* to pick up a Hermon asynchronous internal error.
*
* Note that this internal error can be notified if the interrupt is
* registered, but even so there are some cases that an interrupt against
* it cannot be raised so that Hermon RPM recommeds to poll this internal
* error buffer periodically instead. This function is invoked at
* 10ms interval in kernel context though the function itself can be
* called in interrupt context.
*
* Argument
* arg: pointer to Hermon state structure
*
* Return value
* Nothing
*
* Caller's context
* hermon_inter_err_chk() can be called in user, kernel, interrupt
* context or high interrupt context.
*
*/
void
{
/* initialize the FMA retry loop */
#ifdef FMA_TEST
if (hermon_test_num != 0) {
return;
}
#endif
if (state->hs_fm_poll_suspend) {
return;
}
/* Get the access handle for Hermon CMD I/O space */
/* the FMA retry loop starts. */
fm_test);
/* the FMA retry loop ends. */
fm_test);
if (word != 0) {
/* if fm_disable is on, Hermon FM functions don't work */
if (state->hs_fm_disable) {
"Hermon Fatal Internal Error. "
"Hermon state=0x%p", (void *)state);
} else {
}
}
/* issue the ereport pended in the interrupt context */
if (state->hs_fm_async_errcnt > 0) {
}
return;
}
/*
* boolean_t
* hermon_cmd_retry_ok(hermon_cmd_post_t *cmd, int status)
*
* Overview
* In the case that a HW error is detected, if it can be isolated
* enough, Hermon FM retries the operation which caused the error.
* However, this retry can induce another error; since the retry is
* achieved as a block basis, not a statement basis, once the state
* was set inside the Hermon HW already in the previous operation, the
* retry can cause for example, a CMD_BAD_SYS_STATE error, as a result.
* In this case, CMD_BAD_SYS_STATE should be taken as a side effect
* but a harmless result. hermon_cmd_retry_ok() checks this kind of
* situation then returns if the state Hermon CMD returns is OK or not.
*
* Argument
* cmd: pointer to hermon_cmd_post_t structure
* status: Hermon CMD status
*
* Return value
* B_TRUE this state is no problem
* B_FALSE this state should be taken as an error
*
* Caller's context
* hermon_cmd_retry_ok() can be called in user, kernel, interrupt
* context or high interrupt context.
*
* Note that status except for HERMON_CMD_SUCCESS shouldn't be accepted
* in the debug module to catch a hidden software bug, so that ASSERT()
* is enabled in the case.
*/
{
if (status == HERMON_CMD_SUCCESS)
return (B_TRUE);
/*
* The wrong status such as HERMON_CMD_BAD_SYS_STATE or
* HERMON_CMD_BAD_RES_STATE can return as a side effect
* because of the Hermon FM operation retry when a PIO
* error is detected during the I/O transaction. In the
* case, the driver may set the same value in Hermon
* though it was set already, then Hermon returns HERMON_
* CMD_BAD_{RES,SYS}_STATE as a result, which should be
* taken as OK.
*/
case INIT_HCA:
/*
* HERMON_CMD_BAD_SYS_STATE can be gotten in case of
* ICM not mapped or HCA already initialized.
*/
if (status == HERMON_CMD_BAD_SYS_STATE)
return (B_TRUE);
return (B_FALSE);
case CLOSE_HCA:
/*
* HERMON_CMD_BAD_SYS_STATE can be gotten in case of Firmware
* area is not mapped or HCA already closed.
*/
if (status == HERMON_CMD_BAD_SYS_STATE)
return (B_TRUE);
return (B_FALSE);
case CLOSE_PORT:
/*
* HERMON_CMD_BAD_SYS_STATE can be gotten in case of HCA not
* initialized or in case that IB ports are already down.
*/
if (status == HERMON_CMD_BAD_SYS_STATE)
return (B_TRUE);
return (B_FALSE);
case SW2HW_MPT:
/*
* HERMON_CMD_BAD_RES_STATE can be gotten in case of MPT
* entry already in hardware ownership.
*/
if (status == HERMON_CMD_BAD_RES_STATE)
return (B_TRUE);
return (B_FALSE);
case HW2SW_MPT:
/*
* HERMON_CMD_BAD_RES_STATE can be gotten in case of MPT
* entry already in software ownership.
*/
if (status == HERMON_CMD_BAD_RES_STATE)
return (B_TRUE);
return (B_FALSE);
case SW2HW_EQ:
/*
* HERMON_CMD_BAD_RES_STATE can be gotten in case of EQ
* entry already in hardware ownership.
*/
if (status == HERMON_CMD_BAD_RES_STATE)
return (B_TRUE);
return (B_FALSE);
case HW2SW_EQ:
/*
* HERMON_CMD_BAD_RES_STATE can be gotten in case of EQ
* entry already in software ownership.
*/
if (status == HERMON_CMD_BAD_RES_STATE)
return (B_TRUE);
return (B_FALSE);
case SW2HW_CQ:
/*
* HERMON_CMD_BAD_RES_STATE can be gotten in case of CQ
* entry already in hardware ownership.
*/
if (status == HERMON_CMD_BAD_RES_STATE)
return (B_TRUE);
return (B_FALSE);
case HW2SW_CQ:
/*
* HERMON_CMD_BAD_RES_STATE can be gotten in case of CQ
* entry already in software ownership.
*/
if (status == HERMON_CMD_BAD_RES_STATE)
return (B_TRUE);
return (B_FALSE);
case SW2HW_SRQ:
/*
* HERMON_CMD_BAD_RES_STATE can be gotten in case of SRQ
* entry already in hardware ownership.
*/
if (status == HERMON_CMD_BAD_RES_STATE)
return (B_TRUE);
return (B_FALSE);
case HW2SW_SRQ:
/*
* HERMON_CMD_BAD_RES_STATE can be gotten in case of SRQ
* entry already in software ownership.
*/
if (status == HERMON_CMD_BAD_RES_STATE)
return (B_TRUE);
return (B_FALSE);
default:
break;
}
/* other cases */
return (B_FALSE);
}
#ifdef FMA_TEST
/*
* Hermon FMA test variables
*/
static struct i_hca_fm_test *i_hca_test_register(char *, int, int,
void (*)(struct i_hca_fm_test *, ddi_fm_error_t *),
void *, mod_hash_t *, mod_hash_t *, int);
static void i_hca_test_free_item(mod_hash_val_t);
static void i_hca_test_set_item(int, struct i_hca_fm_test *);
/*
* Hermon FMA Function Test Interface
*/
/* Attach Errors */
/* Initial Value */
/* PIO Transient Errors */
/* PIO Persistent Errors */
};
/*
* void
* hermon_trigger_pio_error(hermon_test_t *tst, ddi_fm_error_t *derr)
*
* Overview
* hermon_trigger_pio_error() is a PIO error injection function
* to cause a pseduo PIO error.
*
* Argument
* tst: pointer to HCA FM function test structure. If the structure
* is not used, the NULL value must be passed instead.
* derr: pointer to ddi_fm_error_t structure
*
* Return value
* Nothing
*
* Caller's context
* hermon_trigger_pio_error() can be called in user, kernel, interrupt
* context or high interrupt context.
*/
static void
{
return;
}
return;
return;
}
}
}
/*
* struct hermon_fm_test *
* hermon_test_register(hermon_state_t *state, char *filename, int linenum,
* int type)
*
* Overview
* hermon_test_register() registers a Hermon FM test item for the
* function test.
*
* Argument
* state: pointer to Hermon state structure
* filename: source file name where the function call is implemented
* This value is usually a __FILE__ pre-defined macro.
* linenum: line number where the function call is described in the
* file specified above.
* This value is usually a __LINE__ pre-defined macro.
* type: HW error type
* HCA_TEST_PIO pio error
* HCA_TEST_IBA ib specific error
*
* Return value
* pointer to Hermon FM function test structure registered.
*
* Caller's context
* hermon_test_register() can be called in user, kernel or interrupt
* context.
*
* Note that no test item is registered if Hermon FM is disabled.
*/
int type)
{
(void (*)(struct i_hca_fm_test *, ddi_fm_error_t *))
if (state->hs_fm_disable)
return (NULL);
}
#endif /* FMA_TEST */
/*
* HCA FM Common Interface
*
* These functions should be used for any HCA drivers, but probably
* Customized functins should have the driver name prefix such as
* hermon_xxxx() and be defined separately but whose functions should
* call the common interface inside.
*/
/*
* void
* i_hca_fm_init(struct i_hca_fm *hca_fm)
*
* Overview
* i_hca_fm_init() is an initialization function which sets up the acc
* handle kmem_cache if this function is called the first time.
*
* Argument
* hca_fm: pointer to HCA FM structure
*
* Return value
* Nothing
*
* Caller's context
* i_hca_fm_init() can be called in user or kernel context, but cannot
* be called in interrupt context.
*/
static void
{
sizeof (struct i_hca_acc_handle), 0, NULL,
}
}
/*
* void
* i_hca_fm_fini(struct i_hca_fm *hca_fm)
*
* Overview
* i_hca_fm_fini() is a finalization function which frees up the acc
* handle kmem_cache if this function is called the last time.
*
* Argument
* hca_fm: pointer to HCA FM structure
*
* Return value
* Nothing
*
* Caller's context
* i_hca_fm_fini() can be called in user or kernel context, but cannot
* be called in interrupt context.
*/
static void
{
if (hca_fm->fm_acc_cache) {
}
}
}
/*
* void
* i_hca_fm_ereport(dev_info_t *dip, int type, char *detail)
*
* Overview
* i_hca_fm_ereport() is a wrapper function of ddi_fm_ereport_post() but
* generates an ena before it calls ddi_fm_ereport_post() for HCA
* specific HW errors.
*
* Argument
* dip: pointer to this device dev_info structure
* type: error type
* HCA_SYS_ERR FMA reporting HW error
* HCA_IBA_ERR HCA specific HW error
* detail: definition of leaf driver detected ereports which is one of:
* DDI_FM_DEVICE_INVAL_STATE
* DDI_FM_DEVICE_NO_RESPONSE
* DDI_FM_DEVICE_STALL
* DDI_FM_DEVICE_BADINT_LIMIT
* DDI_FM_DEVICE_INTERN_CORR
* DDI_FM_DEVICE_INTERN_UNCORR
*
* Return value
* Nothing
*
* Caller's context
* i_hca_fm_ereport() can be called in user, kernel or interrupt context.
*/
static void
{
if (type == HCA_IBA_ERR) {
/* this is an error of its own */
}
}
/*
* struct i_hca_acc_handle *
* i_hca_get_acc_handle(struct i_hca_fm *hca_fm, ddi_acc_handle_t handle)
*
* Overview
* i_hca_get_acc_handle() returns ddi_acc_handle_t used for HCA FM.
*
* Argument
* hca_fm: pointer to HCA FM structure
* handle: ddi_acc_handle_t
*
* Return value
* handle: pointer to ddi_acc_handle_t used for HCA FM
*
* Caller's context
* i_hca_get_acc_handle() can be called in user, kernel or interrupt
* context.
*/
static struct i_hca_acc_handle *
{
/* Retrieve the HCA FM access handle */
return (hdlp);
}
}
return (hdlp);
}
/*
* int
* i_hca_regs_map_setup(struct i_hca_fm *hca_fm, dev_info_t *dip,
* uint_t rnumber, caddr_t *addrp, offset_t offset, offset_t len,
* ddi_device_acc_attr_t *accattrp, ddi_acc_handle_t *handle)
*
* Overview
* i_hca_regs_map_setup() is a wrapper function of ddi_regs_map_setup(),
* but allocates the HCA FM acc handle structure and initializes it.
*
* Argument
* hca_fm: pointer to HCA FM structure
* dip: pointer to this device dev_info structure
* rnumber: index number to the register address space set
* addrp: platform-dependent value (same as ddi_regs_map_setup())
* offset: offset into the register address space
* len: address space length to be mapped
* accattrp: pointer to device access attribute structure
* handle: pointer to ddi_acc_handle_t used for HCA FM
*
* Return value
* ddi function status value which are:
* DDI_SUCCESS
* DDI_FAILURE
* DDI_ME_RNUMBER_RNGE
* DDI_REGS_ACC_CONFLICT
*
* Caller's context
* i_hca_regs_map_setup() can be called in user or kernel context only.
*/
static int
{
int status;
/* Allocate an access handle */
return (status);
}
/* Allocate HCA FM acc handle structure */
/* Initialize fields */
handlep->thread_cnt = 0;
/* Register this handle */
}
} else {
}
return (status);
}
/*
* void
* i_hca_regs_map_free(struct i_hca_fm *hca_fm, ddi_acc_handle_t *handlep)
*
* Overview
* i_hca_regs_map_setup() is a wrapper function of ddi_regs_map_free(),
* and frees the HCA FM acc handle structure allocated by
* i_hca_regs_map_setup().
*
* Argument
* hca_fm: pointer to HCA FM structure
* handle: pointer to ddi_acc_handle_t used for HCA FM
*
* Return value
* Nothing
*
* Caller's context
* i_hca_regs_map_free() can be called in user or kernel context only.
*
* Note that the handle passed to i_hca_regs_map_free() is NULL-cleared
* after this function is called.
*/
static void
{
/* De-register this handle */
break;
}
} else {
}
/* Release this handle */
}
/*
* int
* i_hca_pci_config_setup(struct i_hca_fm *hca_fm, dev_info_t *dip,
* ddi_acc_handle_t *handle, boolean_t fm_protect)
*
* Overview
* i_hca_pci_config_setup() is a wrapper function of pci_config_setup(),
* but allocates the HCA FM acc handle structure and initializes it.
*
* Argument
* hca_fm: pointer to HCA FM structure
* dip: pointer to this device dev_info structure
* handle: pointer to ddi_acc_handle_t used for HCA PCI config space
* with FMA
* fm_protect: flag to tell if an fma-protected access handle should
* be used
*
* Return value
* ddi function status value which are:
* DDI_SUCCESS
* DDI_FAILURE
*
* Caller's context
* i_hca_pci_config_setup() can be called in user or kernel context only.
*/
static int
{
int status;
/* Allocate an access handle */
return (status);
}
/* Allocate HCA FM acc handle structure */
/* Initialize fields */
handlep->thread_cnt = 0;
/* Register this handle */
}
} else {
}
return (status);
}
/*
* void
* i_hca_pci_config_teardown(struct i_hca_fm *hca_fm,
* ddi_acc_handle_t *handlep)
*
* Overview
* i_hca_pci_config_teardown() is a wrapper function of
* pci_config_teardown(), and frees the HCA FM acc handle structure
* allocated by i_hca_pci_config_setup().
*
* Argument
* hca_fm: pointer to HCA FM structure
* handle: pointer to ddi_acc_handle_t used for HCA FM
*
* Return value
* Nothing
*
* Caller's context
* i_hca_pci_config_teardown() can be called in user or kernel context
* only.
*
* Note that the handle passed to i_hca_pci_config_teardown() is NULL-cleared
* after this function is called.
*/
static void
{
/* De-register this handle */
break;
}
} else {
}
/* Release this handle */
}
/*
* int
* i_hca_pio_start(dev_info_t *dip, struct i_acc_handle *handle,
* struct i_hca_fm_test *tst)
*
* Overview
* i_hca_pio_start() is one of a pair of HCA FM fuctions for PIO, which
* should be called before HCA drivers issue PIOs against I/O space.
* See HCA FM comments at the beginning of this file in detail.
*
* Argument
* dip: pointer to this device dev_info structure
* handle: pointer to ddi_acc_handle_t used for HCA FM
* tst: pointer to HCA FM function test structure. If the structure
* is not used, the NULL value must be passed instead.
*
* Return value
* error status showing whether or not this error can retry
* HCA_PIO_OK No HW errors
* HCA_PIO_TRANSIENT This error could be transient
* HCA_PIO_PERSISTENT This error is persistent
*
* Caller's context
* i_hca_pio_start() can be called in user, kernel or interrupt context.
*/
/* ARGSUSED */
static int
struct i_hca_fm_test *tst)
{
/* Count up the number of threads issuing this PIO */
hdlp->thread_cnt++;
/* Get the PIO error via FMA */
#ifdef FMA_TEST
/* Trigger PIO errors */
}
#endif /* FMA_TEST */
switch (derr.fme_status) {
case DDI_FM_OK:
/* Not have to clear the fma error log */
return (HCA_PIO_OK);
case DDI_FM_NONFATAL:
/* Now clear this error */
/* Log this error and notify it as a persistent error */
return (HCA_PIO_PERSISTENT);
/* In theory, this shouldn't happen */
case DDI_FM_FATAL:
case DDI_FM_UNKNOWN:
default:
/* Return this as a persistent error */
return (HCA_PIO_PERSISTENT);
}
}
/*
* int
* i_hca_pio_end(dev_info_t *dip, ddi_acc_handle_t handle, int *cnt,
* struct i_hca_fm_test *tst)
*
* Overview
* i_hca_pio_end() is the other of a pair of HCA FM fuctions for PIO,
* which should be called after HCA drivers issue PIOs against I/O space.
* See HCA FM comments at the beginning of this file in detail.
*
* Argument
* dip: pointer to this device dev_info structure
* handle: pointer to ddi_acc_handle_t used for HCA FM
* cnt: pointer to the counter variable which holds the nubmer of retry
* when a HW error is detected.
* tst: pointer to HCA FM function test structure. If the structure
* is not used, the NULL value must be passed instead.
*
* Return value
* error status showing whether or not this error can retry
* HCA_PIO_OK No HW errors
* HCA_PIO_TRANSIENT This error could be transient
* HCA_PIO_PERSISTENT This error is persistent
*
* Caller's context
* i_hca_pio_end() can be called in user, kernel or interrupt context.
*/
/* ARGSUSED */
static int
struct i_hca_fm_test *tst)
{
/* Get the PIO error via FMA */
#ifdef FMA_TEST
/* Trigger PIO errors */
}
#endif /* FMA_TEST */
/* Evaluate the PIO error */
switch (derr.fme_status) {
case DDI_FM_OK:
/* Count down the number of threads issuing this PIO */
hdlp->thread_cnt--;
/* Not have to clear the fma error log */
return (HCA_PIO_OK);
case DDI_FM_NONFATAL:
/* Now clear this error */
/*
* Check if this error comes from another thread running
* with the same handle almost at the same time.
*/
/* Count down the number of threads */
hdlp->thread_cnt--;
/* Return this as a persistent error */
return (HCA_PIO_PERSISTENT);
}
/* Now determine if this error is persistent or not */
if (--(*cnt) >= 0) {
return (HCA_PIO_TRANSIENT);
} else {
/* Count down the number of threads */
hdlp->thread_cnt--;
return (HCA_PIO_PERSISTENT);
}
/* In theory, this shouldn't happen */
case DDI_FM_FATAL:
case DDI_FM_UNKNOWN:
default:
/* Return this as a persistent error */
return (HCA_PIO_PERSISTENT);
}
}
/*
* HCA FM Test Interface
*
* These functions should be used for any HCA drivers, but probably
* Customized functins should have the driver name prefix such as
* hermon_xxxx() and be defined separately but whose function should
* call the common interface inside.
*/
#ifdef FMA_TEST
/*
* void
* i_hca_test_init(mod_hash_t **strHashp, mod_hash_t **idHashp)
*
* Overview
* i_hca_test_init() creates two hash tables, one of which is for string,
* and the other of which is for ID, then saves pointers to arguments
* passed. This function uses the mod_hash utilities to manage the
*
* Argument
* strHashp: pointer to String hash table pointer
* idHashp: pointer to ID hash table pointer
*
* Return value
* Nothing
*
* Caller's context
* i_hca_test_init() can be called in user or kernel context only.
*/
static void
{
}
/*
* void
* i_hca_test_fini(mod_hash_t **strHashp, mod_hash_t **idHashp)
*
* Overview
* i_hca_test_fini() releases two hash tables used for HCA FM test.
*
* Argument
* strHashp: pointer to String hash table pointer
* idHashp: pointer to ID hash table pointer
*
* Return value
* Nothing
*
* Caller's context
* i_hca_test_fini() can be called in user, kernel or interrupt context.
*
*/
static void
{
}
/*
* struct i_hca_fm_test *
* i_hca_test_register(char *filename, int linenum, int type,
* void (*pio_injection)(struct i_hca_fm_test *, ddi_fm_error_t *),
* void *private, mod_hash_t *strHash, mod_hash_t *idHash, int preTestNum)
*
* Overview
* i_hca_test_register() registers an HCA FM test item against HCA FM
* function callings specified with the file name and the line number
* (passed as the arguments).
*
* Argument
* filename: source file name where the function call is implemented
* This value is usually a __FILE__ pre-defined macro.
* linenum: line number where the function call is described in the
* file specified above.
* This value is usually a __LINE__ pre-defined macro.
* type: HW error type
* HCA_TEST_PIO pio error
* HCA_TEST_IBA ib specific error
* pio_injection: pio error injection callback function invoked when the
* function specified above (with the file name and the
* line number) is executed. If the function is not a PIO,
* request, this parameter should be NULL.
* private: the argument passed to either of injection functions when
* they're invoked.
* strHashp: pointer to String hash table
* idHashp: pointer to ID hash table
* preTestNum: the index of the pre-defined testset for this test item.
*
* Return value
* pointer to HCA FM function test structure registered.
*
* Caller's context
* i_hca_test_register() can be called in user, kernel or interrupt
* context.
*
*/
static struct i_hca_fm_test *
{
int status;
(mod_hash_val_t *)&t_item);
switch (status) {
case MH_ERR_NOTFOUND:
t_item = (struct i_hca_fm_test *)
/* Set the error number */
/* Set type and other static information */
/* Set the pre-defined hermon test item */
break;
case MH_ERR_NOMEM:
break;
case MH_ERR_DUPLICATE:
break;
default:
/* OK, this is already registered. */
break;
}
return (t_item);
}
/*
* void
* i_hca_test_set_item(int num, struct i_hca_fm_test *t_item)
*
* Overview
* i_hca_test_set_item() is a private function used in
* i_hca_test_register() above. This function sets the testset specified
* (with the index number) to HCA FM function test structure.
*
* Argument
* num: index to test set (testset structure array)
* t_item: pointer to HCA fM function test structure
*
* Return value
* Nothing
*
* Caller's context
* i_hca_test_set_item() can be called in user, kernel, interrupt
* context or hight interrupt context.
*
*/
static void
{
return;
}
/* Set the testsuite */
}
/*
* void
* i_hca_test_free_item(mod_hash_val_t val)
*
* Overview
* i_hca_test_free_item() is a private function used to free HCA FM
* function test structure when i_hca_test_fini() is called. This function
* is registered as a destructor when the hash table is created in
* i_hca_test_init().
*
* Argument
* val: pointer to the value stored in hash table (pointer to HCA FM
* function test structure)
*
* Return value
* Nothing
*
* Caller's context
* i_hca_test_free_item() can be called in user, kernel or interrupt
* context.
*
*/
static void
{
}
#endif /* FMA_TEST */