/*
* 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 Miscellaneous routines - Address Handle, Multicast, Protection
* Domain, and port-related operations
*
* Implements all the routines necessary for allocating, freeing, querying
* and modifying Address Handles and Protection Domains. Also implements
* Multicast Groups. Lastly, it implements the routines necessary for
* port-related query and modify operations.
*/
#include <sys/sysmacros.h>
extern int hermon_rdma_debug;
int hermon_fmr_verbose = 0;
static hermon_udbr_page_t *
{
int status;
KM_SLEEP);
#ifdef __sparc
#endif
if (status != DDI_SUCCESS) {
"ddi_dma_buf_bind_handle failed: %d", status);
return (NULL);
}
if (status != DDI_SUCCESS) {
"ddi_dma_buf_bind_handle failed: %d", status);
return (NULL);
}
/* create db entry for mmap */
return (pagep);
}
/*ARGSUSED*/
static int
{
int dbr_index;
break;
}
break;
else
return (DDI_FAILURE);
}
}
/* Since nfree > 0, we're assured the loops below will succeed */
/* First, find a 64-bit (not ~0) that has a free dbr */
break;
/* Second, find a byte (not 0xff) that has a free dbr */
break;
/* Third, find a bit that is free (0) */
break;
/* Mark it as allocated */
sizeof (uint64_t);
return (DDI_SUCCESS);
}
static void
{
break;
"found for index %x", index);
return;
}
break;
" found for index %x, kvaddr %p, DBR index %x",
return;
}
return;
}
}
/*
* hermon_dbr_page_alloc()
* first page allocation - called from attach or open
* in this case, we want exactly one page per call, and aligned on a
* page - and may need to be mapped to the user for access
*/
int
{
int status;
int i;
/*
* Initialize many of the default DMA attributes. Then set additional
* alignment restrictions if necessary for the dbr memory, meaning
* page aligned. Also use the configured value for IOMMU bypass
*/
#ifdef __sparc
#endif
if (status != DDI_SUCCESS) {
return (DDI_FAILURE);
}
if (status != DDI_SUCCESS) {
return (DDI_FAILURE);
}
/* this memory won't be IB registered, so do the bind here */
if (status != DDI_SUCCESS) {
status);
return (DDI_FAILURE);
}
/* init the info structure with returned info */
/* extract the phys addr from the cookie */
info->dbr_firstfree = 0;
/* link all DBrs onto the free list */
for (i = 0; i < HERMON_NUM_DBR_PER_PAGE; i++) {
}
return (DDI_SUCCESS);
}
/*
* hermon_dbr_alloc()
* will check for available dbrs in current
* page - if needed it will allocate another and link them
*/
int
{
int status;
mapoffset));
break; /* found a page w/ one available */
if (status != DDI_SUCCESS) {
/* do error handling */
return (DDI_FAILURE);
}
/* got a new page, so link it in. */
}
*record = 0;
return (DDI_SUCCESS);
}
/*
* hermon_dbr_free()
* will update the counter in the header, and invalidate
* the dbr, but will NEVER free pages of dbrs - small
* price to pay, but userland access never will anyway
*/
void
{
return;
}
break;
}
/*
* hermon_dbr_kern_free()
* Context: Can be called only from detach context.
*
* Free all kernel dbr pages. This includes the freeing of all the dma
* resources acquired during the allocation of the pages.
*
* Also, free all the user dbr pages.
*/
void
{
}
/* probably need to remove "db" */
if (status == DDI_SUCCESS)
}
}
}
/*
* hermon_ah_alloc()
* Context: Can be called only from user or kernel context.
*/
int
{
int status;
/*
* Someday maybe the "ibt_adds_vect_t *attr_p" will be NULL to
* indicate that we wish to allocate an "invalid" (i.e. empty)
* address handle XXX
*/
/* Validate that specified port number is legal */
return (IBT_HCA_PORT_INVALID);
}
/*
* Allocate the software structure for tracking the address handle
* (i.e. the Hermon Address Handle struct).
*/
if (status != DDI_SUCCESS) {
return (IBT_INSUFF_RESOURCE);
}
/* Increment the reference count on the protection domain (PD) */
KM_SLEEP);
/*
* Fill in the UDAV data. We first zero out the UDAV, then populate
* it by then calling hermon_set_addr_path() to fill in the common
* portions that can be pulled from the "ibt_adds_vect_t" passed in
*/
if (status != DDI_SUCCESS) {
return (status);
}
/*
* Fill in the rest of the Hermon Address Handle struct.
*
* NOTE: We are saving away a copy of the "av_dgid.gid_guid" field
* here because we may need to return it later to the IBTF (as a
* result of a subsequent query operation). Unlike the other UDAV
* parameters, the value of "av_dgid.gid_guid" is not always preserved.
* The reason for this is described in hermon_set_addr_path().
*/
return (DDI_SUCCESS);
}
/*
* hermon_ah_free()
* Context: Can be called only from user or kernel context.
*/
/* ARGSUSED */
int
{
/*
* Pull all the necessary information from the Hermon Address Handle
* struct. This is necessary here because the resource for the
* AH is going to be freed up as part of this operation.
*/
/* Free the UDAV memory */
/* Decrement the reference count on the protection domain (PD) */
/* Free the Hermon Address Handle structure */
/* Set the ahhdl pointer to NULL and return success */
return (DDI_SUCCESS);
}
/*
* hermon_ah_query()
* Context: Can be called from interrupt or base context.
*/
/* ARGSUSED */
int
{
/*
* Pull the PD and UDAV from the Hermon Address Handle structure
*/
/*
* Fill in "ibt_adds_vect_t". We call hermon_get_addr_path() to fill
* the common portions that can be pulled from the UDAV we pass in.
*
* NOTE: We will also fill the "av_dgid.gid_guid" field from the
* "ah_save_guid" field we have previously saved away. The reason
* for this is described in hermon_ah_alloc() and hermon_ah_modify().
*/
return (DDI_SUCCESS);
}
/*
* hermon_ah_modify()
* Context: Can be called from interrupt or base context.
*/
/* ARGSUSED */
int
{
/* Validate that specified port number is legal */
return (IBT_HCA_PORT_INVALID);
}
/* Save a copy of the current UDAV data in old_udav. */
/*
* Fill in the new UDAV with the caller's data, passed in via the
* "ibt_adds_vect_t" structure.
*
* NOTE: We also need to save away a copy of the "av_dgid.gid_guid"
* field here (just as we did during hermon_ah_alloc()) because we
* may need to return it later to the IBTF (as a result of a
* subsequent query operation). As explained in hermon_ah_alloc(),
* unlike the other UDAV parameters, the value of "av_dgid.gid_guid"
* is not always preserved. The reason for this is described in
* hermon_set_addr_path().
*/
if (status != DDI_SUCCESS) {
return (status);
}
/*
* Copy changes into the new UDAV.
* Note: We copy in 64-bit chunks. For the first two of these
* chunks it is necessary to read the current contents of the
* UDAV, mask off the modifiable portions (maintaining any
* of the "reserved" portions), and then mask on the new data.
*/
for (i = 0; i < size; i++) {
/*
* Apply mask to change only the relevant values.
*/
if (i == 0) {
} else if (i == 1) {
} else {
data_old = 0;
}
/* Store the updated values to the UDAV */
}
/*
* Put the valid PD number back into the UDAV entry, as it
* might have been clobbered above.
*/
return (DDI_SUCCESS);
}
/*
* hermon_mcg_attach()
* Context: Can be called only from user or kernel context.
*/
int
{
int status;
/*
* It is only allowed to attach MCG to UD queue pairs. Verify
* that the intended QP is of the appropriate transport type
*/
return (IBT_QP_SRV_TYPE_INVALID);
}
/*
* Check for invalid Multicast DLID. Specifically, all Multicast
* LIDs should be within a well defined range. If the specified LID
* is outside of that range, then return an error.
*/
if (hermon_mlid_is_valid(lid) == 0) {
return (IBT_MC_MLID_INVALID);
}
/*
* Check for invalid Multicast GID. All Multicast GIDs should have
* a well-defined pattern of bits and flags that are allowable. If
* the specified GID does not meet the criteria, then return an error.
*/
if (hermon_mgid_is_valid(gid) == 0) {
return (IBT_MC_MGID_INVALID);
}
/*
* Compute the MGID hash value. Since the MCG table is arranged as
* a number of separate hash chains, this operation converts the
* specified MGID into the starting index of an entry in the hash
* table (i.e. the index for the start of the appropriate hash chain).
* Subsequent operations below will walk the chain searching for the
* right place to add this new QP.
*/
if (status != HERMON_CMD_SUCCESS) {
status);
if (status == HERMON_CMD_INVALID_STATUS) {
}
return (ibc_get_ci_failure(0));
}
/*
* Grab the multicast group mutex. Then grab the pre-allocated
* Zero out the temporary MCG entry before we begin.
*/
/*
* Walk through the array of MCG entries starting at "mgid_hash".
* Try to find the appropriate place for this new QP to be added.
* This could happen when the first entry of the chain has MGID == 0
* (which means that the hash chain is empty), or because we find
* an entry with the same MGID (in which case we'll add the QP to
* that MCG), or because we come to the end of the chain (in which
* case this is the first QP being added to the multicast group that
* corresponds to the MGID. The hermon_mcg_walk_mgid_hash() routine
* walks the list and returns an index into the MCG table. The entry
* at this index is then checked to determine which case we have
* fallen into (see below). Note: We are using the "shadow" MCG
* list (of hermon_mcg_t structs) for this lookup because the real
* MCG entries are in hardware (and the lookup process would be much
* more time consuming).
*/
/*
* If MGID == 0, then the hash chain is empty. Just fill in the
* current entry. Note: No need to allocate an MCG table entry
* as all the hash chain "heads" are already preallocated.
*/
/* Fill in the current entry in the "shadow" MCG list */
/*
* Try to add the new QP number to the list. This (and the
* above) routine fills in a temporary MCG. The "mcg_entry"
* and "mcg_entry_qplist" pointers simply point to different
* offsets within the same temporary copy of the MCG (for
* convenience). Note: If this fails, we need to invalidate
* the entries we've already put into the "shadow" list entry
* above.
*/
&qp_found);
if (status != DDI_SUCCESS) {
return (status);
}
if (!qp_found)
/* set the member count */
/*
* Once the temporary MCG has been filled in, write the entry
* into the appropriate location in the Hermon MCG entry table.
* If it's successful, then drop the lock and return success.
* Note: In general, this operation shouldn't fail. If it
* does, then it is an indication that something (probably in
* HW, but maybe in SW) has gone seriously wrong. We still
* want to zero out the entries that we've filled in above
* (in the hermon_mcg_setup_new_hdr() routine).
*/
if (status != HERMON_CMD_SUCCESS) {
"%08x\n", status);
if (status == HERMON_CMD_INVALID_STATUS) {
}
return (ibc_get_ci_failure(0));
}
/*
* Now that we know all the Hermon firmware accesses have been
* successful, we update the "shadow" MCG entry by incrementing
* the "number of attached QPs" count.
*
* We increment only if the QP is not already part of the
* MCG by checking the 'qp_found' flag returned from the
* qplist_add above.
*/
if (!qp_found) {
mcg->mcg_num_qps++;
/*
* Increment the refcnt for this QP. Because the QP
* was added to this MCG, the refcnt must be
* incremented.
*/
}
/*
* We drop the lock and return success.
*/
return (DDI_SUCCESS);
}
/*
* If the specified MGID matches the MGID in the current entry, then
* we need to try to add the QP to the current MCG entry. In this
* case, it means that we need to read the existing MCG entry (into
* the temporary MCG), add the new QP number to the temporary entry
* (using the same method we used above), and write the entry back
* to the hardware (same as above).
*/
/*
* Read the current MCG entry into the temporary MCG. Note:
* In general, this operation shouldn't fail. If it does,
* then it is an indication that something (probably in HW,
* but maybe in SW) has gone seriously wrong.
*/
if (status != HERMON_CMD_SUCCESS) {
"%08x\n", status);
if (status == HERMON_CMD_INVALID_STATUS) {
}
return (ibc_get_ci_failure(0));
}
/*
* Try to add the new QP number to the list. This routine
* fills in the necessary pieces of the temporary MCG. The
* "mcg_entry_qplist" pointer is used to point to the portion
* of the temporary MCG that holds the QP numbers.
*
* Note: hermon_mcg_qplist_add() returns SUCCESS if it
* already found the QP in the list. In this case, the QP is
* not added on to the list again. Check the flag 'qp_found'
* if this value is needed to be known.
*
*/
&qp_found);
if (status != DDI_SUCCESS) {
return (status);
}
if (!qp_found)
/* set the member count */
/*
* Once the temporary MCG has been updated, write the entry
* into the appropriate location in the Hermon MCG entry table.
* If it's successful, then drop the lock and return success.
* Note: In general, this operation shouldn't fail. If it
* does, then it is an indication that something (probably in
* HW, but maybe in SW) has gone seriously wrong.
*/
if (status != HERMON_CMD_SUCCESS) {
"%08x\n", status);
if (status == HERMON_CMD_INVALID_STATUS) {
}
return (ibc_get_ci_failure(0));
}
/*
* Now that we know all the Hermon firmware accesses have been
* successful, we update the current "shadow" MCG entry by
* incrementing the "number of attached QPs" count.
*
* We increment only if the QP is not already part of the
* MCG by checking the 'qp_found' flag returned
* hermon_mcg_walk_mgid_hashfrom the qplist_add above.
*/
if (!qp_found) {
mcg->mcg_num_qps++;
/*
* Increment the refcnt for this QP. Because the QP
* was added to this MCG, the refcnt must be
* incremented.
*/
}
/*
* We drop the lock and return success.
*/
return (DDI_SUCCESS);
}
/*
* If we've reached here, then we're at the end of the hash chain.
* We need to allocate a new MCG entry, fill it in, write it to Hermon,
* and update the previous entry to link the new one to the end of the
* chain.
*/
/*
* Allocate an MCG table entry. This will be filled in with all
* the necessary parameters to define the multicast group. Then it
* will be written to the hardware in the next-to-last step below.
*/
if (status != DDI_SUCCESS) {
return (IBT_INSUFF_RESOURCE);
}
/*
* Fill in the new entry in the "shadow" MCG list. Note: Just as
* it does above, hermon_mcg_setup_new_hdr() also fills in a portion
* of the temporary MCG entry (the rest of which will be filled in by
* hermon_mcg_qplist_add() below)
*/
/*
* Try to add the new QP number to the list. This routine fills in
* the final necessary pieces of the temporary MCG. The
* "mcg_entry_qplist" pointer is used to point to the portion of the
* temporary MCG that holds the QP numbers. If we fail here, we
* must undo the previous resource allocation.
*
* Note: hermon_mcg_qplist_add() can we return SUCCESS if it already
* found the QP in the list. In this case, the QP is not added on to
* the list again. Check the flag 'qp_found' if this value is needed
* to be known.
*/
&qp_found);
if (status != DDI_SUCCESS) {
return (status);
}
/* set the member count */
/*
* Once the temporary MCG has been updated, write the entry into the
* appropriate location in the Hermon MCG entry table. If this is
* successful, then we need to chain the previous entry to this one.
* Note: In general, this operation shouldn't fail. If it does, then
* it is an indication that something (probably in HW, but maybe in
* SW) has gone seriously wrong.
*/
if (status != HERMON_CMD_SUCCESS) {
status);
if (status == HERMON_CMD_INVALID_STATUS) {
}
return (ibc_get_ci_failure(0));
}
/*
* Now read the current MCG entry (the one previously at the end of
* hash chain) into the temporary MCG. We are going to update its
* "next_gid_indx" now and write the entry back to the MCG table.
* Note: In general, this operation shouldn't fail. If it does, then
* it is an indication that something (probably in HW, but maybe in SW)
* has gone seriously wrong. We will free up the MCG entry resource,
* but we will not undo the previously written MCG entry in the HW.
* This is OK, though, because the MCG entry is not currently attached
* to any hash chain.
*/
if (status != HERMON_CMD_SUCCESS) {
status);
if (status == HERMON_CMD_INVALID_STATUS) {
}
return (ibc_get_ci_failure(0));
}
/*
* Finally, we update the "next_gid_indx" field in the temporary MCG
* and attempt to write the entry back into the Hermon MCG table. If
* this succeeds, then we update the "shadow" list to reflect the
* change, drop the lock, and return success. Note: In general, this
* operation shouldn't fail. If it does, then it is an indication
* that something (probably in HW, but maybe in SW) has gone seriously
* wrong. Just as we do above, we will free up the MCG entry resource,
* but we will not try to undo the previously written MCG entry. This
* is OK, though, because (since we failed here to update the end of
* the chain) that other entry is not currently attached to any chain.
*/
if (status != HERMON_CMD_SUCCESS) {
status);
if (status == HERMON_CMD_INVALID_STATUS) {
}
return (ibc_get_ci_failure(0));
}
/*
* Now that we know all the Hermon firmware accesses have been
* successful, we update the new "shadow" MCG entry by incrementing
* the "number of attached QPs" count. Then we drop the lock and
* return success.
*/
newmcg->mcg_num_qps++;
/*
* Increment the refcnt for this QP. Because the QP
* was added to this MCG, the refcnt must be
* incremented.
*/
return (DDI_SUCCESS);
}
/*
* hermon_mcg_detach()
* Context: Can be called only from user or kernel context.
*/
int
{
int status;
/*
* Check for invalid Multicast DLID. Specifically, all Multicast
* LIDs should be within a well defined range. If the specified LID
* is outside of that range, then return an error.
*/
if (hermon_mlid_is_valid(lid) == 0) {
return (IBT_MC_MLID_INVALID);
}
/*
* Compute the MGID hash value. As described above, the MCG table is
* arranged as a number of separate hash chains. This operation
* converts the specified MGID into the starting index of an entry in
* the hash table (i.e. the index for the start of the appropriate
* hash chain). Subsequent operations below will walk the chain
* searching for a matching entry from which to attempt to remove
* the specified QP.
*/
if (status != HERMON_CMD_SUCCESS) {
status);
if (status == HERMON_CMD_INVALID_STATUS) {
}
return (ibc_get_ci_failure(0));
}
/*
* Grab the multicast group mutex. Then grab the pre-allocated
*/
/*
* Walk through the array of MCG entries starting at "mgid_hash".
* Try to find an MCG entry with a matching MGID. The
* hermon_mcg_walk_mgid_hash() routine walks the list and returns an
* index into the MCG table. The entry at this index is checked to
* determine whether it is a match or not. If it is a match, then
* we continue on to attempt to remove the QP from the MCG. If it
* is not a match (or not a valid MCG entry), then we return an error.
*/
/*
* If MGID == 0 (the hash chain is empty) or if the specified MGID
* does not match the MGID in the current entry, then return
* IBT_MC_MGID_INVALID (to indicate that the specified MGID is not
* valid).
*/
return (IBT_MC_MGID_INVALID);
}
/*
* Read the current MCG entry into the temporary MCG. Note: In
* general, this operation shouldn't fail. If it does, then it is
* an indication that something (probably in HW, but maybe in SW)
* has gone seriously wrong.
*/
if (status != HERMON_CMD_SUCCESS) {
status);
if (status == HERMON_CMD_INVALID_STATUS) {
}
return (ibc_get_ci_failure(0));
}
/*
* Search the QP number list for a match. If a match is found, then
* remove the entry from the QP list. Otherwise, if no match is found,
* return an error.
*/
if (status != DDI_SUCCESS) {
return (status);
}
/*
* Decrement the MCG count for this QP. When the 'qp_mcg'
* field becomes 0, then this QP is no longer a member of any
* MCG.
*/
/*
* If the current MCG's QP number list is about to be made empty
* ("mcg_num_qps" == 1), then remove the entry itself from the hash
* chain. Otherwise, just write the updated MCG entry back to the
* hardware. In either case, once we successfully update the hardware
* chain, then we decrement the "shadow" list entry's "mcg_num_qps"
* count (or zero out the entire "shadow" list entry) before returning
* success. Note: Zeroing out the "shadow" list entry is done
* inside of hermon_mcg_hash_list_remove().
*/
/* Remove an MCG entry from the hash chain */
if (status != DDI_SUCCESS) {
return (status);
}
} else {
/*
* Write the updated MCG entry back to the Hermon MCG table.
* If this succeeds, then we update the "shadow" list to
* reflect the change (i.e. decrement the "mcg_num_qps"),
* drop the lock, and return success. Note: In general,
* this operation shouldn't fail. If it does, then it is an
* indication that something (probably in HW, but maybe in SW)
* has gone seriously wrong.
*/
if (status != HERMON_CMD_SUCCESS) {
"%08x\n", status);
if (status == HERMON_CMD_INVALID_STATUS) {
}
return (ibc_get_ci_failure(0));
}
mcg->mcg_num_qps--;
}
return (DDI_SUCCESS);
}
/*
* hermon_qp_mcg_refcnt_inc()
* Context: Can be called from interrupt or base context.
*/
static void
{
/* Increment the QP's MCG reference count */
qp->qp_mcg_refcnt++;
}
/*
* hermon_qp_mcg_refcnt_dec()
* Context: Can be called from interrupt or base context.
*/
static void
{
/* Decrement the QP's MCG reference count */
qp->qp_mcg_refcnt--;
}
/*
* hermon_mcg_qplist_add()
* Context: Can be called from interrupt or base context.
*/
static int
{
/*
* Determine if we have exceeded the maximum number of QP per
* multicast group. If we have, then return an error
*/
return (IBT_HCA_MCG_QP_EXCEEDED);
}
/*
* Determine if the QP is already attached to this MCG table. If it
* is, then we break out and treat this operation as a NO-OP
*/
qplist_indx++) {
break;
}
}
/*
* If the QP was already on the list, set 'qp_found' to TRUE. We still
* return SUCCESS in this case, but the qplist will not have been
* updated because the QP was already on the list.
*/
*qp_found = 1;
} else {
/*
* Otherwise, append the new QP number to the end of the
* current QP list. Note: We will increment the "mcg_num_qps"
* field on the "shadow" MCG list entry later (after we know
* that all necessary Hermon firmware accesses have been
* successful).
*
* Set 'qp_found' to 0 so we know the QP was added on to the
* list for sure.
*/
*qp_found = 0;
}
return (DDI_SUCCESS);
}
/*
* hermon_mcg_qplist_remove()
* Context: Can be called from interrupt or base context.
*/
static int
{
/*
* Search the MCG QP list for a matching QPN. When
* it's found, we swap the last entry with the current
* one, set the last entry to zero, decrement the last
* entry, and return. If it's not found, then it's
* and error.
*/
for (i = 0; i < qplist_indx; i++) {
return (DDI_SUCCESS);
}
}
return (IBT_QP_HDL_INVALID);
}
/*
* hermon_mcg_walk_mgid_hash()
* Context: Can be called from interrupt or base context.
*/
static uint_t
{
/* Start at the head of the hash chain */
/* If the first entry in the chain has MGID == 0, then stop */
if ((curr_mcghdl->mcg_mgid_h == 0) &&
(curr_mcghdl->mcg_mgid_l == 0)) {
goto end_mgid_hash_walk;
}
/* If the first entry in the chain matches the MGID, then stop */
goto end_mgid_hash_walk;
}
/* Otherwise, walk the hash chain looking for a match */
while (curr_mcghdl->mcg_next_indx != 0) {
break;
}
}
/*
* If necessary, return the index of the previous entry too. This
* is primarily used for detaching a QP from a multicast group. It
* may be necessary, in that case, to delete an MCG entry from the
* hash chain and having the index of the previous entry is helpful.
*/
}
return (curr_indx);
}
/*
* hermon_mcg_setup_new_hdr()
* Context: Can be called from interrupt or base context.
*/
static void
{
/*
* Fill in the fields of the "shadow" entry used by software
* to track MCG hardware entry
*/
mcg->mcg_next_indx = 0;
mcg->mcg_num_qps = 0;
/*
* Fill the header fields of the MCG entry (in the temporary copy)
*/
mcg_hdr->next_gid_indx = 0;
}
/*
* hermon_mcg_hash_list_remove()
* Context: Can be called only from user or kernel context.
*/
static int
{
int status;
/* Get the pointer to "shadow" list for current entry */
/*
* If this is the first entry on a hash chain, then attempt to replace
* the entry with the next entry on the chain. If there are no
* subsequent entries on the chain, then this is the only entry and
* should be invalidated.
*/
/*
* If this is the only entry on the chain, then invalidate it.
* Note: Invalidating an MCG entry means writing all zeros
* to the entry. This is only necessary for those MCG
* entries that are the "head" entries of the individual hash
* chains. Regardless of whether this operation returns
* success or failure, return that result to the caller.
*/
if (next_indx == 0) {
return (status);
}
/*
* Otherwise, this is just the first entry on the chain, so
* grab the next one
*/
/*
* Read the next MCG entry into the temporary MCG. Note:
* In general, this operation shouldn't fail. If it does,
* then it is an indication that something (probably in HW,
* but maybe in SW) has gone seriously wrong.
*/
if (status != HERMON_CMD_SUCCESS) {
"%08x\n", status);
if (status == HERMON_CMD_INVALID_STATUS) {
}
return (ibc_get_ci_failure(0));
}
/*
* using the current index. This essentially removes the
* current MCG entry from the list by writing over it with
* the next one. If this is successful, then we can do the
* same operation for the "shadow" list. And we can also
* free up the Hermon MCG entry resource that was associated
* with the (old) next entry. Note: In general, this
* operation shouldn't fail. If it does, then it is an
* indication that something (probably in HW, but maybe in SW)
* has gone seriously wrong.
*/
if (status != HERMON_CMD_SUCCESS) {
"%08x\n", status);
if (status == HERMON_CMD_INVALID_STATUS) {
}
return (ibc_get_ci_failure(0));
}
/*
* Copy all the software tracking information from the next
* entry on the "shadow" MCG list into the current entry on
* the list. Then invalidate (zero out) the other "shadow"
* list entry.
*/
/*
* Free up the Hermon MCG entry resource used by the "next"
* MCG entry. That resource is no longer needed by any
* MCG entry which is first on a hash chain (like the "next"
* entry has just become).
*/
return (DDI_SUCCESS);
}
/*
* Else if this is the last entry on the hash chain (or a middle
* entry, then we update the previous entry's "next_gid_index" field
* to make it point instead to the next entry on the chain. By
* skipping over the removed entry in this way, we can then free up
* any resources associated with the current entry. Note: We don't
* need to invalidate the "skipped over" hardware entry because it
* finally re-used, it will be written with entirely new values.
*/
/*
* Read the next MCG entry into the temporary MCG. Note: In general,
* this operation shouldn't fail. If it does, then it is an
* indication that something (probably in HW, but maybe in SW) has
* gone seriously wrong.
*/
if (status != HERMON_CMD_SUCCESS) {
status);
if (status == HERMON_CMD_INVALID_STATUS) {
}
return (ibc_get_ci_failure(0));
}
/*
* Finally, we update the "next_gid_indx" field in the temporary MCG
* and attempt to write the entry back into the Hermon MCG table. If
* this succeeds, then we update the "shadow" list to reflect the
* change, free up the Hermon MCG entry resource that was associated
* with the current entry, and return success. Note: In general,
* this operation shouldn't fail. If it does, then it is an indication
* that something (probably in HW, but maybe in SW) has gone seriously
* wrong.
*/
if (status != HERMON_CMD_SUCCESS) {
status);
if (status == HERMON_CMD_INVALID_STATUS) {
}
return (ibc_get_ci_failure(0));
}
/*
* Get the pointer to the "shadow" MCG list entry for the previous
* MCG. Update its "mcg_next_indx" to point to the next entry
* the one after the current entry. Note: This next index may be
* zero, indicating the end of the list.
*/
/*
* Free up the Hermon MCG entry resource used by the current entry.
* This resource is no longer needed because the chain now skips over
* the current entry. Then invalidate (zero out) the current "shadow"
* list entry.
*/
return (DDI_SUCCESS);
}
/*
* hermon_mcg_entry_invalidate()
* Context: Can be called only from user or kernel context.
*/
static int
{
int status;
/*
* Invalidate the hardware MCG entry by zeroing out this temporary
* MCG and writing it the the hardware. Note: In general, this
* operation shouldn't fail. If it does, then it is an indication
* that something (probably in HW, but maybe in SW) has gone seriously
* wrong.
*/
if (status != HERMON_CMD_SUCCESS) {
status);
if (status == HERMON_CMD_INVALID_STATUS) {
}
return (ibc_get_ci_failure(0));
}
return (DDI_SUCCESS);
}
/*
* hermon_mgid_is_valid()
* Context: Can be called from interrupt or base context.
*/
static int
{
/*
* According to IBA 1.1 specification (section 4.1.1) a valid
* "multicast GID" must have its top eight bits set to all ones
*/
if (topbits != HERMON_MCG_TOPBITS) {
return (0);
}
/*
* The next 4 bits are the "flag" bits. These are valid only
* if they are "0" (which correspond to permanently assigned/
* "well-known" multicast GIDs) or "1" (for so-called "transient"
* multicast GIDs). All other values are reserved.
*/
if (!((flags == HERMON_MCG_FLAGS_PERM) ||
(flags == HERMON_MCG_FLAGS_NONPERM))) {
return (0);
}
/*
* The next 4 bits are the "scope" bits. These are valid only
* if they are "2" (Link-local), "5" (Site-local), "8"
* (Organization-local) or "E" (Global). All other values
* are reserved (or currently unassigned).
*/
if (!((scope == HERMON_MCG_SCOPE_LINKLOC) ||
(scope == HERMON_MCG_SCOPE_SITELOC) ||
(scope == HERMON_MCG_SCOPE_ORGLOC) ||
(scope == HERMON_MCG_SCOPE_GLOBAL))) {
return (0);
}
/*
* If it passes all of the above checks, then we will consider it
* a valid multicast GID.
*/
return (1);
}
/*
* hermon_mlid_is_valid()
* Context: Can be called from interrupt or base context.
*/
static int
{
/*
* According to IBA 1.1 specification (section 4.1.1) a valid
* "multicast DLID" must be between 0xC000 and 0xFFFE.
*/
return (0);
}
return (1);
}
/*
* hermon_pd_alloc()
* Context: Can be called only from user or kernel context.
*/
int
{
int status;
/*
* Allocate the software structure for tracking the protection domain
* (i.e. the Hermon Protection Domain handle). By default each PD
* structure will have a unique PD number assigned to it. All that
* is necessary is for software to initialize the PD reference count
* (to zero) and return success.
*/
if (status != DDI_SUCCESS) {
return (IBT_INSUFF_RESOURCE);
}
return (DDI_SUCCESS);
}
/*
* hermon_pd_free()
* Context: Can be called only from user or kernel context.
*/
int
{
/*
* Pull all the necessary information from the Hermon Protection Domain
* handle. This is necessary here because the resource for the
* PD is going to be freed up as part of this operation.
*/
/*
* Check the PD reference count. If the reference count is non-zero,
* then it means that this protection domain is still referenced by
* some memory region, queue pair, address handle, or other IB object
* If it is non-zero, then return an error. Otherwise, free the
* Hermon resource and return success.
*/
return (IBT_PD_IN_USE);
}
/* Free the Hermon Protection Domain handle */
/* Set the pdhdl pointer to NULL and return success */
return (DDI_SUCCESS);
}
/*
* hermon_pd_refcnt_inc()
* Context: Can be called from interrupt or base context.
*/
void
{
/* Increment the protection domain's reference count */
}
/*
* hermon_pd_refcnt_dec()
* Context: Can be called from interrupt or base context.
*/
void
{
/* Decrement the protection domain's reference count */
}
/*
* hermon_port_query()
* Context: Can be called only from user or kernel context.
*/
int
{
/* Validate that specified port number is legal */
return (IBT_HCA_PORT_INVALID);
}
/*
* We use the Hermon MAD_IFC command to post a GetPortInfo MAD
* to the firmware (for the specified port number). This returns
* a full PortInfo MAD (in "portinfo") which we subsequently
* parse to fill in the "ibt_hca_portinfo_t" structure returned
* to the IBTF.
*/
if (status != HERMON_CMD_SUCCESS) {
if (status == HERMON_CMD_INVALID_STATUS) {
}
return (ibc_get_ci_failure(0));
}
/*
* Parse the PortInfo MAD and fill in the IBTF structure
*/
/*
* Convert InfiniBand-defined port capability flags to the format
* specified by the IBTF
*/
/*
* Fill in the SGID table. Since the only access to the Hermon
* GID tables is through the firmware's MAD_IFC interface, we
* post as many GetGUIDInfo MADs as necessary to read in the entire
* contents of the SGID table (for the specified port). Note: The
* GetGUIDInfo command only gets eight GUIDs per operation. These
* GUIDs are then appended to the GID prefix for the port (from the
* GetPortInfo above) to form the entire SGID table.
*/
if (status != HERMON_CMD_SUCCESS) {
if (status == HERMON_CMD_INVALID_STATUS) {
}
return (ibc_get_ci_failure(0));
}
/* Figure out how many of the entries are valid */
for (j = 0; j < sgid_max; j++) {
indx = (i + j);
guidinfo.GUIDBlocks[j];
}
}
/*
* Fill in the PKey table. Just as for the GID tables above, the
* only access to the Hermon PKey tables is through the firmware's
* MAD_IFC interface. We post as many GetPKeyTable MADs as necessary
* to read in the entire contents of the PKey table (for the specified
* port). Note: The GetPKeyTable command only gets 32 PKeys per
* operation.
*/
if (status != HERMON_CMD_SUCCESS) {
if (status == HERMON_CMD_INVALID_STATUS) {
}
return (ibc_get_ci_failure(0));
}
/* Figure out how many of the entries are valid */
for (j = 0; j < pkey_max; j++) {
indx = (i + j);
}
}
return (DDI_SUCCESS);
}
/*
* hermon_port_modify()
* Context: Can be called only from user or kernel context.
*/
/* ARGSUSED */
int
{
int status;
/*
* Return an error if either of the unsupported flags are set
*/
if ((flags & IBT_PORT_SHUTDOWN) ||
(flags & IBT_PORT_SET_INIT_TYPE)) {
return (IBT_NOT_SUPPORTED);
}
/*
* Determine whether we are trying to reset the QKey counter
*/
if (flags & IBT_PORT_RESET_QKEY)
/* Validate that specified port number is legal */
return (IBT_HCA_PORT_INVALID);
}
/*
* Use the Hermon MAD_IFC command to post a GetPortInfo MAD to the
* firmware (for the specified port number). This returns a full
* PortInfo MAD (in "portinfo") from which we pull the current
* capability mask. We then modify the capability mask as directed
* by the "pmod_flags" field, and write the updated capability mask
* using the Hermon SET_IB command (below).
*/
if (status != HERMON_CMD_SUCCESS) {
if (status == HERMON_CMD_INVALID_STATUS) {
}
return (ibc_get_ci_failure(0));
}
/*
* Convert InfiniBand-defined port capability flags to the format
* specified by the IBTF. Specifically, we modify the capability
* mask based on the specified values.
*/
if (flags & IBT_PORT_RESET_SM)
capmask &= ~SM_CAP_MASK_IS_SM;
else if (flags & IBT_PORT_SET_SM)
if (flags & IBT_PORT_RESET_SNMP)
else if (flags & IBT_PORT_SET_SNMP)
if (flags & IBT_PORT_RESET_DEVMGT)
else if (flags & IBT_PORT_SET_DEVMGT)
if (flags & IBT_PORT_RESET_VENDOR)
else if (flags & IBT_PORT_SET_VENDOR)
/*
* Use the Hermon SET_PORT command to update the capability mask and
* (possibly) reset the QKey violation counter for the specified port.
* Note: In general, this operation shouldn't fail. If it does, then
* it is an indication that something (probably in HW, but maybe in
* SW) has gone seriously wrong.
*/
if (status != HERMON_CMD_SUCCESS) {
if (status == HERMON_CMD_INVALID_STATUS) {
}
return (ibc_get_ci_failure(0));
}
return (DDI_SUCCESS);
}
/*
* hermon_set_addr_path()
* Context: Can be called from interrupt or base context.
*
* Note: This routine is used for two purposes. It is used to fill in the
* Hermon UDAV fields, and it is used to fill in the address path information
* for QPs. Because the two Hermon structures are similar, common fields can
* be filled in here. Because they are different, however, we pass
* an additional flag to indicate which type is being filled and do each one
* uniquely
*/
int
{
case IBT_SRATE_2: /* 1xSDR-2.5Gb/s injection rate */
case IBT_SRATE_10: /* 4xSDR-10.0Gb/s injection rate */
case IBT_SRATE_30: /* 12xSDR-30Gb/s injection rate */
case IBT_SRATE_5: /* 1xDDR-5Gb/s injection rate */
case IBT_SRATE_20: /* 4xDDR-20Gb/s injection rate */
case IBT_SRATE_40: /* 4xQDR-40Gb/s injection rate */
case IBT_SRATE_60: /* 12xDDR-60Gb/s injection rate */
case IBT_SRATE_80: /* 8xQDR-80Gb/s injection rate */
case IBT_SRATE_120: /* 12xQDR-120Gb/s injection rate */
case IBT_SRATE_NOT_SPECIFIED: /* Max */
path->max_stat_rate = 0; break;
default:
return (IBT_STATIC_RATE_INVALID);
}
/* If "grh" flag is set, then check for valid SGID index too */
return (IBT_SGID_INVALID);
}
/*
* Fill in all "global" values regardless of the value in the GRH
* flag. Because "grh" is not set unless "av_send_grh" is set, the
* hardware will ignore the other "global" values as necessary. Note:
* SW does this here to enable later query operations to return
* exactly the same params that were passed when the addr path was
* last written.
*/
if (type == HERMON_ADDRPATH_QP) {
} else {
/*
* For Hermon UDAV, the "mgid_index" field is the index into
* a combined table (not a per-port table), but having sections
* for each port. So some extra calculations are necessary.
*/
av->av_sgid_ix;
}
/*
* According to Hermon PRM, the (31:0) part of rgid_l must be set to
* "0x2" if the 'grh' or 'g' bit is cleared. It also says that we
* only need to do it for UDAV's. So we enforce that here.
*
* NOTE: The entire 64 bits worth of GUID info is actually being
* preserved (for UDAVs) by the callers of this function
* (hermon_ah_alloc() and hermon_ah_modify()) and as long as the
* 'grh' bit is not set, the upper 32 bits (63:32) of rgid_l are
* "don't care".
*/
sizeof (uint64_t));
sizeof (uint64_t));
} else {
path->flow_label = 0;
}
/* extract the default service level */
return (DDI_SUCCESS);
}
/*
* hermon_get_addr_path()
* Context: Can be called from interrupt or base context.
*
* Note: Just like hermon_set_addr_path() above, this routine is used for two
* purposes. It is used to read in the Hermon UDAV fields, and it is used to
* read in the address path information for QPs. Because the two Hermon
* structures are similar, common fields can be read in here. But because
* they are slightly different, we pass an additional flag to indicate which
* type is being read.
*/
void
{
/* Set "av_ipd" value from max_stat_rate */
switch (path->max_stat_rate) {
case 7: /* 1xSDR-2.5Gb/s injection rate */
case 8: /* 4xSDR-10.0Gb/s injection rate */
case 9: /* 12xSDR-30Gb/s injection rate */
case 10: /* 1xDDR-5Gb/s injection rate */
case 11: /* 4xDDR-20Gb/s injection rate */
case 12: /* xQDR-40Gb/s injection rate */
case 13: /* 12xDDR-60Gb/s injection rate */
case 14: /* 8xQDR-80Gb/s injection rate */
case 15: /* 12xQDR-120Gb/s injection rate */
case 0: /* max */
default: /* 1x injection rate */
}
/*
* Extract all "global" values regardless of the value in the GRH
* flag. Because "av_send_grh" is set only if "grh" is set, software
* knows to ignore the other "global" values as necessary. Note: SW
* does it this way to enable these query operations to return exactly
* the same params that were passed when the addr path was last written.
*/
if (type == HERMON_ADDRPATH_QP) {
} else {
/*
* For Hermon UDAV, the "mgid_index" field is the index into
* a combined table (not a per-port table).
*/
}
/* this is for alignment issue w/ the addr path struct in Hermon */
}
/*
* hermon_portnum_is_valid()
* Context: Can be called from interrupt or base context.
*/
int
{
return (1);
} else {
return (0);
}
}
/*
* hermon_pkeyindex_is_valid()
* Context: Can be called from interrupt or base context.
*/
int
{
if (pkeyindx < max_pkeyindx) {
return (1);
} else {
return (0);
}
}
/*
* hermon_queue_alloc()
* Context: Can be called from interrupt or base context.
*/
int
{
/* Set the callback flag appropriately */
/*
* Initialize many of the default DMA attributes. Then set additional
* alignment restrictions as necessary for the queue memory. Also
* respect the configured value for IOMMU bypass
*/
#ifdef __sparc
}
#endif
/* Allocate a DMA handle */
if (status != DDI_SUCCESS) {
return (DDI_FAILURE);
}
/*
* Determine the amount of memory to allocate, depending on the values
* in "qa_bind_align" and "qa_alloc_align". The problem we are trying
* to solve here is that allocating a DMA handle with IOMMU bypass
* (DDI_DMA_FORCE_PHYSICAL) constrains us to only requesting alignments
* that are less restrictive than the page size. Since we may need
* stricter alignments on the memory allocated by ddi_dma_mem_alloc()
* (e.g. in Hermon QP work queue memory allocation), we use the
* following method to calculate how much additional memory to request,
* and we enforce our own alignment on the allocated result.
*/
} else {
}
/*
* If we are to allocate the queue from system memory, then use
* ddi_dma_mem_alloc() to find the space. Otherwise, this is a
* host memory allocation, use ddi_umem_alloc(). In either case,
* return a pointer to the memory range allocated (including any
* necessary alignment adjustments), the "real" memory pointer,
* the "real" size, and a ddi_acc_handle_t to use when reading
*/
/* Allocate system memory for the queue */
if (status != DDI_SUCCESS) {
return (DDI_FAILURE);
}
/*
* Save temporary copy of the real pointer. (This may be
* modified in the last step below).
*/
} else { /* HERMON_QUEUE_LOCATION_USERLAND */
/* Allocate userland mappable memory for the queue */
&qa_info->qa_umemcookie);
return (DDI_FAILURE);
}
/*
* Save temporary copy of the real pointer. (This may be
* modified in the last step below).
*/
}
/*
* The next to last step is to ensure that the final address
* ("qa_buf_aligned") has the appropriate "alloc" alignment
* restriction applied to it (if necessary).
*/
}
/*
* The last step is to figure out the offset of the start relative
* passed to the HW
*/
return (DDI_SUCCESS);
}
/*
* hermon_queue_free()
* Context: Can be called from interrupt or base context.
*/
void
{
/*
* Depending on how (i.e. from where) we allocated the memory for
* this queue, we choose the appropriate method for releasing the
* resources.
*/
}
/* Always free the dma handle */
}
/*
* hermon_create_fmr_pool()
* Create a pool of FMRs.
* Context: Can be called from kernel context only.
*/
int
{
int status;
int sleep;
int i;
if ((sleep == HERMON_SLEEP) &&
(sleep != HERMON_SLEEPFLAG_FOR_CONTEXT())) {
return (IBT_INVALID_PARAM);
}
goto fail;
}
fmrpool->fmr_pool_size = 0;
fmrpool->fmr_dirty_len = 0;
fmrpool->fmr_remap_len = 0;
fmrpool->fmr_stat_register = 0;
for (i = 0; i < fmr_attr->fmr_pool_size; i++) {
if (status != DDI_SUCCESS) {
goto fail2;
}
sizeof (hermon_fmr_list_t), sleep);
fmr->fmr_remaps = 0;
if (!i) /* address of last entry's link */
}
/* Set to return pool */
return (IBT_SUCCESS);
}
fail:
if (status == DDI_FAILURE) {
return (ibc_get_ci_failure(0));
} else {
return (status);
}
}
/*
* hermon_destroy_fmr_pool()
* Destroy an FMR pool and free all associated resources.
* Context: Can be called from kernel context only.
*/
int
{
--fmrpool->fmr_pool_size;
}
return (DDI_SUCCESS);
}
/*
* hermon_flush_fmr_pool()
* Ensure that all unmapped FMRs are fully invalidated.
* Context: Can be called from kernel context only.
*/
/* ARGSUSED */
int
{
/*
* Force the unmapping of all entries on the dirty list, regardless of
* whether the watermark has been hit yet.
*/
/* grab the pool lock */
return (DDI_SUCCESS);
}
/*
* hermon_register_physical_fmr()
* Map memory into FMR
* Context: Can be called from interrupt or base context.
*/
int
{
int status;
/* Check length */
fmrpool->fmr_max_pages)) {
return (IBT_MR_LEN_INVALID);
}
if (hermon_fmr_verbose & 2)
if (fmrpool->fmr_remap_list) {
/* add to free list */
*(fmrpool->fmr_free_list_tail) =
/* reset list */
fmrpool->fmr_remap_len = 0;
}
}
if (hermon_fmr_verbose & 2)
}
/* grab next free entry */
return (IBT_INSUFF_RESOURCE);
}
if (status != DDI_SUCCESS) {
return (status);
}
if (hermon_rdma_debug & 0x4)
fmr->fmr_remaps = 0;
}
fmr->fmr_remaps++;
return (DDI_SUCCESS);
}
/*
* hermon_deregister_fmr()
* Unmap FMR
* Context: Can be called from kernel context only.
*/
int
{
int len;
/* mark as owned by software */
if (fmr->fmr_remaps <
/* add to remap list */
if (hermon_rdma_debug & 0x4)
fmrpool->fmr_remap_len++;
/* conditionally add remap list back to free list */
if (fmrpool->fmr_remap_len >=
fmrpool->fmr_remap_len = 0;
}
if (fmrlast) {
}
} else {
/* add to dirty list */
if (hermon_rdma_debug & 0x4)
fmrpool->fmr_dirty_len++;
if (fmrpool->fmr_dirty_len >=
} else
}
return (DDI_SUCCESS);
}
/*
* hermon_fmr_cleanup()
* Context: Called from any context.
*/
static void
{
int status;
if (fmrpool->fmr_stat_register == 0)
return;
fmrpool->fmr_stat_register = 0;
if (hermon_fmr_verbose)
if (status != HERMON_CMD_SUCCESS) {
}
fmrpool->fmr_remap_gen++;
/* add everything back to the free list */
if (fmrpool->fmr_dirty_list) {
/* add to free list */
/* reset list */
fmrpool->fmr_dirty_len = 0;
}
if (fmrpool->fmr_remap_list) {
/* add to free list */
/* reset list */
fmrpool->fmr_remap_len = 0;
}
(void) fmrpool->fmr_flush_function(
}
}