19e39c5baSBill Taylor /*
29e39c5baSBill Taylor  * CDDL HEADER START
39e39c5baSBill Taylor  *
49e39c5baSBill Taylor  * The contents of this file are subject to the terms of the
59e39c5baSBill Taylor  * Common Development and Distribution License (the "License").
69e39c5baSBill Taylor  * You may not use this file except in compliance with the License.
79e39c5baSBill Taylor  *
89e39c5baSBill Taylor  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
99e39c5baSBill Taylor  * or http://www.opensolaris.org/os/licensing.
109e39c5baSBill Taylor  * See the License for the specific language governing permissions
119e39c5baSBill Taylor  * and limitations under the License.
129e39c5baSBill Taylor  *
139e39c5baSBill Taylor  * When distributing Covered Code, include this CDDL HEADER in each
149e39c5baSBill Taylor  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
159e39c5baSBill Taylor  * If applicable, add the following below this CDDL HEADER, with the
169e39c5baSBill Taylor  * fields enclosed by brackets "[]" replaced with your own identifying
179e39c5baSBill Taylor  * information: Portions Copyright [yyyy] [name of copyright owner]
189e39c5baSBill Taylor  *
199e39c5baSBill Taylor  * CDDL HEADER END
209e39c5baSBill Taylor  */
219e39c5baSBill Taylor 
229e39c5baSBill Taylor /*
23*c7facc54SBill Taylor  * Copyright 2010 Sun Microsystems, Inc.  All rights reserved.
249e39c5baSBill Taylor  * Use is subject to license terms.
259e39c5baSBill Taylor  */
269e39c5baSBill Taylor 
279e39c5baSBill Taylor /*
289e39c5baSBill Taylor  * tavor_misc.c
299e39c5baSBill Taylor  *    Tavor Miscellaneous routines - Address Handle, Multicast, Protection
309e39c5baSBill Taylor  *    Domain, and port-related operations
319e39c5baSBill Taylor  *
329e39c5baSBill Taylor  *    Implements all the routines necessary for allocating, freeing, querying
339e39c5baSBill Taylor  *    and modifying Address Handles and Protection Domains.  Also implements
349e39c5baSBill Taylor  *    all the routines necessary for adding and removing Queue Pairs to/from
359e39c5baSBill Taylor  *    Multicast Groups.  Lastly, it implements the routines necessary for
369e39c5baSBill Taylor  *    port-related query and modify operations.
379e39c5baSBill Taylor  */
389e39c5baSBill Taylor 
399e39c5baSBill Taylor #include <sys/types.h>
409e39c5baSBill Taylor #include <sys/conf.h>
419e39c5baSBill Taylor #include <sys/ddi.h>
429e39c5baSBill Taylor #include <sys/sunddi.h>
439e39c5baSBill Taylor #include <sys/modctl.h>
449e39c5baSBill Taylor #include <sys/bitmap.h>
459e39c5baSBill Taylor #include <sys/sysmacros.h>
469e39c5baSBill Taylor 
479e39c5baSBill Taylor #include <sys/ib/adapters/tavor/tavor.h>
489e39c5baSBill Taylor 
499e39c5baSBill Taylor static void tavor_udav_sync(tavor_ahhdl_t ah, tavor_hw_udav_t *udav,
509e39c5baSBill Taylor     uint_t flag);
519e39c5baSBill Taylor static int tavor_mcg_qplist_add(tavor_state_t *state, tavor_mcghdl_t mcg,
529e39c5baSBill Taylor     tavor_hw_mcg_qp_list_t *mcg_qplist, tavor_qphdl_t qp, uint_t *qp_found);
539e39c5baSBill Taylor static int tavor_mcg_qplist_remove(tavor_mcghdl_t mcg,
549e39c5baSBill Taylor     tavor_hw_mcg_qp_list_t *mcg_qplist, tavor_qphdl_t qp);
559e39c5baSBill Taylor static void tavor_qp_mcg_refcnt_inc(tavor_qphdl_t qp);
569e39c5baSBill Taylor static void tavor_qp_mcg_refcnt_dec(tavor_qphdl_t qp);
579e39c5baSBill Taylor static uint_t tavor_mcg_walk_mgid_hash(tavor_state_t *state,
589e39c5baSBill Taylor     uint64_t start_indx, ib_gid_t mgid, uint_t *prev_indx);
599e39c5baSBill Taylor static void tavor_mcg_setup_new_hdr(tavor_mcghdl_t mcg,
609e39c5baSBill Taylor     tavor_hw_mcg_t *mcg_hdr, ib_gid_t mgid, tavor_rsrc_t *mcg_rsrc);
619e39c5baSBill Taylor static int tavor_mcg_hash_list_remove(tavor_state_t *state, uint_t curr_indx,
629e39c5baSBill Taylor     uint_t prev_indx, tavor_hw_mcg_t *mcg_entry);
639e39c5baSBill Taylor static int tavor_mcg_entry_invalidate(tavor_state_t *state,
649e39c5baSBill Taylor     tavor_hw_mcg_t *mcg_entry, uint_t indx);
659e39c5baSBill Taylor static int tavor_mgid_is_valid(ib_gid_t gid);
669e39c5baSBill Taylor static int tavor_mlid_is_valid(ib_lid_t lid);
679e39c5baSBill Taylor 
689e39c5baSBill Taylor 
699e39c5baSBill Taylor /*
709e39c5baSBill Taylor  * tavor_ah_alloc()
719e39c5baSBill Taylor  *    Context: Can be called only from user or kernel context.
729e39c5baSBill Taylor  */
739e39c5baSBill Taylor int
tavor_ah_alloc(tavor_state_t * state,tavor_pdhdl_t pd,ibt_adds_vect_t * attr_p,tavor_ahhdl_t * ahhdl,uint_t sleepflag)749e39c5baSBill Taylor tavor_ah_alloc(tavor_state_t *state, tavor_pdhdl_t pd,
759e39c5baSBill Taylor     ibt_adds_vect_t *attr_p, tavor_ahhdl_t *ahhdl, uint_t sleepflag)
769e39c5baSBill Taylor {
779e39c5baSBill Taylor 	tavor_rsrc_t		*udav, *rsrc;
789e39c5baSBill Taylor 	tavor_hw_udav_t		udav_entry;
799e39c5baSBill Taylor 	tavor_ahhdl_t		ah;
809e39c5baSBill Taylor 	ibt_mr_attr_t		mr_attr;
819e39c5baSBill Taylor 	tavor_mr_options_t	op;
829e39c5baSBill Taylor 	tavor_mrhdl_t		mr;
839e39c5baSBill Taylor 	uint64_t		data;
849e39c5baSBill Taylor 	uint32_t		size;
859e39c5baSBill Taylor 	int			status, i, flag;
869e39c5baSBill Taylor 
879e39c5baSBill Taylor 	/*
889e39c5baSBill Taylor 	 * Someday maybe the "ibt_adds_vect_t *attr_p" will be NULL to
899e39c5baSBill Taylor 	 * indicate that we wish to allocate an "invalid" (i.e. empty)
909e39c5baSBill Taylor 	 * address handle XXX
919e39c5baSBill Taylor 	 */
929e39c5baSBill Taylor 
939e39c5baSBill Taylor 	/* Validate that specified port number is legal */
949e39c5baSBill Taylor 	if (!tavor_portnum_is_valid(state, attr_p->av_port_num)) {
959e39c5baSBill Taylor 		goto ahalloc_fail;
969e39c5baSBill Taylor 	}
979e39c5baSBill Taylor 
989e39c5baSBill Taylor 	/*
999e39c5baSBill Taylor 	 * Allocate a UDAV entry.  This will be filled in with all the
1009e39c5baSBill Taylor 	 * necessary parameters to define the Address Handle.  Unlike the
1019e39c5baSBill Taylor 	 * other hardware resources no ownership transfer takes place as
1029e39c5baSBill Taylor 	 * these UDAV entries are always owned by hardware.
1039e39c5baSBill Taylor 	 */
1049e39c5baSBill Taylor 	status = tavor_rsrc_alloc(state, TAVOR_UDAV, 1, sleepflag, &udav);
1059e39c5baSBill Taylor 	if (status != DDI_SUCCESS) {
1069e39c5baSBill Taylor 		goto ahalloc_fail;
1079e39c5baSBill Taylor 	}
1089e39c5baSBill Taylor 
1099e39c5baSBill Taylor 	/*
1109e39c5baSBill Taylor 	 * Allocate the software structure for tracking the address handle
1119e39c5baSBill Taylor 	 * (i.e. the Tavor Address Handle struct).  If we fail here, we must
1129e39c5baSBill Taylor 	 * undo the previous resource allocation.
1139e39c5baSBill Taylor 	 */
1149e39c5baSBill Taylor 	status = tavor_rsrc_alloc(state, TAVOR_AHHDL, 1, sleepflag, &rsrc);
1159e39c5baSBill Taylor 	if (status != DDI_SUCCESS) {
1169e39c5baSBill Taylor 		goto ahalloc_fail1;
1179e39c5baSBill Taylor 	}
1189e39c5baSBill Taylor 	ah = (tavor_ahhdl_t)rsrc->tr_addr;
1199e39c5baSBill Taylor 	_NOTE(NOW_INVISIBLE_TO_OTHER_THREADS(*ah))
1209e39c5baSBill Taylor 
1219e39c5baSBill Taylor 	/* Increment the reference count on the protection domain (PD) */
1229e39c5baSBill Taylor 	tavor_pd_refcnt_inc(pd);
1239e39c5baSBill Taylor 
1249e39c5baSBill Taylor 	/*
1259e39c5baSBill Taylor 	 * Fill in the UDAV entry.  Note: We are only filling in a temporary
1269e39c5baSBill Taylor 	 * copy here, which we will later copy into the actual entry in
1279e39c5baSBill Taylor 	 * Tavor DDR memory.  This starts be zeroing out the temporary copy
1289e39c5baSBill Taylor 	 * and then calling tavor_set_addr_path() to fill in the common
1299e39c5baSBill Taylor 	 * portions that can be pulled from the "ibt_adds_vect_t" passed in
1309e39c5baSBill Taylor 	 */
1319e39c5baSBill Taylor 	bzero(&udav_entry, sizeof (tavor_hw_udav_t));
1329e39c5baSBill Taylor 	status = tavor_set_addr_path(state, attr_p,
1339e39c5baSBill Taylor 	    (tavor_hw_addr_path_t *)&udav_entry, TAVOR_ADDRPATH_UDAV, NULL);
1349e39c5baSBill Taylor 	if (status != DDI_SUCCESS) {
1359e39c5baSBill Taylor 		tavor_pd_refcnt_dec(pd);
1369e39c5baSBill Taylor 		tavor_rsrc_free(state, &rsrc);
1379e39c5baSBill Taylor 		tavor_rsrc_free(state, &udav);
1389e39c5baSBill Taylor 		goto ahalloc_fail;
1399e39c5baSBill Taylor 	}
1409e39c5baSBill Taylor 	udav_entry.pd	  = pd->pd_pdnum;
1419e39c5baSBill Taylor 	udav_entry.msg_sz = state->ts_cfg_profile->cp_max_mtu - 1;
1429e39c5baSBill Taylor 
1439e39c5baSBill Taylor 	/*
1449e39c5baSBill Taylor 	 * Register the memory for the UDAV.  The memory for the UDAV must
1459e39c5baSBill Taylor 	 * be registered in the Tavor TPT tables.  This gives us the LKey
1469e39c5baSBill Taylor 	 * that we will need when we later post a UD work request that
1479e39c5baSBill Taylor 	 * uses this address handle.
1489e39c5baSBill Taylor 	 * We might be able to pre-register all the memory for the UDAV XXX
1499e39c5baSBill Taylor 	 */
1509e39c5baSBill Taylor 	flag = (sleepflag == TAVOR_SLEEP) ? IBT_MR_SLEEP : IBT_MR_NOSLEEP;
1519e39c5baSBill Taylor 	mr_attr.mr_vaddr = (uint64_t)(uintptr_t)udav->tr_addr;
1529e39c5baSBill Taylor 	mr_attr.mr_len	 = udav->tr_len;
1539e39c5baSBill Taylor 	mr_attr.mr_as	 = NULL;
1549e39c5baSBill Taylor 	mr_attr.mr_flags = flag;
1559e39c5baSBill Taylor 	op.mro_bind_type = state->ts_cfg_profile->cp_iommu_bypass;
1569e39c5baSBill Taylor 	op.mro_bind_dmahdl = NULL;
1579e39c5baSBill Taylor 	op.mro_bind_override_addr = 0;
1589e39c5baSBill Taylor 	status = tavor_mr_register(state, pd, &mr_attr, &mr, &op);
1599e39c5baSBill Taylor 	if (status != DDI_SUCCESS) {
1609e39c5baSBill Taylor 		goto ahalloc_fail2;
1619e39c5baSBill Taylor 	}
1629e39c5baSBill Taylor 
1639e39c5baSBill Taylor 	/*
1649e39c5baSBill Taylor 	 * Fill in the UDAV entry.  Here we copy all the information from
1659e39c5baSBill Taylor 	 * the temporary UDAV into the DDR memory for the real UDAV entry.
1669e39c5baSBill Taylor 	 * Note that we copy everything but the first 64-bit word.  This
1679e39c5baSBill Taylor 	 * is where the PD number for the address handle resides.
1689e39c5baSBill Taylor 	 * By filling everything except the PD and then writing the PD in
1699e39c5baSBill Taylor 	 * a separate step below, we can ensure that the UDAV is not
1709e39c5baSBill Taylor 	 * accessed while there are partially written values in it (something
1719e39c5baSBill Taylor 	 * which really should not happen anyway).  This is guaranteed
1729e39c5baSBill Taylor 	 * because we take measures to ensure that the PD number is zero for
1739e39c5baSBill Taylor 	 * all unused UDAV (and because PD#0 is reserved for Tavor).
1749e39c5baSBill Taylor 	 */
1759e39c5baSBill Taylor 	size = sizeof (tavor_hw_udav_t) >> 3;
1769e39c5baSBill Taylor 	for (i = 1; i < size; i++) {
1779e39c5baSBill Taylor 		data = ((uint64_t *)&udav_entry)[i];
1789e39c5baSBill Taylor 		ddi_put64(udav->tr_acchdl, ((uint64_t *)udav->tr_addr + i),
1799e39c5baSBill Taylor 		    data);
1809e39c5baSBill Taylor 	}
1819e39c5baSBill Taylor 	data = ((uint64_t *)&udav_entry)[0];
1829e39c5baSBill Taylor 	ddi_put64(udav->tr_acchdl, (uint64_t *)udav->tr_addr, data);
1839e39c5baSBill Taylor 
1849e39c5baSBill Taylor 	/*
1859e39c5baSBill Taylor 	 * Fill in the rest of the Tavor Address Handle struct.  Having
1869e39c5baSBill Taylor 	 * successfully copied the UDAV into the hardware, we update the
1879e39c5baSBill Taylor 	 * following fields for use in further operations on the AH.
1889e39c5baSBill Taylor 	 *
1899e39c5baSBill Taylor 	 * NOTE: We are saving away a copy of the "av_dgid.gid_guid" field
1909e39c5baSBill Taylor 	 * here because we may need to return it later to the IBTF (as a
1919e39c5baSBill Taylor 	 * result of a subsequent query operation).  Unlike the other UDAV
1929e39c5baSBill Taylor 	 * parameters, the value of "av_dgid.gid_guid" is not always preserved
1939e39c5baSBill Taylor 	 * by being written to hardware.  The reason for this is described in
1949e39c5baSBill Taylor 	 * tavor_set_addr_path().
1959e39c5baSBill Taylor 	 */
1969e39c5baSBill Taylor 	ah->ah_udavrsrcp = udav;
1979e39c5baSBill Taylor 	ah->ah_rsrcp	 = rsrc;
1989e39c5baSBill Taylor 	ah->ah_pdhdl	 = pd;
1999e39c5baSBill Taylor 	ah->ah_mrhdl	 = mr;
2009e39c5baSBill Taylor 	ah->ah_save_guid = attr_p->av_dgid.gid_guid;
2019e39c5baSBill Taylor 	ah->ah_save_srate = attr_p->av_srate;
2029e39c5baSBill Taylor 	*ahhdl = ah;
2039e39c5baSBill Taylor 
2049e39c5baSBill Taylor 	/* Determine if later ddi_dma_sync will be necessary */
2059e39c5baSBill Taylor 	ah->ah_sync = TAVOR_UDAV_IS_SYNC_REQ(state);
2069e39c5baSBill Taylor 
2079e39c5baSBill Taylor 	/* Sync the UDAV for use by the hardware */
2089e39c5baSBill Taylor 	tavor_udav_sync(ah, udav->tr_addr, DDI_DMA_SYNC_FORDEV);
2099e39c5baSBill Taylor 
2109e39c5baSBill Taylor 	return (DDI_SUCCESS);
2119e39c5baSBill Taylor 
2129e39c5baSBill Taylor ahalloc_fail2:
2139e39c5baSBill Taylor 	tavor_pd_refcnt_dec(pd);
2149e39c5baSBill Taylor 	tavor_rsrc_free(state, &rsrc);
2159e39c5baSBill Taylor ahalloc_fail1:
2169e39c5baSBill Taylor 	tavor_rsrc_free(state, &udav);
2179e39c5baSBill Taylor ahalloc_fail:
2189e39c5baSBill Taylor 	return (status);
2199e39c5baSBill Taylor }
2209e39c5baSBill Taylor 
2219e39c5baSBill Taylor 
2229e39c5baSBill Taylor /*
2239e39c5baSBill Taylor  * tavor_ah_free()
2249e39c5baSBill Taylor  *    Context: Can be called only from user or kernel context.
2259e39c5baSBill Taylor  */
2269e39c5baSBill Taylor /* ARGSUSED */
2279e39c5baSBill Taylor int
tavor_ah_free(tavor_state_t * state,tavor_ahhdl_t * ahhdl,uint_t sleepflag)2289e39c5baSBill Taylor tavor_ah_free(tavor_state_t *state, tavor_ahhdl_t *ahhdl, uint_t sleepflag)
2299e39c5baSBill Taylor {
2309e39c5baSBill Taylor 	tavor_rsrc_t		*udav, *rsrc;
2319e39c5baSBill Taylor 	tavor_pdhdl_t		pd;
2329e39c5baSBill Taylor 	tavor_mrhdl_t		mr;
2339e39c5baSBill Taylor 	tavor_ahhdl_t		ah;
2349e39c5baSBill Taylor 	int			status;
2359e39c5baSBill Taylor 
2369e39c5baSBill Taylor 	/*
2379e39c5baSBill Taylor 	 * Pull all the necessary information from the Tavor Address Handle
2389e39c5baSBill Taylor 	 * struct.  This is necessary here because the resource for the
2399e39c5baSBill Taylor 	 * AH is going to be freed up as part of this operation.
2409e39c5baSBill Taylor 	 */
2419e39c5baSBill Taylor 	ah    = *ahhdl;
2429e39c5baSBill Taylor 	mutex_enter(&ah->ah_lock);
2439e39c5baSBill Taylor 	udav  = ah->ah_udavrsrcp;
2449e39c5baSBill Taylor 	rsrc  = ah->ah_rsrcp;
2459e39c5baSBill Taylor 	pd    = ah->ah_pdhdl;
2469e39c5baSBill Taylor 	mr    = ah->ah_mrhdl;
2479e39c5baSBill Taylor 	mutex_exit(&ah->ah_lock);
2489e39c5baSBill Taylor 	_NOTE(NOW_INVISIBLE_TO_OTHER_THREADS(*ah))
2499e39c5baSBill Taylor 
2509e39c5baSBill Taylor 	/*
2519e39c5baSBill Taylor 	 * Deregister the memory for the UDAV.  If this fails for any reason,
2529e39c5baSBill Taylor 	 * then it is an indication that something (either in HW or SW) has
2539e39c5baSBill Taylor 	 * gone seriously wrong.  So we print a warning message and return
2549e39c5baSBill Taylor 	 * failure.
2559e39c5baSBill Taylor 	 */
2569e39c5baSBill Taylor 	status = tavor_mr_deregister(state, &mr, TAVOR_MR_DEREG_ALL,
2579e39c5baSBill Taylor 	    sleepflag);
2589e39c5baSBill Taylor 	if (status != DDI_SUCCESS) {
2599e39c5baSBill Taylor 		return (ibc_get_ci_failure(0));
2609e39c5baSBill Taylor 	}
2619e39c5baSBill Taylor 
2629e39c5baSBill Taylor 	/*
2639e39c5baSBill Taylor 	 * Write zero to the first 64-bit word in the UDAV entry.  As
2649e39c5baSBill Taylor 	 * described above (in tavor_ah_alloc), the PD number is stored in
2659e39c5baSBill Taylor 	 * the first 64-bits of each UDAV and setting this to zero is
2669e39c5baSBill Taylor 	 * guaranteed to invalidate the entry.
2679e39c5baSBill Taylor 	 */
2689e39c5baSBill Taylor 	ddi_put64(udav->tr_acchdl, (uint64_t *)udav->tr_addr, 0);
2699e39c5baSBill Taylor 
2709e39c5baSBill Taylor 	/* Sync the UDAV for use by the hardware */
2719e39c5baSBill Taylor 	tavor_udav_sync(ah, udav->tr_addr, DDI_DMA_SYNC_FORDEV);
2729e39c5baSBill Taylor 
2739e39c5baSBill Taylor 	/* Decrement the reference count on the protection domain (PD) */
2749e39c5baSBill Taylor 	tavor_pd_refcnt_dec(pd);
2759e39c5baSBill Taylor 
2769e39c5baSBill Taylor 	/* Free the Tavor Address Handle structure */
2779e39c5baSBill Taylor 	tavor_rsrc_free(state, &rsrc);
2789e39c5baSBill Taylor 
2799e39c5baSBill Taylor 	/* Free up the UDAV entry resource */
2809e39c5baSBill Taylor 	tavor_rsrc_free(state, &udav);
2819e39c5baSBill Taylor 
2829e39c5baSBill Taylor 	/* Set the ahhdl pointer to NULL and return success */
2839e39c5baSBill Taylor 	*ahhdl = NULL;
2849e39c5baSBill Taylor 
2859e39c5baSBill Taylor 	return (DDI_SUCCESS);
2869e39c5baSBill Taylor }
2879e39c5baSBill Taylor 
2889e39c5baSBill Taylor 
2899e39c5baSBill Taylor /*
2909e39c5baSBill Taylor  * tavor_ah_query()
2919e39c5baSBill Taylor  *    Context: Can be called from interrupt or base context.
2929e39c5baSBill Taylor  */
2939e39c5baSBill Taylor /* ARGSUSED */
2949e39c5baSBill Taylor int
tavor_ah_query(tavor_state_t * state,tavor_ahhdl_t ah,tavor_pdhdl_t * pd,ibt_adds_vect_t * attr_p)2959e39c5baSBill Taylor tavor_ah_query(tavor_state_t *state, tavor_ahhdl_t ah, tavor_pdhdl_t *pd,
2969e39c5baSBill Taylor     ibt_adds_vect_t *attr_p)
2979e39c5baSBill Taylor {
2989e39c5baSBill Taylor 	tavor_hw_udav_t		udav_entry;
2999e39c5baSBill Taylor 	tavor_rsrc_t		*udav;
3009e39c5baSBill Taylor 	uint64_t		data;
3019e39c5baSBill Taylor 	uint32_t		size;
3029e39c5baSBill Taylor 	int			i;
3039e39c5baSBill Taylor 
3049e39c5baSBill Taylor 	mutex_enter(&ah->ah_lock);
3059e39c5baSBill Taylor 	_NOTE(NOW_INVISIBLE_TO_OTHER_THREADS(*attr_p))
3069e39c5baSBill Taylor 
3079e39c5baSBill Taylor 	/*
3089e39c5baSBill Taylor 	 * Pull all the necessary information from the Tavor Address Handle
3099e39c5baSBill Taylor 	 * structure
3109e39c5baSBill Taylor 	 */
3119e39c5baSBill Taylor 	udav	= ah->ah_udavrsrcp;
3129e39c5baSBill Taylor 	*pd	= ah->ah_pdhdl;
3139e39c5baSBill Taylor 
3149e39c5baSBill Taylor 	/*
3159e39c5baSBill Taylor 	 * Copy the UDAV entry into the temporary copy.  Here we copy all
3169e39c5baSBill Taylor 	 * the information from the UDAV entry in DDR memory into the
3179e39c5baSBill Taylor 	 * temporary UDAV.  Note:  We don't need to sync the UDAV for
3189e39c5baSBill Taylor 	 * reading by software because Tavor HW never modifies the entry.
3199e39c5baSBill Taylor 	 */
3209e39c5baSBill Taylor 	size = sizeof (tavor_hw_udav_t) >> 3;
3219e39c5baSBill Taylor 	for (i = 0; i < size; i++) {
3229e39c5baSBill Taylor 		data = ddi_get64(udav->tr_acchdl,
3239e39c5baSBill Taylor 		    ((uint64_t *)udav->tr_addr + i));
3249e39c5baSBill Taylor 		((uint64_t *)&udav_entry)[i] = data;
3259e39c5baSBill Taylor 	}
3269e39c5baSBill Taylor 
3279e39c5baSBill Taylor 	/*
3289e39c5baSBill Taylor 	 * Fill in "ibt_adds_vect_t".  We call tavor_get_addr_path() to fill
3299e39c5baSBill Taylor 	 * the common portions that can be pulled from the UDAV we pass in.
3309e39c5baSBill Taylor 	 *
3319e39c5baSBill Taylor 	 * NOTE: We will also fill the "av_dgid.gid_guid" field from the
3329e39c5baSBill Taylor 	 * "ah_save_guid" field we have previously saved away.  The reason
3339e39c5baSBill Taylor 	 * for this is described in tavor_ah_alloc() and tavor_ah_modify().
3349e39c5baSBill Taylor 	 */
3359e39c5baSBill Taylor 	tavor_get_addr_path(state, (tavor_hw_addr_path_t *)&udav_entry,
3369e39c5baSBill Taylor 	    attr_p, TAVOR_ADDRPATH_UDAV, NULL);
3379e39c5baSBill Taylor 
3389e39c5baSBill Taylor 	attr_p->av_dgid.gid_guid = ah->ah_save_guid;
3399e39c5baSBill Taylor 	attr_p->av_srate = ah->ah_save_srate;
3409e39c5baSBill Taylor 
3419e39c5baSBill Taylor 	mutex_exit(&ah->ah_lock);
3429e39c5baSBill Taylor 	return (DDI_SUCCESS);
3439e39c5baSBill Taylor }
3449e39c5baSBill Taylor 
3459e39c5baSBill Taylor 
3469e39c5baSBill Taylor /*
3479e39c5baSBill Taylor  * tavor_ah_modify()
3489e39c5baSBill Taylor  *    Context: Can be called from interrupt or base context.
3499e39c5baSBill Taylor  */
3509e39c5baSBill Taylor /* ARGSUSED */
3519e39c5baSBill Taylor int
tavor_ah_modify(tavor_state_t * state,tavor_ahhdl_t ah,ibt_adds_vect_t * attr_p)3529e39c5baSBill Taylor tavor_ah_modify(tavor_state_t *state, tavor_ahhdl_t ah,
3539e39c5baSBill Taylor     ibt_adds_vect_t *attr_p)
3549e39c5baSBill Taylor {
3559e39c5baSBill Taylor 	tavor_hw_udav_t		udav_entry;
3569e39c5baSBill Taylor 	tavor_rsrc_t		*udav;
3579e39c5baSBill Taylor 	uint64_t		data_new, data_old;
3589e39c5baSBill Taylor 	uint32_t		udav_pd, size, portnum_new;
3599e39c5baSBill Taylor 	int			i, status;
3609e39c5baSBill Taylor 
3619e39c5baSBill Taylor 	/* Validate that specified port number is legal */
3629e39c5baSBill Taylor 	if (!tavor_portnum_is_valid(state, attr_p->av_port_num)) {
3639e39c5baSBill Taylor 		return (IBT_HCA_PORT_INVALID);
3649e39c5baSBill Taylor 	}
3659e39c5baSBill Taylor 
3669e39c5baSBill Taylor 	mutex_enter(&ah->ah_lock);
3679e39c5baSBill Taylor 
3689e39c5baSBill Taylor 	/*
3699e39c5baSBill Taylor 	 * Pull all the necessary information from the Tavor Address Handle
3709e39c5baSBill Taylor 	 * structure
3719e39c5baSBill Taylor 	 */
3729e39c5baSBill Taylor 	udav = ah->ah_udavrsrcp;
3739e39c5baSBill Taylor 
3749e39c5baSBill Taylor 	/*
3759e39c5baSBill Taylor 	 * Fill in the UDAV entry.  Note: we are only filling in a temporary
3769e39c5baSBill Taylor 	 * copy here, which we will later copy into the actual entry in
3779e39c5baSBill Taylor 	 * Tavor DDR memory.  This starts be zeroing out the temporary copy
3789e39c5baSBill Taylor 	 * and then calling tavor_set_addr_path() to fill in the common
3799e39c5baSBill Taylor 	 * portions that can be pulled from the "ibt_adds_vect_t" passed in
3809e39c5baSBill Taylor 	 *
3819e39c5baSBill Taylor 	 * NOTE: We also need to save away a copy of the "av_dgid.gid_guid"
3829e39c5baSBill Taylor 	 * field here (just as we did during tavor_ah_alloc()) because we
3839e39c5baSBill Taylor 	 * may need to return it later to the IBTF (as a result of a
3849e39c5baSBill Taylor 	 * subsequent query operation).  As explained in tavor_ah_alloc(),
3859e39c5baSBill Taylor 	 * unlike the other UDAV parameters, the value of "av_dgid.gid_guid"
3869e39c5baSBill Taylor 	 * is not always preserved by being written to hardware.  The reason
3879e39c5baSBill Taylor 	 * for this is described in tavor_set_addr_path().
3889e39c5baSBill Taylor 	 */
3899e39c5baSBill Taylor 	bzero(&udav_entry, sizeof (tavor_hw_udav_t));
3909e39c5baSBill Taylor 	status = tavor_set_addr_path(state, attr_p,
3919e39c5baSBill Taylor 	    (tavor_hw_addr_path_t *)&udav_entry, TAVOR_ADDRPATH_UDAV, NULL);
3929e39c5baSBill Taylor 	if (status != DDI_SUCCESS) {
3939e39c5baSBill Taylor 		mutex_exit(&ah->ah_lock);
3949e39c5baSBill Taylor 		return (status);
3959e39c5baSBill Taylor 	}
3969e39c5baSBill Taylor 	ah->ah_save_guid = attr_p->av_dgid.gid_guid;
3979e39c5baSBill Taylor 	ah->ah_save_srate = attr_p->av_srate;
3989e39c5baSBill Taylor 
3999e39c5baSBill Taylor 	/*
4009e39c5baSBill Taylor 	 * Save away the current PD number for this UDAV.  Then temporarily
4019e39c5baSBill Taylor 	 * invalidate the entry (by setting the PD to zero).  Note:  Since
4029e39c5baSBill Taylor 	 * the first 32 bits of the UDAV actually contain the current port
4039e39c5baSBill Taylor 	 * number _and_ current PD number, we need to mask off some bits.
4049e39c5baSBill Taylor 	 */
4059e39c5baSBill Taylor 	udav_pd = ddi_get32(udav->tr_acchdl, (uint32_t *)udav->tr_addr);
4069e39c5baSBill Taylor 	udav_pd = udav_pd & 0xFFFFFF;
4079e39c5baSBill Taylor 	ddi_put32(udav->tr_acchdl, (uint32_t *)udav->tr_addr, 0);
4089e39c5baSBill Taylor 
4099e39c5baSBill Taylor 	/* Sync the UDAV for use by the hardware */
4109e39c5baSBill Taylor 	tavor_udav_sync(ah, udav->tr_addr, DDI_DMA_SYNC_FORDEV);
4119e39c5baSBill Taylor 
4129e39c5baSBill Taylor 	/*
4139e39c5baSBill Taylor 	 * Copy UDAV structure to the entry
4149e39c5baSBill Taylor 	 *    Note:  We copy in 64-bit chunks.  For the first two of these
4159e39c5baSBill Taylor 	 *    chunks it is necessary to read the current contents of the
4169e39c5baSBill Taylor 	 *    UDAV, mask off the modifiable portions (maintaining any
4179e39c5baSBill Taylor 	 *    of the "reserved" portions), and then mask on the new data.
4189e39c5baSBill Taylor 	 */
4199e39c5baSBill Taylor 	size = sizeof (tavor_hw_udav_t) >> 3;
4209e39c5baSBill Taylor 	for (i = 0; i < size; i++) {
4219e39c5baSBill Taylor 		data_new = ((uint64_t *)&udav_entry)[i];
4229e39c5baSBill Taylor 		data_old = ddi_get64(udav->tr_acchdl,
4239e39c5baSBill Taylor 		    ((uint64_t *)udav->tr_addr + i));
4249e39c5baSBill Taylor 
4259e39c5baSBill Taylor 		/*
4269e39c5baSBill Taylor 		 * Apply mask to change only the relevant values.  Note: We
4279e39c5baSBill Taylor 		 * extract the new portnum from the address handle here
4289e39c5baSBill Taylor 		 * because the "PD" and "portnum" fields are in the same
4299e39c5baSBill Taylor 		 * 32-bit word in the UDAV.  We will use the (new) port
4309e39c5baSBill Taylor 		 * number extracted here when we write the valid PD number
4319e39c5baSBill Taylor 		 * in the last step below.
4329e39c5baSBill Taylor 		 */
4339e39c5baSBill Taylor 		if (i == 0) {
4349e39c5baSBill Taylor 			data_old = data_old & TAVOR_UDAV_MODIFY_MASK0;
4359e39c5baSBill Taylor 			portnum_new = data_new >> 56;
4369e39c5baSBill Taylor 		} else if (i == 1) {
4379e39c5baSBill Taylor 			data_old = data_old & TAVOR_UDAV_MODIFY_MASK1;
4389e39c5baSBill Taylor 		} else {
4399e39c5baSBill Taylor 			data_old = 0;
4409e39c5baSBill Taylor 		}
4419e39c5baSBill Taylor 
4429e39c5baSBill Taylor 		/* Write the updated values to the UDAV (in DDR) */
4439e39c5baSBill Taylor 		data_new = data_old | data_new;
4449e39c5baSBill Taylor 		ddi_put64(udav->tr_acchdl, ((uint64_t *)udav->tr_addr + i),
4459e39c5baSBill Taylor 		    data_new);
4469e39c5baSBill Taylor 	}
4479e39c5baSBill Taylor 
4489e39c5baSBill Taylor 	/*
4499e39c5baSBill Taylor 	 * Sync the body of the UDAV for use by the hardware.  After we
4509e39c5baSBill Taylor 	 * have updated the PD number (to make the UDAV valid), we sync
4519e39c5baSBill Taylor 	 * again to push the entire entry out for hardware access.
4529e39c5baSBill Taylor 	 */
4539e39c5baSBill Taylor 	tavor_udav_sync(ah, udav->tr_addr, DDI_DMA_SYNC_FORDEV);
4549e39c5baSBill Taylor 
4559e39c5baSBill Taylor 	/*
4569e39c5baSBill Taylor 	 * Put the valid PD number back into UDAV entry.  Note: Because port
4579e39c5baSBill Taylor 	 * number and PD number are in the same word, we must mask the
4589e39c5baSBill Taylor 	 * new port number with the old PD number before writing it back
4599e39c5baSBill Taylor 	 * to the UDAV entry
4609e39c5baSBill Taylor 	 */
4619e39c5baSBill Taylor 	udav_pd = ((portnum_new << 24) | udav_pd);
4629e39c5baSBill Taylor 	ddi_put32(udav->tr_acchdl, (uint32_t *)udav->tr_addr, udav_pd);
4639e39c5baSBill Taylor 
4649e39c5baSBill Taylor 	/* Sync the rest of the UDAV for use by the hardware */
4659e39c5baSBill Taylor 	tavor_udav_sync(ah, udav->tr_addr, DDI_DMA_SYNC_FORDEV);
4669e39c5baSBill Taylor 
4679e39c5baSBill Taylor 	mutex_exit(&ah->ah_lock);
4689e39c5baSBill Taylor 	return (DDI_SUCCESS);
4699e39c5baSBill Taylor }
4709e39c5baSBill Taylor 
4719e39c5baSBill Taylor 
4729e39c5baSBill Taylor /*
4739e39c5baSBill Taylor  * tavor_udav_sync()
4749e39c5baSBill Taylor  *    Context: Can be called from interrupt or base context.
4759e39c5baSBill Taylor  */
4769e39c5baSBill Taylor /* ARGSUSED */
4779e39c5baSBill Taylor static void
tavor_udav_sync(tavor_ahhdl_t ah,tavor_hw_udav_t * udav,uint_t flag)4789e39c5baSBill Taylor tavor_udav_sync(tavor_ahhdl_t ah, tavor_hw_udav_t *udav, uint_t flag)
4799e39c5baSBill Taylor {
4809e39c5baSBill Taylor 	ddi_dma_handle_t	dmahdl;
4819e39c5baSBill Taylor 	off_t			offset;
4829e39c5baSBill Taylor 	int			status;
4839e39c5baSBill Taylor 
4849e39c5baSBill Taylor 	_NOTE(NOW_INVISIBLE_TO_OTHER_THREADS(*ah))
4859e39c5baSBill Taylor 
4869e39c5baSBill Taylor 	/* Determine if AH needs to be synced or not */
4879e39c5baSBill Taylor 	if (ah->ah_sync == 0) {
4889e39c5baSBill Taylor 		return;
4899e39c5baSBill Taylor 	}
4909e39c5baSBill Taylor 
4919e39c5baSBill Taylor 	/* Get the DMA handle from AH handle */
4929e39c5baSBill Taylor 	dmahdl = ah->ah_mrhdl->mr_bindinfo.bi_dmahdl;
4939e39c5baSBill Taylor 
4949e39c5baSBill Taylor 	/* Calculate offset into address handle */
4959e39c5baSBill Taylor 	offset = (off_t)0;
4969e39c5baSBill Taylor 	status = ddi_dma_sync(dmahdl, offset, sizeof (tavor_hw_udav_t), flag);
4979e39c5baSBill Taylor 	if (status != DDI_SUCCESS) {
4989e39c5baSBill Taylor 		return;
4999e39c5baSBill Taylor 	}
5009e39c5baSBill Taylor }
5019e39c5baSBill Taylor 
5029e39c5baSBill Taylor 
5039e39c5baSBill Taylor /*
5049e39c5baSBill Taylor  * tavor_mcg_attach()
5059e39c5baSBill Taylor  *    Context: Can be called only from user or kernel context.
5069e39c5baSBill Taylor  */
5079e39c5baSBill Taylor int
tavor_mcg_attach(tavor_state_t * state,tavor_qphdl_t qp,ib_gid_t gid,ib_lid_t lid)5089e39c5baSBill Taylor tavor_mcg_attach(tavor_state_t *state, tavor_qphdl_t qp, ib_gid_t gid,
5099e39c5baSBill Taylor     ib_lid_t lid)
5109e39c5baSBill Taylor {
5119e39c5baSBill Taylor 	tavor_rsrc_t		*rsrc;
5129e39c5baSBill Taylor 	tavor_hw_mcg_t		*mcg_entry;
5139e39c5baSBill Taylor 	tavor_hw_mcg_qp_list_t	*mcg_entry_qplist;
5149e39c5baSBill Taylor 	tavor_mcghdl_t		mcg, newmcg;
5159e39c5baSBill Taylor 	uint64_t		mgid_hash;
5169e39c5baSBill Taylor 	uint32_t		end_indx;
5179e39c5baSBill Taylor 	int			status;
5189e39c5baSBill Taylor 	uint_t			qp_found;
5199e39c5baSBill Taylor 
5209e39c5baSBill Taylor 	/*
5219e39c5baSBill Taylor 	 * It is only allowed to attach MCG to UD queue pairs.  Verify
5229e39c5baSBill Taylor 	 * that the intended QP is of the appropriate transport type
5239e39c5baSBill Taylor 	 */
5249e39c5baSBill Taylor 	if (qp->qp_serv_type != TAVOR_QP_UD) {
5259e39c5baSBill Taylor 		goto mcgattach_fail;
5269e39c5baSBill Taylor 	}
5279e39c5baSBill Taylor 
5289e39c5baSBill Taylor 	/*
5299e39c5baSBill Taylor 	 * Check for invalid Multicast DLID.  Specifically, all Multicast
5309e39c5baSBill Taylor 	 * LIDs should be within a well defined range.  If the specified LID
5319e39c5baSBill Taylor 	 * is outside of that range, then return an error.
5329e39c5baSBill Taylor 	 */
5339e39c5baSBill Taylor 	if (tavor_mlid_is_valid(lid) == 0) {
5349e39c5baSBill Taylor 		goto mcgattach_fail;
5359e39c5baSBill Taylor 	}
5369e39c5baSBill Taylor 	/*
5379e39c5baSBill Taylor 	 * Check for invalid Multicast GID.  All Multicast GIDs should have
5389e39c5baSBill Taylor 	 * a well-defined pattern of bits and flags that are allowable.  If
5399e39c5baSBill Taylor 	 * the specified GID does not meet the criteria, then return an error.
5409e39c5baSBill Taylor 	 */
5419e39c5baSBill Taylor 	if (tavor_mgid_is_valid(gid) == 0) {
5429e39c5baSBill Taylor 		goto mcgattach_fail;
5439e39c5baSBill Taylor 	}
5449e39c5baSBill Taylor 
5459e39c5baSBill Taylor 	/*
5469e39c5baSBill Taylor 	 * Compute the MGID hash value.  Since the MCG table is arranged as
5479e39c5baSBill Taylor 	 * a number of separate hash chains, this operation converts the
5489e39c5baSBill Taylor 	 * specified MGID into the starting index of an entry in the hash
5499e39c5baSBill Taylor 	 * table (i.e. the index for the start of the appropriate hash chain).
5509e39c5baSBill Taylor 	 * Subsequent operations below will walk the chain searching for the
5519e39c5baSBill Taylor 	 * right place to add this new QP.
5529e39c5baSBill Taylor 	 */
5539e39c5baSBill Taylor 	status = tavor_mgid_hash_cmd_post(state, gid.gid_prefix, gid.gid_guid,
5549e39c5baSBill Taylor 	    &mgid_hash, TAVOR_SLEEPFLAG_FOR_CONTEXT());
5559e39c5baSBill Taylor 	if (status != TAVOR_CMD_SUCCESS) {
5569e39c5baSBill Taylor 		cmn_err(CE_CONT, "Tavor: MGID_HASH command failed: %08x\n",
5579e39c5baSBill Taylor 		    status);
5589e39c5baSBill Taylor 		return (ibc_get_ci_failure(0));
5599e39c5baSBill Taylor 	}
5609e39c5baSBill Taylor 
5619e39c5baSBill Taylor 	/*
5629e39c5baSBill Taylor 	 * Grab the multicast group mutex.  Then grab the pre-allocated
5639e39c5baSBill Taylor 	 * temporary buffer used for holding and/or modifying MCG entries.
5649e39c5baSBill Taylor 	 * Zero out the temporary MCG entry before we begin.
5659e39c5baSBill Taylor 	 */
5669e39c5baSBill Taylor 	mutex_enter(&state->ts_mcglock);
5679e39c5baSBill Taylor 	mcg_entry = state->ts_mcgtmp;
5689e39c5baSBill Taylor 	mcg_entry_qplist = TAVOR_MCG_GET_QPLIST_PTR(mcg_entry);
5699e39c5baSBill Taylor 	bzero(mcg_entry, TAVOR_MCGMEM_SZ(state));
5709e39c5baSBill Taylor 
5719e39c5baSBill Taylor 	/*
5729e39c5baSBill Taylor 	 * Walk through the array of MCG entries starting at "mgid_hash".
5739e39c5baSBill Taylor 	 * Try to find the appropriate place for this new QP to be added.
5749e39c5baSBill Taylor 	 * This could happen when the first entry of the chain has MGID == 0
5759e39c5baSBill Taylor 	 * (which means that the hash chain is empty), or because we find
5769e39c5baSBill Taylor 	 * an entry with the same MGID (in which case we'll add the QP to
5779e39c5baSBill Taylor 	 * that MCG), or because we come to the end of the chain (in which
5789e39c5baSBill Taylor 	 * case this is the first QP being added to the multicast group that
5799e39c5baSBill Taylor 	 * corresponds to the MGID.  The tavor_mcg_walk_mgid_hash() routine
5809e39c5baSBill Taylor 	 * walks the list and returns an index into the MCG table.  The entry
5819e39c5baSBill Taylor 	 * at this index is then checked to determine which case we have
5829e39c5baSBill Taylor 	 * fallen into (see below).  Note:  We are using the "shadow" MCG
5839e39c5baSBill Taylor 	 * list (of tavor_mcg_t structs) for this lookup because the real
5849e39c5baSBill Taylor 	 * MCG entries are in hardware (and the lookup process would be much
5859e39c5baSBill Taylor 	 * more time consuming).
5869e39c5baSBill Taylor 	 */
5879e39c5baSBill Taylor 	end_indx = tavor_mcg_walk_mgid_hash(state, mgid_hash, gid, NULL);
5889e39c5baSBill Taylor 	mcg	 = &state->ts_mcghdl[end_indx];
5899e39c5baSBill Taylor 
5909e39c5baSBill Taylor 	/*
5919e39c5baSBill Taylor 	 * If MGID == 0, then the hash chain is empty.  Just fill in the
5929e39c5baSBill Taylor 	 * current entry.  Note:  No need to allocate an MCG table entry
5939e39c5baSBill Taylor 	 * as all the hash chain "heads" are already preallocated.
5949e39c5baSBill Taylor 	 */
5959e39c5baSBill Taylor 	if ((mcg->mcg_mgid_h == 0) && (mcg->mcg_mgid_l == 0)) {
5969e39c5baSBill Taylor 
5979e39c5baSBill Taylor 		/* Fill in the current entry in the "shadow" MCG list */
5989e39c5baSBill Taylor 		tavor_mcg_setup_new_hdr(mcg, mcg_entry, gid, NULL);
5999e39c5baSBill Taylor 
6009e39c5baSBill Taylor 		/*
6019e39c5baSBill Taylor 		 * Try to add the new QP number to the list.  This (and the
6029e39c5baSBill Taylor 		 * above) routine fills in a temporary MCG.  The "mcg_entry"
6039e39c5baSBill Taylor 		 * and "mcg_entry_qplist" pointers simply point to different
6049e39c5baSBill Taylor 		 * offsets within the same temporary copy of the MCG (for
6059e39c5baSBill Taylor 		 * convenience).  Note:  If this fails, we need to invalidate
6069e39c5baSBill Taylor 		 * the entries we've already put into the "shadow" list entry
6079e39c5baSBill Taylor 		 * above.
6089e39c5baSBill Taylor 		 */
6099e39c5baSBill Taylor 		status = tavor_mcg_qplist_add(state, mcg, mcg_entry_qplist, qp,
6109e39c5baSBill Taylor 		    &qp_found);
6119e39c5baSBill Taylor 		if (status != DDI_SUCCESS) {
6129e39c5baSBill Taylor 			bzero(mcg, sizeof (struct tavor_sw_mcg_list_s));
6139e39c5baSBill Taylor 			mutex_exit(&state->ts_mcglock);
6149e39c5baSBill Taylor 			goto mcgattach_fail;
6159e39c5baSBill Taylor 		}
6169e39c5baSBill Taylor 
6179e39c5baSBill Taylor 		/*
6189e39c5baSBill Taylor 		 * Once the temporary MCG has been filled in, write the entry
6199e39c5baSBill Taylor 		 * into the appropriate location in the Tavor MCG entry table.
6209e39c5baSBill Taylor 		 * If it's successful, then drop the lock and return success.
6219e39c5baSBill Taylor 		 * Note: In general, this operation shouldn't fail.  If it
6229e39c5baSBill Taylor 		 * does, then it is an indication that something (probably in
6239e39c5baSBill Taylor 		 * HW, but maybe in SW) has gone seriously wrong.  We still
6249e39c5baSBill Taylor 		 * want to zero out the entries that we've filled in above
6259e39c5baSBill Taylor 		 * (in the tavor_mcg_setup_new_hdr() routine).
6269e39c5baSBill Taylor 		 */
6279e39c5baSBill Taylor 		status = tavor_write_mgm_cmd_post(state, mcg_entry, end_indx,
6289e39c5baSBill Taylor 		    TAVOR_CMD_NOSLEEP_SPIN);
6299e39c5baSBill Taylor 		if (status != TAVOR_CMD_SUCCESS) {
6309e39c5baSBill Taylor 			bzero(mcg, sizeof (struct tavor_sw_mcg_list_s));
6319e39c5baSBill Taylor 			mutex_exit(&state->ts_mcglock);
6329e39c5baSBill Taylor 			TAVOR_WARNING(state, "failed to write MCG entry");
6339e39c5baSBill Taylor 			cmn_err(CE_CONT, "Tavor: WRITE_MGM command failed: "
6349e39c5baSBill Taylor 			    "%08x\n", status);
6359e39c5baSBill Taylor 			return (ibc_get_ci_failure(0));
6369e39c5baSBill Taylor 		}
6379e39c5baSBill Taylor 
6389e39c5baSBill Taylor 		/*
6399e39c5baSBill Taylor 		 * Now that we know all the Tavor firmware accesses have been
6409e39c5baSBill Taylor 		 * successful, we update the "shadow" MCG entry by incrementing
6419e39c5baSBill Taylor 		 * the "number of attached QPs" count.
6429e39c5baSBill Taylor 		 *
6439e39c5baSBill Taylor 		 * We increment only if the QP is not already part of the
6449e39c5baSBill Taylor 		 * MCG by checking the 'qp_found' flag returned from the
6459e39c5baSBill Taylor 		 * qplist_add above.
6469e39c5baSBill Taylor 		 */
6479e39c5baSBill Taylor 		if (!qp_found) {
6489e39c5baSBill Taylor 			mcg->mcg_num_qps++;
6499e39c5baSBill Taylor 
6509e39c5baSBill Taylor 			/*
6519e39c5baSBill Taylor 			 * Increment the refcnt for this QP.  Because the QP
6529e39c5baSBill Taylor 			 * was added to this MCG, the refcnt must be
6539e39c5baSBill Taylor 			 * incremented.
6549e39c5baSBill Taylor 			 */
6559e39c5baSBill Taylor 			tavor_qp_mcg_refcnt_inc(qp);
6569e39c5baSBill Taylor 		}
6579e39c5baSBill Taylor 
6589e39c5baSBill Taylor 		/*
6599e39c5baSBill Taylor 		 * We drop the lock and return success.
6609e39c5baSBill Taylor 		 */
6619e39c5baSBill Taylor 		mutex_exit(&state->ts_mcglock);
6629e39c5baSBill Taylor 		return (DDI_SUCCESS);
6639e39c5baSBill Taylor 	}
6649e39c5baSBill Taylor 
6659e39c5baSBill Taylor 	/*
6669e39c5baSBill Taylor 	 * If the specified MGID matches the MGID in the current entry, then
6679e39c5baSBill Taylor 	 * we need to try to add the QP to the current MCG entry.  In this
6689e39c5baSBill Taylor 	 * case, it means that we need to read the existing MCG entry (into
6699e39c5baSBill Taylor 	 * the temporary MCG), add the new QP number to the temporary entry
6709e39c5baSBill Taylor 	 * (using the same method we used above), and write the entry back
6719e39c5baSBill Taylor 	 * to the hardware (same as above).
6729e39c5baSBill Taylor 	 */
6739e39c5baSBill Taylor 	if ((mcg->mcg_mgid_h == gid.gid_prefix) &&
6749e39c5baSBill Taylor 	    (mcg->mcg_mgid_l == gid.gid_guid)) {
6759e39c5baSBill Taylor 
6769e39c5baSBill Taylor 		/*
6779e39c5baSBill Taylor 		 * Read the current MCG entry into the temporary MCG.  Note:
6789e39c5baSBill Taylor 		 * In general, this operation shouldn't fail.  If it does,
6799e39c5baSBill Taylor 		 * then it is an indication that something (probably in HW,
6809e39c5baSBill Taylor 		 * but maybe in SW) has gone seriously wrong.
6819e39c5baSBill Taylor 		 */
6829e39c5baSBill Taylor 		status = tavor_read_mgm_cmd_post(state, mcg_entry, end_indx,
6839e39c5baSBill Taylor 		    TAVOR_CMD_NOSLEEP_SPIN);
6849e39c5baSBill Taylor 		if (status != TAVOR_CMD_SUCCESS) {
6859e39c5baSBill Taylor 			mutex_exit(&state->ts_mcglock);
6869e39c5baSBill Taylor 			TAVOR_WARNING(state, "failed to read MCG entry");
6879e39c5baSBill Taylor 			cmn_err(CE_CONT, "Tavor: READ_MGM command failed: "
6889e39c5baSBill Taylor 			    "%08x\n", status);
6899e39c5baSBill Taylor 			return (ibc_get_ci_failure(0));
6909e39c5baSBill Taylor 		}
6919e39c5baSBill Taylor 
6929e39c5baSBill Taylor 		/*
6939e39c5baSBill Taylor 		 * Try to add the new QP number to the list.  This routine
6949e39c5baSBill Taylor 		 * fills in the necessary pieces of the temporary MCG.  The
6959e39c5baSBill Taylor 		 * "mcg_entry_qplist" pointer is used to point to the portion
6969e39c5baSBill Taylor 		 * of the temporary MCG that holds the QP numbers.
6979e39c5baSBill Taylor 		 *
6989e39c5baSBill Taylor 		 * Note: tavor_mcg_qplist_add() returns SUCCESS if it
6999e39c5baSBill Taylor 		 * already found the QP in the list.  In this case, the QP is
7009e39c5baSBill Taylor 		 * not added on to the list again.  Check the flag 'qp_found'
7019e39c5baSBill Taylor 		 * if this value is needed to be known.
7029e39c5baSBill Taylor 		 *
7039e39c5baSBill Taylor 		 */
7049e39c5baSBill Taylor 		status = tavor_mcg_qplist_add(state, mcg, mcg_entry_qplist, qp,
7059e39c5baSBill Taylor 		    &qp_found);
7069e39c5baSBill Taylor 		if (status != DDI_SUCCESS) {
7079e39c5baSBill Taylor 			mutex_exit(&state->ts_mcglock);
7089e39c5baSBill Taylor 			/* Set "status" and "errormsg" and goto failure */
7099e39c5baSBill Taylor 			goto mcgattach_fail;
7109e39c5baSBill Taylor 		}
7119e39c5baSBill Taylor 
7129e39c5baSBill Taylor 		/*
7139e39c5baSBill Taylor 		 * Once the temporary MCG has been updated, write the entry
7149e39c5baSBill Taylor 		 * into the appropriate location in the Tavor MCG entry table.
7159e39c5baSBill Taylor 		 * If it's successful, then drop the lock and return success.
7169e39c5baSBill Taylor 		 * Note: In general, this operation shouldn't fail.  If it
7179e39c5baSBill Taylor 		 * does, then it is an indication that something (probably in
7189e39c5baSBill Taylor 		 * HW, but maybe in SW) has gone seriously wrong.
7199e39c5baSBill Taylor 		 */
7209e39c5baSBill Taylor 		status = tavor_write_mgm_cmd_post(state, mcg_entry, end_indx,
7219e39c5baSBill Taylor 		    TAVOR_CMD_NOSLEEP_SPIN);
7229e39c5baSBill Taylor 		if (status != TAVOR_CMD_SUCCESS) {
7239e39c5baSBill Taylor 			mutex_exit(&state->ts_mcglock);
7249e39c5baSBill Taylor 			TAVOR_WARNING(state, "failed to write MCG entry");
7259e39c5baSBill Taylor 			cmn_err(CE_CONT, "Tavor: WRITE_MGM command failed: "
7269e39c5baSBill Taylor 			    "%08x\n", status);
7279e39c5baSBill Taylor 			return (ibc_get_ci_failure(0));
7289e39c5baSBill Taylor 		}
7299e39c5baSBill Taylor 
7309e39c5baSBill Taylor 		/*
7319e39c5baSBill Taylor 		 * Now that we know all the Tavor firmware accesses have been
7329e39c5baSBill Taylor 		 * successful, we update the current "shadow" MCG entry by
7339e39c5baSBill Taylor 		 * incrementing the "number of attached QPs" count.
7349e39c5baSBill Taylor 		 *
7359e39c5baSBill Taylor 		 * We increment only if the QP is not already part of the
7369e39c5baSBill Taylor 		 * MCG by checking the 'qp_found' flag returned from the
7379e39c5baSBill Taylor 		 * qplist_add above.
7389e39c5baSBill Taylor 		 */
7399e39c5baSBill Taylor 		if (!qp_found) {
7409e39c5baSBill Taylor 			mcg->mcg_num_qps++;
7419e39c5baSBill Taylor 
7429e39c5baSBill Taylor 			/*
7439e39c5baSBill Taylor 			 * Increment the refcnt for this QP.  Because the QP
7449e39c5baSBill Taylor 			 * was added to this MCG, the refcnt must be
7459e39c5baSBill Taylor 			 * incremented.
7469e39c5baSBill Taylor 			 */
7479e39c5baSBill Taylor 			tavor_qp_mcg_refcnt_inc(qp);
7489e39c5baSBill Taylor 		}
7499e39c5baSBill Taylor 
7509e39c5baSBill Taylor 		/*
7519e39c5baSBill Taylor 		 * We drop the lock and return success.
7529e39c5baSBill Taylor 		 */
7539e39c5baSBill Taylor 		mutex_exit(&state->ts_mcglock);
7549e39c5baSBill Taylor 		return (DDI_SUCCESS);
7559e39c5baSBill Taylor 	}
7569e39c5baSBill Taylor 
7579e39c5baSBill Taylor 	/*
7589e39c5baSBill Taylor 	 * If we've reached here, then we're at the end of the hash chain.
7599e39c5baSBill Taylor 	 * We need to allocate a new MCG entry, fill it in, write it to Tavor,
7609e39c5baSBill Taylor 	 * and update the previous entry to link the new one to the end of the
7619e39c5baSBill Taylor 	 * chain.
7629e39c5baSBill Taylor 	 */
7639e39c5baSBill Taylor 
7649e39c5baSBill Taylor 	/*
7659e39c5baSBill Taylor 	 * Allocate an MCG table entry.  This will be filled in with all
7669e39c5baSBill Taylor 	 * the necessary parameters to define the multicast group.  Then it
7679e39c5baSBill Taylor 	 * will be written to the hardware in the next-to-last step below.
7689e39c5baSBill Taylor 	 */
7699e39c5baSBill Taylor 	status = tavor_rsrc_alloc(state, TAVOR_MCG, 1, TAVOR_NOSLEEP, &rsrc);
7709e39c5baSBill Taylor 	if (status != DDI_SUCCESS) {
7719e39c5baSBill Taylor 		mutex_exit(&state->ts_mcglock);
7729e39c5baSBill Taylor 		goto mcgattach_fail;
7739e39c5baSBill Taylor 	}
7749e39c5baSBill Taylor 
7759e39c5baSBill Taylor 	/*
7769e39c5baSBill Taylor 	 * Fill in the new entry in the "shadow" MCG list.  Note:  Just as
7779e39c5baSBill Taylor 	 * it does above, tavor_mcg_setup_new_hdr() also fills in a portion
7789e39c5baSBill Taylor 	 * of the temporary MCG entry (the rest of which will be filled in by
7799e39c5baSBill Taylor 	 * tavor_mcg_qplist_add() below)
7809e39c5baSBill Taylor 	 */
7819e39c5baSBill Taylor 	newmcg = &state->ts_mcghdl[rsrc->tr_indx];
7829e39c5baSBill Taylor 	tavor_mcg_setup_new_hdr(newmcg, mcg_entry, gid, rsrc);
7839e39c5baSBill Taylor 
7849e39c5baSBill Taylor 	/*
7859e39c5baSBill Taylor 	 * Try to add the new QP number to the list.  This routine fills in
7869e39c5baSBill Taylor 	 * the final necessary pieces of the temporary MCG.  The
7879e39c5baSBill Taylor 	 * "mcg_entry_qplist" pointer is used to point to the portion of the
7889e39c5baSBill Taylor 	 * temporary MCG that holds the QP numbers.  If we fail here, we
7899e39c5baSBill Taylor 	 * must undo the previous resource allocation.
7909e39c5baSBill Taylor 	 *
7919e39c5baSBill Taylor 	 * Note: tavor_mcg_qplist_add() can we return SUCCESS if it already
7929e39c5baSBill Taylor 	 * found the QP in the list.  In this case, the QP is not added on to
7939e39c5baSBill Taylor 	 * the list again.  Check the flag 'qp_found' if this value is needed
7949e39c5baSBill Taylor 	 * to be known.
7959e39c5baSBill Taylor 	 */
7969e39c5baSBill Taylor 	status = tavor_mcg_qplist_add(state, newmcg, mcg_entry_qplist, qp,
7979e39c5baSBill Taylor 	    &qp_found);
7989e39c5baSBill Taylor 	if (status != DDI_SUCCESS) {
7999e39c5baSBill Taylor 		bzero(newmcg, sizeof (struct tavor_sw_mcg_list_s));
8009e39c5baSBill Taylor 		tavor_rsrc_free(state, &rsrc);
8019e39c5baSBill Taylor 		mutex_exit(&state->ts_mcglock);
8029e39c5baSBill Taylor 		goto mcgattach_fail;
8039e39c5baSBill Taylor 	}
8049e39c5baSBill Taylor 
8059e39c5baSBill Taylor 	/*
8069e39c5baSBill Taylor 	 * Once the temporary MCG has been updated, write the entry into the
8079e39c5baSBill Taylor 	 * appropriate location in the Tavor MCG entry table.  If this is
8089e39c5baSBill Taylor 	 * successful, then we need to chain the previous entry to this one.
8099e39c5baSBill Taylor 	 * Note: In general, this operation shouldn't fail.  If it does, then
8109e39c5baSBill Taylor 	 * it is an indication that something (probably in HW, but maybe in
8119e39c5baSBill Taylor 	 * SW) has gone seriously wrong.
8129e39c5baSBill Taylor 	 */
8139e39c5baSBill Taylor 	status = tavor_write_mgm_cmd_post(state, mcg_entry, rsrc->tr_indx,
8149e39c5baSBill Taylor 	    TAVOR_CMD_NOSLEEP_SPIN);
8159e39c5baSBill Taylor 	if (status != TAVOR_CMD_SUCCESS) {
8169e39c5baSBill Taylor 		bzero(newmcg, sizeof (struct tavor_sw_mcg_list_s));
8179e39c5baSBill Taylor 		tavor_rsrc_free(state, &rsrc);
8189e39c5baSBill Taylor 		mutex_exit(&state->ts_mcglock);
8199e39c5baSBill Taylor 		TAVOR_WARNING(state, "failed to write MCG entry");
8209e39c5baSBill Taylor 		cmn_err(CE_CONT, "Tavor: WRITE_MGM command failed: %08x\n",
8219e39c5baSBill Taylor 		    status);
8229e39c5baSBill Taylor 		return (ibc_get_ci_failure(0));
8239e39c5baSBill Taylor 	}
8249e39c5baSBill Taylor 
8259e39c5baSBill Taylor 	/*
8269e39c5baSBill Taylor 	 * Now read the current MCG entry (the one previously at the end of
8279e39c5baSBill Taylor 	 * hash chain) into the temporary MCG.  We are going to update its
8289e39c5baSBill Taylor 	 * "next_gid_indx" now and write the entry back to the MCG table.
8299e39c5baSBill Taylor 	 * Note:  In general, this operation shouldn't fail.  If it does, then
8309e39c5baSBill Taylor 	 * it is an indication that something (probably in HW, but maybe in SW)
8319e39c5baSBill Taylor 	 * has gone seriously wrong.  We will free up the MCG entry resource,
8329e39c5baSBill Taylor 	 * but we will not undo the previously written MCG entry in the HW.
8339e39c5baSBill Taylor 	 * This is OK, though, because the MCG entry is not currently attached
8349e39c5baSBill Taylor 	 * to any hash chain.
8359e39c5baSBill Taylor 	 */
8369e39c5baSBill Taylor 	status = tavor_read_mgm_cmd_post(state, mcg_entry, end_indx,
8379e39c5baSBill Taylor 	    TAVOR_CMD_NOSLEEP_SPIN);
8389e39c5baSBill Taylor 	if (status != TAVOR_CMD_SUCCESS) {
8399e39c5baSBill Taylor 		bzero(newmcg, sizeof (struct tavor_sw_mcg_list_s));
8409e39c5baSBill Taylor 		tavor_rsrc_free(state, &rsrc);
8419e39c5baSBill Taylor 		mutex_exit(&state->ts_mcglock);
8429e39c5baSBill Taylor 		TAVOR_WARNING(state, "failed to read MCG entry");
8439e39c5baSBill Taylor 		cmn_err(CE_CONT, "Tavor: READ_MGM command failed: %08x\n",
8449e39c5baSBill Taylor 		    status);
8459e39c5baSBill Taylor 		return (ibc_get_ci_failure(0));
8469e39c5baSBill Taylor 	}
8479e39c5baSBill Taylor 
8489e39c5baSBill Taylor 	/*
8499e39c5baSBill Taylor 	 * Finally, we update the "next_gid_indx" field in the temporary MCG
8509e39c5baSBill Taylor 	 * and attempt to write the entry back into the Tavor MCG table.  If
8519e39c5baSBill Taylor 	 * this succeeds, then we update the "shadow" list to reflect the
8529e39c5baSBill Taylor 	 * change, drop the lock, and return success.  Note:  In general, this
8539e39c5baSBill Taylor 	 * operation shouldn't fail.  If it does, then it is an indication
8549e39c5baSBill Taylor 	 * that something (probably in HW, but maybe in SW) has gone seriously
8559e39c5baSBill Taylor 	 * wrong.  Just as we do above, we will free up the MCG entry resource,
8569e39c5baSBill Taylor 	 * but we will not try to undo the previously written MCG entry.  This
8579e39c5baSBill Taylor 	 * is OK, though, because (since we failed here to update the end of
8589e39c5baSBill Taylor 	 * the chain) that other entry is not currently attached to any chain.
8599e39c5baSBill Taylor 	 */
8609e39c5baSBill Taylor 	mcg_entry->next_gid_indx = rsrc->tr_indx;
8619e39c5baSBill Taylor 	status = tavor_write_mgm_cmd_post(state, mcg_entry, end_indx,
8629e39c5baSBill Taylor 	    TAVOR_CMD_NOSLEEP_SPIN);
8639e39c5baSBill Taylor 	if (status != TAVOR_CMD_SUCCESS) {
8649e39c5baSBill Taylor 		bzero(newmcg, sizeof (struct tavor_sw_mcg_list_s));
8659e39c5baSBill Taylor 		tavor_rsrc_free(state, &rsrc);
8669e39c5baSBill Taylor 		mutex_exit(&state->ts_mcglock);
8679e39c5baSBill Taylor 		TAVOR_WARNING(state, "failed to write MCG entry");
8689e39c5baSBill Taylor 		cmn_err(CE_CONT, "Tavor: WRITE_MGM command failed: %08x\n",
8699e39c5baSBill Taylor 		    status);
8709e39c5baSBill Taylor 		return (ibc_get_ci_failure(0));
8719e39c5baSBill Taylor 	}
8729e39c5baSBill Taylor 	mcg = &state->ts_mcghdl[end_indx];
8739e39c5baSBill Taylor 	mcg->mcg_next_indx = rsrc->tr_indx;
8749e39c5baSBill Taylor 
8759e39c5baSBill Taylor 	/*
8769e39c5baSBill Taylor 	 * Now that we know all the Tavor firmware accesses have been
8779e39c5baSBill Taylor 	 * successful, we update the new "shadow" MCG entry by incrementing
8789e39c5baSBill Taylor 	 * the "number of attached QPs" count.  Then we drop the lock and
8799e39c5baSBill Taylor 	 * return success.
8809e39c5baSBill Taylor 	 */
8819e39c5baSBill Taylor 	newmcg->mcg_num_qps++;
8829e39c5baSBill Taylor 
8839e39c5baSBill Taylor 	/*
8849e39c5baSBill Taylor 	 * Increment the refcnt for this QP.  Because the QP
8859e39c5baSBill Taylor 	 * was added to this MCG, the refcnt must be
8869e39c5baSBill Taylor 	 * incremented.
8879e39c5baSBill Taylor 	 */
8889e39c5baSBill Taylor 	tavor_qp_mcg_refcnt_inc(qp);
8899e39c5baSBill Taylor 
8909e39c5baSBill Taylor 	mutex_exit(&state->ts_mcglock);
8919e39c5baSBill Taylor 	return (DDI_SUCCESS);
8929e39c5baSBill Taylor 
8939e39c5baSBill Taylor mcgattach_fail:
8949e39c5baSBill Taylor 	return (status);
8959e39c5baSBill Taylor }
8969e39c5baSBill Taylor 
8979e39c5baSBill Taylor 
8989e39c5baSBill Taylor /*
8999e39c5baSBill Taylor  * tavor_mcg_detach()
9009e39c5baSBill Taylor  *    Context: Can be called only from user or kernel context.
9019e39c5baSBill Taylor  */
9029e39c5baSBill Taylor int
tavor_mcg_detach(tavor_state_t * state,tavor_qphdl_t qp,ib_gid_t gid,ib_lid_t lid)9039e39c5baSBill Taylor tavor_mcg_detach(tavor_state_t *state, tavor_qphdl_t qp, ib_gid_t gid,
9049e39c5baSBill Taylor     ib_lid_t lid)
9059e39c5baSBill Taylor {
9069e39c5baSBill Taylor 	tavor_hw_mcg_t		*mcg_entry;
9079e39c5baSBill Taylor 	tavor_hw_mcg_qp_list_t	*mcg_entry_qplist;
9089e39c5baSBill Taylor 	tavor_mcghdl_t		mcg;
9099e39c5baSBill Taylor 	uint64_t		mgid_hash;
9109e39c5baSBill Taylor 	uint32_t		end_indx, prev_indx;
9119e39c5baSBill Taylor 	int			status;
9129e39c5baSBill Taylor 
9139e39c5baSBill Taylor 	/*
9149e39c5baSBill Taylor 	 * Check for invalid Multicast DLID.  Specifically, all Multicast
9159e39c5baSBill Taylor 	 * LIDs should be within a well defined range.  If the specified LID
9169e39c5baSBill Taylor 	 * is outside of that range, then return an error.
9179e39c5baSBill Taylor 	 */
9189e39c5baSBill Taylor 	if (tavor_mlid_is_valid(lid) == 0) {
9199e39c5baSBill Taylor 		return (IBT_MC_MLID_INVALID);
9209e39c5baSBill Taylor 	}
9219e39c5baSBill Taylor 
9229e39c5baSBill Taylor 	/*
9239e39c5baSBill Taylor 	 * Compute the MGID hash value.  As described above, the MCG table is
9249e39c5baSBill Taylor 	 * arranged as a number of separate hash chains.  This operation
9259e39c5baSBill Taylor 	 * converts the specified MGID into the starting index of an entry in
9269e39c5baSBill Taylor 	 * the hash table (i.e. the index for the start of the appropriate
9279e39c5baSBill Taylor 	 * hash chain).  Subsequent operations below will walk the chain
9289e39c5baSBill Taylor 	 * searching for a matching entry from which to attempt to remove
9299e39c5baSBill Taylor 	 * the specified QP.
9309e39c5baSBill Taylor 	 */
9319e39c5baSBill Taylor 	status = tavor_mgid_hash_cmd_post(state, gid.gid_prefix, gid.gid_guid,
9329e39c5baSBill Taylor 	    &mgid_hash, TAVOR_SLEEPFLAG_FOR_CONTEXT());
9339e39c5baSBill Taylor 	if (status != TAVOR_CMD_SUCCESS) {
9349e39c5baSBill Taylor 		cmn_err(CE_CONT, "Tavor: MGID_HASH command failed: %08x\n",
9359e39c5baSBill Taylor 		    status);
9369e39c5baSBill Taylor 		return (ibc_get_ci_failure(0));
9379e39c5baSBill Taylor 	}
9389e39c5baSBill Taylor 
9399e39c5baSBill Taylor 	/*
9409e39c5baSBill Taylor 	 * Grab the multicast group mutex.  Then grab the pre-allocated
9419e39c5baSBill Taylor 	 * temporary buffer used for holding and/or modifying MCG entries.
9429e39c5baSBill Taylor 	 */
9439e39c5baSBill Taylor 	mutex_enter(&state->ts_mcglock);
9449e39c5baSBill Taylor 	mcg_entry = state->ts_mcgtmp;
9459e39c5baSBill Taylor 	mcg_entry_qplist = TAVOR_MCG_GET_QPLIST_PTR(mcg_entry);
9469e39c5baSBill Taylor 
9479e39c5baSBill Taylor 	/*
9489e39c5baSBill Taylor 	 * Walk through the array of MCG entries starting at "mgid_hash".
9499e39c5baSBill Taylor 	 * Try to find an MCG entry with a matching MGID.  The
9509e39c5baSBill Taylor 	 * tavor_mcg_walk_mgid_hash() routine walks the list and returns an
9519e39c5baSBill Taylor 	 * index into the MCG table.  The entry at this index is checked to
9529e39c5baSBill Taylor 	 * determine whether it is a match or not.  If it is a match, then
9539e39c5baSBill Taylor 	 * we continue on to attempt to remove the QP from the MCG.  If it
9549e39c5baSBill Taylor 	 * is not a match (or not a valid MCG entry), then we return an error.
9559e39c5baSBill Taylor 	 */
9569e39c5baSBill Taylor 	end_indx = tavor_mcg_walk_mgid_hash(state, mgid_hash, gid, &prev_indx);
9579e39c5baSBill Taylor 	mcg	 = &state->ts_mcghdl[end_indx];
9589e39c5baSBill Taylor 
9599e39c5baSBill Taylor 	/*
9609e39c5baSBill Taylor 	 * If MGID == 0 (the hash chain is empty) or if the specified MGID
9619e39c5baSBill Taylor 	 * does not match the MGID in the current entry, then return
9629e39c5baSBill Taylor 	 * IBT_MC_MGID_INVALID (to indicate that the specified MGID is not
9639e39c5baSBill Taylor 	 * valid).
9649e39c5baSBill Taylor 	 */
9659e39c5baSBill Taylor 	if (((mcg->mcg_mgid_h == 0) && (mcg->mcg_mgid_l == 0)) ||
9669e39c5baSBill Taylor 	    ((mcg->mcg_mgid_h != gid.gid_prefix) ||
9679e39c5baSBill Taylor 	    (mcg->mcg_mgid_l != gid.gid_guid))) {
9689e39c5baSBill Taylor 		mutex_exit(&state->ts_mcglock);
9699e39c5baSBill Taylor 		return (IBT_MC_MGID_INVALID);
9709e39c5baSBill Taylor 	}
9719e39c5baSBill Taylor 
9729e39c5baSBill Taylor 	/*
9739e39c5baSBill Taylor 	 * Read the current MCG entry into the temporary MCG.  Note: In
9749e39c5baSBill Taylor 	 * general, this operation shouldn't fail.  If it does, then it is
9759e39c5baSBill Taylor 	 * an indication that something (probably in HW, but maybe in SW)
9769e39c5baSBill Taylor 	 * has gone seriously wrong.
9779e39c5baSBill Taylor 	 */
9789e39c5baSBill Taylor 	status = tavor_read_mgm_cmd_post(state, mcg_entry, end_indx,
9799e39c5baSBill Taylor 	    TAVOR_CMD_NOSLEEP_SPIN);
9809e39c5baSBill Taylor 	if (status != TAVOR_CMD_SUCCESS) {
9819e39c5baSBill Taylor 		mutex_exit(&state->ts_mcglock);
9829e39c5baSBill Taylor 		TAVOR_WARNING(state, "failed to read MCG entry");
9839e39c5baSBill Taylor 		cmn_err(CE_CONT, "Tavor: READ_MGM command failed: %08x\n",
9849e39c5baSBill Taylor 		    status);
9859e39c5baSBill Taylor 		return (ibc_get_ci_failure(0));
9869e39c5baSBill Taylor 	}
9879e39c5baSBill Taylor 
9889e39c5baSBill Taylor 	/*
9899e39c5baSBill Taylor 	 * Search the QP number list for a match.  If a match is found, then
9909e39c5baSBill Taylor 	 * remove the entry from the QP list.  Otherwise, if no match is found,
9919e39c5baSBill Taylor 	 * return an error.
9929e39c5baSBill Taylor 	 */
9939e39c5baSBill Taylor 	status = tavor_mcg_qplist_remove(mcg, mcg_entry_qplist, qp);
9949e39c5baSBill Taylor 	if (status != DDI_SUCCESS) {
9959e39c5baSBill Taylor 		mutex_exit(&state->ts_mcglock);
9969e39c5baSBill Taylor 		return (status);
9979e39c5baSBill Taylor 	}
9989e39c5baSBill Taylor 
9999e39c5baSBill Taylor 	/*
10009e39c5baSBill Taylor 	 * Decrement the MCG count for this QP.  When the 'qp_mcg'
10019e39c5baSBill Taylor 	 * field becomes 0, then this QP is no longer a member of any
10029e39c5baSBill Taylor 	 * MCG.
10039e39c5baSBill Taylor 	 */
10049e39c5baSBill Taylor 	tavor_qp_mcg_refcnt_dec(qp);
10059e39c5baSBill Taylor 
10069e39c5baSBill Taylor 	/*
10079e39c5baSBill Taylor 	 * If the current MCG's QP number list is about to be made empty
10089e39c5baSBill Taylor 	 * ("mcg_num_qps" == 1), then remove the entry itself from the hash
10099e39c5baSBill Taylor 	 * chain.  Otherwise, just write the updated MCG entry back to the
10109e39c5baSBill Taylor 	 * hardware.  In either case, once we successfully update the hardware
10119e39c5baSBill Taylor 	 * chain, then we decrement the "shadow" list entry's "mcg_num_qps"
10129e39c5baSBill Taylor 	 * count (or zero out the entire "shadow" list entry) before returning
10139e39c5baSBill Taylor 	 * success.  Note:  Zeroing out the "shadow" list entry is done
10149e39c5baSBill Taylor 	 * inside of tavor_mcg_hash_list_remove().
10159e39c5baSBill Taylor 	 */
10169e39c5baSBill Taylor 	if (mcg->mcg_num_qps == 1) {
10179e39c5baSBill Taylor 
10189e39c5baSBill Taylor 		/* Remove an MCG entry from the hash chain */
10199e39c5baSBill Taylor 		status = tavor_mcg_hash_list_remove(state, end_indx, prev_indx,
10209e39c5baSBill Taylor 		    mcg_entry);
10219e39c5baSBill Taylor 		if (status != DDI_SUCCESS) {
10229e39c5baSBill Taylor 			mutex_exit(&state->ts_mcglock);
10239e39c5baSBill Taylor 			return (status);
10249e39c5baSBill Taylor 		}
10259e39c5baSBill Taylor 
10269e39c5baSBill Taylor 	} else {
10279e39c5baSBill Taylor 		/*
10289e39c5baSBill Taylor 		 * Write the updated MCG entry back to the Tavor MCG table.
10299e39c5baSBill Taylor 		 * If this succeeds, then we update the "shadow" list to
10309e39c5baSBill Taylor 		 * reflect the change (i.e. decrement the "mcg_num_qps"),
10319e39c5baSBill Taylor 		 * drop the lock, and return success.  Note:  In general,
10329e39c5baSBill Taylor 		 * this operation shouldn't fail.  If it does, then it is an
10339e39c5baSBill Taylor 		 * indication that something (probably in HW, but maybe in SW)
10349e39c5baSBill Taylor 		 * has gone seriously wrong.
10359e39c5baSBill Taylor 		 */
10369e39c5baSBill Taylor 		status = tavor_write_mgm_cmd_post(state, mcg_entry, end_indx,
10379e39c5baSBill Taylor 		    TAVOR_CMD_NOSLEEP_SPIN);
10389e39c5baSBill Taylor 		if (status != TAVOR_CMD_SUCCESS) {
10399e39c5baSBill Taylor 			mutex_exit(&state->ts_mcglock);
10409e39c5baSBill Taylor 			TAVOR_WARNING(state, "failed to write MCG entry");
10419e39c5baSBill Taylor 			cmn_err(CE_CONT, "Tavor: WRITE_MGM command failed: "
10429e39c5baSBill Taylor 			    "%08x\n", status);
10439e39c5baSBill Taylor 			return (ibc_get_ci_failure(0));
10449e39c5baSBill Taylor 		}
10459e39c5baSBill Taylor 		mcg->mcg_num_qps--;
10469e39c5baSBill Taylor 	}
10479e39c5baSBill Taylor 
10489e39c5baSBill Taylor 	mutex_exit(&state->ts_mcglock);
10499e39c5baSBill Taylor 	return (DDI_SUCCESS);
10509e39c5baSBill Taylor }
10519e39c5baSBill Taylor 
10529e39c5baSBill Taylor /*
10539e39c5baSBill Taylor  * tavor_qp_mcg_refcnt_inc()
10549e39c5baSBill Taylor  *    Context: Can be called from interrupt or base context.
10559e39c5baSBill Taylor  */
10569e39c5baSBill Taylor static void
tavor_qp_mcg_refcnt_inc(tavor_qphdl_t qp)10579e39c5baSBill Taylor tavor_qp_mcg_refcnt_inc(tavor_qphdl_t qp)
10589e39c5baSBill Taylor {
10599e39c5baSBill Taylor 	/* Increment the QP's MCG reference count */
10609e39c5baSBill Taylor 	mutex_enter(&qp->qp_lock);
10619e39c5baSBill Taylor 	qp->qp_mcg_refcnt++;
10629e39c5baSBill Taylor 	mutex_exit(&qp->qp_lock);
10639e39c5baSBill Taylor }
10649e39c5baSBill Taylor 
10659e39c5baSBill Taylor 
10669e39c5baSBill Taylor /*
10679e39c5baSBill Taylor  * tavor_qp_mcg_refcnt_dec()
10689e39c5baSBill Taylor  *    Context: Can be called from interrupt or base context.
10699e39c5baSBill Taylor  */
10709e39c5baSBill Taylor static void
tavor_qp_mcg_refcnt_dec(tavor_qphdl_t qp)10719e39c5baSBill Taylor tavor_qp_mcg_refcnt_dec(tavor_qphdl_t qp)
10729e39c5baSBill Taylor {
10739e39c5baSBill Taylor 	/* Decrement the QP's MCG reference count */
10749e39c5baSBill Taylor 	mutex_enter(&qp->qp_lock);
10759e39c5baSBill Taylor 	qp->qp_mcg_refcnt--;
10769e39c5baSBill Taylor 	mutex_exit(&qp->qp_lock);
10779e39c5baSBill Taylor }
10789e39c5baSBill Taylor 
10799e39c5baSBill Taylor 
10809e39c5baSBill Taylor /*
10819e39c5baSBill Taylor  * tavor_mcg_qplist_add()
10829e39c5baSBill Taylor  *    Context: Can be called from interrupt or base context.
10839e39c5baSBill Taylor  */
10849e39c5baSBill Taylor static int
tavor_mcg_qplist_add(tavor_state_t * state,tavor_mcghdl_t mcg,tavor_hw_mcg_qp_list_t * mcg_qplist,tavor_qphdl_t qp,uint_t * qp_found)10859e39c5baSBill Taylor tavor_mcg_qplist_add(tavor_state_t *state, tavor_mcghdl_t mcg,
10869e39c5baSBill Taylor     tavor_hw_mcg_qp_list_t *mcg_qplist, tavor_qphdl_t qp,
10879e39c5baSBill Taylor     uint_t *qp_found)
10889e39c5baSBill Taylor {
10899e39c5baSBill Taylor 	uint_t		qplist_indx;
10909e39c5baSBill Taylor 
10919e39c5baSBill Taylor 	ASSERT(MUTEX_HELD(&state->ts_mcglock));
10929e39c5baSBill Taylor 
10939e39c5baSBill Taylor 	qplist_indx = mcg->mcg_num_qps;
10949e39c5baSBill Taylor 
10959e39c5baSBill Taylor 	/*
10969e39c5baSBill Taylor 	 * Determine if we have exceeded the maximum number of QP per
10979e39c5baSBill Taylor 	 * multicast group.  If we have, then return an error
10989e39c5baSBill Taylor 	 */
10999e39c5baSBill Taylor 	if (qplist_indx >= state->ts_cfg_profile->cp_num_qp_per_mcg) {
11009e39c5baSBill Taylor 		return (IBT_HCA_MCG_QP_EXCEEDED);
11019e39c5baSBill Taylor 	}
11029e39c5baSBill Taylor 
11039e39c5baSBill Taylor 	/*
11049e39c5baSBill Taylor 	 * Determine if the QP is already attached to this MCG table.  If it
11059e39c5baSBill Taylor 	 * is, then we break out and treat this operation as a NO-OP
11069e39c5baSBill Taylor 	 */
11079e39c5baSBill Taylor 	for (qplist_indx = 0; qplist_indx < mcg->mcg_num_qps;
11089e39c5baSBill Taylor 	    qplist_indx++) {
11099e39c5baSBill Taylor 		if (mcg_qplist[qplist_indx].qpn == qp->qp_qpnum) {
11109e39c5baSBill Taylor 			break;
11119e39c5baSBill Taylor 		}
11129e39c5baSBill Taylor 	}
11139e39c5baSBill Taylor 
11149e39c5baSBill Taylor 	/*
11159e39c5baSBill Taylor 	 * If the QP was already on the list, set 'qp_found' to TRUE.  We still
11169e39c5baSBill Taylor 	 * return SUCCESS in this case, but the qplist will not have been
11179e39c5baSBill Taylor 	 * updated because the QP was already on the list.
11189e39c5baSBill Taylor 	 */
11199e39c5baSBill Taylor 	if (qplist_indx < mcg->mcg_num_qps) {
11209e39c5baSBill Taylor 		*qp_found = 1;
11219e39c5baSBill Taylor 	} else {
11229e39c5baSBill Taylor 		/*
11239e39c5baSBill Taylor 		 * Otherwise, append the new QP number to the end of the
11249e39c5baSBill Taylor 		 * current QP list.  Note: We will increment the "mcg_num_qps"
11259e39c5baSBill Taylor 		 * field on the "shadow" MCG list entry later (after we know
11269e39c5baSBill Taylor 		 * that all necessary Tavor firmware accesses have been
11279e39c5baSBill Taylor 		 * successful).
11289e39c5baSBill Taylor 		 *
11299e39c5baSBill Taylor 		 * Set 'qp_found' to 0 so we know the QP was added on to the
11309e39c5baSBill Taylor 		 * list for sure.
11319e39c5baSBill Taylor 		 */
11329e39c5baSBill Taylor 		mcg_qplist[qplist_indx].q   = TAVOR_MCG_QPN_VALID;
11339e39c5baSBill Taylor 		mcg_qplist[qplist_indx].qpn = qp->qp_qpnum;
11349e39c5baSBill Taylor 		*qp_found = 0;
11359e39c5baSBill Taylor 	}
11369e39c5baSBill Taylor 
11379e39c5baSBill Taylor 	return (DDI_SUCCESS);
11389e39c5baSBill Taylor }
11399e39c5baSBill Taylor 
11409e39c5baSBill Taylor 
11419e39c5baSBill Taylor 
11429e39c5baSBill Taylor /*
11439e39c5baSBill Taylor  * tavor_mcg_qplist_remove()
11449e39c5baSBill Taylor  *    Context: Can be called from interrupt or base context.
11459e39c5baSBill Taylor  */
11469e39c5baSBill Taylor static int
tavor_mcg_qplist_remove(tavor_mcghdl_t mcg,tavor_hw_mcg_qp_list_t * mcg_qplist,tavor_qphdl_t qp)11479e39c5baSBill Taylor tavor_mcg_qplist_remove(tavor_mcghdl_t mcg, tavor_hw_mcg_qp_list_t *mcg_qplist,
11489e39c5baSBill Taylor     tavor_qphdl_t qp)
11499e39c5baSBill Taylor {
11509e39c5baSBill Taylor 	uint_t		i, qplist_indx;
11519e39c5baSBill Taylor 
11529e39c5baSBill Taylor 	/*
11539e39c5baSBill Taylor 	 * Search the MCG QP list for a matching QPN.  When
11549e39c5baSBill Taylor 	 * it's found, we swap the last entry with the current
11559e39c5baSBill Taylor 	 * one, set the last entry to zero, decrement the last
11569e39c5baSBill Taylor 	 * entry, and return.  If it's not found, then it's
11579e39c5baSBill Taylor 	 * and error.
11589e39c5baSBill Taylor 	 */
11599e39c5baSBill Taylor 	qplist_indx = mcg->mcg_num_qps;
11609e39c5baSBill Taylor 	for (i = 0; i < qplist_indx; i++) {
11619e39c5baSBill Taylor 		if (mcg_qplist[i].qpn == qp->qp_qpnum) {
11629e39c5baSBill Taylor 			mcg_qplist[i] = mcg_qplist[qplist_indx - 1];
11639e39c5baSBill Taylor 			mcg_qplist[qplist_indx - 1].q = TAVOR_MCG_QPN_INVALID;
11649e39c5baSBill Taylor 			mcg_qplist[qplist_indx - 1].qpn = 0;
11659e39c5baSBill Taylor 
11669e39c5baSBill Taylor 			return (DDI_SUCCESS);
11679e39c5baSBill Taylor 		}
11689e39c5baSBill Taylor 	}
11699e39c5baSBill Taylor 
11709e39c5baSBill Taylor 	return (IBT_QP_HDL_INVALID);
11719e39c5baSBill Taylor }
11729e39c5baSBill Taylor 
11739e39c5baSBill Taylor 
11749e39c5baSBill Taylor /*
11759e39c5baSBill Taylor  * tavor_mcg_walk_mgid_hash()
11769e39c5baSBill Taylor  *    Context: Can be called from interrupt or base context.
11779e39c5baSBill Taylor  */
11789e39c5baSBill Taylor static uint_t
tavor_mcg_walk_mgid_hash(tavor_state_t * state,uint64_t start_indx,ib_gid_t mgid,uint_t * p_indx)11799e39c5baSBill Taylor tavor_mcg_walk_mgid_hash(tavor_state_t *state, uint64_t start_indx,
11809e39c5baSBill Taylor     ib_gid_t mgid, uint_t *p_indx)
11819e39c5baSBill Taylor {
11829e39c5baSBill Taylor 	tavor_mcghdl_t	curr_mcghdl;
11839e39c5baSBill Taylor 	uint_t		curr_indx, prev_indx;
11849e39c5baSBill Taylor 
11859e39c5baSBill Taylor 	ASSERT(MUTEX_HELD(&state->ts_mcglock));
11869e39c5baSBill Taylor 
11879e39c5baSBill Taylor 	/* Start at the head of the hash chain */
11889e39c5baSBill Taylor 	curr_indx   = start_indx;
11899e39c5baSBill Taylor 	prev_indx   = curr_indx;
11909e39c5baSBill Taylor 	curr_mcghdl = &state->ts_mcghdl[curr_indx];
11919e39c5baSBill Taylor 
11929e39c5baSBill Taylor 	/* If the first entry in the chain has MGID == 0, then stop */
11939e39c5baSBill Taylor 	if ((curr_mcghdl->mcg_mgid_h == 0) &&
11949e39c5baSBill Taylor 	    (curr_mcghdl->mcg_mgid_l == 0)) {
11959e39c5baSBill Taylor 		goto end_mgid_hash_walk;
11969e39c5baSBill Taylor 	}
11979e39c5baSBill Taylor 
11989e39c5baSBill Taylor 	/* If the first entry in the chain matches the MGID, then stop */
11999e39c5baSBill Taylor 	if ((curr_mcghdl->mcg_mgid_h == mgid.gid_prefix) &&
12009e39c5baSBill Taylor 	    (curr_mcghdl->mcg_mgid_l == mgid.gid_guid)) {
12019e39c5baSBill Taylor 		goto end_mgid_hash_walk;
12029e39c5baSBill Taylor 	}
12039e39c5baSBill Taylor 
12049e39c5baSBill Taylor 	/* Otherwise, walk the hash chain looking for a match */
12059e39c5baSBill Taylor 	while (curr_mcghdl->mcg_next_indx != 0) {
12069e39c5baSBill Taylor 		prev_indx = curr_indx;
12079e39c5baSBill Taylor 		curr_indx = curr_mcghdl->mcg_next_indx;
12089e39c5baSBill Taylor 		curr_mcghdl = &state->ts_mcghdl[curr_indx];
12099e39c5baSBill Taylor 
12109e39c5baSBill Taylor 		if ((curr_mcghdl->mcg_mgid_h == mgid.gid_prefix) &&
12119e39c5baSBill Taylor 		    (curr_mcghdl->mcg_mgid_l == mgid.gid_guid)) {
12129e39c5baSBill Taylor 			break;
12139e39c5baSBill Taylor 		}
12149e39c5baSBill Taylor 	}
12159e39c5baSBill Taylor 
12169e39c5baSBill Taylor end_mgid_hash_walk:
12179e39c5baSBill Taylor 	/*
12189e39c5baSBill Taylor 	 * If necessary, return the index of the previous entry too.  This
12199e39c5baSBill Taylor 	 * is primarily used for detaching a QP from a multicast group.  It
12209e39c5baSBill Taylor 	 * may be necessary, in that case, to delete an MCG entry from the
12219e39c5baSBill Taylor 	 * hash chain and having the index of the previous entry is helpful.
12229e39c5baSBill Taylor 	 */
12239e39c5baSBill Taylor 	if (p_indx != NULL) {
12249e39c5baSBill Taylor 		*p_indx = prev_indx;
12259e39c5baSBill Taylor 	}
12269e39c5baSBill Taylor 	return (curr_indx);
12279e39c5baSBill Taylor }
12289e39c5baSBill Taylor 
12299e39c5baSBill Taylor 
12309e39c5baSBill Taylor /*
12319e39c5baSBill Taylor  * tavor_mcg_setup_new_hdr()
12329e39c5baSBill Taylor  *    Context: Can be called from interrupt or base context.
12339e39c5baSBill Taylor  */
12349e39c5baSBill Taylor static void
tavor_mcg_setup_new_hdr(tavor_mcghdl_t mcg,tavor_hw_mcg_t * mcg_hdr,ib_gid_t mgid,tavor_rsrc_t * mcg_rsrc)12359e39c5baSBill Taylor tavor_mcg_setup_new_hdr(tavor_mcghdl_t mcg, tavor_hw_mcg_t *mcg_hdr,
12369e39c5baSBill Taylor     ib_gid_t mgid, tavor_rsrc_t *mcg_rsrc)
12379e39c5baSBill Taylor {
12389e39c5baSBill Taylor 	/*
12399e39c5baSBill Taylor 	 * Fill in the fields of the "shadow" entry used by software
12409e39c5baSBill Taylor 	 * to track MCG hardware entry
12419e39c5baSBill Taylor 	 */
12429e39c5baSBill Taylor 	mcg->mcg_mgid_h	   = mgid.gid_prefix;
12439e39c5baSBill Taylor 	mcg->mcg_mgid_l	   = mgid.gid_guid;
12449e39c5baSBill Taylor 	mcg->mcg_rsrcp	   = mcg_rsrc;
12459e39c5baSBill Taylor 	mcg->mcg_next_indx = 0;
12469e39c5baSBill Taylor 	mcg->mcg_num_qps   = 0;
12479e39c5baSBill Taylor 
12489e39c5baSBill Taylor 	/*
12499e39c5baSBill Taylor 	 * Fill the header fields of the MCG entry (in the temporary copy)
12509e39c5baSBill Taylor 	 */
12519e39c5baSBill Taylor 	mcg_hdr->mgid_h		= mgid.gid_prefix;
12529e39c5baSBill Taylor 	mcg_hdr->mgid_l		= mgid.gid_guid;
12539e39c5baSBill Taylor 	mcg_hdr->next_gid_indx	= 0;
12549e39c5baSBill Taylor }
12559e39c5baSBill Taylor 
12569e39c5baSBill Taylor 
12579e39c5baSBill Taylor /*
12589e39c5baSBill Taylor  * tavor_mcg_hash_list_remove()
12599e39c5baSBill Taylor  *    Context: Can be called only from user or kernel context.
12609e39c5baSBill Taylor  */
12619e39c5baSBill Taylor static int
tavor_mcg_hash_list_remove(tavor_state_t * state,uint_t curr_indx,uint_t prev_indx,tavor_hw_mcg_t * mcg_entry)12629e39c5baSBill Taylor tavor_mcg_hash_list_remove(tavor_state_t *state, uint_t curr_indx,
12639e39c5baSBill Taylor     uint_t prev_indx, tavor_hw_mcg_t *mcg_entry)
12649e39c5baSBill Taylor {
12659e39c5baSBill Taylor 	tavor_mcghdl_t		curr_mcg, prev_mcg, next_mcg;
12669e39c5baSBill Taylor 	uint_t			next_indx;
12679e39c5baSBill Taylor 	int			status;
12689e39c5baSBill Taylor 
12699e39c5baSBill Taylor 	/* Get the pointer to "shadow" list for current entry */
12709e39c5baSBill Taylor 	curr_mcg = &state->ts_mcghdl[curr_indx];
12719e39c5baSBill Taylor 
12729e39c5baSBill Taylor 	/*
12739e39c5baSBill Taylor 	 * If this is the first entry on a hash chain, then attempt to replace
12749e39c5baSBill Taylor 	 * the entry with the next entry on the chain.  If there are no
12759e39c5baSBill Taylor 	 * subsequent entries on the chain, then this is the only entry and
12769e39c5baSBill Taylor 	 * should be invalidated.
12779e39c5baSBill Taylor 	 */
12789e39c5baSBill Taylor 	if (curr_indx == prev_indx) {
12799e39c5baSBill Taylor 
12809e39c5baSBill Taylor 		/*
12819e39c5baSBill Taylor 		 * If this is the only entry on the chain, then invalidate it.
12829e39c5baSBill Taylor 		 * Note:  Invalidating an MCG entry means writing all zeros
12839e39c5baSBill Taylor 		 * to the entry.  This is only necessary for those MCG
12849e39c5baSBill Taylor 		 * entries that are the "head" entries of the individual hash
12859e39c5baSBill Taylor 		 * chains.  Regardless of whether this operation returns
12869e39c5baSBill Taylor 		 * success or failure, return that result to the caller.
12879e39c5baSBill Taylor 		 */
12889e39c5baSBill Taylor 		next_indx = curr_mcg->mcg_next_indx;
12899e39c5baSBill Taylor 		if (next_indx == 0) {
12909e39c5baSBill Taylor 			status = tavor_mcg_entry_invalidate(state, mcg_entry,
12919e39c5baSBill Taylor 			    curr_indx);
12929e39c5baSBill Taylor 			bzero(curr_mcg, sizeof (struct tavor_sw_mcg_list_s));
12939e39c5baSBill Taylor 			return (status);
12949e39c5baSBill Taylor 		}
12959e39c5baSBill Taylor 
12969e39c5baSBill Taylor 		/*
12979e39c5baSBill Taylor 		 * Otherwise, this is just the first entry on the chain, so
12989e39c5baSBill Taylor 		 * grab the next one
12999e39c5baSBill Taylor 		 */
13009e39c5baSBill Taylor 		next_mcg = &state->ts_mcghdl[next_indx];
13019e39c5baSBill Taylor 
13029e39c5baSBill Taylor 		/*
13039e39c5baSBill Taylor 		 * Read the next MCG entry into the temporary MCG.  Note:
13049e39c5baSBill Taylor 		 * In general, this operation shouldn't fail.  If it does,
13059e39c5baSBill Taylor 		 * then it is an indication that something (probably in HW,
13069e39c5baSBill Taylor 		 * but maybe in SW) has gone seriously wrong.
13079e39c5baSBill Taylor 		 */
13089e39c5baSBill Taylor 		status = tavor_read_mgm_cmd_post(state, mcg_entry, next_indx,
13099e39c5baSBill Taylor 		    TAVOR_CMD_NOSLEEP_SPIN);
13109e39c5baSBill Taylor 		if (status != TAVOR_CMD_SUCCESS) {
13119e39c5baSBill Taylor 			TAVOR_WARNING(state, "failed to read MCG entry");
13129e39c5baSBill Taylor 			cmn_err(CE_CONT, "Tavor: READ_MGM command failed: "
13139e39c5baSBill Taylor 			    "%08x\n", status);
13149e39c5baSBill Taylor 			return (ibc_get_ci_failure(0));
13159e39c5baSBill Taylor 		}
13169e39c5baSBill Taylor 
13179e39c5baSBill Taylor 		/*
13189e39c5baSBill Taylor 		 * Copy/Write the temporary MCG back to the hardware MCG list
13199e39c5baSBill Taylor 		 * using the current index.  This essentially removes the
13209e39c5baSBill Taylor 		 * current MCG entry from the list by writing over it with
13219e39c5baSBill Taylor 		 * the next one.  If this is successful, then we can do the
13229e39c5baSBill Taylor 		 * same operation for the "shadow" list.  And we can also
13239e39c5baSBill Taylor 		 * free up the Tavor MCG entry resource that was associated
13249e39c5baSBill Taylor 		 * with the (old) next entry.  Note:  In general, this
13259e39c5baSBill Taylor 		 * operation shouldn't fail.  If it does, then it is an
13269e39c5baSBill Taylor 		 * indication that something (probably in HW, but maybe in SW)
13279e39c5baSBill Taylor 		 * has gone seriously wrong.
13289e39c5baSBill Taylor 		 */
13299e39c5baSBill Taylor 		status = tavor_write_mgm_cmd_post(state, mcg_entry, curr_indx,
13309e39c5baSBill Taylor 		    TAVOR_CMD_NOSLEEP_SPIN);
13319e39c5baSBill Taylor 		if (status != TAVOR_CMD_SUCCESS) {
13329e39c5baSBill Taylor 			TAVOR_WARNING(state, "failed to write MCG entry");
13339e39c5baSBill Taylor 			cmn_err(CE_CONT, "Tavor: WRITE_MGM command failed: "
13349e39c5baSBill Taylor 			    "%08x\n", status);
13359e39c5baSBill Taylor 			return (ibc_get_ci_failure(0));
13369e39c5baSBill Taylor 		}
13379e39c5baSBill Taylor 
13389e39c5baSBill Taylor 		/*
13399e39c5baSBill Taylor 		 * Copy all the software tracking information from the next
13409e39c5baSBill Taylor 		 * entry on the "shadow" MCG list into the current entry on
13419e39c5baSBill Taylor 		 * the list.  Then invalidate (zero out) the other "shadow"
13429e39c5baSBill Taylor 		 * list entry.
13439e39c5baSBill Taylor 		 */
13449e39c5baSBill Taylor 		bcopy(next_mcg, curr_mcg, sizeof (struct tavor_sw_mcg_list_s));
13459e39c5baSBill Taylor 		bzero(next_mcg, sizeof (struct tavor_sw_mcg_list_s));
13469e39c5baSBill Taylor 
13479e39c5baSBill Taylor 		/*
13489e39c5baSBill Taylor 		 * Free up the Tavor MCG entry resource used by the "next"
13499e39c5baSBill Taylor 		 * MCG entry.  That resource is no longer needed by any
13509e39c5baSBill Taylor 		 * MCG entry which is first on a hash chain (like the "next"
13519e39c5baSBill Taylor 		 * entry has just become).
13529e39c5baSBill Taylor 		 */
13539e39c5baSBill Taylor 		tavor_rsrc_free(state, &curr_mcg->mcg_rsrcp);
13549e39c5baSBill Taylor 
13559e39c5baSBill Taylor 		return (DDI_SUCCESS);
13569e39c5baSBill Taylor 	}
13579e39c5baSBill Taylor 
13589e39c5baSBill Taylor 	/*
13599e39c5baSBill Taylor 	 * Else if this is the last entry on the hash chain (or a middle
13609e39c5baSBill Taylor 	 * entry, then we update the previous entry's "next_gid_index" field
13619e39c5baSBill Taylor 	 * to make it point instead to the next entry on the chain.  By
13629e39c5baSBill Taylor 	 * skipping over the removed entry in this way, we can then free up
13639e39c5baSBill Taylor 	 * any resources associated with the current entry.  Note:  We don't
13649e39c5baSBill Taylor 	 * need to invalidate the "skipped over" hardware entry because it
13659e39c5baSBill Taylor 	 * will no be longer connected to any hash chains, and if/when it is
13669e39c5baSBill Taylor 	 * finally re-used, it will be written with entirely new values.
13679e39c5baSBill Taylor 	 */
13689e39c5baSBill Taylor 
13699e39c5baSBill Taylor 	/*
13709e39c5baSBill Taylor 	 * Read the next MCG entry into the temporary MCG.  Note:  In general,
13719e39c5baSBill Taylor 	 * this operation shouldn't fail.  If it does, then it is an
13729e39c5baSBill Taylor 	 * indication that something (probably in HW, but maybe in SW) has
13739e39c5baSBill Taylor 	 * gone seriously wrong.
13749e39c5baSBill Taylor 	 */
13759e39c5baSBill Taylor 	status = tavor_read_mgm_cmd_post(state, mcg_entry, prev_indx,
13769e39c5baSBill Taylor 	    TAVOR_CMD_NOSLEEP_SPIN);
13779e39c5baSBill Taylor 	if (status != TAVOR_CMD_SUCCESS) {
13789e39c5baSBill Taylor 		TAVOR_WARNING(state, "failed to read MCG entry");
13799e39c5baSBill Taylor 		cmn_err(CE_CONT, "Tavor: READ_MGM command failed: %08x\n",
13809e39c5baSBill Taylor 		    status);
13819e39c5baSBill Taylor 		return (ibc_get_ci_failure(0));
13829e39c5baSBill Taylor 	}
13839e39c5baSBill Taylor 
13849e39c5baSBill Taylor 	/*
13859e39c5baSBill Taylor 	 * Finally, we update the "next_gid_indx" field in the temporary MCG
13869e39c5baSBill Taylor 	 * and attempt to write the entry back into the Tavor MCG table.  If
13879e39c5baSBill Taylor 	 * this succeeds, then we update the "shadow" list to reflect the
13889e39c5baSBill Taylor 	 * change, free up the Tavor MCG entry resource that was associated
13899e39c5baSBill Taylor 	 * with the current entry, and return success.  Note:  In general,
13909e39c5baSBill Taylor 	 * this operation shouldn't fail.  If it does, then it is an indication
13919e39c5baSBill Taylor 	 * that something (probably in HW, but maybe in SW) has gone seriously
13929e39c5baSBill Taylor 	 * wrong.
13939e39c5baSBill Taylor 	 */
13949e39c5baSBill Taylor 	mcg_entry->next_gid_indx = curr_mcg->mcg_next_indx;
13959e39c5baSBill Taylor 	status = tavor_write_mgm_cmd_post(state, mcg_entry, prev_indx,
13969e39c5baSBill Taylor 	    TAVOR_CMD_NOSLEEP_SPIN);
13979e39c5baSBill Taylor 	if (status != TAVOR_CMD_SUCCESS) {
13989e39c5baSBill Taylor 		TAVOR_WARNING(state, "failed to write MCG entry");
13999e39c5baSBill Taylor 		cmn_err(CE_CONT, "Tavor: WRITE_MGM command failed: %08x\n",
14009e39c5baSBill Taylor 		    status);
14019e39c5baSBill Taylor 		return (ibc_get_ci_failure(0));
14029e39c5baSBill Taylor 	}
14039e39c5baSBill Taylor 
14049e39c5baSBill Taylor 	/*
14059e39c5baSBill Taylor 	 * Get the pointer to the "shadow" MCG list entry for the previous
14069e39c5baSBill Taylor 	 * MCG.  Update its "mcg_next_indx" to point to the next entry
14079e39c5baSBill Taylor 	 * the one after the current entry. Note:  This next index may be
14089e39c5baSBill Taylor 	 * zero, indicating the end of the list.
14099e39c5baSBill Taylor 	 */
14109e39c5baSBill Taylor 	prev_mcg = &state->ts_mcghdl[prev_indx];
14119e39c5baSBill Taylor 	prev_mcg->mcg_next_indx = curr_mcg->mcg_next_indx;
14129e39c5baSBill Taylor 
14139e39c5baSBill Taylor 	/*
14149e39c5baSBill Taylor 	 * Free up the Tavor MCG entry resource used by the current entry.
14159e39c5baSBill Taylor 	 * This resource is no longer needed because the chain now skips over
14169e39c5baSBill Taylor 	 * the current entry.  Then invalidate (zero out) the current "shadow"
14179e39c5baSBill Taylor 	 * list entry.
14189e39c5baSBill Taylor 	 */
14199e39c5baSBill Taylor 	tavor_rsrc_free(state, &curr_mcg->mcg_rsrcp);
14209e39c5baSBill Taylor 	bzero(curr_mcg, sizeof (struct tavor_sw_mcg_list_s));
14219e39c5baSBill Taylor 
14229e39c5baSBill Taylor 	return (DDI_SUCCESS);
14239e39c5baSBill Taylor }
14249e39c5baSBill Taylor 
14259e39c5baSBill Taylor 
14269e39c5baSBill Taylor /*
14279e39c5baSBill Taylor  * tavor_mcg_entry_invalidate()
14289e39c5baSBill Taylor  *    Context: Can be called only from user or kernel context.
14299e39c5baSBill Taylor  */
14309e39c5baSBill Taylor static int
tavor_mcg_entry_invalidate(tavor_state_t * state,tavor_hw_mcg_t * mcg_entry,uint_t indx)14319e39c5baSBill Taylor tavor_mcg_entry_invalidate(tavor_state_t *state, tavor_hw_mcg_t *mcg_entry,
14329e39c5baSBill Taylor     uint_t indx)
14339e39c5baSBill Taylor {
14349e39c5baSBill Taylor 	int		status;
14359e39c5baSBill Taylor 
14369e39c5baSBill Taylor 	/*
14379e39c5baSBill Taylor 	 * Invalidate the hardware MCG entry by zeroing out this temporary
14389e39c5baSBill Taylor 	 * MCG and writing it the the hardware.  Note: In general, this
14399e39c5baSBill Taylor 	 * operation shouldn't fail.  If it does, then it is an indication
14409e39c5baSBill Taylor 	 * that something (probably in HW, but maybe in SW) has gone seriously
14419e39c5baSBill Taylor 	 * wrong.
14429e39c5baSBill Taylor 	 */
14439e39c5baSBill Taylor 	bzero(mcg_entry, TAVOR_MCGMEM_SZ(state));
14449e39c5baSBill Taylor 	status = tavor_write_mgm_cmd_post(state, mcg_entry, indx,
14459e39c5baSBill Taylor 	    TAVOR_CMD_NOSLEEP_SPIN);
14469e39c5baSBill Taylor 	if (status != TAVOR_CMD_SUCCESS) {
14479e39c5baSBill Taylor 		TAVOR_WARNING(state, "failed to write MCG entry");
14489e39c5baSBill Taylor 		cmn_err(CE_CONT, "Tavor: WRITE_MGM command failed: %08x\n",
14499e39c5baSBill Taylor 		    status);
14509e39c5baSBill Taylor 		return (ibc_get_ci_failure(0));
14519e39c5baSBill Taylor 	}
14529e39c5baSBill Taylor 
14539e39c5baSBill Taylor 	return (DDI_SUCCESS);
14549e39c5baSBill Taylor }
14559e39c5baSBill Taylor 
14569e39c5baSBill Taylor 
14579e39c5baSBill Taylor /*
14589e39c5baSBill Taylor  * tavor_mgid_is_valid()
14599e39c5baSBill Taylor  *    Context: Can be called from interrupt or base context.
14609e39c5baSBill Taylor  */
14619e39c5baSBill Taylor static int
tavor_mgid_is_valid(ib_gid_t gid)14629e39c5baSBill Taylor tavor_mgid_is_valid(ib_gid_t gid)
14639e39c5baSBill Taylor {
14649e39c5baSBill Taylor 	uint_t		topbits, flags, scope;
14659e39c5baSBill Taylor 
14669e39c5baSBill Taylor 	/*
14679e39c5baSBill Taylor 	 * According to IBA 1.1 specification (section 4.1.1) a valid
14689e39c5baSBill Taylor 	 * "multicast GID" must have its top eight bits set to all ones
14699e39c5baSBill Taylor 	 */
14709e39c5baSBill Taylor 	topbits = (gid.gid_prefix >> TAVOR_MCG_TOPBITS_SHIFT) &
14719e39c5baSBill Taylor 	    TAVOR_MCG_TOPBITS_MASK;
14729e39c5baSBill Taylor 	if (topbits != TAVOR_MCG_TOPBITS) {
14739e39c5baSBill Taylor 		return (0);
14749e39c5baSBill Taylor 	}
14759e39c5baSBill Taylor 
14769e39c5baSBill Taylor 	/*
14779e39c5baSBill Taylor 	 * The next 4 bits are the "flag" bits.  These are valid only
14789e39c5baSBill Taylor 	 * if they are "0" (which correspond to permanently assigned/
14799e39c5baSBill Taylor 	 * "well-known" multicast GIDs) or "1" (for so-called "transient"
14809e39c5baSBill Taylor 	 * multicast GIDs).  All other values are reserved.
14819e39c5baSBill Taylor 	 */
14829e39c5baSBill Taylor 	flags = (gid.gid_prefix >> TAVOR_MCG_FLAGS_SHIFT) &
14839e39c5baSBill Taylor 	    TAVOR_MCG_FLAGS_MASK;
14849e39c5baSBill Taylor 	if (!((flags == TAVOR_MCG_FLAGS_PERM) ||
14859e39c5baSBill Taylor 	    (flags == TAVOR_MCG_FLAGS_NONPERM))) {
14869e39c5baSBill Taylor 		return (0);
14879e39c5baSBill Taylor 	}
14889e39c5baSBill Taylor 
14899e39c5baSBill Taylor 	/*
14909e39c5baSBill Taylor 	 * The next 4 bits are the "scope" bits.  These are valid only
14919e39c5baSBill Taylor 	 * if they are "2" (Link-local), "5" (Site-local), "8"
14929e39c5baSBill Taylor 	 * (Organization-local) or "E" (Global).  All other values
14939e39c5baSBill Taylor 	 * are reserved (or currently unassigned).
14949e39c5baSBill Taylor 	 */
14959e39c5baSBill Taylor 	scope = (gid.gid_prefix >> TAVOR_MCG_SCOPE_SHIFT) &
14969e39c5baSBill Taylor 	    TAVOR_MCG_SCOPE_MASK;
14979e39c5baSBill Taylor 	if (!((scope == TAVOR_MCG_SCOPE_LINKLOC) ||
14989e39c5baSBill Taylor 	    (scope == TAVOR_MCG_SCOPE_SITELOC)	 ||
14999e39c5baSBill Taylor 	    (scope == TAVOR_MCG_SCOPE_ORGLOC)	 ||
15009e39c5baSBill Taylor 	    (scope == TAVOR_MCG_SCOPE_GLOBAL))) {
15019e39c5baSBill Taylor 		return (0);
15029e39c5baSBill Taylor 	}
15039e39c5baSBill Taylor 
15049e39c5baSBill Taylor 	/*
15059e39c5baSBill Taylor 	 * If it passes all of the above checks, then we will consider it
15069e39c5baSBill Taylor 	 * a valid multicast GID.
15079e39c5baSBill Taylor 	 */
15089e39c5baSBill Taylor 	return (1);
15099e39c5baSBill Taylor }
15109e39c5baSBill Taylor 
15119e39c5baSBill Taylor 
15129e39c5baSBill Taylor /*
15139e39c5baSBill Taylor  * tavor_mlid_is_valid()
15149e39c5baSBill Taylor  *    Context: Can be called from interrupt or base context.
15159e39c5baSBill Taylor  */
15169e39c5baSBill Taylor static int
tavor_mlid_is_valid(ib_lid_t lid)15179e39c5baSBill Taylor tavor_mlid_is_valid(ib_lid_t lid)
15189e39c5baSBill Taylor {
15199e39c5baSBill Taylor 	/*
15209e39c5baSBill Taylor 	 * According to IBA 1.1 specification (section 4.1.1) a valid
15219e39c5baSBill Taylor 	 * "multicast DLID" must be between 0xC000 and 0xFFFE.
15229e39c5baSBill Taylor 	 */
15239e39c5baSBill Taylor 	if ((lid < IB_LID_MC_FIRST) || (lid > IB_LID_MC_LAST)) {
15249e39c5baSBill Taylor 		return (0);
15259e39c5baSBill Taylor 	}
15269e39c5baSBill Taylor 
15279e39c5baSBill Taylor 	return (1);
15289e39c5baSBill Taylor }
15299e39c5baSBill Taylor 
15309e39c5baSBill Taylor 
15319e39c5baSBill Taylor /*
15329e39c5baSBill Taylor  * tavor_pd_alloc()
15339e39c5baSBill Taylor  *    Context: Can be called only from user or kernel context.
15349e39c5baSBill Taylor  */
15359e39c5baSBill Taylor int
tavor_pd_alloc(tavor_state_t * state,tavor_pdhdl_t * pdhdl,uint_t sleepflag)15369e39c5baSBill Taylor tavor_pd_alloc(tavor_state_t *state, tavor_pdhdl_t *pdhdl, uint_t sleepflag)
15379e39c5baSBill Taylor {
15389e39c5baSBill Taylor 	tavor_rsrc_t	*rsrc;
15399e39c5baSBill Taylor 	tavor_pdhdl_t	pd;
15409e39c5baSBill Taylor 	int		status;
15419e39c5baSBill Taylor 
15429e39c5baSBill Taylor 	/*
15439e39c5baSBill Taylor 	 * Allocate the software structure for tracking the protection domain
15449e39c5baSBill Taylor 	 * (i.e. the Tavor Protection Domain handle).  By default each PD
15459e39c5baSBill Taylor 	 * structure will have a unique PD number assigned to it.  All that
15469e39c5baSBill Taylor 	 * is necessary is for software to initialize the PD reference count
15479e39c5baSBill Taylor 	 * (to zero) and return success.
15489e39c5baSBill Taylor 	 */
15499e39c5baSBill Taylor 	status = tavor_rsrc_alloc(state, TAVOR_PDHDL, 1, sleepflag, &rsrc);
15509e39c5baSBill Taylor 	if (status != DDI_SUCCESS) {
15519e39c5baSBill Taylor 		return (IBT_INSUFF_RESOURCE);
15529e39c5baSBill Taylor 	}
15539e39c5baSBill Taylor 	pd = (tavor_pdhdl_t)rsrc->tr_addr;
15549e39c5baSBill Taylor 	_NOTE(NOW_INVISIBLE_TO_OTHER_THREADS(*pd))
15559e39c5baSBill Taylor 
15569e39c5baSBill Taylor 	pd->pd_refcnt = 0;
15579e39c5baSBill Taylor 	*pdhdl = pd;
15589e39c5baSBill Taylor 
15599e39c5baSBill Taylor 	return (DDI_SUCCESS);
15609e39c5baSBill Taylor }
15619e39c5baSBill Taylor 
15629e39c5baSBill Taylor 
15639e39c5baSBill Taylor /*
15649e39c5baSBill Taylor  * tavor_pd_free()
15659e39c5baSBill Taylor  *    Context: Can be called only from user or kernel context.
15669e39c5baSBill Taylor  */
15679e39c5baSBill Taylor int
tavor_pd_free(tavor_state_t * state,tavor_pdhdl_t * pdhdl)15689e39c5baSBill Taylor tavor_pd_free(tavor_state_t *state, tavor_pdhdl_t *pdhdl)
15699e39c5baSBill Taylor {
15709e39c5baSBill Taylor 	tavor_rsrc_t	*rsrc;
15719e39c5baSBill Taylor 	tavor_pdhdl_t	pd;
15729e39c5baSBill Taylor 
15739e39c5baSBill Taylor 	/*
15749e39c5baSBill Taylor 	 * Pull all the necessary information from the Tavor Protection Domain
15759e39c5baSBill Taylor 	 * handle.  This is necessary here because the resource for the
15769e39c5baSBill Taylor 	 * PD is going to be freed up as part of this operation.
15779e39c5baSBill Taylor 	 */
15789e39c5baSBill Taylor 	pd   = *pdhdl;
15799e39c5baSBill Taylor 	_NOTE(NOW_INVISIBLE_TO_OTHER_THREADS(*pd))
15809e39c5baSBill Taylor 	rsrc = pd->pd_rsrcp;
15819e39c5baSBill Taylor 
15829e39c5baSBill Taylor 	/*
15839e39c5baSBill Taylor 	 * Check the PD reference count.  If the reference count is non-zero,
15849e39c5baSBill Taylor 	 * then it means that this protection domain is still referenced by
15859e39c5baSBill Taylor 	 * some memory region, queue pair, address handle, or other IB object
15869e39c5baSBill Taylor 	 * If it is non-zero, then return an error.  Otherwise, free the
15879e39c5baSBill Taylor 	 * Tavor resource and return success.
15889e39c5baSBill Taylor 	 */
15899e39c5baSBill Taylor 	if (pd->pd_refcnt != 0) {
15909e39c5baSBill Taylor 		return (IBT_PD_IN_USE);
15919e39c5baSBill Taylor 	}
15929e39c5baSBill Taylor 
15939e39c5baSBill Taylor 	/* Free the Tavor Protection Domain handle */
15949e39c5baSBill Taylor 	tavor_rsrc_free(state, &rsrc);
15959e39c5baSBill Taylor 
15969e39c5baSBill Taylor 	/* Set the pdhdl pointer to NULL and return success */
15979e39c5baSBill Taylor 	*pdhdl = (tavor_pdhdl_t)NULL;
15989e39c5baSBill Taylor 
15999e39c5baSBill Taylor 	return (DDI_SUCCESS);
16009e39c5baSBill Taylor }
16019e39c5baSBill Taylor 
16029e39c5baSBill Taylor 
16039e39c5baSBill Taylor /*
16049e39c5baSBill Taylor  * tavor_pd_refcnt_inc()
16059e39c5baSBill Taylor  *    Context: Can be called from interrupt or base context.
16069e39c5baSBill Taylor  */
16079e39c5baSBill Taylor void
tavor_pd_refcnt_inc(tavor_pdhdl_t pd)16089e39c5baSBill Taylor tavor_pd_refcnt_inc(tavor_pdhdl_t pd)
16099e39c5baSBill Taylor {
16109e39c5baSBill Taylor 	/* Increment the protection domain's reference count */
16119e39c5baSBill Taylor 	mutex_enter(&pd->pd_lock);
16129e39c5baSBill Taylor 	pd->pd_refcnt++;
16139e39c5baSBill Taylor 	mutex_exit(&pd->pd_lock);
16149e39c5baSBill Taylor 
16159e39c5baSBill Taylor }
16169e39c5baSBill Taylor 
16179e39c5baSBill Taylor 
16189e39c5baSBill Taylor /*
16199e39c5baSBill Taylor  * tavor_pd_refcnt_dec()
16209e39c5baSBill Taylor  *    Context: Can be called from interrupt or base context.
16219e39c5baSBill Taylor  */
16229e39c5baSBill Taylor void
tavor_pd_refcnt_dec(tavor_pdhdl_t pd)16239e39c5baSBill Taylor tavor_pd_refcnt_dec(tavor_pdhdl_t pd)
16249e39c5baSBill Taylor {
16259e39c5baSBill Taylor 	/* Decrement the protection domain's reference count */
16269e39c5baSBill Taylor 	mutex_enter(&pd->pd_lock);
16279e39c5baSBill Taylor 	pd->pd_refcnt--;
16289e39c5baSBill Taylor 	mutex_exit(&pd->pd_lock);
16299e39c5baSBill Taylor 
16309e39c5baSBill Taylor }
16319e39c5baSBill Taylor 
16329e39c5baSBill Taylor 
16339e39c5baSBill Taylor /*
16349e39c5baSBill Taylor  * tavor_port_query()
16359e39c5baSBill Taylor  *    Context: Can be called only from user or kernel context.
16369e39c5baSBill Taylor  */
16379e39c5baSBill Taylor int
tavor_port_query(tavor_state_t * state,uint_t port,ibt_hca_portinfo_t * pi)16389e39c5baSBill Taylor tavor_port_query(tavor_state_t *state, uint_t port, ibt_hca_portinfo_t *pi)
16399e39c5baSBill Taylor {
16409e39c5baSBill Taylor 	sm_portinfo_t		portinfo;
16419e39c5baSBill Taylor 	sm_guidinfo_t		guidinfo;
16429e39c5baSBill Taylor 	sm_pkey_table_t		pkeytable;
16439e39c5baSBill Taylor 	ib_gid_t		*sgid;
16449e39c5baSBill Taylor 	uint_t			sgid_max, pkey_max, tbl_size;
16459e39c5baSBill Taylor 	int			i, j, indx, status;
16469e39c5baSBill Taylor 
16479e39c5baSBill Taylor 	_NOTE(NOW_INVISIBLE_TO_OTHER_THREADS(*pi))
16489e39c5baSBill Taylor 
16499e39c5baSBill Taylor 	/* Validate that specified port number is legal */
16509e39c5baSBill Taylor 	if (!tavor_portnum_is_valid(state, port)) {
16519e39c5baSBill Taylor 		return (IBT_HCA_PORT_INVALID);
16529e39c5baSBill Taylor 	}
16539e39c5baSBill Taylor 
16549e39c5baSBill Taylor 	/*
16559e39c5baSBill Taylor 	 * We use the Tavor MAD_IFC command to post a GetPortInfo MAD
16569e39c5baSBill Taylor 	 * to the firmware (for the specified port number).  This returns
16579e39c5baSBill Taylor 	 * a full PortInfo MAD (in "portinfo") which we subsequently
16589e39c5baSBill Taylor 	 * parse to fill in the "ibt_hca_portinfo_t" structure returned
16599e39c5baSBill Taylor 	 * to the IBTF.
16609e39c5baSBill Taylor 	 */
16619e39c5baSBill Taylor 	status = tavor_getportinfo_cmd_post(state, port,
16629e39c5baSBill Taylor 	    TAVOR_SLEEPFLAG_FOR_CONTEXT(), &portinfo);
16639e39c5baSBill Taylor 	if (status != TAVOR_CMD_SUCCESS) {
16649e39c5baSBill Taylor 		cmn_err(CE_CONT, "Tavor: GetPortInfo (port %02d) command "
16659e39c5baSBill Taylor 		    "failed: %08x\n", port, status);
16669e39c5baSBill Taylor 		return (ibc_get_ci_failure(0));
16679e39c5baSBill Taylor 	}
16689e39c5baSBill Taylor 
16699e39c5baSBill Taylor 	/*
16709e39c5baSBill Taylor 	 * Parse the PortInfo MAD and fill in the IBTF structure
16719e39c5baSBill Taylor 	 */
16729e39c5baSBill Taylor 	pi->p_base_lid		= portinfo.LID;
16739e39c5baSBill Taylor 	pi->p_qkey_violations	= portinfo.Q_KeyViolations;
16749e39c5baSBill Taylor 	pi->p_pkey_violations	= portinfo.P_KeyViolations;
16759e39c5baSBill Taylor 	pi->p_sm_sl		= portinfo.MasterSMSL;
16769e39c5baSBill Taylor 	pi->p_sm_lid		= portinfo.MasterSMLID;
16779e39c5baSBill Taylor 	pi->p_linkstate		= portinfo.PortState;
16789e39c5baSBill Taylor 	pi->p_port_num		= portinfo.LocalPortNum;
1679d9c882faSBill Taylor 	pi->p_phys_state	= portinfo.PortPhysicalState;
1680d9c882faSBill Taylor 	pi->p_width_supported	= portinfo.LinkWidthSupported;
1681d9c882faSBill Taylor 	pi->p_width_enabled	= portinfo.LinkWidthEnabled;
1682d9c882faSBill Taylor 	pi->p_width_active	= portinfo.LinkWidthActive;
1683d9c882faSBill Taylor 	pi->p_speed_supported	= portinfo.LinkSpeedSupported;
1684d9c882faSBill Taylor 	pi->p_speed_enabled	= portinfo.LinkSpeedEnabled;
1685d9c882faSBill Taylor 	pi->p_speed_active	= portinfo.LinkSpeedActive;
16869e39c5baSBill Taylor 	pi->p_mtu		= portinfo.MTUCap;
16879e39c5baSBill Taylor 	pi->p_lmc		= portinfo.LMC;
16889e39c5baSBill Taylor 	pi->p_max_vl		= portinfo.VLCap;
16899e39c5baSBill Taylor 	pi->p_subnet_timeout	= portinfo.SubnetTimeOut;
16909e39c5baSBill Taylor 	pi->p_msg_sz		= ((uint32_t)1 << TAVOR_QP_LOG_MAX_MSGSZ);
16919e39c5baSBill Taylor 	tbl_size = state->ts_cfg_profile->cp_log_max_gidtbl;
16929e39c5baSBill Taylor 	pi->p_sgid_tbl_sz	= (1 << tbl_size);
16939e39c5baSBill Taylor 	tbl_size = state->ts_cfg_profile->cp_log_max_pkeytbl;
16949e39c5baSBill Taylor 	pi->p_pkey_tbl_sz	= (1 << tbl_size);
16959e39c5baSBill Taylor 
16969e39c5baSBill Taylor 	/*
16979e39c5baSBill Taylor 	 * Convert InfiniBand-defined port capability flags to the format
16989e39c5baSBill Taylor 	 * specified by the IBTF
16999e39c5baSBill Taylor 	 */
17009e39c5baSBill Taylor 	if (portinfo.CapabilityMask & SM_CAP_MASK_IS_SM)
17019e39c5baSBill Taylor 		pi->p_capabilities |= IBT_PORT_CAP_SM;
17029e39c5baSBill Taylor 	if (portinfo.CapabilityMask & SM_CAP_MASK_IS_SM_DISABLED)
17039e39c5baSBill Taylor 		pi->p_capabilities |= IBT_PORT_CAP_SM_DISABLED;
17049e39c5baSBill Taylor 	if (portinfo.CapabilityMask & SM_CAP_MASK_IS_SNMP_SUPPD)
17059e39c5baSBill Taylor 		pi->p_capabilities |= IBT_PORT_CAP_SNMP_TUNNEL;
17069e39c5baSBill Taylor 	if (portinfo.CapabilityMask & SM_CAP_MASK_IS_DM_SUPPD)
17079e39c5baSBill Taylor 		pi->p_capabilities |= IBT_PORT_CAP_DM;
17089e39c5baSBill Taylor 	if (portinfo.CapabilityMask & SM_CAP_MASK_IS_VM_SUPPD)
17099e39c5baSBill Taylor 		pi->p_capabilities |= IBT_PORT_CAP_VENDOR;
17109e39c5baSBill Taylor 
17119e39c5baSBill Taylor 	/*
17129e39c5baSBill Taylor 	 * Fill in the SGID table.  Since the only access to the Tavor
17139e39c5baSBill Taylor 	 * GID tables is through the firmware's MAD_IFC interface, we
17149e39c5baSBill Taylor 	 * post as many GetGUIDInfo MADs as necessary to read in the entire
17159e39c5baSBill Taylor 	 * contents of the SGID table (for the specified port).  Note:  The
17169e39c5baSBill Taylor 	 * GetGUIDInfo command only gets eight GUIDs per operation.  These
17179e39c5baSBill Taylor 	 * GUIDs are then appended to the GID prefix for the port (from the
17189e39c5baSBill Taylor 	 * GetPortInfo above) to form the entire SGID table.
17199e39c5baSBill Taylor 	 */
17209e39c5baSBill Taylor 	for (i = 0; i < pi->p_sgid_tbl_sz; i += 8) {
17219e39c5baSBill Taylor 		status = tavor_getguidinfo_cmd_post(state, port, i >> 3,
17229e39c5baSBill Taylor 		    TAVOR_SLEEPFLAG_FOR_CONTEXT(), &guidinfo);
17239e39c5baSBill Taylor 		if (status != TAVOR_CMD_SUCCESS) {
17249e39c5baSBill Taylor 			cmn_err(CE_CONT, "Tavor: GetGUIDInfo (port %02d) "
17259e39c5baSBill Taylor 			    "command failed: %08x\n", port, status);
17269e39c5baSBill Taylor 			return (ibc_get_ci_failure(0));
17279e39c5baSBill Taylor 		}
17289e39c5baSBill Taylor 
17299e39c5baSBill Taylor 		/* Figure out how many of the entries are valid */
17309e39c5baSBill Taylor 		sgid_max = min((pi->p_sgid_tbl_sz - i), 8);
17319e39c5baSBill Taylor 		for (j = 0; j < sgid_max; j++) {
17329e39c5baSBill Taylor 			indx = (i + j);
17339e39c5baSBill Taylor 			sgid = &pi->p_sgid_tbl[indx];
17349e39c5baSBill Taylor 			_NOTE(NOW_INVISIBLE_TO_OTHER_THREADS(*sgid))
17359e39c5baSBill Taylor 			sgid->gid_prefix = portinfo.GidPrefix;
17369e39c5baSBill Taylor 			sgid->gid_guid	 = guidinfo.GUIDBlocks[j];
17379e39c5baSBill Taylor 		}
17389e39c5baSBill Taylor 	}
17399e39c5baSBill Taylor 
17409e39c5baSBill Taylor 	/*
17419e39c5baSBill Taylor 	 * Fill in the PKey table.  Just as for the GID tables above, the
17429e39c5baSBill Taylor 	 * only access to the Tavor PKey tables is through the firmware's
17439e39c5baSBill Taylor 	 * MAD_IFC interface.  We post as many GetPKeyTable MADs as necessary
17449e39c5baSBill Taylor 	 * to read in the entire contents of the PKey table (for the specified
17459e39c5baSBill Taylor 	 * port).  Note:  The GetPKeyTable command only gets 32 PKeys per
17469e39c5baSBill Taylor 	 * operation.
17479e39c5baSBill Taylor 	 */
17489e39c5baSBill Taylor 	for (i = 0; i < pi->p_pkey_tbl_sz; i += 32) {
17499e39c5baSBill Taylor 		status = tavor_getpkeytable_cmd_post(state, port, i,
17509e39c5baSBill Taylor 		    TAVOR_SLEEPFLAG_FOR_CONTEXT(), &pkeytable);
17519e39c5baSBill Taylor 		if (status != TAVOR_CMD_SUCCESS) {
17529e39c5baSBill Taylor 			cmn_err(CE_CONT, "Tavor: GetPKeyTable (port %02d) "
17539e39c5baSBill Taylor 			    "command failed: %08x\n", port, status);
17549e39c5baSBill Taylor 			return (ibc_get_ci_failure(0));
17559e39c5baSBill Taylor 		}
17569e39c5baSBill Taylor 
17579e39c5baSBill Taylor 		/* Figure out how many of the entries are valid */
17589e39c5baSBill Taylor 		pkey_max = min((pi->p_pkey_tbl_sz - i), 32);
17599e39c5baSBill Taylor 		for (j = 0; j < pkey_max; j++) {
17609e39c5baSBill Taylor 			indx = (i + j);
17619e39c5baSBill Taylor 			pi->p_pkey_tbl[indx] = pkeytable.P_KeyTableBlocks[j];
17629e39c5baSBill Taylor 		}
17639e39c5baSBill Taylor 	}
17649e39c5baSBill Taylor 
17659e39c5baSBill Taylor 	return (DDI_SUCCESS);
17669e39c5baSBill Taylor }
17679e39c5baSBill Taylor 
17689e39c5baSBill Taylor 
17699e39c5baSBill Taylor /*
17709e39c5baSBill Taylor  * tavor_port_modify()
17719e39c5baSBill Taylor  *    Context: Can be called only from user or kernel context.
17729e39c5baSBill Taylor  */
17739e39c5baSBill Taylor /* ARGSUSED */
17749e39c5baSBill Taylor int
tavor_port_modify(tavor_state_t * state,uint8_t port,ibt_port_modify_flags_t flags,uint8_t init_type)17759e39c5baSBill Taylor tavor_port_modify(tavor_state_t *state, uint8_t port,
17769e39c5baSBill Taylor     ibt_port_modify_flags_t flags, uint8_t init_type)
17779e39c5baSBill Taylor {
17789e39c5baSBill Taylor 	sm_portinfo_t	portinfo;
17799e39c5baSBill Taylor 	uint32_t	capmask, reset_qkey;
17809e39c5baSBill Taylor 	int		status;
17819e39c5baSBill Taylor 
17829e39c5baSBill Taylor 	/*
17839e39c5baSBill Taylor 	 * Return an error if either of the unsupported flags are set
17849e39c5baSBill Taylor 	 */
17859e39c5baSBill Taylor 	if ((flags & IBT_PORT_SHUTDOWN) ||
17869e39c5baSBill Taylor 	    (flags & IBT_PORT_SET_INIT_TYPE)) {
17879e39c5baSBill Taylor 		return (IBT_NOT_SUPPORTED);
17889e39c5baSBill Taylor 	}
17899e39c5baSBill Taylor 
17909e39c5baSBill Taylor 	/*
17919e39c5baSBill Taylor 	 * Determine whether we are trying to reset the QKey counter
17929e39c5baSBill Taylor 	 */
17939e39c5baSBill Taylor 	reset_qkey = (flags & IBT_PORT_RESET_QKEY) ? 1 : 0;
17949e39c5baSBill Taylor 
17959e39c5baSBill Taylor 	/* Validate that specified port number is legal */
17969e39c5baSBill Taylor 	if (!tavor_portnum_is_valid(state, port)) {
17979e39c5baSBill Taylor 		return (IBT_HCA_PORT_INVALID);
17989e39c5baSBill Taylor 	}
17999e39c5baSBill Taylor 
18009e39c5baSBill Taylor 	/*
18019e39c5baSBill Taylor 	 * Use the Tavor MAD_IFC command to post a GetPortInfo MAD to the
18029e39c5baSBill Taylor 	 * firmware (for the specified port number).  This returns a full
18039e39c5baSBill Taylor 	 * PortInfo MAD (in "portinfo") from which we pull the current
18049e39c5baSBill Taylor 	 * capability mask.  We then modify the capability mask as directed
18059e39c5baSBill Taylor 	 * by the "pmod_flags" field, and write the updated capability mask
18069e39c5baSBill Taylor 	 * using the Tavor SET_IB command (below).
18079e39c5baSBill Taylor 	 */
18089e39c5baSBill Taylor 	status = tavor_getportinfo_cmd_post(state, port,
18099e39c5baSBill Taylor 	    TAVOR_SLEEPFLAG_FOR_CONTEXT(), &portinfo);
18109e39c5baSBill Taylor 	if (status != TAVOR_CMD_SUCCESS) {
18119e39c5baSBill Taylor 		return (ibc_get_ci_failure(0));
18129e39c5baSBill Taylor 	}
18139e39c5baSBill Taylor 
18149e39c5baSBill Taylor 	/*
18159e39c5baSBill Taylor 	 * Convert InfiniBand-defined port capability flags to the format
18169e39c5baSBill Taylor 	 * specified by the IBTF.  Specifically, we modify the capability
18179e39c5baSBill Taylor 	 * mask based on the specified values.
18189e39c5baSBill Taylor 	 */
18199e39c5baSBill Taylor 	capmask = portinfo.CapabilityMask;
18209e39c5baSBill Taylor 
18219e39c5baSBill Taylor 	if (flags & IBT_PORT_RESET_SM)
18229e39c5baSBill Taylor 		capmask &= ~SM_CAP_MASK_IS_SM;
18239e39c5baSBill Taylor 	else if (flags & IBT_PORT_SET_SM)
18249e39c5baSBill Taylor 		capmask |= SM_CAP_MASK_IS_SM;
18259e39c5baSBill Taylor 
18269e39c5baSBill Taylor 	if (flags & IBT_PORT_RESET_SNMP)
18279e39c5baSBill Taylor 		capmask &= ~SM_CAP_MASK_IS_SNMP_SUPPD;
18289e39c5baSBill Taylor 	else if (flags & IBT_PORT_SET_SNMP)
18299e39c5baSBill Taylor 		capmask |= SM_CAP_MASK_IS_SNMP_SUPPD;
18309e39c5baSBill Taylor 
18319e39c5baSBill Taylor 	if (flags & IBT_PORT_RESET_DEVMGT)
18329e39c5baSBill Taylor 		capmask &= ~SM_CAP_MASK_IS_DM_SUPPD;
18339e39c5baSBill Taylor 	else if (flags & IBT_PORT_SET_DEVMGT)
18349e39c5baSBill Taylor 		capmask |= SM_CAP_MASK_IS_DM_SUPPD;
18359e39c5baSBill Taylor 
18369e39c5baSBill Taylor 	if (flags & IBT_PORT_RESET_VENDOR)
18379e39c5baSBill Taylor 		capmask &= ~SM_CAP_MASK_IS_VM_SUPPD;
18389e39c5baSBill Taylor 	else if (flags & IBT_PORT_SET_VENDOR)
18399e39c5baSBill Taylor 		capmask |= SM_CAP_MASK_IS_VM_SUPPD;
18409e39c5baSBill Taylor 
18419e39c5baSBill Taylor 	/*
18429e39c5baSBill Taylor 	 * Use the Tavor SET_IB command to update the capability mask and
18439e39c5baSBill Taylor 	 * (possibly) reset the QKey violation counter for the specified port.
18449e39c5baSBill Taylor 	 * Note: In general, this operation shouldn't fail.  If it does, then
18459e39c5baSBill Taylor 	 * it is an indication that something (probably in HW, but maybe in
18469e39c5baSBill Taylor 	 * SW) has gone seriously wrong.
18479e39c5baSBill Taylor 	 */
18489e39c5baSBill Taylor 	status = tavor_set_ib_cmd_post(state, capmask, port, reset_qkey,
18499e39c5baSBill Taylor 	    TAVOR_SLEEPFLAG_FOR_CONTEXT());
18509e39c5baSBill Taylor 	if (status != TAVOR_CMD_SUCCESS) {
18519e39c5baSBill Taylor 		TAVOR_WARNING(state, "failed to modify port capabilities");
18529e39c5baSBill Taylor 		cmn_err(CE_CONT, "Tavor: SET_IB (port %02d) command failed: "
18539e39c5baSBill Taylor 		    "%08x\n", port, status);
18549e39c5baSBill Taylor 		return (ibc_get_ci_failure(0));
18559e39c5baSBill Taylor 	}
18569e39c5baSBill Taylor 
18579e39c5baSBill Taylor 	return (DDI_SUCCESS);
18589e39c5baSBill Taylor }
18599e39c5baSBill Taylor 
18609e39c5baSBill Taylor 
18619e39c5baSBill Taylor /*
18629e39c5baSBill Taylor  * tavor_set_addr_path()
18639e39c5baSBill Taylor  *    Context: Can be called from interrupt or base context.
18649e39c5baSBill Taylor  *
18659e39c5baSBill Taylor  * Note: This routine is used for two purposes.  It is used to fill in the
18669e39c5baSBill Taylor  * Tavor UDAV fields, and it is used to fill in the address path information
18679e39c5baSBill Taylor  * for QPs.  Because the two Tavor structures are similar, common fields can
18689e39c5baSBill Taylor  * be filled in here.  Because they are slightly different, however, we pass
18699e39c5baSBill Taylor  * an additional flag to indicate which type is being filled.
18709e39c5baSBill Taylor  */
18719e39c5baSBill Taylor int
tavor_set_addr_path(tavor_state_t * state,ibt_adds_vect_t * av,tavor_hw_addr_path_t * path,uint_t type,tavor_qphdl_t qp)18729e39c5baSBill Taylor tavor_set_addr_path(tavor_state_t *state, ibt_adds_vect_t *av,
18739e39c5baSBill Taylor     tavor_hw_addr_path_t *path, uint_t type, tavor_qphdl_t qp)
18749e39c5baSBill Taylor {
18759e39c5baSBill Taylor 	uint_t		gidtbl_sz;
18769e39c5baSBill Taylor 
18779e39c5baSBill Taylor 	_NOTE(NOW_INVISIBLE_TO_OTHER_THREADS(*av))
18789e39c5baSBill Taylor 	_NOTE(NOW_INVISIBLE_TO_OTHER_THREADS(*path))
18799e39c5baSBill Taylor 
18809e39c5baSBill Taylor 	path->ml_path	= av->av_src_path;
18819e39c5baSBill Taylor 	path->rlid	= av->av_dlid;
18829e39c5baSBill Taylor 	path->sl	= av->av_srvl;
18839e39c5baSBill Taylor 
18849e39c5baSBill Taylor 	/* Port number only valid (in "av_port_num") if this is a UDAV */
18859e39c5baSBill Taylor 	if (type == TAVOR_ADDRPATH_UDAV) {
18869e39c5baSBill Taylor 		path->portnum = av->av_port_num;
18879e39c5baSBill Taylor 	}
18889e39c5baSBill Taylor 
18899e39c5baSBill Taylor 	/*
18909e39c5baSBill Taylor 	 * Validate (and fill in) static rate.
18919e39c5baSBill Taylor 	 *
18929e39c5baSBill Taylor 	 * The stat_rate_sup is used to decide how to set the rate and
18939e39c5baSBill Taylor 	 * if it is zero, the driver uses the old interface.
18949e39c5baSBill Taylor 	 */
18959e39c5baSBill Taylor 	if (state->ts_devlim.stat_rate_sup) {
18969e39c5baSBill Taylor 		if (av->av_srate == IBT_SRATE_20) {
18979e39c5baSBill Taylor 			path->max_stat_rate = 0; /* 4x@DDR injection rate */
18989e39c5baSBill Taylor 		} else if (av->av_srate == IBT_SRATE_5) {
18999e39c5baSBill Taylor 			path->max_stat_rate = 3; /* 1x@DDR injection rate */
19009e39c5baSBill Taylor 		} else if (av->av_srate == IBT_SRATE_10) {
19019e39c5baSBill Taylor 			path->max_stat_rate = 2; /* 4x@SDR injection rate */
19029e39c5baSBill Taylor 		} else if (av->av_srate == IBT_SRATE_2) {
19039e39c5baSBill Taylor 			path->max_stat_rate = 1; /* 1x@SDR injection rate */
19049e39c5baSBill Taylor 		} else if (av->av_srate == IBT_SRATE_NOT_SPECIFIED) {
19059e39c5baSBill Taylor 			path->max_stat_rate = 0; /* Max */
19069e39c5baSBill Taylor 		} else {
19079e39c5baSBill Taylor 			return (IBT_STATIC_RATE_INVALID);
19089e39c5baSBill Taylor 		}
19099e39c5baSBill Taylor 	} else {
19109e39c5baSBill Taylor 		if (av->av_srate == IBT_SRATE_10) {
19119e39c5baSBill Taylor 			path->max_stat_rate = 0; /* 4x@SDR injection rate */
19129e39c5baSBill Taylor 		} else if (av->av_srate == IBT_SRATE_2) {
19139e39c5baSBill Taylor 			path->max_stat_rate = 1; /* 1x@SDR injection rate */
19149e39c5baSBill Taylor 		} else if (av->av_srate == IBT_SRATE_NOT_SPECIFIED) {
19159e39c5baSBill Taylor 			path->max_stat_rate = 0; /* Max */
19169e39c5baSBill Taylor 		} else {
19179e39c5baSBill Taylor 			return (IBT_STATIC_RATE_INVALID);
19189e39c5baSBill Taylor 		}
19199e39c5baSBill Taylor 	}
19209e39c5baSBill Taylor 
19219e39c5baSBill Taylor 	/*
19229e39c5baSBill Taylor 	 * If this is a QP operation save asoft copy.
19239e39c5baSBill Taylor 	 */
19249e39c5baSBill Taylor 	if (qp) {
19259e39c5baSBill Taylor 		_NOTE(NOW_INVISIBLE_TO_OTHER_THREADS(qp->qp_save_srate))
19269e39c5baSBill Taylor 		qp->qp_save_srate = av->av_srate;
19279e39c5baSBill Taylor 	}
19289e39c5baSBill Taylor 
19299e39c5baSBill Taylor 	/* If "grh" flag is set, then check for valid SGID index too */
19309e39c5baSBill Taylor 	gidtbl_sz = (1 << state->ts_devlim.log_max_gid);
19319e39c5baSBill Taylor 	if ((av->av_send_grh) && (av->av_sgid_ix > gidtbl_sz)) {
19329e39c5baSBill Taylor 		return (IBT_SGID_INVALID);
19339e39c5baSBill Taylor 	}
19349e39c5baSBill Taylor 
19359e39c5baSBill Taylor 	/*
19369e39c5baSBill Taylor 	 * Fill in all "global" values regardless of the value in the GRH
19379e39c5baSBill Taylor 	 * flag.  Because "grh" is not set unless "av_send_grh" is set, the
19389e39c5baSBill Taylor 	 * hardware will ignore the other "global" values as necessary.  Note:
19399e39c5baSBill Taylor 	 * SW does this here to enable later query operations to return
19409e39c5baSBill Taylor 	 * exactly the same params that were passed when the addr path was
19419e39c5baSBill Taylor 	 * last written.
19429e39c5baSBill Taylor 	 */
19439e39c5baSBill Taylor 	path->grh = av->av_send_grh;
19449e39c5baSBill Taylor 	if (type == TAVOR_ADDRPATH_QP) {
19459e39c5baSBill Taylor 		path->mgid_index = av->av_sgid_ix;
19469e39c5baSBill Taylor 	} else {
19479e39c5baSBill Taylor 		/*
19489e39c5baSBill Taylor 		 * For Tavor UDAV, the "mgid_index" field is the index into
19499e39c5baSBill Taylor 		 * a combined table (not a per-port table). So some extra
19509e39c5baSBill Taylor 		 * calculations are necessary.
19519e39c5baSBill Taylor 		 */
19529e39c5baSBill Taylor 		path->mgid_index = ((av->av_port_num - 1) * gidtbl_sz) +
19539e39c5baSBill Taylor 		    av->av_sgid_ix;
19549e39c5baSBill Taylor 	}
19559e39c5baSBill Taylor 	path->flow_label = av->av_flow;
19569e39c5baSBill Taylor 	path->tclass	 = av->av_tclass;
19579e39c5baSBill Taylor 	path->hop_limit	 = av->av_hop;
19589e39c5baSBill Taylor 	path->rgid_h	 = av->av_dgid.gid_prefix;
19599e39c5baSBill Taylor 
19609e39c5baSBill Taylor 	/*
19619e39c5baSBill Taylor 	 * According to Tavor PRM, the (31:0) part of rgid_l must be set to
19629e39c5baSBill Taylor 	 * "0x2" if the 'grh' or 'g' bit is cleared.  It also says that we
19639e39c5baSBill Taylor 	 * only need to do it for UDAV's.  So we enforce that here.
19649e39c5baSBill Taylor 	 *
19659e39c5baSBill Taylor 	 * NOTE: The entire 64 bits worth of GUID info is actually being
19669e39c5baSBill Taylor 	 * preserved (for UDAVs) by the callers of this function
19679e39c5baSBill Taylor 	 * (tavor_ah_alloc() and tavor_ah_modify()) and as long as the
19689e39c5baSBill Taylor 	 * 'grh' bit is not set, the upper 32 bits (63:32) of rgid_l are
19699e39c5baSBill Taylor 	 * "don't care".
19709e39c5baSBill Taylor 	 */
19719e39c5baSBill Taylor 	if ((path->grh) || (type == TAVOR_ADDRPATH_QP)) {
19729e39c5baSBill Taylor 		path->rgid_l = av->av_dgid.gid_guid;
19739e39c5baSBill Taylor 	} else {
19749e39c5baSBill Taylor 		path->rgid_l = 0x2;
19759e39c5baSBill Taylor 	}
19769e39c5baSBill Taylor 
19779e39c5baSBill Taylor 	return (DDI_SUCCESS);
19789e39c5baSBill Taylor }
19799e39c5baSBill Taylor 
19809e39c5baSBill Taylor 
19819e39c5baSBill Taylor /*
19829e39c5baSBill Taylor  * tavor_get_addr_path()
19839e39c5baSBill Taylor  *    Context: Can be called from interrupt or base context.
19849e39c5baSBill Taylor  *
19859e39c5baSBill Taylor  * Note: Just like tavor_set_addr_path() above, this routine is used for two
19869e39c5baSBill Taylor  * purposes.  It is used to read in the Tavor UDAV fields, and it is used to
19879e39c5baSBill Taylor  * read in the address path information for QPs.  Because the two Tavor
19889e39c5baSBill Taylor  * structures are similar, common fields can be read in here.  But because
19899e39c5baSBill Taylor  * they are slightly different, we pass an additional flag to indicate which
19909e39c5baSBill Taylor  * type is being read.
19919e39c5baSBill Taylor  */
19929e39c5baSBill Taylor void
tavor_get_addr_path(tavor_state_t * state,tavor_hw_addr_path_t * path,ibt_adds_vect_t * av,uint_t type,tavor_qphdl_t qp)19939e39c5baSBill Taylor tavor_get_addr_path(tavor_state_t *state, tavor_hw_addr_path_t *path,
19949e39c5baSBill Taylor     ibt_adds_vect_t *av, uint_t type, tavor_qphdl_t qp)
19959e39c5baSBill Taylor {
19969e39c5baSBill Taylor 	uint_t		gidtbl_sz;
19979e39c5baSBill Taylor 
19989e39c5baSBill Taylor 	_NOTE(NOW_INVISIBLE_TO_OTHER_THREADS(*path))
19999e39c5baSBill Taylor 	_NOTE(NOW_INVISIBLE_TO_OTHER_THREADS(*av))
20009e39c5baSBill Taylor 
20019e39c5baSBill Taylor 	av->av_src_path	= path->ml_path;
20029e39c5baSBill Taylor 	av->av_port_num	= path->portnum;
20039e39c5baSBill Taylor 	av->av_dlid	= path->rlid;
20049e39c5baSBill Taylor 	av->av_srvl	= path->sl;
20059e39c5baSBill Taylor 
20069e39c5baSBill Taylor 	/*
20079e39c5baSBill Taylor 	 * Set "av_ipd" value from max_stat_rate.
20089e39c5baSBill Taylor 	 */
20099e39c5baSBill Taylor 	if (qp) {
20109e39c5baSBill Taylor 		/*
20119e39c5baSBill Taylor 		 * If a QP operation use the soft copy
20129e39c5baSBill Taylor 		 */
20139e39c5baSBill Taylor 		_NOTE(NOW_INVISIBLE_TO_OTHER_THREADS(qp->qp_save_srate))
20149e39c5baSBill Taylor 		av->av_srate = qp->qp_save_srate;
20159e39c5baSBill Taylor 	} else {
20169e39c5baSBill Taylor 		/*
20179e39c5baSBill Taylor 		 * The stat_rate_sup is used to decide how the srate value is
20189e39c5baSBill Taylor 		 * set and
20199e39c5baSBill Taylor 		 * if it is zero, the driver uses the old interface.
20209e39c5baSBill Taylor 		 */
20219e39c5baSBill Taylor 		if (state->ts_devlim.stat_rate_sup) {
20229e39c5baSBill Taylor 			if (path->max_stat_rate	== 0) {
20239e39c5baSBill Taylor 				av->av_srate = IBT_SRATE_20; /* 4x@DDR rate */
20249e39c5baSBill Taylor 			} else if (path->max_stat_rate	== 1) {
20259e39c5baSBill Taylor 				av->av_srate = IBT_SRATE_2;  /* 1x@SDR rate */
20269e39c5baSBill Taylor 			} else if (path->max_stat_rate	== 2) {
20279e39c5baSBill Taylor 				av->av_srate = IBT_SRATE_10; /* 4x@SDR rate */
20289e39c5baSBill Taylor 			} else if (path->max_stat_rate	== 3) {
20299e39c5baSBill Taylor 				av->av_srate = IBT_SRATE_5;  /* 1xDDR rate */
20309e39c5baSBill Taylor 			}
20319e39c5baSBill Taylor 		} else {
20329e39c5baSBill Taylor 			if (path->max_stat_rate	== 0) {
20339e39c5baSBill Taylor 				av->av_srate = IBT_SRATE_10; /* 4x@SDR rate */
20349e39c5baSBill Taylor 			} else if (path->max_stat_rate	== 1) {
20359e39c5baSBill Taylor 				av->av_srate = IBT_SRATE_2;  /* 1x@SDR rate */
20369e39c5baSBill Taylor 			}
20379e39c5baSBill Taylor 		}
20389e39c5baSBill Taylor 	}
20399e39c5baSBill Taylor 
20409e39c5baSBill Taylor 	/*
20419e39c5baSBill Taylor 	 * Extract all "global" values regardless of the value in the GRH
20429e39c5baSBill Taylor 	 * flag.  Because "av_send_grh" is set only if "grh" is set, software
20439e39c5baSBill Taylor 	 * knows to ignore the other "global" values as necessary.  Note: SW
20449e39c5baSBill Taylor 	 * does it this way to enable these query operations to return exactly
20459e39c5baSBill Taylor 	 * the same params that were passed when the addr path was last written.
20469e39c5baSBill Taylor 	 */
20479e39c5baSBill Taylor 	av->av_send_grh		= path->grh;
20489e39c5baSBill Taylor 	if (type == TAVOR_ADDRPATH_QP) {
20499e39c5baSBill Taylor 		av->av_sgid_ix  = path->mgid_index;
20509e39c5baSBill Taylor 	} else {
20519e39c5baSBill Taylor 		/*
20529e39c5baSBill Taylor 		 * For Tavor UDAV, the "mgid_index" field is the index into
20539e39c5baSBill Taylor 		 * a combined table (not a per-port table). So some extra
20549e39c5baSBill Taylor 		 * calculations are necessary.
20559e39c5baSBill Taylor 		 */
20569e39c5baSBill Taylor 		gidtbl_sz = (1 << state->ts_devlim.log_max_gid);
20579e39c5baSBill Taylor 		av->av_sgid_ix = path->mgid_index - ((av->av_port_num - 1) *
20589e39c5baSBill Taylor 		    gidtbl_sz);
20599e39c5baSBill Taylor 	}
20609e39c5baSBill Taylor 	av->av_flow		= path->flow_label;
20619e39c5baSBill Taylor 	av->av_tclass		= path->tclass;
20629e39c5baSBill Taylor 	av->av_hop		= path->hop_limit;
20639e39c5baSBill Taylor 	av->av_dgid.gid_prefix	= path->rgid_h;
20649e39c5baSBill Taylor 	av->av_dgid.gid_guid	= path->rgid_l;
20659e39c5baSBill Taylor }
20669e39c5baSBill Taylor 
20679e39c5baSBill Taylor 
20689e39c5baSBill Taylor /*
20699e39c5baSBill Taylor  * tavor_portnum_is_valid()
20709e39c5baSBill Taylor  *    Context: Can be called from interrupt or base context.
20719e39c5baSBill Taylor  */
20729e39c5baSBill Taylor int
tavor_portnum_is_valid(tavor_state_t * state,uint_t portnum)20739e39c5baSBill Taylor tavor_portnum_is_valid(tavor_state_t *state, uint_t portnum)
20749e39c5baSBill Taylor {
20759e39c5baSBill Taylor 	uint_t	max_port;
20769e39c5baSBill Taylor 
20779e39c5baSBill Taylor 	max_port = state->ts_cfg_profile->cp_num_ports;
20789e39c5baSBill Taylor 	if ((portnum <= max_port) && (portnum != 0)) {
20799e39c5baSBill Taylor 		return (1);
20809e39c5baSBill Taylor 	} else {
20819e39c5baSBill Taylor 		return (0);
20829e39c5baSBill Taylor 	}
20839e39c5baSBill Taylor }
20849e39c5baSBill Taylor 
20859e39c5baSBill Taylor 
20869e39c5baSBill Taylor /*
20879e39c5baSBill Taylor  * tavor_pkeyindex_is_valid()
20889e39c5baSBill Taylor  *    Context: Can be called from interrupt or base context.
20899e39c5baSBill Taylor  */
20909e39c5baSBill Taylor int
tavor_pkeyindex_is_valid(tavor_state_t * state,uint_t pkeyindx)20919e39c5baSBill Taylor tavor_pkeyindex_is_valid(tavor_state_t *state, uint_t pkeyindx)
20929e39c5baSBill Taylor {
20939e39c5baSBill Taylor 	uint_t	max_pkeyindx;
20949e39c5baSBill Taylor 
20959e39c5baSBill Taylor 	max_pkeyindx = 1 << state->ts_cfg_profile->cp_log_max_pkeytbl;
20969e39c5baSBill Taylor 	if (pkeyindx < max_pkeyindx) {
20979e39c5baSBill Taylor 		return (1);
20989e39c5baSBill Taylor 	} else {
20999e39c5baSBill Taylor 		return (0);
21009e39c5baSBill Taylor 	}
21019e39c5baSBill Taylor }
21029e39c5baSBill Taylor 
21039e39c5baSBill Taylor 
21049e39c5baSBill Taylor /*
21059e39c5baSBill Taylor  * tavor_queue_alloc()
21069e39c5baSBill Taylor  *    Context: Can be called from interrupt or base context.
21079e39c5baSBill Taylor  */
21089e39c5baSBill Taylor int
tavor_queue_alloc(tavor_state_t * state,tavor_qalloc_info_t * qa_info,uint_t sleepflag)21099e39c5baSBill Taylor tavor_queue_alloc(tavor_state_t *state, tavor_qalloc_info_t *qa_info,
21109e39c5baSBill Taylor     uint_t sleepflag)
21119e39c5baSBill Taylor {
21129e39c5baSBill Taylor 	ddi_dma_attr_t		dma_attr;
21139e39c5baSBill Taylor 	int			(*callback)(caddr_t);
21149e39c5baSBill Taylor 	uint64_t		realsize, alloc_mask;
21159e39c5baSBill Taylor 	uint_t			dma_xfer_mode, type;
21169e39c5baSBill Taylor 	int			flag, status;
21179e39c5baSBill Taylor 
21189e39c5baSBill Taylor 	_NOTE(NOW_INVISIBLE_TO_OTHER_THREADS(*qa_info))
21199e39c5baSBill Taylor 
21209e39c5baSBill Taylor 	/* Set the callback flag appropriately */
21219e39c5baSBill Taylor 	callback = (sleepflag == TAVOR_SLEEP) ? DDI_DMA_SLEEP :
21229e39c5baSBill Taylor 	    DDI_DMA_DONTWAIT;
21239e39c5baSBill Taylor 
21249e39c5baSBill Taylor 	/*
21259e39c5baSBill Taylor 	 * Initialize many of the default DMA attributes.  Then set additional
21269e39c5baSBill Taylor 	 * alignment restrictions as necessary for the queue memory.  Also
21279e39c5baSBill Taylor 	 * respect the configured value for IOMMU bypass
21289e39c5baSBill Taylor 	 */
21299e39c5baSBill Taylor 	tavor_dma_attr_init(&dma_attr);
21309e39c5baSBill Taylor 	dma_attr.dma_attr_align = qa_info->qa_bind_align;
21319e39c5baSBill Taylor 	type = state->ts_cfg_profile->cp_iommu_bypass;
21329e39c5baSBill Taylor 	if (type == TAVOR_BINDMEM_BYPASS) {
21339e39c5baSBill Taylor 		dma_attr.dma_attr_flags = DDI_DMA_FORCE_PHYSICAL;
21349e39c5baSBill Taylor 	}
21359e39c5baSBill Taylor 
21369e39c5baSBill Taylor 	/* Allocate a DMA handle */
21379e39c5baSBill Taylor 	status = ddi_dma_alloc_handle(state->ts_dip, &dma_attr, callback, NULL,
21389e39c5baSBill Taylor 	    &qa_info->qa_dmahdl);
21399e39c5baSBill Taylor 	if (status != DDI_SUCCESS) {
21409e39c5baSBill Taylor 		return (DDI_FAILURE);
21419e39c5baSBill Taylor 	}
21429e39c5baSBill Taylor 
21439e39c5baSBill Taylor 	/*
21449e39c5baSBill Taylor 	 * Determine the amount of memory to allocate, depending on the values
21459e39c5baSBill Taylor 	 * in "qa_bind_align" and "qa_alloc_align".  The problem we are trying
21469e39c5baSBill Taylor 	 * to solve here is that allocating a DMA handle with IOMMU bypass
21479e39c5baSBill Taylor 	 * (DDI_DMA_FORCE_PHYSICAL) constrains us to only requesting alignments
21489e39c5baSBill Taylor 	 * that are less than the page size.  Since we may need stricter
21499e39c5baSBill Taylor 	 * alignments on the memory allocated by ddi_dma_mem_alloc() (e.g. in
21509e39c5baSBill Taylor 	 * Tavor QP work queue memory allocation), we use the following method
21519e39c5baSBill Taylor 	 * to calculate how much additional memory to request, and we enforce
21529e39c5baSBill Taylor 	 * our own alignment on the allocated result.
21539e39c5baSBill Taylor 	 */
21549e39c5baSBill Taylor 	alloc_mask = qa_info->qa_alloc_align - 1;
21559e39c5baSBill Taylor 	if (qa_info->qa_bind_align == qa_info->qa_alloc_align) {
21569e39c5baSBill Taylor 		realsize = qa_info->qa_size;
21579e39c5baSBill Taylor 	} else {
21589e39c5baSBill Taylor 		realsize = qa_info->qa_size + alloc_mask;
21599e39c5baSBill Taylor 	}
21609e39c5baSBill Taylor 
21619e39c5baSBill Taylor 	/*
21629e39c5baSBill Taylor 	 * If we are to allocate the queue from system memory, then use
21639e39c5baSBill Taylor 	 * ddi_dma_mem_alloc() to find the space.  Otherwise, if we are to
21649e39c5baSBill Taylor 	 * allocate the queue from locally-attached DDR memory, then use the
21659e39c5baSBill Taylor 	 * vmem allocator to find the space.  In either case, return a pointer
21669e39c5baSBill Taylor 	 * to the memory range allocated (including any necessary alignment
21679e39c5baSBill Taylor 	 * adjustments), the "real" memory pointer, the "real" size, and a
21689e39c5baSBill Taylor 	 * ddi_acc_handle_t to use when reading from/writing to the memory.
21699e39c5baSBill Taylor 	 */
21709e39c5baSBill Taylor 	if (qa_info->qa_location == TAVOR_QUEUE_LOCATION_NORMAL) {
21719e39c5baSBill Taylor 
21729e39c5baSBill Taylor 		/*
21739e39c5baSBill Taylor 		 * Determine whether to map STREAMING or CONSISTENT.  This is
21749e39c5baSBill Taylor 		 * based on the value set in the configuration profile at
21759e39c5baSBill Taylor 		 * attach time.
21769e39c5baSBill Taylor 		 */
21779e39c5baSBill Taylor 		dma_xfer_mode = state->ts_cfg_profile->cp_streaming_consistent;
21789e39c5baSBill Taylor 
21799e39c5baSBill Taylor 		/* Allocate system memory for the queue */
21809e39c5baSBill Taylor 		status = ddi_dma_mem_alloc(qa_info->qa_dmahdl, realsize,
21819e39c5baSBill Taylor 		    &state->ts_reg_accattr, dma_xfer_mode, callback, NULL,
21829e39c5baSBill Taylor 		    (caddr_t *)&qa_info->qa_buf_real,
21839e39c5baSBill Taylor 		    (size_t *)&qa_info->qa_buf_realsz, &qa_info->qa_acchdl);
21849e39c5baSBill Taylor 		if (status != DDI_SUCCESS) {
21859e39c5baSBill Taylor 			ddi_dma_free_handle(&qa_info->qa_dmahdl);
21869e39c5baSBill Taylor 			return (DDI_FAILURE);
21879e39c5baSBill Taylor 		}
21889e39c5baSBill Taylor 
21899e39c5baSBill Taylor 		/*
21909e39c5baSBill Taylor 		 * Save temporary copy of the real pointer.  (This may be
21919e39c5baSBill Taylor 		 * modified in the last step below).
21929e39c5baSBill Taylor 		 */
21939e39c5baSBill Taylor 		qa_info->qa_buf_aligned = qa_info->qa_buf_real;
21949e39c5baSBill Taylor 
21959e39c5baSBill Taylor 	} else if (qa_info->qa_location == TAVOR_QUEUE_LOCATION_USERLAND) {
21969e39c5baSBill Taylor 
21979e39c5baSBill Taylor 		/* Allocate userland mappable memory for the queue */
21989e39c5baSBill Taylor 		flag = (sleepflag == TAVOR_SLEEP) ? DDI_UMEM_SLEEP :
21999e39c5baSBill Taylor 		    DDI_UMEM_NOSLEEP;
22009e39c5baSBill Taylor 		qa_info->qa_buf_real = ddi_umem_alloc(realsize, flag,
22019e39c5baSBill Taylor 		    &qa_info->qa_umemcookie);
22029e39c5baSBill Taylor 		if (qa_info->qa_buf_real == NULL) {
22039e39c5baSBill Taylor 			ddi_dma_free_handle(&qa_info->qa_dmahdl);
22049e39c5baSBill Taylor 			return (DDI_FAILURE);
22059e39c5baSBill Taylor 		}
22069e39c5baSBill Taylor 
22079e39c5baSBill Taylor 		/*
22089e39c5baSBill Taylor 		 * Save temporary copy of the real pointer.  (This may be
22099e39c5baSBill Taylor 		 * modified in the last step below).
22109e39c5baSBill Taylor 		 */
22119e39c5baSBill Taylor 		qa_info->qa_buf_aligned = qa_info->qa_buf_real;
22129e39c5baSBill Taylor 
22139e39c5baSBill Taylor 	} else {  /* TAVOR_QUEUE_LOCATION_INDDR */
22149e39c5baSBill Taylor 
22159e39c5baSBill Taylor 		/* Allocate DDR memory for the queue */
22169e39c5baSBill Taylor 		flag = (sleepflag == TAVOR_SLEEP) ? VM_SLEEP : VM_NOSLEEP;
22179e39c5baSBill Taylor 		qa_info->qa_buf_real = (uint32_t *)vmem_xalloc(
22189e39c5baSBill Taylor 		    state->ts_ddrvmem, realsize, qa_info->qa_bind_align, 0, 0,
22199e39c5baSBill Taylor 		    NULL, NULL, flag);
22209e39c5baSBill Taylor 		if (qa_info->qa_buf_real == NULL) {
22219e39c5baSBill Taylor 			ddi_dma_free_handle(&qa_info->qa_dmahdl);
22229e39c5baSBill Taylor 			return (DDI_FAILURE);
22239e39c5baSBill Taylor 		}
22249e39c5baSBill Taylor 
22259e39c5baSBill Taylor 		/*
22269e39c5baSBill Taylor 		 * Since "qa_buf_real" will be a PCI address (the offset into
22279e39c5baSBill Taylor 		 * the DDR memory), we first need to do some calculations to
22289e39c5baSBill Taylor 		 * convert it to its kernel mapped address.  (Note: This may
22299e39c5baSBill Taylor 		 * be modified again below, when any additional "alloc"
22309e39c5baSBill Taylor 		 * alignment constraint is applied).
22319e39c5baSBill Taylor 		 */
22329e39c5baSBill Taylor 		qa_info->qa_buf_aligned = (uint32_t *)(uintptr_t)(((uintptr_t)
22339e39c5baSBill Taylor 		    state->ts_reg_ddr_baseaddr) + ((uintptr_t)
22349e39c5baSBill Taylor 		    qa_info->qa_buf_real - state->ts_ddr.ddr_baseaddr));
22359e39c5baSBill Taylor 		qa_info->qa_buf_realsz	= realsize;
22369e39c5baSBill Taylor 		qa_info->qa_acchdl	= state->ts_reg_ddrhdl;
22379e39c5baSBill Taylor 	}
22389e39c5baSBill Taylor 
22399e39c5baSBill Taylor 	/*
22409e39c5baSBill Taylor 	 * The last step is to ensure that the final address ("qa_buf_aligned")
22419e39c5baSBill Taylor 	 * has the appropriate "alloc" alignment restriction applied to it
22429e39c5baSBill Taylor 	 * (if necessary).
22439e39c5baSBill Taylor 	 */
22449e39c5baSBill Taylor 	if (qa_info->qa_bind_align != qa_info->qa_alloc_align) {
22459e39c5baSBill Taylor 		qa_info->qa_buf_aligned = (uint32_t *)(uintptr_t)(((uintptr_t)
22469e39c5baSBill Taylor 		    qa_info->qa_buf_aligned + alloc_mask) & ~alloc_mask);
22479e39c5baSBill Taylor 	}
22489e39c5baSBill Taylor 
22499e39c5baSBill Taylor 	return (DDI_SUCCESS);
22509e39c5baSBill Taylor }
22519e39c5baSBill Taylor 
22529e39c5baSBill Taylor 
22539e39c5baSBill Taylor /*
22549e39c5baSBill Taylor  * tavor_queue_free()
22559e39c5baSBill Taylor  *    Context: Can be called from interrupt or base context.
22569e39c5baSBill Taylor  */
22579e39c5baSBill Taylor void
tavor_queue_free(tavor_state_t * state,tavor_qalloc_info_t * qa_info)22589e39c5baSBill Taylor tavor_queue_free(tavor_state_t *state, tavor_qalloc_info_t *qa_info)
22599e39c5baSBill Taylor {
22609e39c5baSBill Taylor 	_NOTE(NOW_INVISIBLE_TO_OTHER_THREADS(*qa_info))
22619e39c5baSBill Taylor 
22629e39c5baSBill Taylor 	/*
22639e39c5baSBill Taylor 	 * Depending on how (i.e. from where) we allocated the memory for
22649e39c5baSBill Taylor 	 * this queue, we choose the appropriate method for releasing the
22659e39c5baSBill Taylor 	 * resources.
22669e39c5baSBill Taylor 	 */
22679e39c5baSBill Taylor 	if (qa_info->qa_location == TAVOR_QUEUE_LOCATION_NORMAL) {
22689e39c5baSBill Taylor 
22699e39c5baSBill Taylor 		ddi_dma_mem_free(&qa_info->qa_acchdl);
22709e39c5baSBill Taylor 
22719e39c5baSBill Taylor 	} else if (qa_info->qa_location == TAVOR_QUEUE_LOCATION_USERLAND) {
22729e39c5baSBill Taylor 
22739e39c5baSBill Taylor 		ddi_umem_free(qa_info->qa_umemcookie);
22749e39c5baSBill Taylor 
22759e39c5baSBill Taylor 	} else {  /* TAVOR_QUEUE_LOCATION_INDDR */
22769e39c5baSBill Taylor 
22779e39c5baSBill Taylor 		vmem_xfree(state->ts_ddrvmem, qa_info->qa_buf_real,
22789e39c5baSBill Taylor 		    qa_info->qa_buf_realsz);
22799e39c5baSBill Taylor 	}
22809e39c5baSBill Taylor 
22819e39c5baSBill Taylor 	/* Always free the dma handle */
22829e39c5baSBill Taylor 	ddi_dma_free_handle(&qa_info->qa_dmahdl);
22839e39c5baSBill Taylor }
22849e39c5baSBill Taylor 
22859e39c5baSBill Taylor 
22869e39c5baSBill Taylor /*
22879e39c5baSBill Taylor  * tavor_dmaattr_get()
22889e39c5baSBill Taylor  *    Context: Can be called from interrupt or base context.
22899e39c5baSBill Taylor  */
22909e39c5baSBill Taylor void
tavor_dma_attr_init(ddi_dma_attr_t * dma_attr)22919e39c5baSBill Taylor tavor_dma_attr_init(ddi_dma_attr_t *dma_attr)
22929e39c5baSBill Taylor {
22939e39c5baSBill Taylor 	_NOTE(NOW_INVISIBLE_TO_OTHER_THREADS(*dma_attr))
22949e39c5baSBill Taylor 
22959e39c5baSBill Taylor 	dma_attr->dma_attr_version	= DMA_ATTR_V0;
22969e39c5baSBill Taylor 	dma_attr->dma_attr_addr_lo	= 0;
22979e39c5baSBill Taylor 	dma_attr->dma_attr_addr_hi	= 0xFFFFFFFFFFFFFFFFull;
22989e39c5baSBill Taylor 	dma_attr->dma_attr_count_max	= 0xFFFFFFFFFFFFFFFFull;
22999e39c5baSBill Taylor 	dma_attr->dma_attr_align	= 1;
23009e39c5baSBill Taylor 	dma_attr->dma_attr_burstsizes	= 0x3FF;
23019e39c5baSBill Taylor 	dma_attr->dma_attr_minxfer	= 1;
23029e39c5baSBill Taylor 	dma_attr->dma_attr_maxxfer	= 0xFFFFFFFFFFFFFFFFull;
23039e39c5baSBill Taylor 	dma_attr->dma_attr_seg		= 0xFFFFFFFFFFFFFFFFull;
23049e39c5baSBill Taylor 	dma_attr->dma_attr_sgllen	= 0x7FFFFFFF;
23059e39c5baSBill Taylor 	dma_attr->dma_attr_granular	= 1;
23069e39c5baSBill Taylor 	dma_attr->dma_attr_flags	= 0;
23079e39c5baSBill Taylor }
2308