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*17a2b317SBill Taylor  * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
249e39c5baSBill Taylor  */
259e39c5baSBill Taylor 
269e39c5baSBill Taylor /*
279e39c5baSBill Taylor  * hermon_misc.c
289e39c5baSBill Taylor  *    Hermon Miscellaneous routines - Address Handle, Multicast, Protection
299e39c5baSBill Taylor  *    Domain, and port-related operations
309e39c5baSBill Taylor  *
319e39c5baSBill Taylor  *    Implements all the routines necessary for allocating, freeing, querying
329e39c5baSBill Taylor  *    and modifying Address Handles and Protection Domains.  Also implements
339e39c5baSBill Taylor  *    all the routines necessary for adding and removing Queue Pairs to/from
349e39c5baSBill Taylor  *    Multicast Groups.  Lastly, it implements the routines necessary for
359e39c5baSBill Taylor  *    port-related query and modify operations.
369e39c5baSBill Taylor  */
379e39c5baSBill Taylor 
389e39c5baSBill Taylor #include <sys/types.h>
399e39c5baSBill Taylor #include <sys/conf.h>
409e39c5baSBill Taylor #include <sys/ddi.h>
419e39c5baSBill Taylor #include <sys/sunddi.h>
429e39c5baSBill Taylor #include <sys/modctl.h>
439e39c5baSBill Taylor #include <sys/bitmap.h>
449e39c5baSBill Taylor #include <sys/sysmacros.h>
459e39c5baSBill Taylor 
469e39c5baSBill Taylor #include <sys/ib/adapters/hermon/hermon.h>
479e39c5baSBill Taylor 
48c7facc54SBill Taylor extern int hermon_rdma_debug;
49*17a2b317SBill Taylor int hermon_fmr_verbose = 0;
509e39c5baSBill Taylor 
519e39c5baSBill Taylor static int hermon_mcg_qplist_add(hermon_state_t *state, hermon_mcghdl_t mcg,
529e39c5baSBill Taylor     hermon_hw_mcg_qp_list_t *mcg_qplist, hermon_qphdl_t qp, uint_t *qp_found);
539e39c5baSBill Taylor static int hermon_mcg_qplist_remove(hermon_mcghdl_t mcg,
549e39c5baSBill Taylor     hermon_hw_mcg_qp_list_t *mcg_qplist, hermon_qphdl_t qp);
559e39c5baSBill Taylor static void hermon_qp_mcg_refcnt_inc(hermon_qphdl_t qp);
569e39c5baSBill Taylor static void hermon_qp_mcg_refcnt_dec(hermon_qphdl_t qp);
579e39c5baSBill Taylor static uint_t hermon_mcg_walk_mgid_hash(hermon_state_t *state,
589e39c5baSBill Taylor     uint64_t start_indx, ib_gid_t mgid, uint_t *prev_indx);
599e39c5baSBill Taylor static void hermon_mcg_setup_new_hdr(hermon_mcghdl_t mcg,
609e39c5baSBill Taylor     hermon_hw_mcg_t *mcg_hdr, ib_gid_t mgid, hermon_rsrc_t *mcg_rsrc);
619e39c5baSBill Taylor static int hermon_mcg_hash_list_remove(hermon_state_t *state, uint_t curr_indx,
629e39c5baSBill Taylor     uint_t prev_indx, hermon_hw_mcg_t *mcg_entry);
639e39c5baSBill Taylor static int hermon_mcg_entry_invalidate(hermon_state_t *state,
649e39c5baSBill Taylor     hermon_hw_mcg_t *mcg_entry, uint_t indx);
659e39c5baSBill Taylor static int hermon_mgid_is_valid(ib_gid_t gid);
669e39c5baSBill Taylor static int hermon_mlid_is_valid(ib_lid_t lid);
67*17a2b317SBill Taylor static void hermon_fmr_cleanup(hermon_fmrhdl_t pool);
689e39c5baSBill Taylor 
699e39c5baSBill Taylor 
709e39c5baSBill Taylor #define	HERMON_MAX_DBR_PAGES_PER_USER	64
719e39c5baSBill Taylor #define	HERMON_DBR_KEY(index, page) \
729e39c5baSBill Taylor 	(((uint64_t)index) * HERMON_MAX_DBR_PAGES_PER_USER + (page))
739e39c5baSBill Taylor 
749e39c5baSBill Taylor static hermon_udbr_page_t *
hermon_dbr_new_user_page(hermon_state_t * state,uint_t index,uint_t page)759e39c5baSBill Taylor hermon_dbr_new_user_page(hermon_state_t *state, uint_t index,
769e39c5baSBill Taylor     uint_t page)
779e39c5baSBill Taylor {
789e39c5baSBill Taylor 	hermon_udbr_page_t *pagep;
799e39c5baSBill Taylor 	ddi_dma_attr_t dma_attr;
809e39c5baSBill Taylor 	uint_t cookiecnt;
811ed53a3fSBill Taylor 	int status;
829e39c5baSBill Taylor 	hermon_umap_db_entry_t *umapdb;
83*17a2b317SBill Taylor 	ulong_t pagesize = PAGESIZE;
849e39c5baSBill Taylor 
859e39c5baSBill Taylor 	pagep = kmem_alloc(sizeof (*pagep), KM_SLEEP);
869e39c5baSBill Taylor 	pagep->upg_index = page;
87*17a2b317SBill Taylor 	pagep->upg_nfree = pagesize / sizeof (hermon_dbr_t);
881ed53a3fSBill Taylor 
891ed53a3fSBill Taylor 	/* Allocate 1 bit per dbr for free/alloc management (0 => "free") */
90*17a2b317SBill Taylor 	pagep->upg_free = kmem_zalloc(pagesize / sizeof (hermon_dbr_t) / 8,
911ed53a3fSBill Taylor 	    KM_SLEEP);
92*17a2b317SBill Taylor 	pagep->upg_kvaddr = ddi_umem_alloc(pagesize, DDI_UMEM_SLEEP,
939e39c5baSBill Taylor 	    &pagep->upg_umemcookie); /* not HERMON_PAGESIZE here */
949e39c5baSBill Taylor 
959e39c5baSBill Taylor 	pagep->upg_buf = ddi_umem_iosetup(pagep->upg_umemcookie, 0,
96*17a2b317SBill Taylor 	    pagesize, B_WRITE, 0, 0, NULL, DDI_UMEM_SLEEP);
979e39c5baSBill Taylor 
989e39c5baSBill Taylor 	hermon_dma_attr_init(state, &dma_attr);
99c7facc54SBill Taylor #ifdef	__sparc
100c7facc54SBill Taylor 	if (state->hs_cfg_profile->cp_iommu_bypass == HERMON_BINDMEM_BYPASS)
101c7facc54SBill Taylor 		dma_attr.dma_attr_flags = DDI_DMA_FORCE_PHYSICAL;
102c7facc54SBill Taylor #endif
1039e39c5baSBill Taylor 	status = ddi_dma_alloc_handle(state->hs_dip, &dma_attr,
1049e39c5baSBill Taylor 	    DDI_DMA_SLEEP, NULL, &pagep->upg_dmahdl);
1059e39c5baSBill Taylor 	if (status != DDI_SUCCESS) {
1069e39c5baSBill Taylor 		IBTF_DPRINTF_L2("hermon", "hermon_new_user_page: "
1079e39c5baSBill Taylor 		    "ddi_dma_buf_bind_handle failed: %d", status);
1089e39c5baSBill Taylor 		return (NULL);
1099e39c5baSBill Taylor 	}
1109e39c5baSBill Taylor 	status = ddi_dma_buf_bind_handle(pagep->upg_dmahdl,
1119e39c5baSBill Taylor 	    pagep->upg_buf, DDI_DMA_RDWR | DDI_DMA_CONSISTENT,
1129e39c5baSBill Taylor 	    DDI_DMA_SLEEP, NULL, &pagep->upg_dmacookie, &cookiecnt);
1139e39c5baSBill Taylor 	if (status != DDI_SUCCESS) {
1149e39c5baSBill Taylor 		IBTF_DPRINTF_L2("hermon", "hermon_dbr_new_user_page: "
1159e39c5baSBill Taylor 		    "ddi_dma_buf_bind_handle failed: %d", status);
1169e39c5baSBill Taylor 		ddi_dma_free_handle(&pagep->upg_dmahdl);
1179e39c5baSBill Taylor 		return (NULL);
1189e39c5baSBill Taylor 	}
1199e39c5baSBill Taylor 	ASSERT(cookiecnt == 1);
1209e39c5baSBill Taylor 
1219e39c5baSBill Taylor 	/* create db entry for mmap */
1229e39c5baSBill Taylor 	umapdb = hermon_umap_db_alloc(state->hs_instance,
1239e39c5baSBill Taylor 	    HERMON_DBR_KEY(index, page), MLNX_UMAP_DBRMEM_RSRC,
1249e39c5baSBill Taylor 	    (uint64_t)(uintptr_t)pagep);
1259e39c5baSBill Taylor 	hermon_umap_db_add(umapdb);
1269e39c5baSBill Taylor 	return (pagep);
1279e39c5baSBill Taylor }
1289e39c5baSBill Taylor 
1299e39c5baSBill Taylor 
1309e39c5baSBill Taylor /*ARGSUSED*/
1319e39c5baSBill Taylor static int
hermon_user_dbr_alloc(hermon_state_t * state,uint_t index,ddi_acc_handle_t * acchdl,hermon_dbr_t ** vdbr,uint64_t * pdbr,uint64_t * mapoffset)1329e39c5baSBill Taylor hermon_user_dbr_alloc(hermon_state_t *state, uint_t index,
1339e39c5baSBill Taylor     ddi_acc_handle_t *acchdl, hermon_dbr_t **vdbr, uint64_t *pdbr,
1349e39c5baSBill Taylor     uint64_t *mapoffset)
1359e39c5baSBill Taylor {
1369e39c5baSBill Taylor 	hermon_user_dbr_t *udbr;
1379e39c5baSBill Taylor 	hermon_udbr_page_t *pagep;
1389e39c5baSBill Taylor 	uint_t next_page;
1391ed53a3fSBill Taylor 	int dbr_index;
1401ed53a3fSBill Taylor 	int i1, i2, i3, last;
1411ed53a3fSBill Taylor 	uint64_t u64, mask;
1429e39c5baSBill Taylor 
1439e39c5baSBill Taylor 	mutex_enter(&state->hs_dbr_lock);
1449e39c5baSBill Taylor 	for (udbr = state->hs_user_dbr; udbr != NULL; udbr = udbr->udbr_link)
1459e39c5baSBill Taylor 		if (udbr->udbr_index == index)
1469e39c5baSBill Taylor 			break;
1479e39c5baSBill Taylor 	if (udbr == NULL) {
1489e39c5baSBill Taylor 		udbr = kmem_alloc(sizeof (*udbr), KM_SLEEP);
1499e39c5baSBill Taylor 		udbr->udbr_link = state->hs_user_dbr;
1509e39c5baSBill Taylor 		state->hs_user_dbr = udbr;
1519e39c5baSBill Taylor 		udbr->udbr_index = index;
1529e39c5baSBill Taylor 		udbr->udbr_pagep = NULL;
1539e39c5baSBill Taylor 	}
1549e39c5baSBill Taylor 	pagep = udbr->udbr_pagep;
1559e39c5baSBill Taylor 	next_page = (pagep == NULL) ? 0 : (pagep->upg_index + 1);
1569e39c5baSBill Taylor 	while (pagep != NULL)
1579e39c5baSBill Taylor 		if (pagep->upg_nfree > 0)
1589e39c5baSBill Taylor 			break;
1599e39c5baSBill Taylor 		else
1609e39c5baSBill Taylor 			pagep = pagep->upg_link;
1619e39c5baSBill Taylor 	if (pagep == NULL) {
1629e39c5baSBill Taylor 		pagep = hermon_dbr_new_user_page(state, index, next_page);
1639e39c5baSBill Taylor 		if (pagep == NULL) {
1649e39c5baSBill Taylor 			mutex_exit(&state->hs_dbr_lock);
1659e39c5baSBill Taylor 			return (DDI_FAILURE);
1669e39c5baSBill Taylor 		}
1679e39c5baSBill Taylor 		pagep->upg_link = udbr->udbr_pagep;
1689e39c5baSBill Taylor 		udbr->udbr_pagep = pagep;
1699e39c5baSBill Taylor 	}
1701ed53a3fSBill Taylor 
1711ed53a3fSBill Taylor 	/* Since nfree > 0, we're assured the loops below will succeed */
1721ed53a3fSBill Taylor 
1731ed53a3fSBill Taylor 	/* First, find a 64-bit (not ~0) that has a free dbr */
1741ed53a3fSBill Taylor 	last = PAGESIZE / sizeof (uint64_t) / 64;
1751ed53a3fSBill Taylor 	mask = ~0ull;
1761ed53a3fSBill Taylor 	for (i1 = 0; i1 < last; i1++)
1771ed53a3fSBill Taylor 		if ((pagep->upg_free[i1] & mask) != mask)
1781ed53a3fSBill Taylor 			break;
1791ed53a3fSBill Taylor 	u64 = pagep->upg_free[i1];
1801ed53a3fSBill Taylor 
1811ed53a3fSBill Taylor 	/* Second, find a byte (not 0xff) that has a free dbr */
1821ed53a3fSBill Taylor 	last = sizeof (uint64_t) / sizeof (uint8_t);
1831ed53a3fSBill Taylor 	for (i2 = 0, mask = 0xff; i2 < last; i2++, mask <<= 8)
1841ed53a3fSBill Taylor 		if ((u64 & mask) != mask)
1851ed53a3fSBill Taylor 			break;
1861ed53a3fSBill Taylor 
1871ed53a3fSBill Taylor 	/* Third, find a bit that is free (0) */
1881ed53a3fSBill Taylor 	for (i3 = 0; i3 < sizeof (uint64_t) / sizeof (uint8_t); i3++)
1891ed53a3fSBill Taylor 		if ((u64 & (1ul << (i3 + 8 * i2))) == 0)
1901ed53a3fSBill Taylor 			break;
1911ed53a3fSBill Taylor 
1921ed53a3fSBill Taylor 	/* Mark it as allocated */
1931ed53a3fSBill Taylor 	pagep->upg_free[i1] |= (1ul << (i3 + 8 * i2));
1941ed53a3fSBill Taylor 
1951ed53a3fSBill Taylor 	dbr_index = ((i1 * sizeof (uint64_t)) + i2) * sizeof (uint64_t) + i3;
1969e39c5baSBill Taylor 	pagep->upg_nfree--;
1971ed53a3fSBill Taylor 	((uint64_t *)(void *)pagep->upg_kvaddr)[dbr_index] = 0;	/* clear dbr */
1989e39c5baSBill Taylor 	*mapoffset = ((HERMON_DBR_KEY(index, pagep->upg_index) <<
1999e39c5baSBill Taylor 	    MLNX_UMAP_RSRC_TYPE_SHIFT) | MLNX_UMAP_DBRMEM_RSRC) << PAGESHIFT;
2001ed53a3fSBill Taylor 	*vdbr = (hermon_dbr_t *)((uint64_t *)(void *)pagep->upg_kvaddr +
2011ed53a3fSBill Taylor 	    dbr_index);
2021ed53a3fSBill Taylor 	*pdbr = pagep->upg_dmacookie.dmac_laddress + dbr_index *
2031ed53a3fSBill Taylor 	    sizeof (uint64_t);
2049e39c5baSBill Taylor 
2059e39c5baSBill Taylor 	mutex_exit(&state->hs_dbr_lock);
2069e39c5baSBill Taylor 	return (DDI_SUCCESS);
2079e39c5baSBill Taylor }
2089e39c5baSBill Taylor 
2099e39c5baSBill Taylor static void
hermon_user_dbr_free(hermon_state_t * state,uint_t index,hermon_dbr_t * record)2109e39c5baSBill Taylor hermon_user_dbr_free(hermon_state_t *state, uint_t index, hermon_dbr_t *record)
2119e39c5baSBill Taylor {
2129e39c5baSBill Taylor 	hermon_user_dbr_t	*udbr;
2139e39c5baSBill Taylor 	hermon_udbr_page_t	*pagep;
2149e39c5baSBill Taylor 	caddr_t			kvaddr;
2159e39c5baSBill Taylor 	uint_t			dbr_index;
2169e39c5baSBill Taylor 	uint_t			max_free = PAGESIZE / sizeof (hermon_dbr_t);
2171ed53a3fSBill Taylor 	int			i1, i2;
2189e39c5baSBill Taylor 
2199e39c5baSBill Taylor 	dbr_index = (uintptr_t)record & PAGEOFFSET; /* offset (not yet index) */
2209e39c5baSBill Taylor 	kvaddr = (caddr_t)record - dbr_index;
2219e39c5baSBill Taylor 	dbr_index /= sizeof (hermon_dbr_t); /* now it's the index */
2229e39c5baSBill Taylor 
2239e39c5baSBill Taylor 	mutex_enter(&state->hs_dbr_lock);
2249e39c5baSBill Taylor 	for (udbr = state->hs_user_dbr; udbr != NULL; udbr = udbr->udbr_link)
2259e39c5baSBill Taylor 		if (udbr->udbr_index == index)
2269e39c5baSBill Taylor 			break;
2279e39c5baSBill Taylor 	if (udbr == NULL) {
2289e39c5baSBill Taylor 		IBTF_DPRINTF_L2("hermon", "free user dbr: udbr struct not "
2299e39c5baSBill Taylor 		    "found for index %x", index);
2309e39c5baSBill Taylor 		mutex_exit(&state->hs_dbr_lock);
2319e39c5baSBill Taylor 		return;
2329e39c5baSBill Taylor 	}
2339e39c5baSBill Taylor 	for (pagep = udbr->udbr_pagep; pagep != NULL; pagep = pagep->upg_link)
2349e39c5baSBill Taylor 		if (pagep->upg_kvaddr == kvaddr)
2359e39c5baSBill Taylor 			break;
2369e39c5baSBill Taylor 	if (pagep == NULL) {
2379e39c5baSBill Taylor 		IBTF_DPRINTF_L2("hermon", "free user dbr: pagep struct not"
2389e39c5baSBill Taylor 		    " found for index %x, kvaddr %p, DBR index %x",
2399e39c5baSBill Taylor 		    index, kvaddr, dbr_index);
2409e39c5baSBill Taylor 		mutex_exit(&state->hs_dbr_lock);
2419e39c5baSBill Taylor 		return;
2429e39c5baSBill Taylor 	}
2439e39c5baSBill Taylor 	if (pagep->upg_nfree >= max_free) {
2449e39c5baSBill Taylor 		IBTF_DPRINTF_L2("hermon", "free user dbr: overflow: "
2459e39c5baSBill Taylor 		    "UCE index %x, DBR index %x", index, dbr_index);
2469e39c5baSBill Taylor 		mutex_exit(&state->hs_dbr_lock);
2479e39c5baSBill Taylor 		return;
2489e39c5baSBill Taylor 	}
2499e39c5baSBill Taylor 	ASSERT(dbr_index < max_free);
2501ed53a3fSBill Taylor 	i1 = dbr_index / 64;
2511ed53a3fSBill Taylor 	i2 = dbr_index % 64;
2521ed53a3fSBill Taylor 	ASSERT((pagep->upg_free[i1] & (1ul << i2)) == (1ul << i2));
2531ed53a3fSBill Taylor 	pagep->upg_free[i1] &= ~(1ul << i2);
2549e39c5baSBill Taylor 	pagep->upg_nfree++;
2559e39c5baSBill Taylor 	mutex_exit(&state->hs_dbr_lock);
2569e39c5baSBill Taylor }
2579e39c5baSBill Taylor 
2589e39c5baSBill Taylor /*
2599e39c5baSBill Taylor  * hermon_dbr_page_alloc()
2609e39c5baSBill Taylor  *	first page allocation - called from attach or open
2619e39c5baSBill Taylor  *	in this case, we want exactly one page per call, and aligned on a
2629e39c5baSBill Taylor  *	page - and may need to be mapped to the user for access
2639e39c5baSBill Taylor  */
2649e39c5baSBill Taylor int
hermon_dbr_page_alloc(hermon_state_t * state,hermon_dbr_info_t ** dinfo)2659e39c5baSBill Taylor hermon_dbr_page_alloc(hermon_state_t *state, hermon_dbr_info_t **dinfo)
2669e39c5baSBill Taylor {
2679e39c5baSBill Taylor 	int			status;
2689e39c5baSBill Taylor 	ddi_dma_handle_t	dma_hdl;
2699e39c5baSBill Taylor 	ddi_acc_handle_t	acc_hdl;
2709e39c5baSBill Taylor 	ddi_dma_attr_t		dma_attr;
2719e39c5baSBill Taylor 	ddi_dma_cookie_t	cookie;
2729e39c5baSBill Taylor 	uint_t			cookie_cnt;
2739e39c5baSBill Taylor 	int			i;
2749e39c5baSBill Taylor 	hermon_dbr_info_t 	*info;
275dd9e16daSagiri 	caddr_t			dmaaddr;
2769e39c5baSBill Taylor 	uint64_t		dmalen;
277*17a2b317SBill Taylor 	ulong_t			pagesize = PAGESIZE;
2789e39c5baSBill Taylor 
2799e39c5baSBill Taylor 	info = kmem_zalloc(sizeof (hermon_dbr_info_t), KM_SLEEP);
2809e39c5baSBill Taylor 
2819e39c5baSBill Taylor 	/*
2829e39c5baSBill Taylor 	 * Initialize many of the default DMA attributes.  Then set additional
2839e39c5baSBill Taylor 	 * alignment restrictions if necessary for the dbr memory, meaning
2849e39c5baSBill Taylor 	 * page aligned.  Also use the configured value for IOMMU bypass
2859e39c5baSBill Taylor 	 */
2869e39c5baSBill Taylor 	hermon_dma_attr_init(state, &dma_attr);
287*17a2b317SBill Taylor 	dma_attr.dma_attr_align = pagesize;
2889e39c5baSBill Taylor 	dma_attr.dma_attr_sgllen = 1;	/* make sure only one cookie */
289c7facc54SBill Taylor #ifdef	__sparc
290c7facc54SBill Taylor 	if (state->hs_cfg_profile->cp_iommu_bypass == HERMON_BINDMEM_BYPASS)
291c7facc54SBill Taylor 		dma_attr.dma_attr_flags = DDI_DMA_FORCE_PHYSICAL;
292c7facc54SBill Taylor #endif
2939e39c5baSBill Taylor 
2949e39c5baSBill Taylor 	status = ddi_dma_alloc_handle(state->hs_dip, &dma_attr,
2959e39c5baSBill Taylor 	    DDI_DMA_SLEEP, NULL, &dma_hdl);
2969e39c5baSBill Taylor 	if (status != DDI_SUCCESS) {
2979e39c5baSBill Taylor 		kmem_free((void *)info, sizeof (hermon_dbr_info_t));
2989e39c5baSBill Taylor 		cmn_err(CE_NOTE, "dbr DMA handle alloc failed\n");
2999e39c5baSBill Taylor 		return (DDI_FAILURE);
3009e39c5baSBill Taylor 	}
3019e39c5baSBill Taylor 
302*17a2b317SBill Taylor 	status = ddi_dma_mem_alloc(dma_hdl, pagesize,
3039e39c5baSBill Taylor 	    &state->hs_reg_accattr, DDI_DMA_CONSISTENT, DDI_DMA_SLEEP,
304dd9e16daSagiri 	    NULL, &dmaaddr, (size_t *)&dmalen, &acc_hdl);
3059e39c5baSBill Taylor 	if (status != DDI_SUCCESS)	{
3069e39c5baSBill Taylor 		ddi_dma_free_handle(&dma_hdl);
3079e39c5baSBill Taylor 		cmn_err(CE_CONT, "dbr DMA mem alloc failed(status %d)", status);
3089e39c5baSBill Taylor 		kmem_free((void *)info, sizeof (hermon_dbr_info_t));
3099e39c5baSBill Taylor 		return (DDI_FAILURE);
3109e39c5baSBill Taylor 	}
3119e39c5baSBill Taylor 
3129e39c5baSBill Taylor 	/* this memory won't be IB registered, so do the bind here */
3139e39c5baSBill Taylor 	status = ddi_dma_addr_bind_handle(dma_hdl, NULL,
314dd9e16daSagiri 	    dmaaddr, (size_t)dmalen, DDI_DMA_RDWR |
3159e39c5baSBill Taylor 	    DDI_DMA_CONSISTENT, DDI_DMA_SLEEP, NULL, &cookie, &cookie_cnt);
3169e39c5baSBill Taylor 	if (status != DDI_SUCCESS) {
3179e39c5baSBill Taylor 		ddi_dma_mem_free(&acc_hdl);
3189e39c5baSBill Taylor 		ddi_dma_free_handle(&dma_hdl);
3199e39c5baSBill Taylor 		kmem_free((void *)info, sizeof (hermon_dbr_info_t));
3209e39c5baSBill Taylor 		cmn_err(CE_CONT, "dbr DMA bind handle failed (status %d)",
3219e39c5baSBill Taylor 		    status);
3229e39c5baSBill Taylor 		return (DDI_FAILURE);
3239e39c5baSBill Taylor 	}
3249e39c5baSBill Taylor 	*dinfo = info;		/* Pass back the pointer */
3259e39c5baSBill Taylor 
3269e39c5baSBill Taylor 	/* init the info structure with returned info */
3279e39c5baSBill Taylor 	info->dbr_dmahdl = dma_hdl;
3289e39c5baSBill Taylor 	info->dbr_acchdl = acc_hdl;
329dd9e16daSagiri 	info->dbr_page   = (hermon_dbr_t *)(void *)dmaaddr;
330dd9e16daSagiri 	info->dbr_link = NULL;
3319e39c5baSBill Taylor 	/* extract the phys addr from the cookie */
3329e39c5baSBill Taylor 	info->dbr_paddr = cookie.dmac_laddress;
333dd9e16daSagiri 	info->dbr_firstfree = 0;
334dd9e16daSagiri 	info->dbr_nfree = HERMON_NUM_DBR_PER_PAGE;
3359e39c5baSBill Taylor 	/* link all DBrs onto the free list */
3369e39c5baSBill Taylor 	for (i = 0; i < HERMON_NUM_DBR_PER_PAGE; i++) {
337dd9e16daSagiri 		info->dbr_page[i] = i + 1;
3389e39c5baSBill Taylor 	}
3399e39c5baSBill Taylor 
3409e39c5baSBill Taylor 	return (DDI_SUCCESS);
3419e39c5baSBill Taylor }
3429e39c5baSBill Taylor 
3439e39c5baSBill Taylor 
3449e39c5baSBill Taylor /*
3459e39c5baSBill Taylor  * hermon_dbr_alloc()
3469e39c5baSBill Taylor  *	DBr record allocation - called from alloc cq/qp/srq
3479e39c5baSBill Taylor  *	will check for available dbrs in current
3489e39c5baSBill Taylor  *	page - if needed it will allocate another and link them
3499e39c5baSBill Taylor  */
3509e39c5baSBill Taylor 
3519e39c5baSBill Taylor int
hermon_dbr_alloc(hermon_state_t * state,uint_t index,ddi_acc_handle_t * acchdl,hermon_dbr_t ** vdbr,uint64_t * pdbr,uint64_t * mapoffset)3529e39c5baSBill Taylor hermon_dbr_alloc(hermon_state_t *state, uint_t index, ddi_acc_handle_t *acchdl,
3539e39c5baSBill Taylor     hermon_dbr_t **vdbr, uint64_t *pdbr, uint64_t *mapoffset)
3549e39c5baSBill Taylor {
3559e39c5baSBill Taylor 	hermon_dbr_t		*record = NULL;
356dd9e16daSagiri 	hermon_dbr_info_t	*info = NULL;
357dd9e16daSagiri 	uint32_t		idx;
3589e39c5baSBill Taylor 	int			status;
3599e39c5baSBill Taylor 
3609e39c5baSBill Taylor 	if (index != state->hs_kernel_uar_index)
3619e39c5baSBill Taylor 		return (hermon_user_dbr_alloc(state, index, acchdl, vdbr, pdbr,
3629e39c5baSBill Taylor 		    mapoffset));
3639e39c5baSBill Taylor 
3649e39c5baSBill Taylor 	mutex_enter(&state->hs_dbr_lock);
365dd9e16daSagiri 	for (info = state->hs_kern_dbr; info != NULL; info = info->dbr_link)
366dd9e16daSagiri 		if (info->dbr_nfree != 0)
3679e39c5baSBill Taylor 			break;		/* found a page w/ one available */
3689e39c5baSBill Taylor 
369dd9e16daSagiri 	if (info == NULL) {	/* did NOT find a page with one available */
370dd9e16daSagiri 		status = hermon_dbr_page_alloc(state, &info);
3719e39c5baSBill Taylor 		if (status != DDI_SUCCESS) {
3729e39c5baSBill Taylor 			/* do error handling */
3739e39c5baSBill Taylor 			mutex_exit(&state->hs_dbr_lock);
3749e39c5baSBill Taylor 			return (DDI_FAILURE);
3759e39c5baSBill Taylor 		}
3769e39c5baSBill Taylor 		/* got a new page, so link it in. */
377dd9e16daSagiri 		info->dbr_link = state->hs_kern_dbr;
378dd9e16daSagiri 		state->hs_kern_dbr = info;
3799e39c5baSBill Taylor 	}
380dd9e16daSagiri 	idx = info->dbr_firstfree;
381dd9e16daSagiri 	record = info->dbr_page + idx;
382dd9e16daSagiri 	info->dbr_firstfree = *record;
383dd9e16daSagiri 	info->dbr_nfree--;
3849e39c5baSBill Taylor 	*record = 0;
3859e39c5baSBill Taylor 
386dd9e16daSagiri 	*acchdl = info->dbr_acchdl;
3879e39c5baSBill Taylor 	*vdbr = record;
388dd9e16daSagiri 	*pdbr = info->dbr_paddr + idx * sizeof (hermon_dbr_t);
3899e39c5baSBill Taylor 	mutex_exit(&state->hs_dbr_lock);
3909e39c5baSBill Taylor 	return (DDI_SUCCESS);
3919e39c5baSBill Taylor }
3929e39c5baSBill Taylor 
3939e39c5baSBill Taylor /*
3949e39c5baSBill Taylor  * hermon_dbr_free()
3959e39c5baSBill Taylor  *	DBr record deallocation - called from free cq/qp
3969e39c5baSBill Taylor  *	will update the counter in the header, and invalidate
3979e39c5baSBill Taylor  *	the dbr, but will NEVER free pages of dbrs - small
3989e39c5baSBill Taylor  *	price to pay, but userland access never will anyway
3999e39c5baSBill Taylor  */
4009e39c5baSBill Taylor void
hermon_dbr_free(hermon_state_t * state,uint_t indx,hermon_dbr_t * record)4019e39c5baSBill Taylor hermon_dbr_free(hermon_state_t *state, uint_t indx, hermon_dbr_t *record)
4029e39c5baSBill Taylor {
403dd9e16daSagiri 	hermon_dbr_t		*page;
404dd9e16daSagiri 	hermon_dbr_info_t	*info;
4059e39c5baSBill Taylor 
4069e39c5baSBill Taylor 	if (indx != state->hs_kernel_uar_index) {
4079e39c5baSBill Taylor 		hermon_user_dbr_free(state, indx, record);
4089e39c5baSBill Taylor 		return;
4099e39c5baSBill Taylor 	}
410dd9e16daSagiri 	page = (hermon_dbr_t *)(uintptr_t)((uintptr_t)record & PAGEMASK);
4119e39c5baSBill Taylor 	mutex_enter(&state->hs_dbr_lock);
412dd9e16daSagiri 	for (info = state->hs_kern_dbr; info != NULL; info = info->dbr_link)
413dd9e16daSagiri 		if (info->dbr_page == page)
414dd9e16daSagiri 			break;
415dd9e16daSagiri 	ASSERT(info != NULL);
416dd9e16daSagiri 	*record = info->dbr_firstfree;
417dd9e16daSagiri 	info->dbr_firstfree = record - info->dbr_page;
418dd9e16daSagiri 	info->dbr_nfree++;
4199e39c5baSBill Taylor 	mutex_exit(&state->hs_dbr_lock);
4209e39c5baSBill Taylor }
4219e39c5baSBill Taylor 
4229e39c5baSBill Taylor /*
4239e39c5baSBill Taylor  * hermon_dbr_kern_free()
4249e39c5baSBill Taylor  *    Context: Can be called only from detach context.
4259e39c5baSBill Taylor  *
4269e39c5baSBill Taylor  *	Free all kernel dbr pages.  This includes the freeing of all the dma
4279e39c5baSBill Taylor  *	resources acquired during the allocation of the pages.
4289e39c5baSBill Taylor  *
4299e39c5baSBill Taylor  *	Also, free all the user dbr pages.
4309e39c5baSBill Taylor  */
4319e39c5baSBill Taylor void
hermon_dbr_kern_free(hermon_state_t * state)4329e39c5baSBill Taylor hermon_dbr_kern_free(hermon_state_t *state)
4339e39c5baSBill Taylor {
434dd9e16daSagiri 	hermon_dbr_info_t	*info, *link;
4359e39c5baSBill Taylor 	hermon_user_dbr_t	*udbr, *next;
4369e39c5baSBill Taylor 	hermon_udbr_page_t	*pagep, *nextp;
4379e39c5baSBill Taylor 	hermon_umap_db_entry_t	*umapdb;
4389e39c5baSBill Taylor 	int			instance, status;
4399e39c5baSBill Taylor 	uint64_t		value;
4409e39c5baSBill Taylor 	extern			hermon_umap_db_t hermon_userland_rsrc_db;
4419e39c5baSBill Taylor 
4429e39c5baSBill Taylor 	mutex_enter(&state->hs_dbr_lock);
443dd9e16daSagiri 	for (info = state->hs_kern_dbr; info != NULL; info = link) {
444dd9e16daSagiri 		(void) ddi_dma_unbind_handle(info->dbr_dmahdl);
445dd9e16daSagiri 		ddi_dma_mem_free(&info->dbr_acchdl);	/* free page */
446dd9e16daSagiri 		ddi_dma_free_handle(&info->dbr_dmahdl);
447dd9e16daSagiri 		link = info->dbr_link;
448dd9e16daSagiri 		kmem_free(info, sizeof (hermon_dbr_info_t));
4499e39c5baSBill Taylor 	}
4509e39c5baSBill Taylor 
4519e39c5baSBill Taylor 	udbr = state->hs_user_dbr;
4529e39c5baSBill Taylor 	instance = state->hs_instance;
4539e39c5baSBill Taylor 	mutex_enter(&hermon_userland_rsrc_db.hdl_umapdb_lock);
4549e39c5baSBill Taylor 	while (udbr != NULL) {
4559e39c5baSBill Taylor 		pagep = udbr->udbr_pagep;
4569e39c5baSBill Taylor 		while (pagep != NULL) {
4579e39c5baSBill Taylor 			/* probably need to remove "db" */
4589e39c5baSBill Taylor 			(void) ddi_dma_unbind_handle(pagep->upg_dmahdl);
4599e39c5baSBill Taylor 			ddi_dma_free_handle(&pagep->upg_dmahdl);
4609e39c5baSBill Taylor 			freerbuf(pagep->upg_buf);
4619e39c5baSBill Taylor 			ddi_umem_free(pagep->upg_umemcookie);
4629e39c5baSBill Taylor 			status = hermon_umap_db_find_nolock(instance,
4639e39c5baSBill Taylor 			    HERMON_DBR_KEY(udbr->udbr_index,
4649e39c5baSBill Taylor 			    pagep->upg_index), MLNX_UMAP_DBRMEM_RSRC,
4659e39c5baSBill Taylor 			    &value, HERMON_UMAP_DB_REMOVE, &umapdb);
4669e39c5baSBill Taylor 			if (status == DDI_SUCCESS)
4679e39c5baSBill Taylor 				hermon_umap_db_free(umapdb);
4681ed53a3fSBill Taylor 			kmem_free(pagep->upg_free,
4691ed53a3fSBill Taylor 			    PAGESIZE / sizeof (hermon_dbr_t) / 8);
4709e39c5baSBill Taylor 			nextp = pagep->upg_link;
4719e39c5baSBill Taylor 			kmem_free(pagep, sizeof (*pagep));
4729e39c5baSBill Taylor 			pagep = nextp;
4739e39c5baSBill Taylor 		}
4749e39c5baSBill Taylor 		next = udbr->udbr_link;
4759e39c5baSBill Taylor 		kmem_free(udbr, sizeof (*udbr));
4769e39c5baSBill Taylor 		udbr = next;
4779e39c5baSBill Taylor 	}
4789e39c5baSBill Taylor 	mutex_exit(&hermon_userland_rsrc_db.hdl_umapdb_lock);
4799e39c5baSBill Taylor 	mutex_exit(&state->hs_dbr_lock);
4809e39c5baSBill Taylor }
4819e39c5baSBill Taylor 
4829e39c5baSBill Taylor /*
4839e39c5baSBill Taylor  * hermon_ah_alloc()
4849e39c5baSBill Taylor  *    Context: Can be called only from user or kernel context.
4859e39c5baSBill Taylor  */
4869e39c5baSBill Taylor int
hermon_ah_alloc(hermon_state_t * state,hermon_pdhdl_t pd,ibt_adds_vect_t * attr_p,hermon_ahhdl_t * ahhdl,uint_t sleepflag)4879e39c5baSBill Taylor hermon_ah_alloc(hermon_state_t *state, hermon_pdhdl_t pd,
4889e39c5baSBill Taylor     ibt_adds_vect_t *attr_p, hermon_ahhdl_t *ahhdl, uint_t sleepflag)
4899e39c5baSBill Taylor {
4909e39c5baSBill Taylor 	hermon_rsrc_t		*rsrc;
4919e39c5baSBill Taylor 	hermon_hw_udav_t	*udav;
4929e39c5baSBill Taylor 	hermon_ahhdl_t		ah;
4939e39c5baSBill Taylor 	int			status;
4949e39c5baSBill Taylor 
4959e39c5baSBill Taylor 	/*
4969e39c5baSBill Taylor 	 * Someday maybe the "ibt_adds_vect_t *attr_p" will be NULL to
4979e39c5baSBill Taylor 	 * indicate that we wish to allocate an "invalid" (i.e. empty)
4989e39c5baSBill Taylor 	 * address handle XXX
4999e39c5baSBill Taylor 	 */
5009e39c5baSBill Taylor 
5019e39c5baSBill Taylor 	/* Validate that specified port number is legal */
5029e39c5baSBill Taylor 	if (!hermon_portnum_is_valid(state, attr_p->av_port_num)) {
5039e39c5baSBill Taylor 		return (IBT_HCA_PORT_INVALID);
5049e39c5baSBill Taylor 	}
5059e39c5baSBill Taylor 
5069e39c5baSBill Taylor 	/*
5079e39c5baSBill Taylor 	 * Allocate the software structure for tracking the address handle
5089e39c5baSBill Taylor 	 * (i.e. the Hermon Address Handle struct).
5099e39c5baSBill Taylor 	 */
5109e39c5baSBill Taylor 	status = hermon_rsrc_alloc(state, HERMON_AHHDL, 1, sleepflag, &rsrc);
5119e39c5baSBill Taylor 	if (status != DDI_SUCCESS) {
5129e39c5baSBill Taylor 		return (IBT_INSUFF_RESOURCE);
5139e39c5baSBill Taylor 	}
5149e39c5baSBill Taylor 	ah = (hermon_ahhdl_t)rsrc->hr_addr;
5159e39c5baSBill Taylor 	_NOTE(NOW_INVISIBLE_TO_OTHER_THREADS(*ah))
5169e39c5baSBill Taylor 
5179e39c5baSBill Taylor 	/* Increment the reference count on the protection domain (PD) */
5189e39c5baSBill Taylor 	hermon_pd_refcnt_inc(pd);
5199e39c5baSBill Taylor 
5209e39c5baSBill Taylor 	udav = (hermon_hw_udav_t *)kmem_zalloc(sizeof (hermon_hw_udav_t),
5219e39c5baSBill Taylor 	    KM_SLEEP);
5229e39c5baSBill Taylor 	_NOTE(NOW_INVISIBLE_TO_OTHER_THREADS(*udav))
5239e39c5baSBill Taylor 
5249e39c5baSBill Taylor 	/*
5259e39c5baSBill Taylor 	 * Fill in the UDAV data. We first zero out the UDAV, then populate
5269e39c5baSBill Taylor 	 * it by then calling hermon_set_addr_path() to fill in the common
5279e39c5baSBill Taylor 	 * portions that can be pulled from the "ibt_adds_vect_t" passed in
5289e39c5baSBill Taylor 	 */
5299e39c5baSBill Taylor 	status = hermon_set_addr_path(state, attr_p,
5309e39c5baSBill Taylor 	    (hermon_hw_addr_path_t *)udav, HERMON_ADDRPATH_UDAV);
5319e39c5baSBill Taylor 	if (status != DDI_SUCCESS) {
5329e39c5baSBill Taylor 		hermon_pd_refcnt_dec(pd);
5339e39c5baSBill Taylor 		hermon_rsrc_free(state, &rsrc);
5349e39c5baSBill Taylor 		return (status);
5359e39c5baSBill Taylor 	}
5369e39c5baSBill Taylor 	udav->pd	= pd->pd_pdnum;
5379e39c5baSBill Taylor 	udav->sl	= attr_p->av_srvl;
5389e39c5baSBill Taylor 
5399e39c5baSBill Taylor 	/*
5409e39c5baSBill Taylor 	 * Fill in the rest of the Hermon Address Handle struct.
5419e39c5baSBill Taylor 	 *
5429e39c5baSBill Taylor 	 * NOTE: We are saving away a copy of the "av_dgid.gid_guid" field
5439e39c5baSBill Taylor 	 * here because we may need to return it later to the IBTF (as a
5449e39c5baSBill Taylor 	 * result of a subsequent query operation).  Unlike the other UDAV
5459e39c5baSBill Taylor 	 * parameters, the value of "av_dgid.gid_guid" is not always preserved.
5469e39c5baSBill Taylor 	 * The reason for this is described in hermon_set_addr_path().
5479e39c5baSBill Taylor 	 */
5489e39c5baSBill Taylor 	ah->ah_rsrcp	 = rsrc;
5499e39c5baSBill Taylor 	ah->ah_pdhdl	 = pd;
5509e39c5baSBill Taylor 	ah->ah_udav	 = udav;
5519e39c5baSBill Taylor 	ah->ah_save_guid = attr_p->av_dgid.gid_guid;
5529e39c5baSBill Taylor 	*ahhdl = ah;
5539e39c5baSBill Taylor 
5549e39c5baSBill Taylor 	return (DDI_SUCCESS);
5559e39c5baSBill Taylor }
5569e39c5baSBill Taylor 
5579e39c5baSBill Taylor 
5589e39c5baSBill Taylor /*
5599e39c5baSBill Taylor  * hermon_ah_free()
5609e39c5baSBill Taylor  *    Context: Can be called only from user or kernel context.
5619e39c5baSBill Taylor  */
5629e39c5baSBill Taylor /* ARGSUSED */
5639e39c5baSBill Taylor int
hermon_ah_free(hermon_state_t * state,hermon_ahhdl_t * ahhdl,uint_t sleepflag)5649e39c5baSBill Taylor hermon_ah_free(hermon_state_t *state, hermon_ahhdl_t *ahhdl, uint_t sleepflag)
5659e39c5baSBill Taylor {
5669e39c5baSBill Taylor 	hermon_rsrc_t		*rsrc;
5679e39c5baSBill Taylor 	hermon_pdhdl_t		pd;
5689e39c5baSBill Taylor 	hermon_ahhdl_t		ah;
5699e39c5baSBill Taylor 
5709e39c5baSBill Taylor 	/*
5719e39c5baSBill Taylor 	 * Pull all the necessary information from the Hermon Address Handle
5729e39c5baSBill Taylor 	 * struct.  This is necessary here because the resource for the
5739e39c5baSBill Taylor 	 * AH is going to be freed up as part of this operation.
5749e39c5baSBill Taylor 	 */
5759e39c5baSBill Taylor 	ah    = *ahhdl;
5769e39c5baSBill Taylor 	mutex_enter(&ah->ah_lock);
5779e39c5baSBill Taylor 	rsrc  = ah->ah_rsrcp;
5789e39c5baSBill Taylor 	pd    = ah->ah_pdhdl;
5799e39c5baSBill Taylor 	mutex_exit(&ah->ah_lock);
5809e39c5baSBill Taylor 	_NOTE(NOW_INVISIBLE_TO_OTHER_THREADS(*ah))
5819e39c5baSBill Taylor 
5829e39c5baSBill Taylor 	/* Free the UDAV memory */
5839e39c5baSBill Taylor 	kmem_free(ah->ah_udav, sizeof (hermon_hw_udav_t));
5849e39c5baSBill Taylor 
5859e39c5baSBill Taylor 	/* Decrement the reference count on the protection domain (PD) */
5869e39c5baSBill Taylor 	hermon_pd_refcnt_dec(pd);
5879e39c5baSBill Taylor 
5889e39c5baSBill Taylor 	/* Free the Hermon Address Handle structure */
5899e39c5baSBill Taylor 	hermon_rsrc_free(state, &rsrc);
5909e39c5baSBill Taylor 
5919e39c5baSBill Taylor 	/* Set the ahhdl pointer to NULL and return success */
5929e39c5baSBill Taylor 	*ahhdl = NULL;
5939e39c5baSBill Taylor 
5949e39c5baSBill Taylor 	return (DDI_SUCCESS);
5959e39c5baSBill Taylor }
5969e39c5baSBill Taylor 
5979e39c5baSBill Taylor 
5989e39c5baSBill Taylor /*
5999e39c5baSBill Taylor  * hermon_ah_query()
6009e39c5baSBill Taylor  *    Context: Can be called from interrupt or base context.
6019e39c5baSBill Taylor  */
6029e39c5baSBill Taylor /* ARGSUSED */
6039e39c5baSBill Taylor int
hermon_ah_query(hermon_state_t * state,hermon_ahhdl_t ah,hermon_pdhdl_t * pd,ibt_adds_vect_t * attr_p)6049e39c5baSBill Taylor hermon_ah_query(hermon_state_t *state, hermon_ahhdl_t ah, hermon_pdhdl_t *pd,
6059e39c5baSBill Taylor     ibt_adds_vect_t *attr_p)
6069e39c5baSBill Taylor {
6079e39c5baSBill Taylor 	mutex_enter(&ah->ah_lock);
6089e39c5baSBill Taylor 	_NOTE(NOW_INVISIBLE_TO_OTHER_THREADS(*attr_p))
6099e39c5baSBill Taylor 
6109e39c5baSBill Taylor 	/*
6119e39c5baSBill Taylor 	 * Pull the PD and UDAV from the Hermon Address Handle structure
6129e39c5baSBill Taylor 	 */
6139e39c5baSBill Taylor 	*pd = ah->ah_pdhdl;
6149e39c5baSBill Taylor 
6159e39c5baSBill Taylor 	/*
6169e39c5baSBill Taylor 	 * Fill in "ibt_adds_vect_t".  We call hermon_get_addr_path() to fill
6179e39c5baSBill Taylor 	 * the common portions that can be pulled from the UDAV we pass in.
6189e39c5baSBill Taylor 	 *
6199e39c5baSBill Taylor 	 * NOTE: We will also fill the "av_dgid.gid_guid" field from the
6209e39c5baSBill Taylor 	 * "ah_save_guid" field we have previously saved away.  The reason
6219e39c5baSBill Taylor 	 * for this is described in hermon_ah_alloc() and hermon_ah_modify().
6229e39c5baSBill Taylor 	 */
6239e39c5baSBill Taylor 	hermon_get_addr_path(state, (hermon_hw_addr_path_t *)ah->ah_udav,
6249e39c5baSBill Taylor 	    attr_p, HERMON_ADDRPATH_UDAV);
6259e39c5baSBill Taylor 
6269e39c5baSBill Taylor 	attr_p->av_dgid.gid_guid = ah->ah_save_guid;
6279e39c5baSBill Taylor 
6289e39c5baSBill Taylor 	mutex_exit(&ah->ah_lock);
6299e39c5baSBill Taylor 	return (DDI_SUCCESS);
6309e39c5baSBill Taylor }
6319e39c5baSBill Taylor 
6329e39c5baSBill Taylor 
6339e39c5baSBill Taylor /*
6349e39c5baSBill Taylor  * hermon_ah_modify()
6359e39c5baSBill Taylor  *    Context: Can be called from interrupt or base context.
6369e39c5baSBill Taylor  */
6379e39c5baSBill Taylor /* ARGSUSED */
6389e39c5baSBill Taylor int
hermon_ah_modify(hermon_state_t * state,hermon_ahhdl_t ah,ibt_adds_vect_t * attr_p)6399e39c5baSBill Taylor hermon_ah_modify(hermon_state_t *state, hermon_ahhdl_t ah,
6409e39c5baSBill Taylor     ibt_adds_vect_t *attr_p)
6419e39c5baSBill Taylor {
6429e39c5baSBill Taylor 	hermon_hw_udav_t	old_udav;
6439e39c5baSBill Taylor 	uint64_t		data_old;
6449e39c5baSBill Taylor 	int			status, size, i;
6459e39c5baSBill Taylor 
6469e39c5baSBill Taylor 	/* Validate that specified port number is legal */
6479e39c5baSBill Taylor 	if (!hermon_portnum_is_valid(state, attr_p->av_port_num)) {
6489e39c5baSBill Taylor 		return (IBT_HCA_PORT_INVALID);
6499e39c5baSBill Taylor 	}
6509e39c5baSBill Taylor 
6519e39c5baSBill Taylor 	mutex_enter(&ah->ah_lock);
6529e39c5baSBill Taylor 
6539e39c5baSBill Taylor 	/* Save a copy of the current UDAV data in old_udav. */
6549e39c5baSBill Taylor 	bcopy(ah->ah_udav, &old_udav, sizeof (hermon_hw_udav_t));
6559e39c5baSBill Taylor 
6569e39c5baSBill Taylor 	/*
6579e39c5baSBill Taylor 	 * Fill in the new UDAV with the caller's data, passed in via the
6589e39c5baSBill Taylor 	 * "ibt_adds_vect_t" structure.
6599e39c5baSBill Taylor 	 *
6609e39c5baSBill Taylor 	 * NOTE: We also need to save away a copy of the "av_dgid.gid_guid"
6619e39c5baSBill Taylor 	 * field here (just as we did during hermon_ah_alloc()) because we
6629e39c5baSBill Taylor 	 * may need to return it later to the IBTF (as a result of a
6639e39c5baSBill Taylor 	 * subsequent query operation).  As explained in hermon_ah_alloc(),
6649e39c5baSBill Taylor 	 * unlike the other UDAV parameters, the value of "av_dgid.gid_guid"
6659e39c5baSBill Taylor 	 * is not always preserved. The reason for this is described in
6669e39c5baSBill Taylor 	 * hermon_set_addr_path().
6679e39c5baSBill Taylor 	 */
6689e39c5baSBill Taylor 	status = hermon_set_addr_path(state, attr_p,
6699e39c5baSBill Taylor 	    (hermon_hw_addr_path_t *)ah->ah_udav, HERMON_ADDRPATH_UDAV);
6709e39c5baSBill Taylor 	if (status != DDI_SUCCESS) {
6719e39c5baSBill Taylor 		mutex_exit(&ah->ah_lock);
6729e39c5baSBill Taylor 		return (status);
6739e39c5baSBill Taylor 	}
6749e39c5baSBill Taylor 	ah->ah_save_guid = attr_p->av_dgid.gid_guid;
6759e39c5baSBill Taylor 	_NOTE(NOW_INVISIBLE_TO_OTHER_THREADS(*(ah->ah_udav)))
6769e39c5baSBill Taylor 	ah->ah_udav->sl  = attr_p->av_srvl;
6779e39c5baSBill Taylor 
6789e39c5baSBill Taylor 	/*
6799e39c5baSBill Taylor 	 * Copy changes into the new UDAV.
6809e39c5baSBill Taylor 	 *    Note:  We copy in 64-bit chunks.  For the first two of these
6819e39c5baSBill Taylor 	 *    chunks it is necessary to read the current contents of the
6829e39c5baSBill Taylor 	 *    UDAV, mask off the modifiable portions (maintaining any
6839e39c5baSBill Taylor 	 *    of the "reserved" portions), and then mask on the new data.
6849e39c5baSBill Taylor 	 */
6859e39c5baSBill Taylor 	size = sizeof (hermon_hw_udav_t) >> 3;
6869e39c5baSBill Taylor 	for (i = 0; i < size; i++) {
6879e39c5baSBill Taylor 		data_old = ((uint64_t *)&old_udav)[i];
6889e39c5baSBill Taylor 
6899e39c5baSBill Taylor 		/*
6909e39c5baSBill Taylor 		 * Apply mask to change only the relevant values.
6919e39c5baSBill Taylor 		 */
6929e39c5baSBill Taylor 		if (i == 0) {
6939e39c5baSBill Taylor 			data_old = data_old & HERMON_UDAV_MODIFY_MASK0;
6949e39c5baSBill Taylor 		} else if (i == 1) {
6959e39c5baSBill Taylor 			data_old = data_old & HERMON_UDAV_MODIFY_MASK1;
6969e39c5baSBill Taylor 		} else {
6979e39c5baSBill Taylor 			data_old = 0;
6989e39c5baSBill Taylor 		}
6999e39c5baSBill Taylor 
7009e39c5baSBill Taylor 		/* Store the updated values to the UDAV */
7019e39c5baSBill Taylor 		((uint64_t *)ah->ah_udav)[i] |= data_old;
7029e39c5baSBill Taylor 	}
7039e39c5baSBill Taylor 
7049e39c5baSBill Taylor 	/*
7059e39c5baSBill Taylor 	 * Put the valid PD number back into the UDAV entry, as it
7069e39c5baSBill Taylor 	 * might have been clobbered above.
7079e39c5baSBill Taylor 	 */
7089e39c5baSBill Taylor 	ah->ah_udav->pd = old_udav.pd;
7099e39c5baSBill Taylor 
7109e39c5baSBill Taylor 
7119e39c5baSBill Taylor 	mutex_exit(&ah->ah_lock);
7129e39c5baSBill Taylor 	return (DDI_SUCCESS);
7139e39c5baSBill Taylor }
7149e39c5baSBill Taylor 
7159e39c5baSBill Taylor /*
7169e39c5baSBill Taylor  * hermon_mcg_attach()
7179e39c5baSBill Taylor  *    Context: Can be called only from user or kernel context.
7189e39c5baSBill Taylor  */
7199e39c5baSBill Taylor int
hermon_mcg_attach(hermon_state_t * state,hermon_qphdl_t qp,ib_gid_t gid,ib_lid_t lid)7209e39c5baSBill Taylor hermon_mcg_attach(hermon_state_t *state, hermon_qphdl_t qp, ib_gid_t gid,
7219e39c5baSBill Taylor     ib_lid_t lid)
7229e39c5baSBill Taylor {
7239e39c5baSBill Taylor 	hermon_rsrc_t		*rsrc;
7249e39c5baSBill Taylor 	hermon_hw_mcg_t		*mcg_entry;
7259e39c5baSBill Taylor 	hermon_hw_mcg_qp_list_t	*mcg_entry_qplist;
7269e39c5baSBill Taylor 	hermon_mcghdl_t		mcg, newmcg;
7279e39c5baSBill Taylor 	uint64_t		mgid_hash;
7289e39c5baSBill Taylor 	uint32_t		end_indx;
7299e39c5baSBill Taylor 	int			status;
7309e39c5baSBill Taylor 	uint_t			qp_found;
7319e39c5baSBill Taylor 
7329e39c5baSBill Taylor 	/*
7339e39c5baSBill Taylor 	 * It is only allowed to attach MCG to UD queue pairs.  Verify
7349e39c5baSBill Taylor 	 * that the intended QP is of the appropriate transport type
7359e39c5baSBill Taylor 	 */
7369e39c5baSBill Taylor 	if (qp->qp_serv_type != HERMON_QP_UD) {
7379e39c5baSBill Taylor 		return (IBT_QP_SRV_TYPE_INVALID);
7389e39c5baSBill Taylor 	}
7399e39c5baSBill Taylor 
7409e39c5baSBill Taylor 	/*
7419e39c5baSBill Taylor 	 * Check for invalid Multicast DLID.  Specifically, all Multicast
7429e39c5baSBill Taylor 	 * LIDs should be within a well defined range.  If the specified LID
7439e39c5baSBill Taylor 	 * is outside of that range, then return an error.
7449e39c5baSBill Taylor 	 */
7459e39c5baSBill Taylor 	if (hermon_mlid_is_valid(lid) == 0) {
7469e39c5baSBill Taylor 		return (IBT_MC_MLID_INVALID);
7479e39c5baSBill Taylor 	}
7489e39c5baSBill Taylor 	/*
7499e39c5baSBill Taylor 	 * Check for invalid Multicast GID.  All Multicast GIDs should have
7509e39c5baSBill Taylor 	 * a well-defined pattern of bits and flags that are allowable.  If
7519e39c5baSBill Taylor 	 * the specified GID does not meet the criteria, then return an error.
7529e39c5baSBill Taylor 	 */
7539e39c5baSBill Taylor 	if (hermon_mgid_is_valid(gid) == 0) {
7549e39c5baSBill Taylor 		return (IBT_MC_MGID_INVALID);
7559e39c5baSBill Taylor 	}
7569e39c5baSBill Taylor 
7579e39c5baSBill Taylor 	/*
7589e39c5baSBill Taylor 	 * Compute the MGID hash value.  Since the MCG table is arranged as
7599e39c5baSBill Taylor 	 * a number of separate hash chains, this operation converts the
7609e39c5baSBill Taylor 	 * specified MGID into the starting index of an entry in the hash
7619e39c5baSBill Taylor 	 * table (i.e. the index for the start of the appropriate hash chain).
7629e39c5baSBill Taylor 	 * Subsequent operations below will walk the chain searching for the
7639e39c5baSBill Taylor 	 * right place to add this new QP.
7649e39c5baSBill Taylor 	 */
7659e39c5baSBill Taylor 	status = hermon_mgid_hash_cmd_post(state, gid.gid_prefix, gid.gid_guid,
7669e39c5baSBill Taylor 	    &mgid_hash, HERMON_SLEEPFLAG_FOR_CONTEXT());
7679e39c5baSBill Taylor 	if (status != HERMON_CMD_SUCCESS) {
7689e39c5baSBill Taylor 		cmn_err(CE_CONT, "Hermon: MGID_HASH command failed: %08x\n",
7699e39c5baSBill Taylor 		    status);
7709e39c5baSBill Taylor 		if (status == HERMON_CMD_INVALID_STATUS) {
7719e39c5baSBill Taylor 			hermon_fm_ereport(state, HCA_SYS_ERR, HCA_ERR_SRV_LOST);
7729e39c5baSBill Taylor 		}
7739e39c5baSBill Taylor 		return (ibc_get_ci_failure(0));
7749e39c5baSBill Taylor 	}
7759e39c5baSBill Taylor 
7769e39c5baSBill Taylor 	/*
7779e39c5baSBill Taylor 	 * Grab the multicast group mutex.  Then grab the pre-allocated
7789e39c5baSBill Taylor 	 * temporary buffer used for holding and/or modifying MCG entries.
7799e39c5baSBill Taylor 	 * Zero out the temporary MCG entry before we begin.
7809e39c5baSBill Taylor 	 */
7819e39c5baSBill Taylor 	mutex_enter(&state->hs_mcglock);
7829e39c5baSBill Taylor 	mcg_entry = state->hs_mcgtmp;
7839e39c5baSBill Taylor 	mcg_entry_qplist = HERMON_MCG_GET_QPLIST_PTR(mcg_entry);
7849e39c5baSBill Taylor 	bzero(mcg_entry, HERMON_MCGMEM_SZ(state));
7859e39c5baSBill Taylor 
7869e39c5baSBill Taylor 	/*
7879e39c5baSBill Taylor 	 * Walk through the array of MCG entries starting at "mgid_hash".
7889e39c5baSBill Taylor 	 * Try to find the appropriate place for this new QP to be added.
7899e39c5baSBill Taylor 	 * This could happen when the first entry of the chain has MGID == 0
7909e39c5baSBill Taylor 	 * (which means that the hash chain is empty), or because we find
7919e39c5baSBill Taylor 	 * an entry with the same MGID (in which case we'll add the QP to
7929e39c5baSBill Taylor 	 * that MCG), or because we come to the end of the chain (in which
7939e39c5baSBill Taylor 	 * case this is the first QP being added to the multicast group that
7949e39c5baSBill Taylor 	 * corresponds to the MGID.  The hermon_mcg_walk_mgid_hash() routine
7959e39c5baSBill Taylor 	 * walks the list and returns an index into the MCG table.  The entry
7969e39c5baSBill Taylor 	 * at this index is then checked to determine which case we have
7979e39c5baSBill Taylor 	 * fallen into (see below).  Note:  We are using the "shadow" MCG
7989e39c5baSBill Taylor 	 * list (of hermon_mcg_t structs) for this lookup because the real
7999e39c5baSBill Taylor 	 * MCG entries are in hardware (and the lookup process would be much
8009e39c5baSBill Taylor 	 * more time consuming).
8019e39c5baSBill Taylor 	 */
8029e39c5baSBill Taylor 	end_indx = hermon_mcg_walk_mgid_hash(state, mgid_hash, gid, NULL);
8039e39c5baSBill Taylor 	mcg	 = &state->hs_mcghdl[end_indx];
8049e39c5baSBill Taylor 
8059e39c5baSBill Taylor 	/*
8069e39c5baSBill Taylor 	 * If MGID == 0, then the hash chain is empty.  Just fill in the
8079e39c5baSBill Taylor 	 * current entry.  Note:  No need to allocate an MCG table entry
8089e39c5baSBill Taylor 	 * as all the hash chain "heads" are already preallocated.
8099e39c5baSBill Taylor 	 */
8109e39c5baSBill Taylor 	if ((mcg->mcg_mgid_h == 0) && (mcg->mcg_mgid_l == 0)) {
8119e39c5baSBill Taylor 
8129e39c5baSBill Taylor 		/* Fill in the current entry in the "shadow" MCG list */
8139e39c5baSBill Taylor 		hermon_mcg_setup_new_hdr(mcg, mcg_entry, gid, NULL);
8149e39c5baSBill Taylor 
8159e39c5baSBill Taylor 		/*
8169e39c5baSBill Taylor 		 * Try to add the new QP number to the list.  This (and the
8179e39c5baSBill Taylor 		 * above) routine fills in a temporary MCG.  The "mcg_entry"
8189e39c5baSBill Taylor 		 * and "mcg_entry_qplist" pointers simply point to different
8199e39c5baSBill Taylor 		 * offsets within the same temporary copy of the MCG (for
8209e39c5baSBill Taylor 		 * convenience).  Note:  If this fails, we need to invalidate
8219e39c5baSBill Taylor 		 * the entries we've already put into the "shadow" list entry
8229e39c5baSBill Taylor 		 * above.
8239e39c5baSBill Taylor 		 */
8249e39c5baSBill Taylor 		status = hermon_mcg_qplist_add(state, mcg, mcg_entry_qplist, qp,
8259e39c5baSBill Taylor 		    &qp_found);
8269e39c5baSBill Taylor 		if (status != DDI_SUCCESS) {
8279e39c5baSBill Taylor 			bzero(mcg, sizeof (struct hermon_sw_mcg_list_s));
8289e39c5baSBill Taylor 			mutex_exit(&state->hs_mcglock);
8299e39c5baSBill Taylor 			return (status);
8309e39c5baSBill Taylor 		}
8319e39c5baSBill Taylor 		if (!qp_found)
8329e39c5baSBill Taylor 			mcg_entry->member_cnt = (mcg->mcg_num_qps + 1);
8339e39c5baSBill Taylor 			    /* set the member count */
8349e39c5baSBill Taylor 
8359e39c5baSBill Taylor 		/*
8369e39c5baSBill Taylor 		 * Once the temporary MCG has been filled in, write the entry
8379e39c5baSBill Taylor 		 * into the appropriate location in the Hermon MCG entry table.
8389e39c5baSBill Taylor 		 * If it's successful, then drop the lock and return success.
8399e39c5baSBill Taylor 		 * Note: In general, this operation shouldn't fail.  If it
8409e39c5baSBill Taylor 		 * does, then it is an indication that something (probably in
8419e39c5baSBill Taylor 		 * HW, but maybe in SW) has gone seriously wrong.  We still
8429e39c5baSBill Taylor 		 * want to zero out the entries that we've filled in above
8439e39c5baSBill Taylor 		 * (in the hermon_mcg_setup_new_hdr() routine).
8449e39c5baSBill Taylor 		 */
8459e39c5baSBill Taylor 		status = hermon_write_mgm_cmd_post(state, mcg_entry, end_indx,
8469e39c5baSBill Taylor 		    HERMON_CMD_NOSLEEP_SPIN);
8479e39c5baSBill Taylor 		if (status != HERMON_CMD_SUCCESS) {
8489e39c5baSBill Taylor 			bzero(mcg, sizeof (struct hermon_sw_mcg_list_s));
8499e39c5baSBill Taylor 			mutex_exit(&state->hs_mcglock);
8509e39c5baSBill Taylor 			HERMON_WARNING(state, "failed to write MCG entry");
8519e39c5baSBill Taylor 			cmn_err(CE_CONT, "Hermon: WRITE_MGM command failed: "
8529e39c5baSBill Taylor 			    "%08x\n", status);
8539e39c5baSBill Taylor 			if (status == HERMON_CMD_INVALID_STATUS) {
8549e39c5baSBill Taylor 				hermon_fm_ereport(state, HCA_SYS_ERR,
8559e39c5baSBill Taylor 				    HCA_ERR_SRV_LOST);
8569e39c5baSBill Taylor 			}
8579e39c5baSBill Taylor 			return (ibc_get_ci_failure(0));
8589e39c5baSBill Taylor 		}
8599e39c5baSBill Taylor 
8609e39c5baSBill Taylor 		/*
8619e39c5baSBill Taylor 		 * Now that we know all the Hermon firmware accesses have been
8629e39c5baSBill Taylor 		 * successful, we update the "shadow" MCG entry by incrementing
8639e39c5baSBill Taylor 		 * the "number of attached QPs" count.
8649e39c5baSBill Taylor 		 *
8659e39c5baSBill Taylor 		 * We increment only if the QP is not already part of the
8669e39c5baSBill Taylor 		 * MCG by checking the 'qp_found' flag returned from the
8679e39c5baSBill Taylor 		 * qplist_add above.
8689e39c5baSBill Taylor 		 */
8699e39c5baSBill Taylor 		if (!qp_found) {
8709e39c5baSBill Taylor 			mcg->mcg_num_qps++;
8719e39c5baSBill Taylor 
8729e39c5baSBill Taylor 			/*
8739e39c5baSBill Taylor 			 * Increment the refcnt for this QP.  Because the QP
8749e39c5baSBill Taylor 			 * was added to this MCG, the refcnt must be
8759e39c5baSBill Taylor 			 * incremented.
8769e39c5baSBill Taylor 			 */
8779e39c5baSBill Taylor 			hermon_qp_mcg_refcnt_inc(qp);
8789e39c5baSBill Taylor 		}
8799e39c5baSBill Taylor 
8809e39c5baSBill Taylor 		/*
8819e39c5baSBill Taylor 		 * We drop the lock and return success.
8829e39c5baSBill Taylor 		 */
8839e39c5baSBill Taylor 		mutex_exit(&state->hs_mcglock);
8849e39c5baSBill Taylor 		return (DDI_SUCCESS);
8859e39c5baSBill Taylor 	}
8869e39c5baSBill Taylor 
8879e39c5baSBill Taylor 	/*
8889e39c5baSBill Taylor 	 * If the specified MGID matches the MGID in the current entry, then
8899e39c5baSBill Taylor 	 * we need to try to add the QP to the current MCG entry.  In this
8909e39c5baSBill Taylor 	 * case, it means that we need to read the existing MCG entry (into
8919e39c5baSBill Taylor 	 * the temporary MCG), add the new QP number to the temporary entry
8929e39c5baSBill Taylor 	 * (using the same method we used above), and write the entry back
8939e39c5baSBill Taylor 	 * to the hardware (same as above).
8949e39c5baSBill Taylor 	 */
8959e39c5baSBill Taylor 	if ((mcg->mcg_mgid_h == gid.gid_prefix) &&
8969e39c5baSBill Taylor 	    (mcg->mcg_mgid_l == gid.gid_guid)) {
8979e39c5baSBill Taylor 
8989e39c5baSBill Taylor 		/*
8999e39c5baSBill Taylor 		 * Read the current MCG entry into the temporary MCG.  Note:
9009e39c5baSBill Taylor 		 * In general, this operation shouldn't fail.  If it does,
9019e39c5baSBill Taylor 		 * then it is an indication that something (probably in HW,
9029e39c5baSBill Taylor 		 * but maybe in SW) has gone seriously wrong.
9039e39c5baSBill Taylor 		 */
9049e39c5baSBill Taylor 		status = hermon_read_mgm_cmd_post(state, mcg_entry, end_indx,
9059e39c5baSBill Taylor 		    HERMON_CMD_NOSLEEP_SPIN);
9069e39c5baSBill Taylor 		if (status != HERMON_CMD_SUCCESS) {
9079e39c5baSBill Taylor 			mutex_exit(&state->hs_mcglock);
9089e39c5baSBill Taylor 			HERMON_WARNING(state, "failed to read MCG entry");
9099e39c5baSBill Taylor 			cmn_err(CE_CONT, "Hermon: READ_MGM command failed: "
9109e39c5baSBill Taylor 			    "%08x\n", status);
9119e39c5baSBill Taylor 			if (status == HERMON_CMD_INVALID_STATUS) {
9129e39c5baSBill Taylor 				hermon_fm_ereport(state, HCA_SYS_ERR,
9139e39c5baSBill Taylor 				    HCA_ERR_SRV_LOST);
9149e39c5baSBill Taylor 			}
9159e39c5baSBill Taylor 			return (ibc_get_ci_failure(0));
9169e39c5baSBill Taylor 		}
9179e39c5baSBill Taylor 
9189e39c5baSBill Taylor 		/*
9199e39c5baSBill Taylor 		 * Try to add the new QP number to the list.  This routine
9209e39c5baSBill Taylor 		 * fills in the necessary pieces of the temporary MCG.  The
9219e39c5baSBill Taylor 		 * "mcg_entry_qplist" pointer is used to point to the portion
9229e39c5baSBill Taylor 		 * of the temporary MCG that holds the QP numbers.
9239e39c5baSBill Taylor 		 *
9249e39c5baSBill Taylor 		 * Note: hermon_mcg_qplist_add() returns SUCCESS if it
9259e39c5baSBill Taylor 		 * already found the QP in the list.  In this case, the QP is
9269e39c5baSBill Taylor 		 * not added on to the list again.  Check the flag 'qp_found'
9279e39c5baSBill Taylor 		 * if this value is needed to be known.
9289e39c5baSBill Taylor 		 *
9299e39c5baSBill Taylor 		 */
9309e39c5baSBill Taylor 		status = hermon_mcg_qplist_add(state, mcg, mcg_entry_qplist, qp,
9319e39c5baSBill Taylor 		    &qp_found);
9329e39c5baSBill Taylor 		if (status != DDI_SUCCESS) {
9339e39c5baSBill Taylor 			mutex_exit(&state->hs_mcglock);
9349e39c5baSBill Taylor 			return (status);
9359e39c5baSBill Taylor 		}
9369e39c5baSBill Taylor 		if (!qp_found)
9379e39c5baSBill Taylor 			mcg_entry->member_cnt = (mcg->mcg_num_qps + 1);
9389e39c5baSBill Taylor 			    /* set the member count */
9399e39c5baSBill Taylor 
9409e39c5baSBill Taylor 		/*
9419e39c5baSBill Taylor 		 * Once the temporary MCG has been updated, write the entry
9429e39c5baSBill Taylor 		 * into the appropriate location in the Hermon MCG entry table.
9439e39c5baSBill Taylor 		 * If it's successful, then drop the lock and return success.
9449e39c5baSBill Taylor 		 * Note: In general, this operation shouldn't fail.  If it
9459e39c5baSBill Taylor 		 * does, then it is an indication that something (probably in
9469e39c5baSBill Taylor 		 * HW, but maybe in SW) has gone seriously wrong.
9479e39c5baSBill Taylor 		 */
9489e39c5baSBill Taylor 		status = hermon_write_mgm_cmd_post(state, mcg_entry, end_indx,
9499e39c5baSBill Taylor 		    HERMON_CMD_NOSLEEP_SPIN);
9509e39c5baSBill Taylor 		if (status != HERMON_CMD_SUCCESS) {
9519e39c5baSBill Taylor 			mutex_exit(&state->hs_mcglock);
9529e39c5baSBill Taylor 			HERMON_WARNING(state, "failed to write MCG entry");
9539e39c5baSBill Taylor 			cmn_err(CE_CONT, "Hermon: WRITE_MGM command failed: "
9549e39c5baSBill Taylor 			    "%08x\n", status);
9559e39c5baSBill Taylor 			if (status == HERMON_CMD_INVALID_STATUS) {
9569e39c5baSBill Taylor 				hermon_fm_ereport(state, HCA_SYS_ERR,
9579e39c5baSBill Taylor 				    HCA_ERR_SRV_LOST);
9589e39c5baSBill Taylor 			}
9599e39c5baSBill Taylor 			return (ibc_get_ci_failure(0));
9609e39c5baSBill Taylor 		}
9619e39c5baSBill Taylor 
9629e39c5baSBill Taylor 		/*
9639e39c5baSBill Taylor 		 * Now that we know all the Hermon firmware accesses have been
9649e39c5baSBill Taylor 		 * successful, we update the current "shadow" MCG entry by
9659e39c5baSBill Taylor 		 * incrementing the "number of attached QPs" count.
9669e39c5baSBill Taylor 		 *
9679e39c5baSBill Taylor 		 * We increment only if the QP is not already part of the
9689e39c5baSBill Taylor 		 * MCG by checking the 'qp_found' flag returned
9699e39c5baSBill Taylor 		 * hermon_mcg_walk_mgid_hashfrom the qplist_add above.
9709e39c5baSBill Taylor 		 */
9719e39c5baSBill Taylor 		if (!qp_found) {
9729e39c5baSBill Taylor 			mcg->mcg_num_qps++;
9739e39c5baSBill Taylor 
9749e39c5baSBill Taylor 			/*
9759e39c5baSBill Taylor 			 * Increment the refcnt for this QP.  Because the QP
9769e39c5baSBill Taylor 			 * was added to this MCG, the refcnt must be
9779e39c5baSBill Taylor 			 * incremented.
9789e39c5baSBill Taylor 			 */
9799e39c5baSBill Taylor 			hermon_qp_mcg_refcnt_inc(qp);
9809e39c5baSBill Taylor 		}
9819e39c5baSBill Taylor 
9829e39c5baSBill Taylor 		/*
9839e39c5baSBill Taylor 		 * We drop the lock and return success.
9849e39c5baSBill Taylor 		 */
9859e39c5baSBill Taylor 		mutex_exit(&state->hs_mcglock);
9869e39c5baSBill Taylor 		return (DDI_SUCCESS);
9879e39c5baSBill Taylor 	}
9889e39c5baSBill Taylor 
9899e39c5baSBill Taylor 	/*
9909e39c5baSBill Taylor 	 * If we've reached here, then we're at the end of the hash chain.
9919e39c5baSBill Taylor 	 * We need to allocate a new MCG entry, fill it in, write it to Hermon,
9929e39c5baSBill Taylor 	 * and update the previous entry to link the new one to the end of the
9939e39c5baSBill Taylor 	 * chain.
9949e39c5baSBill Taylor 	 */
9959e39c5baSBill Taylor 
9969e39c5baSBill Taylor 	/*
9979e39c5baSBill Taylor 	 * Allocate an MCG table entry.  This will be filled in with all
9989e39c5baSBill Taylor 	 * the necessary parameters to define the multicast group.  Then it
9999e39c5baSBill Taylor 	 * will be written to the hardware in the next-to-last step below.
10009e39c5baSBill Taylor 	 */
10019e39c5baSBill Taylor 	status = hermon_rsrc_alloc(state, HERMON_MCG, 1, HERMON_NOSLEEP, &rsrc);
10029e39c5baSBill Taylor 	if (status != DDI_SUCCESS) {
10039e39c5baSBill Taylor 		mutex_exit(&state->hs_mcglock);
10049e39c5baSBill Taylor 		return (IBT_INSUFF_RESOURCE);
10059e39c5baSBill Taylor 	}
10069e39c5baSBill Taylor 
10079e39c5baSBill Taylor 	/*
10089e39c5baSBill Taylor 	 * Fill in the new entry in the "shadow" MCG list.  Note:  Just as
10099e39c5baSBill Taylor 	 * it does above, hermon_mcg_setup_new_hdr() also fills in a portion
10109e39c5baSBill Taylor 	 * of the temporary MCG entry (the rest of which will be filled in by
10119e39c5baSBill Taylor 	 * hermon_mcg_qplist_add() below)
10129e39c5baSBill Taylor 	 */
10139e39c5baSBill Taylor 	newmcg = &state->hs_mcghdl[rsrc->hr_indx];
10149e39c5baSBill Taylor 	hermon_mcg_setup_new_hdr(newmcg, mcg_entry, gid, rsrc);
10159e39c5baSBill Taylor 
10169e39c5baSBill Taylor 	/*
10179e39c5baSBill Taylor 	 * Try to add the new QP number to the list.  This routine fills in
10189e39c5baSBill Taylor 	 * the final necessary pieces of the temporary MCG.  The
10199e39c5baSBill Taylor 	 * "mcg_entry_qplist" pointer is used to point to the portion of the
10209e39c5baSBill Taylor 	 * temporary MCG that holds the QP numbers.  If we fail here, we
10219e39c5baSBill Taylor 	 * must undo the previous resource allocation.
10229e39c5baSBill Taylor 	 *
10239e39c5baSBill Taylor 	 * Note: hermon_mcg_qplist_add() can we return SUCCESS if it already
10249e39c5baSBill Taylor 	 * found the QP in the list.  In this case, the QP is not added on to
10259e39c5baSBill Taylor 	 * the list again.  Check the flag 'qp_found' if this value is needed
10269e39c5baSBill Taylor 	 * to be known.
10279e39c5baSBill Taylor 	 */
10289e39c5baSBill Taylor 	status = hermon_mcg_qplist_add(state, newmcg, mcg_entry_qplist, qp,
10299e39c5baSBill Taylor 	    &qp_found);
10309e39c5baSBill Taylor 	if (status != DDI_SUCCESS) {
10319e39c5baSBill Taylor 		bzero(newmcg, sizeof (struct hermon_sw_mcg_list_s));
10329e39c5baSBill Taylor 		hermon_rsrc_free(state, &rsrc);
10339e39c5baSBill Taylor 		mutex_exit(&state->hs_mcglock);
10349e39c5baSBill Taylor 		return (status);
10359e39c5baSBill Taylor 	}
10369e39c5baSBill Taylor 	mcg_entry->member_cnt = (newmcg->mcg_num_qps + 1);
10379e39c5baSBill Taylor 	    /* set the member count */
10389e39c5baSBill Taylor 
10399e39c5baSBill Taylor 	/*
10409e39c5baSBill Taylor 	 * Once the temporary MCG has been updated, write the entry into the
10419e39c5baSBill Taylor 	 * appropriate location in the Hermon MCG entry table.  If this is
10429e39c5baSBill Taylor 	 * successful, then we need to chain the previous entry to this one.
10439e39c5baSBill Taylor 	 * Note: In general, this operation shouldn't fail.  If it does, then
10449e39c5baSBill Taylor 	 * it is an indication that something (probably in HW, but maybe in
10459e39c5baSBill Taylor 	 * SW) has gone seriously wrong.
10469e39c5baSBill Taylor 	 */
10479e39c5baSBill Taylor 	status = hermon_write_mgm_cmd_post(state, mcg_entry, rsrc->hr_indx,
10489e39c5baSBill Taylor 	    HERMON_CMD_NOSLEEP_SPIN);
10499e39c5baSBill Taylor 	if (status != HERMON_CMD_SUCCESS) {
10509e39c5baSBill Taylor 		bzero(newmcg, sizeof (struct hermon_sw_mcg_list_s));
10519e39c5baSBill Taylor 		hermon_rsrc_free(state, &rsrc);
10529e39c5baSBill Taylor 		mutex_exit(&state->hs_mcglock);
10539e39c5baSBill Taylor 		HERMON_WARNING(state, "failed to write MCG entry");
10549e39c5baSBill Taylor 		cmn_err(CE_CONT, "Hermon: WRITE_MGM command failed: %08x\n",
10559e39c5baSBill Taylor 		    status);
10569e39c5baSBill Taylor 		if (status == HERMON_CMD_INVALID_STATUS) {
10579e39c5baSBill Taylor 			hermon_fm_ereport(state, HCA_SYS_ERR, HCA_ERR_SRV_LOST);
10589e39c5baSBill Taylor 		}
10599e39c5baSBill Taylor 		return (ibc_get_ci_failure(0));
10609e39c5baSBill Taylor 	}
10619e39c5baSBill Taylor 
10629e39c5baSBill Taylor 	/*
10639e39c5baSBill Taylor 	 * Now read the current MCG entry (the one previously at the end of
10649e39c5baSBill Taylor 	 * hash chain) into the temporary MCG.  We are going to update its
10659e39c5baSBill Taylor 	 * "next_gid_indx" now and write the entry back to the MCG table.
10669e39c5baSBill Taylor 	 * Note:  In general, this operation shouldn't fail.  If it does, then
10679e39c5baSBill Taylor 	 * it is an indication that something (probably in HW, but maybe in SW)
10689e39c5baSBill Taylor 	 * has gone seriously wrong.  We will free up the MCG entry resource,
10699e39c5baSBill Taylor 	 * but we will not undo the previously written MCG entry in the HW.
10709e39c5baSBill Taylor 	 * This is OK, though, because the MCG entry is not currently attached
10719e39c5baSBill Taylor 	 * to any hash chain.
10729e39c5baSBill Taylor 	 */
10739e39c5baSBill Taylor 	status = hermon_read_mgm_cmd_post(state, mcg_entry, end_indx,
10749e39c5baSBill Taylor 	    HERMON_CMD_NOSLEEP_SPIN);
10759e39c5baSBill Taylor 	if (status != HERMON_CMD_SUCCESS) {
10769e39c5baSBill Taylor 		bzero(newmcg, sizeof (struct hermon_sw_mcg_list_s));
10779e39c5baSBill Taylor 		hermon_rsrc_free(state, &rsrc);
10789e39c5baSBill Taylor 		mutex_exit(&state->hs_mcglock);
10799e39c5baSBill Taylor 		HERMON_WARNING(state, "failed to read MCG entry");
10809e39c5baSBill Taylor 		cmn_err(CE_CONT, "Hermon: READ_MGM command failed: %08x\n",
10819e39c5baSBill Taylor 		    status);
10829e39c5baSBill Taylor 		if (status == HERMON_CMD_INVALID_STATUS) {
10839e39c5baSBill Taylor 			hermon_fm_ereport(state, HCA_SYS_ERR, HCA_ERR_SRV_LOST);
10849e39c5baSBill Taylor 		}
10859e39c5baSBill Taylor 		return (ibc_get_ci_failure(0));
10869e39c5baSBill Taylor 	}
10879e39c5baSBill Taylor 
10889e39c5baSBill Taylor 	/*
10899e39c5baSBill Taylor 	 * Finally, we update the "next_gid_indx" field in the temporary MCG
10909e39c5baSBill Taylor 	 * and attempt to write the entry back into the Hermon MCG table.  If
10919e39c5baSBill Taylor 	 * this succeeds, then we update the "shadow" list to reflect the
10929e39c5baSBill Taylor 	 * change, drop the lock, and return success.  Note:  In general, this
10939e39c5baSBill Taylor 	 * operation shouldn't fail.  If it does, then it is an indication
10949e39c5baSBill Taylor 	 * that something (probably in HW, but maybe in SW) has gone seriously
10959e39c5baSBill Taylor 	 * wrong.  Just as we do above, we will free up the MCG entry resource,
10969e39c5baSBill Taylor 	 * but we will not try to undo the previously written MCG entry.  This
10979e39c5baSBill Taylor 	 * is OK, though, because (since we failed here to update the end of
10989e39c5baSBill Taylor 	 * the chain) that other entry is not currently attached to any chain.
10999e39c5baSBill Taylor 	 */
11009e39c5baSBill Taylor 	mcg_entry->next_gid_indx = rsrc->hr_indx;
11019e39c5baSBill Taylor 	status = hermon_write_mgm_cmd_post(state, mcg_entry, end_indx,
11029e39c5baSBill Taylor 	    HERMON_CMD_NOSLEEP_SPIN);
11039e39c5baSBill Taylor 	if (status != HERMON_CMD_SUCCESS) {
11049e39c5baSBill Taylor 		bzero(newmcg, sizeof (struct hermon_sw_mcg_list_s));
11059e39c5baSBill Taylor 		hermon_rsrc_free(state, &rsrc);
11069e39c5baSBill Taylor 		mutex_exit(&state->hs_mcglock);
11079e39c5baSBill Taylor 		HERMON_WARNING(state, "failed to write MCG entry");
11089e39c5baSBill Taylor 		cmn_err(CE_CONT, "Hermon: WRITE_MGM command failed: %08x\n",
11099e39c5baSBill Taylor 		    status);
11109e39c5baSBill Taylor 		if (status == HERMON_CMD_INVALID_STATUS) {
11119e39c5baSBill Taylor 			hermon_fm_ereport(state, HCA_SYS_ERR, HCA_ERR_SRV_LOST);
11129e39c5baSBill Taylor 		}
11139e39c5baSBill Taylor 		return (ibc_get_ci_failure(0));
11149e39c5baSBill Taylor 	}
11159e39c5baSBill Taylor 	mcg = &state->hs_mcghdl[end_indx];
11169e39c5baSBill Taylor 	mcg->mcg_next_indx = rsrc->hr_indx;
11179e39c5baSBill Taylor 
11189e39c5baSBill Taylor 	/*
11199e39c5baSBill Taylor 	 * Now that we know all the Hermon firmware accesses have been
11209e39c5baSBill Taylor 	 * successful, we update the new "shadow" MCG entry by incrementing
11219e39c5baSBill Taylor 	 * the "number of attached QPs" count.  Then we drop the lock and
11229e39c5baSBill Taylor 	 * return success.
11239e39c5baSBill Taylor 	 */
11249e39c5baSBill Taylor 	newmcg->mcg_num_qps++;
11259e39c5baSBill Taylor 
11269e39c5baSBill Taylor 	/*
11279e39c5baSBill Taylor 	 * Increment the refcnt for this QP.  Because the QP
11289e39c5baSBill Taylor 	 * was added to this MCG, the refcnt must be
11299e39c5baSBill Taylor 	 * incremented.
11309e39c5baSBill Taylor 	 */
11319e39c5baSBill Taylor 	hermon_qp_mcg_refcnt_inc(qp);
11329e39c5baSBill Taylor 
11339e39c5baSBill Taylor 	mutex_exit(&state->hs_mcglock);
11349e39c5baSBill Taylor 	return (DDI_SUCCESS);
11359e39c5baSBill Taylor }
11369e39c5baSBill Taylor 
11379e39c5baSBill Taylor 
11389e39c5baSBill Taylor /*
11399e39c5baSBill Taylor  * hermon_mcg_detach()
11409e39c5baSBill Taylor  *    Context: Can be called only from user or kernel context.
11419e39c5baSBill Taylor  */
11429e39c5baSBill Taylor int
hermon_mcg_detach(hermon_state_t * state,hermon_qphdl_t qp,ib_gid_t gid,ib_lid_t lid)11439e39c5baSBill Taylor hermon_mcg_detach(hermon_state_t *state, hermon_qphdl_t qp, ib_gid_t gid,
11449e39c5baSBill Taylor     ib_lid_t lid)
11459e39c5baSBill Taylor {
11469e39c5baSBill Taylor 	hermon_hw_mcg_t		*mcg_entry;
11479e39c5baSBill Taylor 	hermon_hw_mcg_qp_list_t	*mcg_entry_qplist;
11489e39c5baSBill Taylor 	hermon_mcghdl_t		mcg;
11499e39c5baSBill Taylor 	uint64_t		mgid_hash;
11509e39c5baSBill Taylor 	uint32_t		end_indx, prev_indx;
11519e39c5baSBill Taylor 	int			status;
11529e39c5baSBill Taylor 
11539e39c5baSBill Taylor 	/*
11549e39c5baSBill Taylor 	 * Check for invalid Multicast DLID.  Specifically, all Multicast
11559e39c5baSBill Taylor 	 * LIDs should be within a well defined range.  If the specified LID
11569e39c5baSBill Taylor 	 * is outside of that range, then return an error.
11579e39c5baSBill Taylor 	 */
11589e39c5baSBill Taylor 	if (hermon_mlid_is_valid(lid) == 0) {
11599e39c5baSBill Taylor 		return (IBT_MC_MLID_INVALID);
11609e39c5baSBill Taylor 	}
11619e39c5baSBill Taylor 
11629e39c5baSBill Taylor 	/*
11639e39c5baSBill Taylor 	 * Compute the MGID hash value.  As described above, the MCG table is
11649e39c5baSBill Taylor 	 * arranged as a number of separate hash chains.  This operation
11659e39c5baSBill Taylor 	 * converts the specified MGID into the starting index of an entry in
11669e39c5baSBill Taylor 	 * the hash table (i.e. the index for the start of the appropriate
11679e39c5baSBill Taylor 	 * hash chain).  Subsequent operations below will walk the chain
11689e39c5baSBill Taylor 	 * searching for a matching entry from which to attempt to remove
11699e39c5baSBill Taylor 	 * the specified QP.
11709e39c5baSBill Taylor 	 */
11719e39c5baSBill Taylor 	status = hermon_mgid_hash_cmd_post(state, gid.gid_prefix, gid.gid_guid,
11729e39c5baSBill Taylor 	    &mgid_hash, HERMON_SLEEPFLAG_FOR_CONTEXT());
11739e39c5baSBill Taylor 	if (status != HERMON_CMD_SUCCESS) {
11749e39c5baSBill Taylor 		cmn_err(CE_CONT, "Hermon: MGID_HASH command failed: %08x\n",
11759e39c5baSBill Taylor 		    status);
11769e39c5baSBill Taylor 		if (status == HERMON_CMD_INVALID_STATUS) {
11779e39c5baSBill Taylor 			hermon_fm_ereport(state, HCA_SYS_ERR, HCA_ERR_SRV_LOST);
11789e39c5baSBill Taylor 		}
11799e39c5baSBill Taylor 		return (ibc_get_ci_failure(0));
11809e39c5baSBill Taylor 	}
11819e39c5baSBill Taylor 
11829e39c5baSBill Taylor 	/*
11839e39c5baSBill Taylor 	 * Grab the multicast group mutex.  Then grab the pre-allocated
11849e39c5baSBill Taylor 	 * temporary buffer used for holding and/or modifying MCG entries.
11859e39c5baSBill Taylor 	 */
11869e39c5baSBill Taylor 	mutex_enter(&state->hs_mcglock);
11879e39c5baSBill Taylor 	mcg_entry = state->hs_mcgtmp;
11889e39c5baSBill Taylor 	mcg_entry_qplist = HERMON_MCG_GET_QPLIST_PTR(mcg_entry);
11899e39c5baSBill Taylor 
11909e39c5baSBill Taylor 	/*
11919e39c5baSBill Taylor 	 * Walk through the array of MCG entries starting at "mgid_hash".
11929e39c5baSBill Taylor 	 * Try to find an MCG entry with a matching MGID.  The
11939e39c5baSBill Taylor 	 * hermon_mcg_walk_mgid_hash() routine walks the list and returns an
11949e39c5baSBill Taylor 	 * index into the MCG table.  The entry at this index is checked to
11959e39c5baSBill Taylor 	 * determine whether it is a match or not.  If it is a match, then
11969e39c5baSBill Taylor 	 * we continue on to attempt to remove the QP from the MCG.  If it
11979e39c5baSBill Taylor 	 * is not a match (or not a valid MCG entry), then we return an error.
11989e39c5baSBill Taylor 	 */
11999e39c5baSBill Taylor 	end_indx = hermon_mcg_walk_mgid_hash(state, mgid_hash, gid, &prev_indx);
12009e39c5baSBill Taylor 	mcg	 = &state->hs_mcghdl[end_indx];
12019e39c5baSBill Taylor 
12029e39c5baSBill Taylor 	/*
12039e39c5baSBill Taylor 	 * If MGID == 0 (the hash chain is empty) or if the specified MGID
12049e39c5baSBill Taylor 	 * does not match the MGID in the current entry, then return
12059e39c5baSBill Taylor 	 * IBT_MC_MGID_INVALID (to indicate that the specified MGID is not
12069e39c5baSBill Taylor 	 * valid).
12079e39c5baSBill Taylor 	 */
12089e39c5baSBill Taylor 	if (((mcg->mcg_mgid_h == 0) && (mcg->mcg_mgid_l == 0)) ||
12099e39c5baSBill Taylor 	    ((mcg->mcg_mgid_h != gid.gid_prefix) ||
12109e39c5baSBill Taylor 	    (mcg->mcg_mgid_l != gid.gid_guid))) {
12119e39c5baSBill Taylor 		mutex_exit(&state->hs_mcglock);
12129e39c5baSBill Taylor 		return (IBT_MC_MGID_INVALID);
12139e39c5baSBill Taylor 	}
12149e39c5baSBill Taylor 
12159e39c5baSBill Taylor 	/*
12169e39c5baSBill Taylor 	 * Read the current MCG entry into the temporary MCG.  Note: In
12179e39c5baSBill Taylor 	 * general, this operation shouldn't fail.  If it does, then it is
12189e39c5baSBill Taylor 	 * an indication that something (probably in HW, but maybe in SW)
12199e39c5baSBill Taylor 	 * has gone seriously wrong.
12209e39c5baSBill Taylor 	 */
12219e39c5baSBill Taylor 	status = hermon_read_mgm_cmd_post(state, mcg_entry, end_indx,
12229e39c5baSBill Taylor 	    HERMON_CMD_NOSLEEP_SPIN);
12239e39c5baSBill Taylor 	if (status != HERMON_CMD_SUCCESS) {
12249e39c5baSBill Taylor 		mutex_exit(&state->hs_mcglock);
12259e39c5baSBill Taylor 		HERMON_WARNING(state, "failed to read MCG entry");
12269e39c5baSBill Taylor 		cmn_err(CE_CONT, "Hermon: READ_MGM command failed: %08x\n",
12279e39c5baSBill Taylor 		    status);
12289e39c5baSBill Taylor 		if (status == HERMON_CMD_INVALID_STATUS) {
12299e39c5baSBill Taylor 			hermon_fm_ereport(state, HCA_SYS_ERR, HCA_ERR_SRV_LOST);
12309e39c5baSBill Taylor 		}
12319e39c5baSBill Taylor 		return (ibc_get_ci_failure(0));
12329e39c5baSBill Taylor 	}
12339e39c5baSBill Taylor 
12349e39c5baSBill Taylor 	/*
12359e39c5baSBill Taylor 	 * Search the QP number list for a match.  If a match is found, then
12369e39c5baSBill Taylor 	 * remove the entry from the QP list.  Otherwise, if no match is found,
12379e39c5baSBill Taylor 	 * return an error.
12389e39c5baSBill Taylor 	 */
12399e39c5baSBill Taylor 	status = hermon_mcg_qplist_remove(mcg, mcg_entry_qplist, qp);
12409e39c5baSBill Taylor 	if (status != DDI_SUCCESS) {
12419e39c5baSBill Taylor 		mutex_exit(&state->hs_mcglock);
12429e39c5baSBill Taylor 		return (status);
12439e39c5baSBill Taylor 	}
12449e39c5baSBill Taylor 
12459e39c5baSBill Taylor 	/*
12469e39c5baSBill Taylor 	 * Decrement the MCG count for this QP.  When the 'qp_mcg'
12479e39c5baSBill Taylor 	 * field becomes 0, then this QP is no longer a member of any
12489e39c5baSBill Taylor 	 * MCG.
12499e39c5baSBill Taylor 	 */
12509e39c5baSBill Taylor 	hermon_qp_mcg_refcnt_dec(qp);
12519e39c5baSBill Taylor 
12529e39c5baSBill Taylor 	/*
12539e39c5baSBill Taylor 	 * If the current MCG's QP number list is about to be made empty
12549e39c5baSBill Taylor 	 * ("mcg_num_qps" == 1), then remove the entry itself from the hash
12559e39c5baSBill Taylor 	 * chain.  Otherwise, just write the updated MCG entry back to the
12569e39c5baSBill Taylor 	 * hardware.  In either case, once we successfully update the hardware
12579e39c5baSBill Taylor 	 * chain, then we decrement the "shadow" list entry's "mcg_num_qps"
12589e39c5baSBill Taylor 	 * count (or zero out the entire "shadow" list entry) before returning
12599e39c5baSBill Taylor 	 * success.  Note:  Zeroing out the "shadow" list entry is done
12609e39c5baSBill Taylor 	 * inside of hermon_mcg_hash_list_remove().
12619e39c5baSBill Taylor 	 */
12629e39c5baSBill Taylor 	if (mcg->mcg_num_qps == 1) {
12639e39c5baSBill Taylor 
12649e39c5baSBill Taylor 		/* Remove an MCG entry from the hash chain */
12659e39c5baSBill Taylor 		status = hermon_mcg_hash_list_remove(state, end_indx, prev_indx,
12669e39c5baSBill Taylor 		    mcg_entry);
12679e39c5baSBill Taylor 		if (status != DDI_SUCCESS) {
12689e39c5baSBill Taylor 			mutex_exit(&state->hs_mcglock);
12699e39c5baSBill Taylor 			return (status);
12709e39c5baSBill Taylor 		}
12719e39c5baSBill Taylor 
12729e39c5baSBill Taylor 	} else {
12739e39c5baSBill Taylor 		/*
12749e39c5baSBill Taylor 		 * Write the updated MCG entry back to the Hermon MCG table.
12759e39c5baSBill Taylor 		 * If this succeeds, then we update the "shadow" list to
12769e39c5baSBill Taylor 		 * reflect the change (i.e. decrement the "mcg_num_qps"),
12779e39c5baSBill Taylor 		 * drop the lock, and return success.  Note:  In general,
12789e39c5baSBill Taylor 		 * this operation shouldn't fail.  If it does, then it is an
12799e39c5baSBill Taylor 		 * indication that something (probably in HW, but maybe in SW)
12809e39c5baSBill Taylor 		 * has gone seriously wrong.
12819e39c5baSBill Taylor 		 */
12829e39c5baSBill Taylor 		mcg_entry->member_cnt = (mcg->mcg_num_qps - 1);
12839e39c5baSBill Taylor 		status = hermon_write_mgm_cmd_post(state, mcg_entry, end_indx,
12849e39c5baSBill Taylor 		    HERMON_CMD_NOSLEEP_SPIN);
12859e39c5baSBill Taylor 		if (status != HERMON_CMD_SUCCESS) {
12869e39c5baSBill Taylor 			mutex_exit(&state->hs_mcglock);
12879e39c5baSBill Taylor 			HERMON_WARNING(state, "failed to write MCG entry");
12889e39c5baSBill Taylor 			cmn_err(CE_CONT, "Hermon: WRITE_MGM command failed: "
12899e39c5baSBill Taylor 			    "%08x\n", status);
12909e39c5baSBill Taylor 			if (status == HERMON_CMD_INVALID_STATUS) {
12919e39c5baSBill Taylor 				hermon_fm_ereport(state, HCA_SYS_ERR,
12929e39c5baSBill Taylor 				    HCA_ERR_SRV_LOST);
12939e39c5baSBill Taylor 			}
12949e39c5baSBill Taylor 			return (ibc_get_ci_failure(0));
12959e39c5baSBill Taylor 		}
12969e39c5baSBill Taylor 		mcg->mcg_num_qps--;
12979e39c5baSBill Taylor 	}
12989e39c5baSBill Taylor 
12999e39c5baSBill Taylor 	mutex_exit(&state->hs_mcglock);
13009e39c5baSBill Taylor 	return (DDI_SUCCESS);
13019e39c5baSBill Taylor }
13029e39c5baSBill Taylor 
13039e39c5baSBill Taylor /*
13049e39c5baSBill Taylor  * hermon_qp_mcg_refcnt_inc()
13059e39c5baSBill Taylor  *    Context: Can be called from interrupt or base context.
13069e39c5baSBill Taylor  */
13079e39c5baSBill Taylor static void
hermon_qp_mcg_refcnt_inc(hermon_qphdl_t qp)13089e39c5baSBill Taylor hermon_qp_mcg_refcnt_inc(hermon_qphdl_t qp)
13099e39c5baSBill Taylor {
13109e39c5baSBill Taylor 	/* Increment the QP's MCG reference count */
13119e39c5baSBill Taylor 	mutex_enter(&qp->qp_lock);
13129e39c5baSBill Taylor 	qp->qp_mcg_refcnt++;
13139e39c5baSBill Taylor 	mutex_exit(&qp->qp_lock);
13149e39c5baSBill Taylor }
13159e39c5baSBill Taylor 
13169e39c5baSBill Taylor 
13179e39c5baSBill Taylor /*
13189e39c5baSBill Taylor  * hermon_qp_mcg_refcnt_dec()
13199e39c5baSBill Taylor  *    Context: Can be called from interrupt or base context.
13209e39c5baSBill Taylor  */
13219e39c5baSBill Taylor static void
hermon_qp_mcg_refcnt_dec(hermon_qphdl_t qp)13229e39c5baSBill Taylor hermon_qp_mcg_refcnt_dec(hermon_qphdl_t qp)
13239e39c5baSBill Taylor {
13249e39c5baSBill Taylor 	/* Decrement the QP's MCG reference count */
13259e39c5baSBill Taylor 	mutex_enter(&qp->qp_lock);
13269e39c5baSBill Taylor 	qp->qp_mcg_refcnt--;
13279e39c5baSBill Taylor 	mutex_exit(&qp->qp_lock);
13289e39c5baSBill Taylor }
13299e39c5baSBill Taylor 
13309e39c5baSBill Taylor 
13319e39c5baSBill Taylor /*
13329e39c5baSBill Taylor  * hermon_mcg_qplist_add()
13339e39c5baSBill Taylor  *    Context: Can be called from interrupt or base context.
13349e39c5baSBill Taylor  */
13359e39c5baSBill Taylor static int
hermon_mcg_qplist_add(hermon_state_t * state,hermon_mcghdl_t mcg,hermon_hw_mcg_qp_list_t * mcg_qplist,hermon_qphdl_t qp,uint_t * qp_found)13369e39c5baSBill Taylor hermon_mcg_qplist_add(hermon_state_t *state, hermon_mcghdl_t mcg,
13379e39c5baSBill Taylor     hermon_hw_mcg_qp_list_t *mcg_qplist, hermon_qphdl_t qp,
13389e39c5baSBill Taylor     uint_t *qp_found)
13399e39c5baSBill Taylor {
13409e39c5baSBill Taylor 	uint_t		qplist_indx;
13419e39c5baSBill Taylor 
13429e39c5baSBill Taylor 	ASSERT(MUTEX_HELD(&state->hs_mcglock));
13439e39c5baSBill Taylor 
13449e39c5baSBill Taylor 	qplist_indx = mcg->mcg_num_qps;
13459e39c5baSBill Taylor 
13469e39c5baSBill Taylor 	/*
13479e39c5baSBill Taylor 	 * Determine if we have exceeded the maximum number of QP per
13489e39c5baSBill Taylor 	 * multicast group.  If we have, then return an error
13499e39c5baSBill Taylor 	 */
13509e39c5baSBill Taylor 	if (qplist_indx >= state->hs_cfg_profile->cp_num_qp_per_mcg) {
13519e39c5baSBill Taylor 		return (IBT_HCA_MCG_QP_EXCEEDED);
13529e39c5baSBill Taylor 	}
13539e39c5baSBill Taylor 
13549e39c5baSBill Taylor 	/*
13559e39c5baSBill Taylor 	 * Determine if the QP is already attached to this MCG table.  If it
13569e39c5baSBill Taylor 	 * is, then we break out and treat this operation as a NO-OP
13579e39c5baSBill Taylor 	 */
13589e39c5baSBill Taylor 	for (qplist_indx = 0; qplist_indx < mcg->mcg_num_qps;
13599e39c5baSBill Taylor 	    qplist_indx++) {
13609e39c5baSBill Taylor 		if (mcg_qplist[qplist_indx].qpn == qp->qp_qpnum) {
13619e39c5baSBill Taylor 			break;
13629e39c5baSBill Taylor 		}
13639e39c5baSBill Taylor 	}
13649e39c5baSBill Taylor 
13659e39c5baSBill Taylor 	/*
13669e39c5baSBill Taylor 	 * If the QP was already on the list, set 'qp_found' to TRUE.  We still
13679e39c5baSBill Taylor 	 * return SUCCESS in this case, but the qplist will not have been
13689e39c5baSBill Taylor 	 * updated because the QP was already on the list.
13699e39c5baSBill Taylor 	 */
13709e39c5baSBill Taylor 	if (qplist_indx < mcg->mcg_num_qps) {
13719e39c5baSBill Taylor 		*qp_found = 1;
13729e39c5baSBill Taylor 	} else {
13739e39c5baSBill Taylor 		/*
13749e39c5baSBill Taylor 		 * Otherwise, append the new QP number to the end of the
13759e39c5baSBill Taylor 		 * current QP list.  Note: We will increment the "mcg_num_qps"
13769e39c5baSBill Taylor 		 * field on the "shadow" MCG list entry later (after we know
13779e39c5baSBill Taylor 		 * that all necessary Hermon firmware accesses have been
13789e39c5baSBill Taylor 		 * successful).
13799e39c5baSBill Taylor 		 *
13809e39c5baSBill Taylor 		 * Set 'qp_found' to 0 so we know the QP was added on to the
13819e39c5baSBill Taylor 		 * list for sure.
13829e39c5baSBill Taylor 		 */
13839e39c5baSBill Taylor 		mcg_qplist[qplist_indx].qpn =
13849e39c5baSBill Taylor 		    (qp->qp_qpnum | HERMON_MCG_QPN_BLOCK_LB);
13859e39c5baSBill Taylor 		*qp_found = 0;
13869e39c5baSBill Taylor 	}
13879e39c5baSBill Taylor 
13889e39c5baSBill Taylor 	return (DDI_SUCCESS);
13899e39c5baSBill Taylor }
13909e39c5baSBill Taylor 
13919e39c5baSBill Taylor 
13929e39c5baSBill Taylor 
13939e39c5baSBill Taylor /*
13949e39c5baSBill Taylor  * hermon_mcg_qplist_remove()
13959e39c5baSBill Taylor  *    Context: Can be called from interrupt or base context.
13969e39c5baSBill Taylor  */
13979e39c5baSBill Taylor static int
hermon_mcg_qplist_remove(hermon_mcghdl_t mcg,hermon_hw_mcg_qp_list_t * mcg_qplist,hermon_qphdl_t qp)13989e39c5baSBill Taylor hermon_mcg_qplist_remove(hermon_mcghdl_t mcg,
13999e39c5baSBill Taylor     hermon_hw_mcg_qp_list_t *mcg_qplist, hermon_qphdl_t qp)
14009e39c5baSBill Taylor {
14019e39c5baSBill Taylor 	uint_t		i, qplist_indx;
14029e39c5baSBill Taylor 
14039e39c5baSBill Taylor 	/*
14049e39c5baSBill Taylor 	 * Search the MCG QP list for a matching QPN.  When
14059e39c5baSBill Taylor 	 * it's found, we swap the last entry with the current
14069e39c5baSBill Taylor 	 * one, set the last entry to zero, decrement the last
14079e39c5baSBill Taylor 	 * entry, and return.  If it's not found, then it's
14089e39c5baSBill Taylor 	 * and error.
14099e39c5baSBill Taylor 	 */
14109e39c5baSBill Taylor 	qplist_indx = mcg->mcg_num_qps;
14119e39c5baSBill Taylor 	for (i = 0; i < qplist_indx; i++) {
14129e39c5baSBill Taylor 		if (mcg_qplist[i].qpn == qp->qp_qpnum) {
14139e39c5baSBill Taylor 			mcg_qplist[i] = mcg_qplist[qplist_indx - 1];
14149e39c5baSBill Taylor 			mcg_qplist[qplist_indx - 1].qpn = 0;
14159e39c5baSBill Taylor 
14169e39c5baSBill Taylor 			return (DDI_SUCCESS);
14179e39c5baSBill Taylor 		}
14189e39c5baSBill Taylor 	}
14199e39c5baSBill Taylor 
14209e39c5baSBill Taylor 	return (IBT_QP_HDL_INVALID);
14219e39c5baSBill Taylor }
14229e39c5baSBill Taylor 
14239e39c5baSBill Taylor 
14249e39c5baSBill Taylor /*
14259e39c5baSBill Taylor  * hermon_mcg_walk_mgid_hash()
14269e39c5baSBill Taylor  *    Context: Can be called from interrupt or base context.
14279e39c5baSBill Taylor  */
14289e39c5baSBill Taylor static uint_t
hermon_mcg_walk_mgid_hash(hermon_state_t * state,uint64_t start_indx,ib_gid_t mgid,uint_t * p_indx)14299e39c5baSBill Taylor hermon_mcg_walk_mgid_hash(hermon_state_t *state, uint64_t start_indx,
14309e39c5baSBill Taylor     ib_gid_t mgid, uint_t *p_indx)
14319e39c5baSBill Taylor {
14329e39c5baSBill Taylor 	hermon_mcghdl_t	curr_mcghdl;
14339e39c5baSBill Taylor 	uint_t		curr_indx, prev_indx;
14349e39c5baSBill Taylor 
14359e39c5baSBill Taylor 	ASSERT(MUTEX_HELD(&state->hs_mcglock));
14369e39c5baSBill Taylor 
14379e39c5baSBill Taylor 	/* Start at the head of the hash chain */
14389e39c5baSBill Taylor 	curr_indx   = (uint_t)start_indx;
14399e39c5baSBill Taylor 	prev_indx   = curr_indx;
14409e39c5baSBill Taylor 	curr_mcghdl = &state->hs_mcghdl[curr_indx];
14419e39c5baSBill Taylor 
14429e39c5baSBill Taylor 	/* If the first entry in the chain has MGID == 0, then stop */
14439e39c5baSBill Taylor 	if ((curr_mcghdl->mcg_mgid_h == 0) &&
14449e39c5baSBill Taylor 	    (curr_mcghdl->mcg_mgid_l == 0)) {
14459e39c5baSBill Taylor 		goto end_mgid_hash_walk;
14469e39c5baSBill Taylor 	}
14479e39c5baSBill Taylor 
14489e39c5baSBill Taylor 	/* If the first entry in the chain matches the MGID, then stop */
14499e39c5baSBill Taylor 	if ((curr_mcghdl->mcg_mgid_h == mgid.gid_prefix) &&
14509e39c5baSBill Taylor 	    (curr_mcghdl->mcg_mgid_l == mgid.gid_guid)) {
14519e39c5baSBill Taylor 		goto end_mgid_hash_walk;
14529e39c5baSBill Taylor 	}
14539e39c5baSBill Taylor 
14549e39c5baSBill Taylor 	/* Otherwise, walk the hash chain looking for a match */
14559e39c5baSBill Taylor 	while (curr_mcghdl->mcg_next_indx != 0) {
14569e39c5baSBill Taylor 		prev_indx = curr_indx;
14579e39c5baSBill Taylor 		curr_indx = curr_mcghdl->mcg_next_indx;
14589e39c5baSBill Taylor 		curr_mcghdl = &state->hs_mcghdl[curr_indx];
14599e39c5baSBill Taylor 
14609e39c5baSBill Taylor 		if ((curr_mcghdl->mcg_mgid_h == mgid.gid_prefix) &&
14619e39c5baSBill Taylor 		    (curr_mcghdl->mcg_mgid_l == mgid.gid_guid)) {
14629e39c5baSBill Taylor 			break;
14639e39c5baSBill Taylor 		}
14649e39c5baSBill Taylor 	}
14659e39c5baSBill Taylor 
14669e39c5baSBill Taylor end_mgid_hash_walk:
14679e39c5baSBill Taylor 	/*
14689e39c5baSBill Taylor 	 * If necessary, return the index of the previous entry too.  This
14699e39c5baSBill Taylor 	 * is primarily used for detaching a QP from a multicast group.  It
14709e39c5baSBill Taylor 	 * may be necessary, in that case, to delete an MCG entry from the
14719e39c5baSBill Taylor 	 * hash chain and having the index of the previous entry is helpful.
14729e39c5baSBill Taylor 	 */
14739e39c5baSBill Taylor 	if (p_indx != NULL) {
14749e39c5baSBill Taylor 		*p_indx = prev_indx;
14759e39c5baSBill Taylor 	}
14769e39c5baSBill Taylor 	return (curr_indx);
14779e39c5baSBill Taylor }
14789e39c5baSBill Taylor 
14799e39c5baSBill Taylor 
14809e39c5baSBill Taylor /*
14819e39c5baSBill Taylor  * hermon_mcg_setup_new_hdr()
14829e39c5baSBill Taylor  *    Context: Can be called from interrupt or base context.
14839e39c5baSBill Taylor  */
14849e39c5baSBill Taylor static void
hermon_mcg_setup_new_hdr(hermon_mcghdl_t mcg,hermon_hw_mcg_t * mcg_hdr,ib_gid_t mgid,hermon_rsrc_t * mcg_rsrc)14859e39c5baSBill Taylor hermon_mcg_setup_new_hdr(hermon_mcghdl_t mcg, hermon_hw_mcg_t *mcg_hdr,
14869e39c5baSBill Taylor     ib_gid_t mgid, hermon_rsrc_t *mcg_rsrc)
14879e39c5baSBill Taylor {
14889e39c5baSBill Taylor 	/*
14899e39c5baSBill Taylor 	 * Fill in the fields of the "shadow" entry used by software
14909e39c5baSBill Taylor 	 * to track MCG hardware entry
14919e39c5baSBill Taylor 	 */
14929e39c5baSBill Taylor 	mcg->mcg_mgid_h	   = mgid.gid_prefix;
14939e39c5baSBill Taylor 	mcg->mcg_mgid_l	   = mgid.gid_guid;
14949e39c5baSBill Taylor 	mcg->mcg_rsrcp	   = mcg_rsrc;
14959e39c5baSBill Taylor 	mcg->mcg_next_indx = 0;
14969e39c5baSBill Taylor 	mcg->mcg_num_qps   = 0;
14979e39c5baSBill Taylor 
14989e39c5baSBill Taylor 	/*
14999e39c5baSBill Taylor 	 * Fill the header fields of the MCG entry (in the temporary copy)
15009e39c5baSBill Taylor 	 */
15019e39c5baSBill Taylor 	mcg_hdr->mgid_h		= mgid.gid_prefix;
15029e39c5baSBill Taylor 	mcg_hdr->mgid_l		= mgid.gid_guid;
15039e39c5baSBill Taylor 	mcg_hdr->next_gid_indx	= 0;
15049e39c5baSBill Taylor }
15059e39c5baSBill Taylor 
15069e39c5baSBill Taylor 
15079e39c5baSBill Taylor /*
15089e39c5baSBill Taylor  * hermon_mcg_hash_list_remove()
15099e39c5baSBill Taylor  *    Context: Can be called only from user or kernel context.
15109e39c5baSBill Taylor  */
15119e39c5baSBill Taylor static int
hermon_mcg_hash_list_remove(hermon_state_t * state,uint_t curr_indx,uint_t prev_indx,hermon_hw_mcg_t * mcg_entry)15129e39c5baSBill Taylor hermon_mcg_hash_list_remove(hermon_state_t *state, uint_t curr_indx,
15139e39c5baSBill Taylor     uint_t prev_indx, hermon_hw_mcg_t *mcg_entry)
15149e39c5baSBill Taylor {
15159e39c5baSBill Taylor 	hermon_mcghdl_t		curr_mcg, prev_mcg, next_mcg;
15169e39c5baSBill Taylor 	uint_t			next_indx;
15179e39c5baSBill Taylor 	int			status;
15189e39c5baSBill Taylor 
15199e39c5baSBill Taylor 	/* Get the pointer to "shadow" list for current entry */
15209e39c5baSBill Taylor 	curr_mcg = &state->hs_mcghdl[curr_indx];
15219e39c5baSBill Taylor 
15229e39c5baSBill Taylor 	/*
15239e39c5baSBill Taylor 	 * If this is the first entry on a hash chain, then attempt to replace
15249e39c5baSBill Taylor 	 * the entry with the next entry on the chain.  If there are no
15259e39c5baSBill Taylor 	 * subsequent entries on the chain, then this is the only entry and
15269e39c5baSBill Taylor 	 * should be invalidated.
15279e39c5baSBill Taylor 	 */
15289e39c5baSBill Taylor 	if (curr_indx == prev_indx) {
15299e39c5baSBill Taylor 
15309e39c5baSBill Taylor 		/*
15319e39c5baSBill Taylor 		 * If this is the only entry on the chain, then invalidate it.
15329e39c5baSBill Taylor 		 * Note:  Invalidating an MCG entry means writing all zeros
15339e39c5baSBill Taylor 		 * to the entry.  This is only necessary for those MCG
15349e39c5baSBill Taylor 		 * entries that are the "head" entries of the individual hash
15359e39c5baSBill Taylor 		 * chains.  Regardless of whether this operation returns
15369e39c5baSBill Taylor 		 * success or failure, return that result to the caller.
15379e39c5baSBill Taylor 		 */
15389e39c5baSBill Taylor 		next_indx = curr_mcg->mcg_next_indx;
15399e39c5baSBill Taylor 		if (next_indx == 0) {
15409e39c5baSBill Taylor 			status = hermon_mcg_entry_invalidate(state, mcg_entry,
15419e39c5baSBill Taylor 			    curr_indx);
15429e39c5baSBill Taylor 			bzero(curr_mcg, sizeof (struct hermon_sw_mcg_list_s));
15439e39c5baSBill Taylor 			return (status);
15449e39c5baSBill Taylor 		}
15459e39c5baSBill Taylor 
15469e39c5baSBill Taylor 		/*
15479e39c5baSBill Taylor 		 * Otherwise, this is just the first entry on the chain, so
15489e39c5baSBill Taylor 		 * grab the next one
15499e39c5baSBill Taylor 		 */
15509e39c5baSBill Taylor 		next_mcg = &state->hs_mcghdl[next_indx];
15519e39c5baSBill Taylor 
15529e39c5baSBill Taylor 		/*
15539e39c5baSBill Taylor 		 * Read the next MCG entry into the temporary MCG.  Note:
15549e39c5baSBill Taylor 		 * In general, this operation shouldn't fail.  If it does,
15559e39c5baSBill Taylor 		 * then it is an indication that something (probably in HW,
15569e39c5baSBill Taylor 		 * but maybe in SW) has gone seriously wrong.
15579e39c5baSBill Taylor 		 */
15589e39c5baSBill Taylor 		status = hermon_read_mgm_cmd_post(state, mcg_entry, next_indx,
15599e39c5baSBill Taylor 		    HERMON_CMD_NOSLEEP_SPIN);
15609e39c5baSBill Taylor 		if (status != HERMON_CMD_SUCCESS) {
15619e39c5baSBill Taylor 			HERMON_WARNING(state, "failed to read MCG entry");
15629e39c5baSBill Taylor 			cmn_err(CE_CONT, "Hermon: READ_MGM command failed: "
15639e39c5baSBill Taylor 			    "%08x\n", status);
15649e39c5baSBill Taylor 			if (status == HERMON_CMD_INVALID_STATUS) {
15659e39c5baSBill Taylor 				hermon_fm_ereport(state, HCA_SYS_ERR,
15669e39c5baSBill Taylor 				    HCA_ERR_SRV_LOST);
15679e39c5baSBill Taylor 			}
15689e39c5baSBill Taylor 			return (ibc_get_ci_failure(0));
15699e39c5baSBill Taylor 		}
15709e39c5baSBill Taylor 
15719e39c5baSBill Taylor 		/*
15729e39c5baSBill Taylor 		 * Copy/Write the temporary MCG back to the hardware MCG list
15739e39c5baSBill Taylor 		 * using the current index.  This essentially removes the
15749e39c5baSBill Taylor 		 * current MCG entry from the list by writing over it with
15759e39c5baSBill Taylor 		 * the next one.  If this is successful, then we can do the
15769e39c5baSBill Taylor 		 * same operation for the "shadow" list.  And we can also
15779e39c5baSBill Taylor 		 * free up the Hermon MCG entry resource that was associated
15789e39c5baSBill Taylor 		 * with the (old) next entry.  Note:  In general, this
15799e39c5baSBill Taylor 		 * operation shouldn't fail.  If it does, then it is an
15809e39c5baSBill Taylor 		 * indication that something (probably in HW, but maybe in SW)
15819e39c5baSBill Taylor 		 * has gone seriously wrong.
15829e39c5baSBill Taylor 		 */
15839e39c5baSBill Taylor 		status = hermon_write_mgm_cmd_post(state, mcg_entry, curr_indx,
15849e39c5baSBill Taylor 		    HERMON_CMD_NOSLEEP_SPIN);
15859e39c5baSBill Taylor 		if (status != HERMON_CMD_SUCCESS) {
15869e39c5baSBill Taylor 			HERMON_WARNING(state, "failed to write MCG entry");
15879e39c5baSBill Taylor 			cmn_err(CE_CONT, "Hermon: WRITE_MGM command failed: "
15889e39c5baSBill Taylor 			    "%08x\n", status);
15899e39c5baSBill Taylor 			if (status == HERMON_CMD_INVALID_STATUS) {
15909e39c5baSBill Taylor 				hermon_fm_ereport(state, HCA_SYS_ERR,
15919e39c5baSBill Taylor 				    HCA_ERR_SRV_LOST);
15929e39c5baSBill Taylor 			}
15939e39c5baSBill Taylor 			return (ibc_get_ci_failure(0));
15949e39c5baSBill Taylor 		}
15959e39c5baSBill Taylor 
15969e39c5baSBill Taylor 		/*
15979e39c5baSBill Taylor 		 * Copy all the software tracking information from the next
15989e39c5baSBill Taylor 		 * entry on the "shadow" MCG list into the current entry on
15999e39c5baSBill Taylor 		 * the list.  Then invalidate (zero out) the other "shadow"
16009e39c5baSBill Taylor 		 * list entry.
16019e39c5baSBill Taylor 		 */
16029e39c5baSBill Taylor 		bcopy(next_mcg, curr_mcg, sizeof (struct hermon_sw_mcg_list_s));
16039e39c5baSBill Taylor 		bzero(next_mcg, sizeof (struct hermon_sw_mcg_list_s));
16049e39c5baSBill Taylor 
16059e39c5baSBill Taylor 		/*
16069e39c5baSBill Taylor 		 * Free up the Hermon MCG entry resource used by the "next"
16079e39c5baSBill Taylor 		 * MCG entry.  That resource is no longer needed by any
16089e39c5baSBill Taylor 		 * MCG entry which is first on a hash chain (like the "next"
16099e39c5baSBill Taylor 		 * entry has just become).
16109e39c5baSBill Taylor 		 */
16119e39c5baSBill Taylor 		hermon_rsrc_free(state, &curr_mcg->mcg_rsrcp);
16129e39c5baSBill Taylor 
16139e39c5baSBill Taylor 		return (DDI_SUCCESS);
16149e39c5baSBill Taylor 	}
16159e39c5baSBill Taylor 
16169e39c5baSBill Taylor 	/*
16179e39c5baSBill Taylor 	 * Else if this is the last entry on the hash chain (or a middle
16189e39c5baSBill Taylor 	 * entry, then we update the previous entry's "next_gid_index" field
16199e39c5baSBill Taylor 	 * to make it point instead to the next entry on the chain.  By
16209e39c5baSBill Taylor 	 * skipping over the removed entry in this way, we can then free up
16219e39c5baSBill Taylor 	 * any resources associated with the current entry.  Note:  We don't
16229e39c5baSBill Taylor 	 * need to invalidate the "skipped over" hardware entry because it
16239e39c5baSBill Taylor 	 * will no be longer connected to any hash chains, and if/when it is
16249e39c5baSBill Taylor 	 * finally re-used, it will be written with entirely new values.
16259e39c5baSBill Taylor 	 */
16269e39c5baSBill Taylor 
16279e39c5baSBill Taylor 	/*
16289e39c5baSBill Taylor 	 * Read the next MCG entry into the temporary MCG.  Note:  In general,
16299e39c5baSBill Taylor 	 * this operation shouldn't fail.  If it does, then it is an
16309e39c5baSBill Taylor 	 * indication that something (probably in HW, but maybe in SW) has
16319e39c5baSBill Taylor 	 * gone seriously wrong.
16329e39c5baSBill Taylor 	 */
16339e39c5baSBill Taylor 	status = hermon_read_mgm_cmd_post(state, mcg_entry, prev_indx,
16349e39c5baSBill Taylor 	    HERMON_CMD_NOSLEEP_SPIN);
16359e39c5baSBill Taylor 	if (status != HERMON_CMD_SUCCESS) {
16369e39c5baSBill Taylor 		HERMON_WARNING(state, "failed to read MCG entry");
16379e39c5baSBill Taylor 		cmn_err(CE_CONT, "Hermon: READ_MGM command failed: %08x\n",
16389e39c5baSBill Taylor 		    status);
16399e39c5baSBill Taylor 		if (status == HERMON_CMD_INVALID_STATUS) {
16409e39c5baSBill Taylor 			hermon_fm_ereport(state, HCA_SYS_ERR, HCA_ERR_SRV_LOST);
16419e39c5baSBill Taylor 		}
16429e39c5baSBill Taylor 		return (ibc_get_ci_failure(0));
16439e39c5baSBill Taylor 	}
16449e39c5baSBill Taylor 
16459e39c5baSBill Taylor 	/*
16469e39c5baSBill Taylor 	 * Finally, we update the "next_gid_indx" field in the temporary MCG
16479e39c5baSBill Taylor 	 * and attempt to write the entry back into the Hermon MCG table.  If
16489e39c5baSBill Taylor 	 * this succeeds, then we update the "shadow" list to reflect the
16499e39c5baSBill Taylor 	 * change, free up the Hermon MCG entry resource that was associated
16509e39c5baSBill Taylor 	 * with the current entry, and return success.  Note:  In general,
16519e39c5baSBill Taylor 	 * this operation shouldn't fail.  If it does, then it is an indication
16529e39c5baSBill Taylor 	 * that something (probably in HW, but maybe in SW) has gone seriously
16539e39c5baSBill Taylor 	 * wrong.
16549e39c5baSBill Taylor 	 */
16559e39c5baSBill Taylor 	mcg_entry->next_gid_indx = curr_mcg->mcg_next_indx;
16569e39c5baSBill Taylor 	status = hermon_write_mgm_cmd_post(state, mcg_entry, prev_indx,
16579e39c5baSBill Taylor 	    HERMON_CMD_NOSLEEP_SPIN);
16589e39c5baSBill Taylor 	if (status != HERMON_CMD_SUCCESS) {
16599e39c5baSBill Taylor 		HERMON_WARNING(state, "failed to write MCG entry");
16609e39c5baSBill Taylor 		cmn_err(CE_CONT, "Hermon: WRITE_MGM command failed: %08x\n",
16619e39c5baSBill Taylor 		    status);
16629e39c5baSBill Taylor 		if (status == HERMON_CMD_INVALID_STATUS) {
16639e39c5baSBill Taylor 			hermon_fm_ereport(state, HCA_SYS_ERR,
16649e39c5baSBill Taylor 			    HCA_ERR_SRV_LOST);
16659e39c5baSBill Taylor 		}
16669e39c5baSBill Taylor 		return (ibc_get_ci_failure(0));
16679e39c5baSBill Taylor 	}
16689e39c5baSBill Taylor 
16699e39c5baSBill Taylor 	/*
16709e39c5baSBill Taylor 	 * Get the pointer to the "shadow" MCG list entry for the previous
16719e39c5baSBill Taylor 	 * MCG.  Update its "mcg_next_indx" to point to the next entry
16729e39c5baSBill Taylor 	 * the one after the current entry. Note:  This next index may be
16739e39c5baSBill Taylor 	 * zero, indicating the end of the list.
16749e39c5baSBill Taylor 	 */
16759e39c5baSBill Taylor 	prev_mcg = &state->hs_mcghdl[prev_indx];
16769e39c5baSBill Taylor 	prev_mcg->mcg_next_indx = curr_mcg->mcg_next_indx;
16779e39c5baSBill Taylor 
16789e39c5baSBill Taylor 	/*
16799e39c5baSBill Taylor 	 * Free up the Hermon MCG entry resource used by the current entry.
16809e39c5baSBill Taylor 	 * This resource is no longer needed because the chain now skips over
16819e39c5baSBill Taylor 	 * the current entry.  Then invalidate (zero out) the current "shadow"
16829e39c5baSBill Taylor 	 * list entry.
16839e39c5baSBill Taylor 	 */
16849e39c5baSBill Taylor 	hermon_rsrc_free(state, &curr_mcg->mcg_rsrcp);
16859e39c5baSBill Taylor 	bzero(curr_mcg, sizeof (struct hermon_sw_mcg_list_s));
16869e39c5baSBill Taylor 
16879e39c5baSBill Taylor 	return (DDI_SUCCESS);
16889e39c5baSBill Taylor }
16899e39c5baSBill Taylor 
16909e39c5baSBill Taylor 
16919e39c5baSBill Taylor /*
16929e39c5baSBill Taylor  * hermon_mcg_entry_invalidate()
16939e39c5baSBill Taylor  *    Context: Can be called only from user or kernel context.
16949e39c5baSBill Taylor  */
16959e39c5baSBill Taylor static int
hermon_mcg_entry_invalidate(hermon_state_t * state,hermon_hw_mcg_t * mcg_entry,uint_t indx)16969e39c5baSBill Taylor hermon_mcg_entry_invalidate(hermon_state_t *state, hermon_hw_mcg_t *mcg_entry,
16979e39c5baSBill Taylor     uint_t indx)
16989e39c5baSBill Taylor {
16999e39c5baSBill Taylor 	int		status;
17009e39c5baSBill Taylor 
17019e39c5baSBill Taylor 	/*
17029e39c5baSBill Taylor 	 * Invalidate the hardware MCG entry by zeroing out this temporary
17039e39c5baSBill Taylor 	 * MCG and writing it the the hardware.  Note: In general, this
17049e39c5baSBill Taylor 	 * operation shouldn't fail.  If it does, then it is an indication
17059e39c5baSBill Taylor 	 * that something (probably in HW, but maybe in SW) has gone seriously
17069e39c5baSBill Taylor 	 * wrong.
17079e39c5baSBill Taylor 	 */
17089e39c5baSBill Taylor 	bzero(mcg_entry, HERMON_MCGMEM_SZ(state));
17099e39c5baSBill Taylor 	status = hermon_write_mgm_cmd_post(state, mcg_entry, indx,
17109e39c5baSBill Taylor 	    HERMON_CMD_NOSLEEP_SPIN);
17119e39c5baSBill Taylor 	if (status != HERMON_CMD_SUCCESS) {
17129e39c5baSBill Taylor 		HERMON_WARNING(state, "failed to write MCG entry");
17139e39c5baSBill Taylor 		cmn_err(CE_CONT, "Hermon: WRITE_MGM command failed: %08x\n",
17149e39c5baSBill Taylor 		    status);
17159e39c5baSBill Taylor 		if (status == HERMON_CMD_INVALID_STATUS) {
17169e39c5baSBill Taylor 			hermon_fm_ereport(state, HCA_SYS_ERR, HCA_ERR_SRV_LOST);
17179e39c5baSBill Taylor 		}
17189e39c5baSBill Taylor 		return (ibc_get_ci_failure(0));
17199e39c5baSBill Taylor 	}
17209e39c5baSBill Taylor 
17219e39c5baSBill Taylor 	return (DDI_SUCCESS);
17229e39c5baSBill Taylor }
17239e39c5baSBill Taylor 
17249e39c5baSBill Taylor 
17259e39c5baSBill Taylor /*
17269e39c5baSBill Taylor  * hermon_mgid_is_valid()
17279e39c5baSBill Taylor  *    Context: Can be called from interrupt or base context.
17289e39c5baSBill Taylor  */
17299e39c5baSBill Taylor static int
hermon_mgid_is_valid(ib_gid_t gid)17309e39c5baSBill Taylor hermon_mgid_is_valid(ib_gid_t gid)
17319e39c5baSBill Taylor {
17329e39c5baSBill Taylor 	uint_t		topbits, flags, scope;
17339e39c5baSBill Taylor 
17349e39c5baSBill Taylor 	/*
17359e39c5baSBill Taylor 	 * According to IBA 1.1 specification (section 4.1.1) a valid
17369e39c5baSBill Taylor 	 * "multicast GID" must have its top eight bits set to all ones
17379e39c5baSBill Taylor 	 */
17389e39c5baSBill Taylor 	topbits = (gid.gid_prefix >> HERMON_MCG_TOPBITS_SHIFT) &
17399e39c5baSBill Taylor 	    HERMON_MCG_TOPBITS_MASK;
17409e39c5baSBill Taylor 	if (topbits != HERMON_MCG_TOPBITS) {
17419e39c5baSBill Taylor 		return (0);
17429e39c5baSBill Taylor 	}
17439e39c5baSBill Taylor 
17449e39c5baSBill Taylor 	/*
17459e39c5baSBill Taylor 	 * The next 4 bits are the "flag" bits.  These are valid only
17469e39c5baSBill Taylor 	 * if they are "0" (which correspond to permanently assigned/
17479e39c5baSBill Taylor 	 * "well-known" multicast GIDs) or "1" (for so-called "transient"
17489e39c5baSBill Taylor 	 * multicast GIDs).  All other values are reserved.
17499e39c5baSBill Taylor 	 */
17509e39c5baSBill Taylor 	flags = (gid.gid_prefix >> HERMON_MCG_FLAGS_SHIFT) &
17519e39c5baSBill Taylor 	    HERMON_MCG_FLAGS_MASK;
17529e39c5baSBill Taylor 	if (!((flags == HERMON_MCG_FLAGS_PERM) ||
17539e39c5baSBill Taylor 	    (flags == HERMON_MCG_FLAGS_NONPERM))) {
17549e39c5baSBill Taylor 		return (0);
17559e39c5baSBill Taylor 	}
17569e39c5baSBill Taylor 
17579e39c5baSBill Taylor 	/*
17589e39c5baSBill Taylor 	 * The next 4 bits are the "scope" bits.  These are valid only
17599e39c5baSBill Taylor 	 * if they are "2" (Link-local), "5" (Site-local), "8"
17609e39c5baSBill Taylor 	 * (Organization-local) or "E" (Global).  All other values
17619e39c5baSBill Taylor 	 * are reserved (or currently unassigned).
17629e39c5baSBill Taylor 	 */
17639e39c5baSBill Taylor 	scope = (gid.gid_prefix >> HERMON_MCG_SCOPE_SHIFT) &
17649e39c5baSBill Taylor 	    HERMON_MCG_SCOPE_MASK;
17659e39c5baSBill Taylor 	if (!((scope == HERMON_MCG_SCOPE_LINKLOC) ||
17669e39c5baSBill Taylor 	    (scope == HERMON_MCG_SCOPE_SITELOC)	 ||
17679e39c5baSBill Taylor 	    (scope == HERMON_MCG_SCOPE_ORGLOC)	 ||
17689e39c5baSBill Taylor 	    (scope == HERMON_MCG_SCOPE_GLOBAL))) {
17699e39c5baSBill Taylor 		return (0);
17709e39c5baSBill Taylor 	}
17719e39c5baSBill Taylor 
17729e39c5baSBill Taylor 	/*
17739e39c5baSBill Taylor 	 * If it passes all of the above checks, then we will consider it
17749e39c5baSBill Taylor 	 * a valid multicast GID.
17759e39c5baSBill Taylor 	 */
17769e39c5baSBill Taylor 	return (1);
17779e39c5baSBill Taylor }
17789e39c5baSBill Taylor 
17799e39c5baSBill Taylor 
17809e39c5baSBill Taylor /*
17819e39c5baSBill Taylor  * hermon_mlid_is_valid()
17829e39c5baSBill Taylor  *    Context: Can be called from interrupt or base context.
17839e39c5baSBill Taylor  */
17849e39c5baSBill Taylor static int
hermon_mlid_is_valid(ib_lid_t lid)17859e39c5baSBill Taylor hermon_mlid_is_valid(ib_lid_t lid)
17869e39c5baSBill Taylor {
17879e39c5baSBill Taylor 	/*
17889e39c5baSBill Taylor 	 * According to IBA 1.1 specification (section 4.1.1) a valid
17899e39c5baSBill Taylor 	 * "multicast DLID" must be between 0xC000 and 0xFFFE.
17909e39c5baSBill Taylor 	 */
17919e39c5baSBill Taylor 	if ((lid < IB_LID_MC_FIRST) || (lid > IB_LID_MC_LAST)) {
17929e39c5baSBill Taylor 		return (0);
17939e39c5baSBill Taylor 	}
17949e39c5baSBill Taylor 
17959e39c5baSBill Taylor 	return (1);
17969e39c5baSBill Taylor }
17979e39c5baSBill Taylor 
17989e39c5baSBill Taylor 
17999e39c5baSBill Taylor /*
18009e39c5baSBill Taylor  * hermon_pd_alloc()
18019e39c5baSBill Taylor  *    Context: Can be called only from user or kernel context.
18029e39c5baSBill Taylor  */
18039e39c5baSBill Taylor int
hermon_pd_alloc(hermon_state_t * state,hermon_pdhdl_t * pdhdl,uint_t sleepflag)18049e39c5baSBill Taylor hermon_pd_alloc(hermon_state_t *state, hermon_pdhdl_t *pdhdl, uint_t sleepflag)
18059e39c5baSBill Taylor {
18069e39c5baSBill Taylor 	hermon_rsrc_t	*rsrc;
18079e39c5baSBill Taylor 	hermon_pdhdl_t	pd;
18089e39c5baSBill Taylor 	int		status;
18099e39c5baSBill Taylor 
18109e39c5baSBill Taylor 	/*
18119e39c5baSBill Taylor 	 * Allocate the software structure for tracking the protection domain
18129e39c5baSBill Taylor 	 * (i.e. the Hermon Protection Domain handle).  By default each PD
18139e39c5baSBill Taylor 	 * structure will have a unique PD number assigned to it.  All that
18149e39c5baSBill Taylor 	 * is necessary is for software to initialize the PD reference count
18159e39c5baSBill Taylor 	 * (to zero) and return success.
18169e39c5baSBill Taylor 	 */
18179e39c5baSBill Taylor 	status = hermon_rsrc_alloc(state, HERMON_PDHDL, 1, sleepflag, &rsrc);
18189e39c5baSBill Taylor 	if (status != DDI_SUCCESS) {
18199e39c5baSBill Taylor 		return (IBT_INSUFF_RESOURCE);
18209e39c5baSBill Taylor 	}
18219e39c5baSBill Taylor 	pd = (hermon_pdhdl_t)rsrc->hr_addr;
18229e39c5baSBill Taylor 	_NOTE(NOW_INVISIBLE_TO_OTHER_THREADS(*pd))
18239e39c5baSBill Taylor 
18249e39c5baSBill Taylor 	pd->pd_refcnt = 0;
18259e39c5baSBill Taylor 	*pdhdl = pd;
18269e39c5baSBill Taylor 
18279e39c5baSBill Taylor 	return (DDI_SUCCESS);
18289e39c5baSBill Taylor }
18299e39c5baSBill Taylor 
18309e39c5baSBill Taylor 
18319e39c5baSBill Taylor /*
18329e39c5baSBill Taylor  * hermon_pd_free()
18339e39c5baSBill Taylor  *    Context: Can be called only from user or kernel context.
18349e39c5baSBill Taylor  */
18359e39c5baSBill Taylor int
hermon_pd_free(hermon_state_t * state,hermon_pdhdl_t * pdhdl)18369e39c5baSBill Taylor hermon_pd_free(hermon_state_t *state, hermon_pdhdl_t *pdhdl)
18379e39c5baSBill Taylor {
18389e39c5baSBill Taylor 	hermon_rsrc_t	*rsrc;
18399e39c5baSBill Taylor 	hermon_pdhdl_t	pd;
18409e39c5baSBill Taylor 
18419e39c5baSBill Taylor 	/*
18429e39c5baSBill Taylor 	 * Pull all the necessary information from the Hermon Protection Domain
18439e39c5baSBill Taylor 	 * handle.  This is necessary here because the resource for the
18449e39c5baSBill Taylor 	 * PD is going to be freed up as part of this operation.
18459e39c5baSBill Taylor 	 */
18469e39c5baSBill Taylor 	pd   = *pdhdl;
18479e39c5baSBill Taylor 	_NOTE(NOW_INVISIBLE_TO_OTHER_THREADS(*pd))
18489e39c5baSBill Taylor 	rsrc = pd->pd_rsrcp;
18499e39c5baSBill Taylor 
18509e39c5baSBill Taylor 	/*
18519e39c5baSBill Taylor 	 * Check the PD reference count.  If the reference count is non-zero,
18529e39c5baSBill Taylor 	 * then it means that this protection domain is still referenced by
18539e39c5baSBill Taylor 	 * some memory region, queue pair, address handle, or other IB object
18549e39c5baSBill Taylor 	 * If it is non-zero, then return an error.  Otherwise, free the
18559e39c5baSBill Taylor 	 * Hermon resource and return success.
18569e39c5baSBill Taylor 	 */
18579e39c5baSBill Taylor 	if (pd->pd_refcnt != 0) {
18589e39c5baSBill Taylor 		return (IBT_PD_IN_USE);
18599e39c5baSBill Taylor 	}
18609e39c5baSBill Taylor 
18619e39c5baSBill Taylor 	/* Free the Hermon Protection Domain handle */
18629e39c5baSBill Taylor 	hermon_rsrc_free(state, &rsrc);
18639e39c5baSBill Taylor 
18649e39c5baSBill Taylor 	/* Set the pdhdl pointer to NULL and return success */
18659e39c5baSBill Taylor 	*pdhdl = (hermon_pdhdl_t)NULL;
18669e39c5baSBill Taylor 
18679e39c5baSBill Taylor 	return (DDI_SUCCESS);
18689e39c5baSBill Taylor }
18699e39c5baSBill Taylor 
18709e39c5baSBill Taylor 
18719e39c5baSBill Taylor /*
18729e39c5baSBill Taylor  * hermon_pd_refcnt_inc()
18739e39c5baSBill Taylor  *    Context: Can be called from interrupt or base context.
18749e39c5baSBill Taylor  */
18759e39c5baSBill Taylor void
hermon_pd_refcnt_inc(hermon_pdhdl_t pd)18769e39c5baSBill Taylor hermon_pd_refcnt_inc(hermon_pdhdl_t pd)
18779e39c5baSBill Taylor {
18789e39c5baSBill Taylor 	/* Increment the protection domain's reference count */
18799e39c5baSBill Taylor 	atomic_inc_32(&pd->pd_refcnt);
18809e39c5baSBill Taylor }
18819e39c5baSBill Taylor 
18829e39c5baSBill Taylor 
18839e39c5baSBill Taylor /*
18849e39c5baSBill Taylor  * hermon_pd_refcnt_dec()
18859e39c5baSBill Taylor  *    Context: Can be called from interrupt or base context.
18869e39c5baSBill Taylor  */
18879e39c5baSBill Taylor void
hermon_pd_refcnt_dec(hermon_pdhdl_t pd)18889e39c5baSBill Taylor hermon_pd_refcnt_dec(hermon_pdhdl_t pd)
18899e39c5baSBill Taylor {
18909e39c5baSBill Taylor 	/* Decrement the protection domain's reference count */
18919e39c5baSBill Taylor 	atomic_dec_32(&pd->pd_refcnt);
18929e39c5baSBill Taylor }
18939e39c5baSBill Taylor 
18949e39c5baSBill Taylor 
18959e39c5baSBill Taylor /*
18969e39c5baSBill Taylor  * hermon_port_query()
18979e39c5baSBill Taylor  *    Context: Can be called only from user or kernel context.
18989e39c5baSBill Taylor  */
18999e39c5baSBill Taylor int
hermon_port_query(hermon_state_t * state,uint_t port,ibt_hca_portinfo_t * pi)19009e39c5baSBill Taylor hermon_port_query(hermon_state_t *state, uint_t port, ibt_hca_portinfo_t *pi)
19019e39c5baSBill Taylor {
19029e39c5baSBill Taylor 	sm_portinfo_t		portinfo;
19039e39c5baSBill Taylor 	sm_guidinfo_t		guidinfo;
19049e39c5baSBill Taylor 	sm_pkey_table_t		pkeytable;
19059e39c5baSBill Taylor 	ib_gid_t		*sgid;
19069e39c5baSBill Taylor 	uint_t			sgid_max, pkey_max, tbl_size;
19079e39c5baSBill Taylor 	int			i, j, indx, status;
19089e39c5baSBill Taylor 	ib_pkey_t		*pkeyp;
19099e39c5baSBill Taylor 	ib_guid_t		*guidp;
19109e39c5baSBill Taylor 
19119e39c5baSBill Taylor 	_NOTE(NOW_INVISIBLE_TO_OTHER_THREADS(*pi))
19129e39c5baSBill Taylor 	_NOTE(NOW_INVISIBLE_TO_OTHER_THREADS(*state))
19139e39c5baSBill Taylor 
19149e39c5baSBill Taylor 	/* Validate that specified port number is legal */
19159e39c5baSBill Taylor 	if (!hermon_portnum_is_valid(state, port)) {
19169e39c5baSBill Taylor 		return (IBT_HCA_PORT_INVALID);
19179e39c5baSBill Taylor 	}
19189e39c5baSBill Taylor 	pkeyp = state->hs_pkey[port - 1];
19199e39c5baSBill Taylor 	guidp = state->hs_guid[port - 1];
19209e39c5baSBill Taylor 
19219e39c5baSBill Taylor 	/*
19229e39c5baSBill Taylor 	 * We use the Hermon MAD_IFC command to post a GetPortInfo MAD
19239e39c5baSBill Taylor 	 * to the firmware (for the specified port number).  This returns
19249e39c5baSBill Taylor 	 * a full PortInfo MAD (in "portinfo") which we subsequently
19259e39c5baSBill Taylor 	 * parse to fill in the "ibt_hca_portinfo_t" structure returned
19269e39c5baSBill Taylor 	 * to the IBTF.
19279e39c5baSBill Taylor 	 */
19289e39c5baSBill Taylor 	status = hermon_getportinfo_cmd_post(state, port,
19299e39c5baSBill Taylor 	    HERMON_SLEEPFLAG_FOR_CONTEXT(), &portinfo);
19309e39c5baSBill Taylor 	if (status != HERMON_CMD_SUCCESS) {
19319e39c5baSBill Taylor 		cmn_err(CE_CONT, "Hermon: GetPortInfo (port %02d) command "
19329e39c5baSBill Taylor 		    "failed: %08x\n", port, status);
19339e39c5baSBill Taylor 		if (status == HERMON_CMD_INVALID_STATUS) {
19349e39c5baSBill Taylor 			hermon_fm_ereport(state, HCA_SYS_ERR, HCA_ERR_SRV_LOST);
19359e39c5baSBill Taylor 		}
19369e39c5baSBill Taylor 		return (ibc_get_ci_failure(0));
19379e39c5baSBill Taylor 	}
19389e39c5baSBill Taylor 
19399e39c5baSBill Taylor 	/*
19409e39c5baSBill Taylor 	 * Parse the PortInfo MAD and fill in the IBTF structure
19419e39c5baSBill Taylor 	 */
19429e39c5baSBill Taylor 	pi->p_base_lid		= portinfo.LID;
19439e39c5baSBill Taylor 	pi->p_qkey_violations	= portinfo.Q_KeyViolations;
19449e39c5baSBill Taylor 	pi->p_pkey_violations	= portinfo.P_KeyViolations;
19459e39c5baSBill Taylor 	pi->p_sm_sl		= portinfo.MasterSMSL;
19469e39c5baSBill Taylor 	pi->p_sm_lid		= portinfo.MasterSMLID;
19479e39c5baSBill Taylor 	pi->p_linkstate		= portinfo.PortState;
19489e39c5baSBill Taylor 	pi->p_port_num		= portinfo.LocalPortNum;
1949d9c882faSBill Taylor 	pi->p_phys_state	= portinfo.PortPhysicalState;
1950d9c882faSBill Taylor 	pi->p_width_supported	= portinfo.LinkWidthSupported;
1951d9c882faSBill Taylor 	pi->p_width_enabled	= portinfo.LinkWidthEnabled;
1952d9c882faSBill Taylor 	pi->p_width_active	= portinfo.LinkWidthActive;
1953d9c882faSBill Taylor 	pi->p_speed_supported	= portinfo.LinkSpeedSupported;
1954d9c882faSBill Taylor 	pi->p_speed_enabled	= portinfo.LinkSpeedEnabled;
1955d9c882faSBill Taylor 	pi->p_speed_active	= portinfo.LinkSpeedActive;
19569e39c5baSBill Taylor 	pi->p_mtu		= portinfo.MTUCap;
19579e39c5baSBill Taylor 	pi->p_lmc		= portinfo.LMC;
19589e39c5baSBill Taylor 	pi->p_max_vl		= portinfo.VLCap;
19599e39c5baSBill Taylor 	pi->p_subnet_timeout	= portinfo.SubnetTimeOut;
19609e39c5baSBill Taylor 	pi->p_msg_sz		= ((uint32_t)1 << HERMON_QP_LOG_MAX_MSGSZ);
19619e39c5baSBill Taylor 	tbl_size = state->hs_cfg_profile->cp_log_max_gidtbl;
19629e39c5baSBill Taylor 	pi->p_sgid_tbl_sz	= (1 << tbl_size);
19639e39c5baSBill Taylor 	tbl_size = state->hs_cfg_profile->cp_log_max_pkeytbl;
19649e39c5baSBill Taylor 	pi->p_pkey_tbl_sz	= (1 << tbl_size);
19659e39c5baSBill Taylor 	state->hs_sn_prefix[port - 1] = portinfo.GidPrefix;
19669e39c5baSBill Taylor 
19679e39c5baSBill Taylor 	/*
19689e39c5baSBill Taylor 	 * Convert InfiniBand-defined port capability flags to the format
19699e39c5baSBill Taylor 	 * specified by the IBTF
19709e39c5baSBill Taylor 	 */
19719e39c5baSBill Taylor 	if (portinfo.CapabilityMask & SM_CAP_MASK_IS_SM)
19729e39c5baSBill Taylor 		pi->p_capabilities |= IBT_PORT_CAP_SM;
19739e39c5baSBill Taylor 	if (portinfo.CapabilityMask & SM_CAP_MASK_IS_SM_DISABLED)
19749e39c5baSBill Taylor 		pi->p_capabilities |= IBT_PORT_CAP_SM_DISABLED;
19759e39c5baSBill Taylor 	if (portinfo.CapabilityMask & SM_CAP_MASK_IS_SNMP_SUPPD)
19769e39c5baSBill Taylor 		pi->p_capabilities |= IBT_PORT_CAP_SNMP_TUNNEL;
19779e39c5baSBill Taylor 	if (portinfo.CapabilityMask & SM_CAP_MASK_IS_DM_SUPPD)
19789e39c5baSBill Taylor 		pi->p_capabilities |= IBT_PORT_CAP_DM;
19799e39c5baSBill Taylor 	if (portinfo.CapabilityMask & SM_CAP_MASK_IS_VM_SUPPD)
19809e39c5baSBill Taylor 		pi->p_capabilities |= IBT_PORT_CAP_VENDOR;
198176c04273SRajkumar Sivaprakasam 	if (portinfo.CapabilityMask & SM_CAP_MASK_IS_CLNT_REREG_SUPPD)
198276c04273SRajkumar Sivaprakasam 		pi->p_capabilities |= IBT_PORT_CAP_CLNT_REREG;
19839e39c5baSBill Taylor 
19849e39c5baSBill Taylor 	/*
19859e39c5baSBill Taylor 	 * Fill in the SGID table.  Since the only access to the Hermon
19869e39c5baSBill Taylor 	 * GID tables is through the firmware's MAD_IFC interface, we
19879e39c5baSBill Taylor 	 * post as many GetGUIDInfo MADs as necessary to read in the entire
19889e39c5baSBill Taylor 	 * contents of the SGID table (for the specified port).  Note:  The
19899e39c5baSBill Taylor 	 * GetGUIDInfo command only gets eight GUIDs per operation.  These
19909e39c5baSBill Taylor 	 * GUIDs are then appended to the GID prefix for the port (from the
19919e39c5baSBill Taylor 	 * GetPortInfo above) to form the entire SGID table.
19929e39c5baSBill Taylor 	 */
19939e39c5baSBill Taylor 	for (i = 0; i < pi->p_sgid_tbl_sz; i += 8) {
19949e39c5baSBill Taylor 		status = hermon_getguidinfo_cmd_post(state, port, i >> 3,
19959e39c5baSBill Taylor 		    HERMON_SLEEPFLAG_FOR_CONTEXT(), &guidinfo);
19969e39c5baSBill Taylor 		if (status != HERMON_CMD_SUCCESS) {
19979e39c5baSBill Taylor 			cmn_err(CE_CONT, "Hermon: GetGUIDInfo (port %02d) "
19989e39c5baSBill Taylor 			    "command failed: %08x\n", port, status);
19999e39c5baSBill Taylor 			if (status == HERMON_CMD_INVALID_STATUS) {
20009e39c5baSBill Taylor 				hermon_fm_ereport(state, HCA_SYS_ERR,
20019e39c5baSBill Taylor 				    HCA_ERR_SRV_LOST);
20029e39c5baSBill Taylor 			}
20039e39c5baSBill Taylor 			return (ibc_get_ci_failure(0));
20049e39c5baSBill Taylor 		}
20059e39c5baSBill Taylor 
20069e39c5baSBill Taylor 		/* Figure out how many of the entries are valid */
20079e39c5baSBill Taylor 		sgid_max = min((pi->p_sgid_tbl_sz - i), 8);
20089e39c5baSBill Taylor 		for (j = 0; j < sgid_max; j++) {
20099e39c5baSBill Taylor 			indx = (i + j);
20109e39c5baSBill Taylor 			sgid = &pi->p_sgid_tbl[indx];
20119e39c5baSBill Taylor 			_NOTE(NOW_INVISIBLE_TO_OTHER_THREADS(*sgid))
20129e39c5baSBill Taylor 			sgid->gid_prefix = portinfo.GidPrefix;
20139e39c5baSBill Taylor 			guidp[indx] = sgid->gid_guid =
20149e39c5baSBill Taylor 			    guidinfo.GUIDBlocks[j];
20159e39c5baSBill Taylor 		}
20169e39c5baSBill Taylor 	}
20179e39c5baSBill Taylor 
20189e39c5baSBill Taylor 	/*
20199e39c5baSBill Taylor 	 * Fill in the PKey table.  Just as for the GID tables above, the
20209e39c5baSBill Taylor 	 * only access to the Hermon PKey tables is through the firmware's
20219e39c5baSBill Taylor 	 * MAD_IFC interface.  We post as many GetPKeyTable MADs as necessary
20229e39c5baSBill Taylor 	 * to read in the entire contents of the PKey table (for the specified
20239e39c5baSBill Taylor 	 * port).  Note:  The GetPKeyTable command only gets 32 PKeys per
20249e39c5baSBill Taylor 	 * operation.
20259e39c5baSBill Taylor 	 */
20269e39c5baSBill Taylor 	for (i = 0; i < pi->p_pkey_tbl_sz; i += 32) {
20279e39c5baSBill Taylor 		status = hermon_getpkeytable_cmd_post(state, port, i,
20289e39c5baSBill Taylor 		    HERMON_SLEEPFLAG_FOR_CONTEXT(), &pkeytable);
20299e39c5baSBill Taylor 		if (status != HERMON_CMD_SUCCESS) {
20309e39c5baSBill Taylor 			cmn_err(CE_CONT, "Hermon: GetPKeyTable (port %02d) "
20319e39c5baSBill Taylor 			    "command failed: %08x\n", port, status);
20329e39c5baSBill Taylor 			if (status == HERMON_CMD_INVALID_STATUS) {
20339e39c5baSBill Taylor 				hermon_fm_ereport(state, HCA_SYS_ERR,
20349e39c5baSBill Taylor 				    HCA_ERR_SRV_LOST);
20359e39c5baSBill Taylor 			}
20369e39c5baSBill Taylor 			return (ibc_get_ci_failure(0));
20379e39c5baSBill Taylor 		}
20389e39c5baSBill Taylor 
20399e39c5baSBill Taylor 		/* Figure out how many of the entries are valid */
20409e39c5baSBill Taylor 		pkey_max = min((pi->p_pkey_tbl_sz - i), 32);
20419e39c5baSBill Taylor 		for (j = 0; j < pkey_max; j++) {
20429e39c5baSBill Taylor 			indx = (i + j);
20439e39c5baSBill Taylor 			pkeyp[indx] = pi->p_pkey_tbl[indx] =
20449e39c5baSBill Taylor 			    pkeytable.P_KeyTableBlocks[j];
20459e39c5baSBill Taylor 		}
20469e39c5baSBill Taylor 	}
20479e39c5baSBill Taylor 
20489e39c5baSBill Taylor 	return (DDI_SUCCESS);
20499e39c5baSBill Taylor }
20509e39c5baSBill Taylor 
20519e39c5baSBill Taylor 
20529e39c5baSBill Taylor /*
20539e39c5baSBill Taylor  * hermon_port_modify()
20549e39c5baSBill Taylor  *    Context: Can be called only from user or kernel context.
20559e39c5baSBill Taylor  */
20569e39c5baSBill Taylor /* ARGSUSED */
20579e39c5baSBill Taylor int
hermon_port_modify(hermon_state_t * state,uint8_t port,ibt_port_modify_flags_t flags,uint8_t init_type)20589e39c5baSBill Taylor hermon_port_modify(hermon_state_t *state, uint8_t port,
20599e39c5baSBill Taylor     ibt_port_modify_flags_t flags, uint8_t init_type)
20609e39c5baSBill Taylor {
20619e39c5baSBill Taylor 	sm_portinfo_t		portinfo;
20629e39c5baSBill Taylor 	uint32_t		capmask;
20639e39c5baSBill Taylor 	int			status;
20649e39c5baSBill Taylor 	hermon_hw_set_port_t	set_port;
20659e39c5baSBill Taylor 
20669e39c5baSBill Taylor 	/*
20679e39c5baSBill Taylor 	 * Return an error if either of the unsupported flags are set
20689e39c5baSBill Taylor 	 */
20699e39c5baSBill Taylor 	if ((flags & IBT_PORT_SHUTDOWN) ||
20709e39c5baSBill Taylor 	    (flags & IBT_PORT_SET_INIT_TYPE)) {
20719e39c5baSBill Taylor 		return (IBT_NOT_SUPPORTED);
20729e39c5baSBill Taylor 	}
20739e39c5baSBill Taylor 
20749e39c5baSBill Taylor 	bzero(&set_port, sizeof (set_port));
20759e39c5baSBill Taylor 
20769e39c5baSBill Taylor 	/*
20779e39c5baSBill Taylor 	 * Determine whether we are trying to reset the QKey counter
20789e39c5baSBill Taylor 	 */
20799e39c5baSBill Taylor 	if (flags & IBT_PORT_RESET_QKEY)
20809e39c5baSBill Taylor 		set_port.rqk = 1;
20819e39c5baSBill Taylor 
20829e39c5baSBill Taylor 	/* Validate that specified port number is legal */
20839e39c5baSBill Taylor 	if (!hermon_portnum_is_valid(state, port)) {
20849e39c5baSBill Taylor 		return (IBT_HCA_PORT_INVALID);
20859e39c5baSBill Taylor 	}
20869e39c5baSBill Taylor 
20879e39c5baSBill Taylor 	/*
20889e39c5baSBill Taylor 	 * Use the Hermon MAD_IFC command to post a GetPortInfo MAD to the
20899e39c5baSBill Taylor 	 * firmware (for the specified port number).  This returns a full
20909e39c5baSBill Taylor 	 * PortInfo MAD (in "portinfo") from which we pull the current
20919e39c5baSBill Taylor 	 * capability mask.  We then modify the capability mask as directed
20929e39c5baSBill Taylor 	 * by the "pmod_flags" field, and write the updated capability mask
20939e39c5baSBill Taylor 	 * using the Hermon SET_IB command (below).
20949e39c5baSBill Taylor 	 */
20959e39c5baSBill Taylor 	status = hermon_getportinfo_cmd_post(state, port,
20969e39c5baSBill Taylor 	    HERMON_SLEEPFLAG_FOR_CONTEXT(), &portinfo);
20979e39c5baSBill Taylor 	if (status != HERMON_CMD_SUCCESS) {
20989e39c5baSBill Taylor 		if (status == HERMON_CMD_INVALID_STATUS) {
20999e39c5baSBill Taylor 			hermon_fm_ereport(state, HCA_SYS_ERR, HCA_ERR_SRV_LOST);
21009e39c5baSBill Taylor 		}
21019e39c5baSBill Taylor 		return (ibc_get_ci_failure(0));
21029e39c5baSBill Taylor 	}
21039e39c5baSBill Taylor 
21049e39c5baSBill Taylor 	/*
21059e39c5baSBill Taylor 	 * Convert InfiniBand-defined port capability flags to the format
21069e39c5baSBill Taylor 	 * specified by the IBTF.  Specifically, we modify the capability
21079e39c5baSBill Taylor 	 * mask based on the specified values.
21089e39c5baSBill Taylor 	 */
21099e39c5baSBill Taylor 	capmask = portinfo.CapabilityMask;
21109e39c5baSBill Taylor 
21119e39c5baSBill Taylor 	if (flags & IBT_PORT_RESET_SM)
21129e39c5baSBill Taylor 		capmask &= ~SM_CAP_MASK_IS_SM;
21139e39c5baSBill Taylor 	else if (flags & IBT_PORT_SET_SM)
21149e39c5baSBill Taylor 		capmask |= SM_CAP_MASK_IS_SM;
21159e39c5baSBill Taylor 
21169e39c5baSBill Taylor 	if (flags & IBT_PORT_RESET_SNMP)
21179e39c5baSBill Taylor 		capmask &= ~SM_CAP_MASK_IS_SNMP_SUPPD;
21189e39c5baSBill Taylor 	else if (flags & IBT_PORT_SET_SNMP)
21199e39c5baSBill Taylor 		capmask |= SM_CAP_MASK_IS_SNMP_SUPPD;
21209e39c5baSBill Taylor 
21219e39c5baSBill Taylor 	if (flags & IBT_PORT_RESET_DEVMGT)
21229e39c5baSBill Taylor 		capmask &= ~SM_CAP_MASK_IS_DM_SUPPD;
21239e39c5baSBill Taylor 	else if (flags & IBT_PORT_SET_DEVMGT)
21249e39c5baSBill Taylor 		capmask |= SM_CAP_MASK_IS_DM_SUPPD;
21259e39c5baSBill Taylor 
21269e39c5baSBill Taylor 	if (flags & IBT_PORT_RESET_VENDOR)
21279e39c5baSBill Taylor 		capmask &= ~SM_CAP_MASK_IS_VM_SUPPD;
21289e39c5baSBill Taylor 	else if (flags & IBT_PORT_SET_VENDOR)
21299e39c5baSBill Taylor 		capmask |= SM_CAP_MASK_IS_VM_SUPPD;
21309e39c5baSBill Taylor 
21319e39c5baSBill Taylor 	set_port.cap_mask = capmask;
21329e39c5baSBill Taylor 
21339e39c5baSBill Taylor 	/*
21349e39c5baSBill Taylor 	 * Use the Hermon SET_PORT command to update the capability mask and
21359e39c5baSBill Taylor 	 * (possibly) reset the QKey violation counter for the specified port.
21369e39c5baSBill Taylor 	 * Note: In general, this operation shouldn't fail.  If it does, then
21379e39c5baSBill Taylor 	 * it is an indication that something (probably in HW, but maybe in
21389e39c5baSBill Taylor 	 * SW) has gone seriously wrong.
21399e39c5baSBill Taylor 	 */
21409e39c5baSBill Taylor 	status = hermon_set_port_cmd_post(state, &set_port, port,
21419e39c5baSBill Taylor 	    HERMON_SLEEPFLAG_FOR_CONTEXT());
21429e39c5baSBill Taylor 	if (status != HERMON_CMD_SUCCESS) {
21439e39c5baSBill Taylor 		HERMON_WARNING(state, "failed to modify port capabilities");
21449e39c5baSBill Taylor 		cmn_err(CE_CONT, "Hermon: SET_IB (port %02d) command failed: "
21459e39c5baSBill Taylor 		    "%08x\n", port, status);
21469e39c5baSBill Taylor 		if (status == HERMON_CMD_INVALID_STATUS) {
21479e39c5baSBill Taylor 			hermon_fm_ereport(state, HCA_SYS_ERR, HCA_ERR_SRV_LOST);
21489e39c5baSBill Taylor 		}
21499e39c5baSBill Taylor 		return (ibc_get_ci_failure(0));
21509e39c5baSBill Taylor 	}
21519e39c5baSBill Taylor 
21529e39c5baSBill Taylor 	return (DDI_SUCCESS);
21539e39c5baSBill Taylor }
21549e39c5baSBill Taylor 
21559e39c5baSBill Taylor 
21569e39c5baSBill Taylor /*
21579e39c5baSBill Taylor  * hermon_set_addr_path()
21589e39c5baSBill Taylor  *    Context: Can be called from interrupt or base context.
21599e39c5baSBill Taylor  *
21609e39c5baSBill Taylor  * Note: This routine is used for two purposes.  It is used to fill in the
21619e39c5baSBill Taylor  * Hermon UDAV fields, and it is used to fill in the address path information
21629e39c5baSBill Taylor  * for QPs.  Because the two Hermon structures are similar, common fields can
21639e39c5baSBill Taylor  * be filled in here.  Because they are different, however, we pass
21649e39c5baSBill Taylor  * an additional flag to indicate which type is being filled and do each one
21659e39c5baSBill Taylor  * uniquely
21669e39c5baSBill Taylor  */
21679e39c5baSBill Taylor 
21689e39c5baSBill Taylor int hermon_srate_override = -1;	/* allows ease of testing */
21699e39c5baSBill Taylor 
21709e39c5baSBill Taylor int
hermon_set_addr_path(hermon_state_t * state,ibt_adds_vect_t * av,hermon_hw_addr_path_t * path,uint_t type)21719e39c5baSBill Taylor hermon_set_addr_path(hermon_state_t *state, ibt_adds_vect_t *av,
21729e39c5baSBill Taylor     hermon_hw_addr_path_t *path, uint_t type)
21739e39c5baSBill Taylor {
21749e39c5baSBill Taylor 	uint_t		gidtbl_sz;
21759e39c5baSBill Taylor 	hermon_hw_udav_t *udav;
21769e39c5baSBill Taylor 
21779e39c5baSBill Taylor 	_NOTE(NOW_INVISIBLE_TO_OTHER_THREADS(*av))
21789e39c5baSBill Taylor 	_NOTE(NOW_INVISIBLE_TO_OTHER_THREADS(*path))
21799e39c5baSBill Taylor 
21809e39c5baSBill Taylor 	udav = (hermon_hw_udav_t *)(void *)path;
21819e39c5baSBill Taylor 	_NOTE(NOW_INVISIBLE_TO_OTHER_THREADS(*udav))
21829e39c5baSBill Taylor 	path->mlid	= av->av_src_path;
21839e39c5baSBill Taylor 	path->rlid	= av->av_dlid;
21849e39c5baSBill Taylor 
2185340050d5SBill Taylor 	switch (av->av_srate) {
2186340050d5SBill Taylor 	case IBT_SRATE_2:	/* 1xSDR-2.5Gb/s injection rate */
2187340050d5SBill Taylor 		path->max_stat_rate = 7; break;
2188340050d5SBill Taylor 	case IBT_SRATE_10:	/* 4xSDR-10.0Gb/s injection rate */
2189340050d5SBill Taylor 		path->max_stat_rate = 8; break;
2190340050d5SBill Taylor 	case IBT_SRATE_30:	/* 12xSDR-30Gb/s injection rate */
2191340050d5SBill Taylor 		path->max_stat_rate = 9; break;
2192340050d5SBill Taylor 	case IBT_SRATE_5:	/* 1xDDR-5Gb/s injection rate */
2193340050d5SBill Taylor 		path->max_stat_rate = 10; break;
2194340050d5SBill Taylor 	case IBT_SRATE_20:	/* 4xDDR-20Gb/s injection rate */
2195340050d5SBill Taylor 		path->max_stat_rate = 11; break;
2196340050d5SBill Taylor 	case IBT_SRATE_40:	/* 4xQDR-40Gb/s injection rate */
2197340050d5SBill Taylor 		path->max_stat_rate = 12; break;
2198340050d5SBill Taylor 	case IBT_SRATE_60:	/* 12xDDR-60Gb/s injection rate */
2199340050d5SBill Taylor 		path->max_stat_rate = 13; break;
2200340050d5SBill Taylor 	case IBT_SRATE_80:	/* 8xQDR-80Gb/s injection rate */
2201340050d5SBill Taylor 		path->max_stat_rate = 14; break;
2202340050d5SBill Taylor 	case IBT_SRATE_120:	/* 12xQDR-120Gb/s injection rate */
2203340050d5SBill Taylor 		path->max_stat_rate = 15; break;
2204340050d5SBill Taylor 	case IBT_SRATE_NOT_SPECIFIED:	/* Max */
2205340050d5SBill Taylor 		path->max_stat_rate = 0; break;
2206340050d5SBill Taylor 	default:
22079e39c5baSBill Taylor 		return (IBT_STATIC_RATE_INVALID);
22089e39c5baSBill Taylor 	}
22099e39c5baSBill Taylor 	if (hermon_srate_override != -1) /* for evaluating HCA firmware */
22109e39c5baSBill Taylor 		path->max_stat_rate = hermon_srate_override;
22119e39c5baSBill Taylor 
22129e39c5baSBill Taylor 	/* If "grh" flag is set, then check for valid SGID index too */
22139e39c5baSBill Taylor 	gidtbl_sz = (1 << state->hs_queryport.log_max_gid);
22149e39c5baSBill Taylor 	if ((av->av_send_grh) && (av->av_sgid_ix > gidtbl_sz)) {
22159e39c5baSBill Taylor 		return (IBT_SGID_INVALID);
22169e39c5baSBill Taylor 	}
22179e39c5baSBill Taylor 
22189e39c5baSBill Taylor 	/*
22199e39c5baSBill Taylor 	 * Fill in all "global" values regardless of the value in the GRH
22209e39c5baSBill Taylor 	 * flag.  Because "grh" is not set unless "av_send_grh" is set, the
22219e39c5baSBill Taylor 	 * hardware will ignore the other "global" values as necessary.  Note:
22229e39c5baSBill Taylor 	 * SW does this here to enable later query operations to return
22239e39c5baSBill Taylor 	 * exactly the same params that were passed when the addr path was
22249e39c5baSBill Taylor 	 * last written.
22259e39c5baSBill Taylor 	 */
22269e39c5baSBill Taylor 	path->grh = av->av_send_grh;
22279e39c5baSBill Taylor 	if (type == HERMON_ADDRPATH_QP) {
22289e39c5baSBill Taylor 		path->mgid_index = av->av_sgid_ix;
22299e39c5baSBill Taylor 	} else {
22309e39c5baSBill Taylor 		/*
22319e39c5baSBill Taylor 		 * For Hermon UDAV, the "mgid_index" field is the index into
22329e39c5baSBill Taylor 		 * a combined table (not a per-port table), but having sections
22339e39c5baSBill Taylor 		 * for each port. So some extra calculations are necessary.
22349e39c5baSBill Taylor 		 */
22359e39c5baSBill Taylor 
22369e39c5baSBill Taylor 		path->mgid_index = ((av->av_port_num - 1) * gidtbl_sz) +
22379e39c5baSBill Taylor 		    av->av_sgid_ix;
22389e39c5baSBill Taylor 
22399e39c5baSBill Taylor 		udav->portnum = av->av_port_num;
22409e39c5baSBill Taylor 	}
22419e39c5baSBill Taylor 
22429e39c5baSBill Taylor 	/*
22439e39c5baSBill Taylor 	 * According to Hermon PRM, the (31:0) part of rgid_l must be set to
22449e39c5baSBill Taylor 	 * "0x2" if the 'grh' or 'g' bit is cleared.  It also says that we
22459e39c5baSBill Taylor 	 * only need to do it for UDAV's.  So we enforce that here.
22469e39c5baSBill Taylor 	 *
22479e39c5baSBill Taylor 	 * NOTE: The entire 64 bits worth of GUID info is actually being
22489e39c5baSBill Taylor 	 * preserved (for UDAVs) by the callers of this function
22499e39c5baSBill Taylor 	 * (hermon_ah_alloc() and hermon_ah_modify()) and as long as the
22509e39c5baSBill Taylor 	 * 'grh' bit is not set, the upper 32 bits (63:32) of rgid_l are
22519e39c5baSBill Taylor 	 * "don't care".
22529e39c5baSBill Taylor 	 */
22539e39c5baSBill Taylor 	if ((path->grh) || (type == HERMON_ADDRPATH_QP)) {
22549e39c5baSBill Taylor 		path->flow_label = av->av_flow;
22559e39c5baSBill Taylor 		path->tclass	 = av->av_tclass;
22569e39c5baSBill Taylor 		path->hop_limit	 = av->av_hop;
22579e39c5baSBill Taylor 		bcopy(&(av->av_dgid.gid_prefix), &(path->rgid_h),
22589e39c5baSBill Taylor 		    sizeof (uint64_t));
22599e39c5baSBill Taylor 		bcopy(&(av->av_dgid.gid_guid), &(path->rgid_l),
22609e39c5baSBill Taylor 		    sizeof (uint64_t));
22619e39c5baSBill Taylor 	} else {
22629e39c5baSBill Taylor 		path->rgid_l	 = 0x2;
22639e39c5baSBill Taylor 		path->flow_label = 0;
22649e39c5baSBill Taylor 		path->tclass	 = 0;
22659e39c5baSBill Taylor 		path->hop_limit	 = 0;
22669e39c5baSBill Taylor 		path->rgid_h	 = 0;
22679e39c5baSBill Taylor 	}
22689e39c5baSBill Taylor 	/* extract the default service level */
22699e39c5baSBill Taylor 	udav->sl = (HERMON_DEF_SCHED_SELECTION & 0x3C) >> 2;
22709e39c5baSBill Taylor 
22719e39c5baSBill Taylor 	return (DDI_SUCCESS);
22729e39c5baSBill Taylor }
22739e39c5baSBill Taylor 
22749e39c5baSBill Taylor 
22759e39c5baSBill Taylor /*
22769e39c5baSBill Taylor  * hermon_get_addr_path()
22779e39c5baSBill Taylor  *    Context: Can be called from interrupt or base context.
22789e39c5baSBill Taylor  *
22799e39c5baSBill Taylor  * Note: Just like hermon_set_addr_path() above, this routine is used for two
22809e39c5baSBill Taylor  * purposes.  It is used to read in the Hermon UDAV fields, and it is used to
22819e39c5baSBill Taylor  * read in the address path information for QPs.  Because the two Hermon
22829e39c5baSBill Taylor  * structures are similar, common fields can be read in here.  But because
22839e39c5baSBill Taylor  * they are slightly different, we pass an additional flag to indicate which
22849e39c5baSBill Taylor  * type is being read.
22859e39c5baSBill Taylor  */
22869e39c5baSBill Taylor void
hermon_get_addr_path(hermon_state_t * state,hermon_hw_addr_path_t * path,ibt_adds_vect_t * av,uint_t type)22879e39c5baSBill Taylor hermon_get_addr_path(hermon_state_t *state, hermon_hw_addr_path_t *path,
22889e39c5baSBill Taylor     ibt_adds_vect_t *av, uint_t type)
22899e39c5baSBill Taylor {
22909e39c5baSBill Taylor 	uint_t		gidtbl_sz;
22919e39c5baSBill Taylor 
22929e39c5baSBill Taylor 	_NOTE(NOW_INVISIBLE_TO_OTHER_THREADS(*path))
22939e39c5baSBill Taylor 	_NOTE(NOW_INVISIBLE_TO_OTHER_THREADS(*av))
22949e39c5baSBill Taylor 
22959e39c5baSBill Taylor 	av->av_src_path	= path->mlid;
22969e39c5baSBill Taylor 	av->av_dlid	= path->rlid;
22979e39c5baSBill Taylor 
22989e39c5baSBill Taylor 	/* Set "av_ipd" value from max_stat_rate */
2299340050d5SBill Taylor 	switch (path->max_stat_rate) {
2300340050d5SBill Taylor 	case 7:				/* 1xSDR-2.5Gb/s injection rate */
2301340050d5SBill Taylor 		av->av_srate = IBT_SRATE_2; break;
2302340050d5SBill Taylor 	case 8:				/* 4xSDR-10.0Gb/s injection rate */
2303340050d5SBill Taylor 		av->av_srate = IBT_SRATE_10; break;
2304340050d5SBill Taylor 	case 9:				/* 12xSDR-30Gb/s injection rate */
2305340050d5SBill Taylor 		av->av_srate = IBT_SRATE_30; break;
2306340050d5SBill Taylor 	case 10:			/* 1xDDR-5Gb/s injection rate */
2307340050d5SBill Taylor 		av->av_srate = IBT_SRATE_5; break;
2308340050d5SBill Taylor 	case 11:			/* 4xDDR-20Gb/s injection rate */
2309340050d5SBill Taylor 		av->av_srate = IBT_SRATE_20; break;
2310340050d5SBill Taylor 	case 12:			/* xQDR-40Gb/s injection rate */
2311340050d5SBill Taylor 		av->av_srate = IBT_SRATE_40; break;
2312340050d5SBill Taylor 	case 13:			/* 12xDDR-60Gb/s injection rate */
2313340050d5SBill Taylor 		av->av_srate = IBT_SRATE_60; break;
2314340050d5SBill Taylor 	case 14:			/* 8xQDR-80Gb/s injection rate */
2315340050d5SBill Taylor 		av->av_srate = IBT_SRATE_80; break;
2316340050d5SBill Taylor 	case 15:			/* 12xQDR-120Gb/s injection rate */
2317340050d5SBill Taylor 		av->av_srate = IBT_SRATE_120; break;
2318340050d5SBill Taylor 	case 0:				/* max */
23199fae04d8Sagiri 		av->av_srate = IBT_SRATE_NOT_SPECIFIED; break;
2320340050d5SBill Taylor 	default:			/* 1x injection rate */
2321340050d5SBill Taylor 		av->av_srate = IBT_SRATE_1X;
23229e39c5baSBill Taylor 	}
23239e39c5baSBill Taylor 
23249e39c5baSBill Taylor 	/*
23259e39c5baSBill Taylor 	 * Extract all "global" values regardless of the value in the GRH
23269e39c5baSBill Taylor 	 * flag.  Because "av_send_grh" is set only if "grh" is set, software
23279e39c5baSBill Taylor 	 * knows to ignore the other "global" values as necessary.  Note: SW
23289e39c5baSBill Taylor 	 * does it this way to enable these query operations to return exactly
23299e39c5baSBill Taylor 	 * the same params that were passed when the addr path was last written.
23309e39c5baSBill Taylor 	 */
23319e39c5baSBill Taylor 	av->av_send_grh		= path->grh;
23329e39c5baSBill Taylor 	if (type == HERMON_ADDRPATH_QP) {
23339e39c5baSBill Taylor 		av->av_sgid_ix  = path->mgid_index;
23349e39c5baSBill Taylor 	} else {
23359e39c5baSBill Taylor 		/*
23369e39c5baSBill Taylor 		 * For Hermon UDAV, the "mgid_index" field is the index into
23379e39c5baSBill Taylor 		 * a combined table (not a per-port table).
23389e39c5baSBill Taylor 		 */
23399e39c5baSBill Taylor 		gidtbl_sz = (1 << state->hs_queryport.log_max_gid);
23409e39c5baSBill Taylor 		av->av_sgid_ix = path->mgid_index - ((av->av_port_num - 1) *
23419e39c5baSBill Taylor 		    gidtbl_sz);
23429e39c5baSBill Taylor 
23439e39c5baSBill Taylor 		av->av_port_num = ((hermon_hw_udav_t *)(void *)path)->portnum;
23449e39c5baSBill Taylor 	}
23459e39c5baSBill Taylor 	av->av_flow		= path->flow_label;
23469e39c5baSBill Taylor 	av->av_tclass		= path->tclass;
23479e39c5baSBill Taylor 	av->av_hop		= path->hop_limit;
23489e39c5baSBill Taylor 	/* this is for alignment issue w/ the addr path struct in Hermon */
23499e39c5baSBill Taylor 	bcopy(&(path->rgid_h), &(av->av_dgid.gid_prefix), sizeof (uint64_t));
23509e39c5baSBill Taylor 	bcopy(&(path->rgid_l), &(av->av_dgid.gid_guid), sizeof (uint64_t));
23519e39c5baSBill Taylor }
23529e39c5baSBill Taylor 
23539e39c5baSBill Taylor 
23549e39c5baSBill Taylor /*
23559e39c5baSBill Taylor  * hermon_portnum_is_valid()
23569e39c5baSBill Taylor  *    Context: Can be called from interrupt or base context.
23579e39c5baSBill Taylor  */
23589e39c5baSBill Taylor int
hermon_portnum_is_valid(hermon_state_t * state,uint_t portnum)23599e39c5baSBill Taylor hermon_portnum_is_valid(hermon_state_t *state, uint_t portnum)
23609e39c5baSBill Taylor {
23619e39c5baSBill Taylor 	uint_t	max_port;
23629e39c5baSBill Taylor 
23639e39c5baSBill Taylor 	max_port = state->hs_cfg_profile->cp_num_ports;
23649e39c5baSBill Taylor 	if ((portnum <= max_port) && (portnum != 0)) {
23659e39c5baSBill Taylor 		return (1);
23669e39c5baSBill Taylor 	} else {
23679e39c5baSBill Taylor 		return (0);
23689e39c5baSBill Taylor 	}
23699e39c5baSBill Taylor }
23709e39c5baSBill Taylor 
23719e39c5baSBill Taylor 
23729e39c5baSBill Taylor /*
23739e39c5baSBill Taylor  * hermon_pkeyindex_is_valid()
23749e39c5baSBill Taylor  *    Context: Can be called from interrupt or base context.
23759e39c5baSBill Taylor  */
23769e39c5baSBill Taylor int
hermon_pkeyindex_is_valid(hermon_state_t * state,uint_t pkeyindx)23779e39c5baSBill Taylor hermon_pkeyindex_is_valid(hermon_state_t *state, uint_t pkeyindx)
23789e39c5baSBill Taylor {
23799e39c5baSBill Taylor 	uint_t	max_pkeyindx;
23809e39c5baSBill Taylor 
23819e39c5baSBill Taylor 	max_pkeyindx = 1 << state->hs_cfg_profile->cp_log_max_pkeytbl;
23829e39c5baSBill Taylor 	if (pkeyindx < max_pkeyindx) {
23839e39c5baSBill Taylor 		return (1);
23849e39c5baSBill Taylor 	} else {
23859e39c5baSBill Taylor 		return (0);
23869e39c5baSBill Taylor 	}
23879e39c5baSBill Taylor }
23889e39c5baSBill Taylor 
23899e39c5baSBill Taylor 
23909e39c5baSBill Taylor /*
23919e39c5baSBill Taylor  * hermon_queue_alloc()
23929e39c5baSBill Taylor  *    Context: Can be called from interrupt or base context.
23939e39c5baSBill Taylor  */
23949e39c5baSBill Taylor int
hermon_queue_alloc(hermon_state_t * state,hermon_qalloc_info_t * qa_info,uint_t sleepflag)23959e39c5baSBill Taylor hermon_queue_alloc(hermon_state_t *state, hermon_qalloc_info_t *qa_info,
23969e39c5baSBill Taylor     uint_t sleepflag)
23979e39c5baSBill Taylor {
23989e39c5baSBill Taylor 	ddi_dma_attr_t		dma_attr;
23999e39c5baSBill Taylor 	int			(*callback)(caddr_t);
24009e39c5baSBill Taylor 	uint64_t		realsize, alloc_mask;
24019e39c5baSBill Taylor 	int			flag, status;
24029e39c5baSBill Taylor 
24039e39c5baSBill Taylor 	_NOTE(NOW_INVISIBLE_TO_OTHER_THREADS(*qa_info))
24049e39c5baSBill Taylor 
24059e39c5baSBill Taylor 	/* Set the callback flag appropriately */
24069e39c5baSBill Taylor 	callback = (sleepflag == HERMON_SLEEP) ? DDI_DMA_SLEEP :
24079e39c5baSBill Taylor 	    DDI_DMA_DONTWAIT;
24089e39c5baSBill Taylor 
24099e39c5baSBill Taylor 	/*
24109e39c5baSBill Taylor 	 * Initialize many of the default DMA attributes.  Then set additional
24119e39c5baSBill Taylor 	 * alignment restrictions as necessary for the queue memory.  Also
24129e39c5baSBill Taylor 	 * respect the configured value for IOMMU bypass
24139e39c5baSBill Taylor 	 */
24149e39c5baSBill Taylor 	hermon_dma_attr_init(state, &dma_attr);
24159e39c5baSBill Taylor 	dma_attr.dma_attr_align = qa_info->qa_bind_align;
2416c7facc54SBill Taylor #ifdef	__sparc
2417c7facc54SBill Taylor 	if (state->hs_cfg_profile->cp_iommu_bypass == HERMON_BINDMEM_BYPASS) {
24189e39c5baSBill Taylor 		dma_attr.dma_attr_flags = DDI_DMA_FORCE_PHYSICAL;
24199e39c5baSBill Taylor 	}
2420c7facc54SBill Taylor #endif
24219e39c5baSBill Taylor 
24229e39c5baSBill Taylor 	/* Allocate a DMA handle */
24239e39c5baSBill Taylor 	status = ddi_dma_alloc_handle(state->hs_dip, &dma_attr, callback, NULL,
24249e39c5baSBill Taylor 	    &qa_info->qa_dmahdl);
24259e39c5baSBill Taylor 	if (status != DDI_SUCCESS) {
24269e39c5baSBill Taylor 		return (DDI_FAILURE);
24279e39c5baSBill Taylor 	}
24289e39c5baSBill Taylor 
24299e39c5baSBill Taylor 	/*
24309e39c5baSBill Taylor 	 * Determine the amount of memory to allocate, depending on the values
24319e39c5baSBill Taylor 	 * in "qa_bind_align" and "qa_alloc_align".  The problem we are trying
24329e39c5baSBill Taylor 	 * to solve here is that allocating a DMA handle with IOMMU bypass
24339e39c5baSBill Taylor 	 * (DDI_DMA_FORCE_PHYSICAL) constrains us to only requesting alignments
24349e39c5baSBill Taylor 	 * that are less restrictive than the page size.  Since we may need
24359e39c5baSBill Taylor 	 * stricter alignments on the memory allocated by ddi_dma_mem_alloc()
24369e39c5baSBill Taylor 	 * (e.g. in Hermon QP work queue memory allocation), we use the
24379e39c5baSBill Taylor 	 * following method to calculate how much additional memory to request,
24389e39c5baSBill Taylor 	 * and we enforce our own alignment on the allocated result.
24399e39c5baSBill Taylor 	 */
24409e39c5baSBill Taylor 	alloc_mask = qa_info->qa_alloc_align - 1;
24419e39c5baSBill Taylor 	if (qa_info->qa_bind_align == qa_info->qa_alloc_align) {
24429e39c5baSBill Taylor 		realsize = qa_info->qa_size;
24439e39c5baSBill Taylor 	} else {
24449e39c5baSBill Taylor 		realsize = qa_info->qa_size + alloc_mask;
24459e39c5baSBill Taylor 	}
24469e39c5baSBill Taylor 
24479e39c5baSBill Taylor 	/*
24489e39c5baSBill Taylor 	 * If we are to allocate the queue from system memory, then use
24499e39c5baSBill Taylor 	 * ddi_dma_mem_alloc() to find the space.  Otherwise, this is a
24509e39c5baSBill Taylor 	 * host memory allocation, use ddi_umem_alloc(). In either case,
24519e39c5baSBill Taylor 	 * return a pointer to the memory range allocated (including any
24529e39c5baSBill Taylor 	 * necessary alignment adjustments), the "real" memory pointer,
24539e39c5baSBill Taylor 	 * the "real" size, and a ddi_acc_handle_t to use when reading
24549e39c5baSBill Taylor 	 * from/writing to the memory.
24559e39c5baSBill Taylor 	 */
24569e39c5baSBill Taylor 	if (qa_info->qa_location == HERMON_QUEUE_LOCATION_NORMAL) {
24579e39c5baSBill Taylor 		/* Allocate system memory for the queue */
24589e39c5baSBill Taylor 		status = ddi_dma_mem_alloc(qa_info->qa_dmahdl, realsize,
24599e39c5baSBill Taylor 		    &state->hs_reg_accattr, DDI_DMA_CONSISTENT, callback, NULL,
24609e39c5baSBill Taylor 		    (caddr_t *)&qa_info->qa_buf_real,
24619e39c5baSBill Taylor 		    (size_t *)&qa_info->qa_buf_realsz, &qa_info->qa_acchdl);
24629e39c5baSBill Taylor 		if (status != DDI_SUCCESS) {
24639e39c5baSBill Taylor 			ddi_dma_free_handle(&qa_info->qa_dmahdl);
24649e39c5baSBill Taylor 			return (DDI_FAILURE);
24659e39c5baSBill Taylor 		}
24669e39c5baSBill Taylor 
24679e39c5baSBill Taylor 		/*
24689e39c5baSBill Taylor 		 * Save temporary copy of the real pointer.  (This may be
24699e39c5baSBill Taylor 		 * modified in the last step below).
24709e39c5baSBill Taylor 		 */
24719e39c5baSBill Taylor 		qa_info->qa_buf_aligned = qa_info->qa_buf_real;
24729e39c5baSBill Taylor 
24739e39c5baSBill Taylor 		bzero(qa_info->qa_buf_real, qa_info->qa_buf_realsz);
24749e39c5baSBill Taylor 
24759e39c5baSBill Taylor 	} else { /* HERMON_QUEUE_LOCATION_USERLAND */
24769e39c5baSBill Taylor 
24779e39c5baSBill Taylor 		/* Allocate userland mappable memory for the queue */
24789e39c5baSBill Taylor 		flag = (sleepflag == HERMON_SLEEP) ? DDI_UMEM_SLEEP :
24799e39c5baSBill Taylor 		    DDI_UMEM_NOSLEEP;
24809e39c5baSBill Taylor 		qa_info->qa_buf_real = ddi_umem_alloc(realsize, flag,
24819e39c5baSBill Taylor 		    &qa_info->qa_umemcookie);
24829e39c5baSBill Taylor 		if (qa_info->qa_buf_real == NULL) {
24839e39c5baSBill Taylor 			ddi_dma_free_handle(&qa_info->qa_dmahdl);
24849e39c5baSBill Taylor 			return (DDI_FAILURE);
24859e39c5baSBill Taylor 		}
24869e39c5baSBill Taylor 
24879e39c5baSBill Taylor 		/*
24889e39c5baSBill Taylor 		 * Save temporary copy of the real pointer.  (This may be
24899e39c5baSBill Taylor 		 * modified in the last step below).
24909e39c5baSBill Taylor 		 */
24919e39c5baSBill Taylor 		qa_info->qa_buf_aligned = qa_info->qa_buf_real;
24929e39c5baSBill Taylor 
24939e39c5baSBill Taylor 	}
24949e39c5baSBill Taylor 
24959e39c5baSBill Taylor 	/*
24969e39c5baSBill Taylor 	 * The next to last step is to ensure that the final address
24979e39c5baSBill Taylor 	 * ("qa_buf_aligned") has the appropriate "alloc" alignment
24989e39c5baSBill Taylor 	 * restriction applied to it (if necessary).
24999e39c5baSBill Taylor 	 */
25009e39c5baSBill Taylor 	if (qa_info->qa_bind_align != qa_info->qa_alloc_align) {
25019e39c5baSBill Taylor 		qa_info->qa_buf_aligned = (uint32_t *)(uintptr_t)(((uintptr_t)
25029e39c5baSBill Taylor 		    qa_info->qa_buf_aligned + alloc_mask) & ~alloc_mask);
25039e39c5baSBill Taylor 	}
25049e39c5baSBill Taylor 	/*
25059e39c5baSBill Taylor 	 * The last step is to figure out the offset of the start relative
25069e39c5baSBill Taylor 	 * to the first page of the region - will be used in the eqc/cqc
25079e39c5baSBill Taylor 	 * passed to the HW
25089e39c5baSBill Taylor 	 */
25099e39c5baSBill Taylor 	qa_info->qa_pgoffs = (uint_t)((uintptr_t)
2510c7facc54SBill Taylor 	    qa_info->qa_buf_aligned & HERMON_PAGEOFFSET);
25119e39c5baSBill Taylor 
25129e39c5baSBill Taylor 	return (DDI_SUCCESS);
25139e39c5baSBill Taylor }
25149e39c5baSBill Taylor 
25159e39c5baSBill Taylor 
25169e39c5baSBill Taylor /*
25179e39c5baSBill Taylor  * hermon_queue_free()
25189e39c5baSBill Taylor  *    Context: Can be called from interrupt or base context.
25199e39c5baSBill Taylor  */
25209e39c5baSBill Taylor void
hermon_queue_free(hermon_qalloc_info_t * qa_info)25219e39c5baSBill Taylor hermon_queue_free(hermon_qalloc_info_t *qa_info)
25229e39c5baSBill Taylor {
25239e39c5baSBill Taylor 	_NOTE(NOW_INVISIBLE_TO_OTHER_THREADS(*qa_info))
25249e39c5baSBill Taylor 
25259e39c5baSBill Taylor 	/*
25269e39c5baSBill Taylor 	 * Depending on how (i.e. from where) we allocated the memory for
25279e39c5baSBill Taylor 	 * this queue, we choose the appropriate method for releasing the
25289e39c5baSBill Taylor 	 * resources.
25299e39c5baSBill Taylor 	 */
25309e39c5baSBill Taylor 	if (qa_info->qa_location == HERMON_QUEUE_LOCATION_NORMAL) {
25319e39c5baSBill Taylor 
25329e39c5baSBill Taylor 		ddi_dma_mem_free(&qa_info->qa_acchdl);
25339e39c5baSBill Taylor 
25349e39c5baSBill Taylor 	} else if (qa_info->qa_location == HERMON_QUEUE_LOCATION_USERLAND) {
25359e39c5baSBill Taylor 
25369e39c5baSBill Taylor 		ddi_umem_free(qa_info->qa_umemcookie);
25379e39c5baSBill Taylor 
25389e39c5baSBill Taylor 	}
25399e39c5baSBill Taylor 
25409e39c5baSBill Taylor 	/* Always free the dma handle */
25419e39c5baSBill Taylor 	ddi_dma_free_handle(&qa_info->qa_dmahdl);
25429e39c5baSBill Taylor }
25439e39c5baSBill Taylor 
25449e39c5baSBill Taylor /*
2545c7facc54SBill Taylor  * hermon_create_fmr_pool()
25469e39c5baSBill Taylor  * Create a pool of FMRs.
25479e39c5baSBill Taylor  *     Context: Can be called from kernel context only.
25489e39c5baSBill Taylor  */
25499e39c5baSBill Taylor int
hermon_create_fmr_pool(hermon_state_t * state,hermon_pdhdl_t pd,ibt_fmr_pool_attr_t * fmr_attr,hermon_fmrhdl_t * fmrpoolp)25509e39c5baSBill Taylor hermon_create_fmr_pool(hermon_state_t *state, hermon_pdhdl_t pd,
25519e39c5baSBill Taylor     ibt_fmr_pool_attr_t *fmr_attr, hermon_fmrhdl_t *fmrpoolp)
25529e39c5baSBill Taylor {
25539e39c5baSBill Taylor 	hermon_fmrhdl_t	fmrpool;
25549e39c5baSBill Taylor 	hermon_fmr_list_t *fmr, *fmr_next;
25559e39c5baSBill Taylor 	hermon_mrhdl_t   mr;
25569e39c5baSBill Taylor 	int		status;
25579e39c5baSBill Taylor 	int		sleep;
25589e39c5baSBill Taylor 	int		i;
25599e39c5baSBill Taylor 
25609e39c5baSBill Taylor 	sleep = (fmr_attr->fmr_flags & IBT_MR_SLEEP) ? HERMON_SLEEP :
25619e39c5baSBill Taylor 	    HERMON_NOSLEEP;
25629e39c5baSBill Taylor 	if ((sleep == HERMON_SLEEP) &&
25639e39c5baSBill Taylor 	    (sleep != HERMON_SLEEPFLAG_FOR_CONTEXT())) {
25649e39c5baSBill Taylor 		return (IBT_INVALID_PARAM);
25659e39c5baSBill Taylor 	}
25669e39c5baSBill Taylor 
25679e39c5baSBill Taylor 	fmrpool = (hermon_fmrhdl_t)kmem_zalloc(sizeof (*fmrpool), sleep);
25689e39c5baSBill Taylor 	if (fmrpool == NULL) {
25699e39c5baSBill Taylor 		status = IBT_INSUFF_RESOURCE;
25709e39c5baSBill Taylor 		goto fail;
25719e39c5baSBill Taylor 	}
25729e39c5baSBill Taylor 	_NOTE(NOW_INVISIBLE_TO_OTHER_THREADS(*fmrpool))
25739e39c5baSBill Taylor 
25749e39c5baSBill Taylor 	mutex_init(&fmrpool->fmr_lock, NULL, MUTEX_DRIVER,
25759e39c5baSBill Taylor 	    DDI_INTR_PRI(state->hs_intrmsi_pri));
2576*17a2b317SBill Taylor 	mutex_init(&fmrpool->remap_lock, NULL, MUTEX_DRIVER,
2577*17a2b317SBill Taylor 	    DDI_INTR_PRI(state->hs_intrmsi_pri));
2578*17a2b317SBill Taylor 	mutex_init(&fmrpool->dirty_lock, NULL, MUTEX_DRIVER,
2579*17a2b317SBill Taylor 	    DDI_INTR_PRI(state->hs_intrmsi_pri));
25809e39c5baSBill Taylor 
25819e39c5baSBill Taylor 	fmrpool->fmr_state	    = state;
25829e39c5baSBill Taylor 	fmrpool->fmr_flush_function = fmr_attr->fmr_func_hdlr;
25839e39c5baSBill Taylor 	fmrpool->fmr_flush_arg	    = fmr_attr->fmr_func_arg;
25849e39c5baSBill Taylor 	fmrpool->fmr_pool_size	    = 0;
25859e39c5baSBill Taylor 	fmrpool->fmr_max_pages	    = fmr_attr->fmr_max_pages_per_fmr;
25869e39c5baSBill Taylor 	fmrpool->fmr_page_sz	    = fmr_attr->fmr_page_sz;
2587*17a2b317SBill Taylor 	fmrpool->fmr_dirty_watermark = fmr_attr->fmr_pool_size / 4;
25889e39c5baSBill Taylor 	fmrpool->fmr_dirty_len	    = 0;
2589*17a2b317SBill Taylor 	fmrpool->fmr_remap_watermark = fmr_attr->fmr_pool_size / 32;
2590*17a2b317SBill Taylor 	fmrpool->fmr_remap_len	    = 0;
25919e39c5baSBill Taylor 	fmrpool->fmr_flags	    = fmr_attr->fmr_flags;
2592*17a2b317SBill Taylor 	fmrpool->fmr_stat_register  = 0;
2593*17a2b317SBill Taylor 	fmrpool->fmr_max_remaps	    = state->hs_cfg_profile->cp_fmr_max_remaps;
2594*17a2b317SBill Taylor 	fmrpool->fmr_remap_gen	    = 1;
25959e39c5baSBill Taylor 
2596*17a2b317SBill Taylor 	fmrpool->fmr_free_list_tail = &fmrpool->fmr_free_list;
25979e39c5baSBill Taylor 	fmrpool->fmr_dirty_list = NULL;
2598*17a2b317SBill Taylor 	fmrpool->fmr_dirty_list_tail = &fmrpool->fmr_dirty_list;
2599*17a2b317SBill Taylor 	fmrpool->fmr_remap_list = NULL;
2600*17a2b317SBill Taylor 	fmrpool->fmr_remap_list_tail = &fmrpool->fmr_remap_list;
2601*17a2b317SBill Taylor 	fmrpool->fmr_pool_size = fmrpool->fmr_free_len =
2602*17a2b317SBill Taylor 	    fmr_attr->fmr_pool_size;
26039e39c5baSBill Taylor 
26049e39c5baSBill Taylor 	for (i = 0; i < fmr_attr->fmr_pool_size; i++) {
26059e39c5baSBill Taylor 		status = hermon_mr_alloc_fmr(state, pd, fmrpool, &mr);
26069e39c5baSBill Taylor 		if (status != DDI_SUCCESS) {
26079e39c5baSBill Taylor 			goto fail2;
26089e39c5baSBill Taylor 		}
26099e39c5baSBill Taylor 
26109e39c5baSBill Taylor 		fmr = (hermon_fmr_list_t *)kmem_zalloc(
26119e39c5baSBill Taylor 		    sizeof (hermon_fmr_list_t), sleep);
26129e39c5baSBill Taylor 		_NOTE(NOW_INVISIBLE_TO_OTHER_THREADS(*fmr))
26139e39c5baSBill Taylor 
26149e39c5baSBill Taylor 		fmr->fmr = mr;
26159e39c5baSBill Taylor 		fmr->fmr_remaps = 0;
2616*17a2b317SBill Taylor 		fmr->fmr_remap_gen = fmrpool->fmr_remap_gen;
26179e39c5baSBill Taylor 		fmr->fmr_pool = fmrpool;
26189e39c5baSBill Taylor 		_NOTE(NOW_INVISIBLE_TO_OTHER_THREADS(*mr))
26199e39c5baSBill Taylor 		mr->mr_fmr = fmr;
26209e39c5baSBill Taylor 
2621*17a2b317SBill Taylor 		if (!i)		/* address of last entry's link */
2622*17a2b317SBill Taylor 			fmrpool->fmr_free_list_tail = &fmr->fmr_next;
26239e39c5baSBill Taylor 		fmr->fmr_next = fmrpool->fmr_free_list;
26249e39c5baSBill Taylor 		fmrpool->fmr_free_list = fmr;
26259e39c5baSBill Taylor 	}
26269e39c5baSBill Taylor 
26279e39c5baSBill Taylor 	/* Set to return pool */
26289e39c5baSBill Taylor 	*fmrpoolp = fmrpool;
26299e39c5baSBill Taylor 
2630*17a2b317SBill Taylor 	IBTF_DPRINTF_L2("fmr", "create_fmr_pool SUCCESS");
26319e39c5baSBill Taylor 	return (IBT_SUCCESS);
26329e39c5baSBill Taylor fail2:
26339e39c5baSBill Taylor 	for (fmr = fmrpool->fmr_free_list; fmr != NULL; fmr = fmr_next) {
26349e39c5baSBill Taylor 		_NOTE(NOW_INVISIBLE_TO_OTHER_THREADS(*fmr))
26359e39c5baSBill Taylor 		fmr_next = fmr->fmr_next;
26369e39c5baSBill Taylor 		(void) hermon_mr_dealloc_fmr(state, &fmr->fmr);
26379e39c5baSBill Taylor 		kmem_free(fmr, sizeof (hermon_fmr_list_t));
26389e39c5baSBill Taylor 	}
26399e39c5baSBill Taylor 	kmem_free(fmrpool, sizeof (*fmrpool));
26409e39c5baSBill Taylor fail:
2641*17a2b317SBill Taylor 	*fmrpoolp = NULL;
2642*17a2b317SBill Taylor 	IBTF_DPRINTF_L2("fmr", "create_fmr_pool FAILED");
26439e39c5baSBill Taylor 	if (status == DDI_FAILURE) {
26449e39c5baSBill Taylor 		return (ibc_get_ci_failure(0));
26459e39c5baSBill Taylor 	} else {
26469e39c5baSBill Taylor 		return (status);
26479e39c5baSBill Taylor 	}
26489e39c5baSBill Taylor }
26499e39c5baSBill Taylor 
26509e39c5baSBill Taylor /*
26519e39c5baSBill Taylor  * hermon_destroy_fmr_pool()
26529e39c5baSBill Taylor  * Destroy an FMR pool and free all associated resources.
26539e39c5baSBill Taylor  *     Context: Can be called from kernel context only.
26549e39c5baSBill Taylor  */
26559e39c5baSBill Taylor int
hermon_destroy_fmr_pool(hermon_state_t * state,hermon_fmrhdl_t fmrpool)26569e39c5baSBill Taylor hermon_destroy_fmr_pool(hermon_state_t *state, hermon_fmrhdl_t fmrpool)
26579e39c5baSBill Taylor {
26589e39c5baSBill Taylor 	hermon_fmr_list_t	*fmr, *fmr_next;
26599e39c5baSBill Taylor 
26609e39c5baSBill Taylor 	mutex_enter(&fmrpool->fmr_lock);
2661*17a2b317SBill Taylor 	hermon_fmr_cleanup(fmrpool);
26629e39c5baSBill Taylor 
26639e39c5baSBill Taylor 	for (fmr = fmrpool->fmr_free_list; fmr != NULL; fmr = fmr_next) {
26649e39c5baSBill Taylor 		fmr_next = fmr->fmr_next;
26659e39c5baSBill Taylor 
26669e39c5baSBill Taylor 		(void) hermon_mr_dealloc_fmr(state, &fmr->fmr);
26679e39c5baSBill Taylor 		kmem_free(fmr, sizeof (hermon_fmr_list_t));
2668*17a2b317SBill Taylor 
2669*17a2b317SBill Taylor 		--fmrpool->fmr_pool_size;
26709e39c5baSBill Taylor 	}
2671*17a2b317SBill Taylor 	ASSERT(fmrpool->fmr_pool_size == 0);
26729e39c5baSBill Taylor 	mutex_exit(&fmrpool->fmr_lock);
26739e39c5baSBill Taylor 
26749e39c5baSBill Taylor 	mutex_destroy(&fmrpool->fmr_lock);
2675*17a2b317SBill Taylor 	mutex_destroy(&fmrpool->dirty_lock);
2676*17a2b317SBill Taylor 	mutex_destroy(&fmrpool->remap_lock);
26779e39c5baSBill Taylor 
26789e39c5baSBill Taylor 	kmem_free(fmrpool, sizeof (*fmrpool));
2679*17a2b317SBill Taylor 	IBTF_DPRINTF_L2("fmr", "destroy_fmr_pool SUCCESS");
26809e39c5baSBill Taylor 	return (DDI_SUCCESS);
26819e39c5baSBill Taylor }
26829e39c5baSBill Taylor 
26839e39c5baSBill Taylor /*
26849e39c5baSBill Taylor  * hermon_flush_fmr_pool()
26859e39c5baSBill Taylor  * Ensure that all unmapped FMRs are fully invalidated.
26869e39c5baSBill Taylor  *     Context: Can be called from kernel context only.
26879e39c5baSBill Taylor  */
2688*17a2b317SBill Taylor /* ARGSUSED */
26899e39c5baSBill Taylor int
hermon_flush_fmr_pool(hermon_state_t * state,hermon_fmrhdl_t fmrpool)26909e39c5baSBill Taylor hermon_flush_fmr_pool(hermon_state_t *state, hermon_fmrhdl_t fmrpool)
26919e39c5baSBill Taylor {
26929e39c5baSBill Taylor 	/*
26939e39c5baSBill Taylor 	 * Force the unmapping of all entries on the dirty list, regardless of
26949e39c5baSBill Taylor 	 * whether the watermark has been hit yet.
26959e39c5baSBill Taylor 	 */
26969e39c5baSBill Taylor 	/* grab the pool lock */
26979e39c5baSBill Taylor 	mutex_enter(&fmrpool->fmr_lock);
2698*17a2b317SBill Taylor 	hermon_fmr_cleanup(fmrpool);
26999e39c5baSBill Taylor 	mutex_exit(&fmrpool->fmr_lock);
2700*17a2b317SBill Taylor 	return (DDI_SUCCESS);
27019e39c5baSBill Taylor }
27029e39c5baSBill Taylor 
27039e39c5baSBill Taylor /*
2704*17a2b317SBill Taylor  * hermon_register_physical_fmr()
27059e39c5baSBill Taylor  * Map memory into FMR
27069e39c5baSBill Taylor  *    Context: Can be called from interrupt or base context.
27079e39c5baSBill Taylor  */
27089e39c5baSBill Taylor int
hermon_register_physical_fmr(hermon_state_t * state,hermon_fmrhdl_t fmrpool,ibt_pmr_attr_t * mem_pattr,hermon_mrhdl_t * mr,ibt_pmr_desc_t * mem_desc_p)27099e39c5baSBill Taylor hermon_register_physical_fmr(hermon_state_t *state, hermon_fmrhdl_t fmrpool,
27109e39c5baSBill Taylor     ibt_pmr_attr_t *mem_pattr, hermon_mrhdl_t *mr,
27119e39c5baSBill Taylor     ibt_pmr_desc_t *mem_desc_p)
27129e39c5baSBill Taylor {
27139e39c5baSBill Taylor 	hermon_fmr_list_t	*fmr;
27149e39c5baSBill Taylor 	int			status;
27159e39c5baSBill Taylor 
27169e39c5baSBill Taylor 	/* Check length */
27179e39c5baSBill Taylor 	if (mem_pattr->pmr_len < 1 || (mem_pattr->pmr_num_buf >
27189e39c5baSBill Taylor 	    fmrpool->fmr_max_pages)) {
27199e39c5baSBill Taylor 		return (IBT_MR_LEN_INVALID);
27209e39c5baSBill Taylor 	}
27219e39c5baSBill Taylor 
2722*17a2b317SBill Taylor 	mutex_enter(&fmrpool->fmr_lock);
2723*17a2b317SBill Taylor 	if (fmrpool->fmr_free_list == NULL) {
2724*17a2b317SBill Taylor 		if (hermon_fmr_verbose & 2)
2725*17a2b317SBill Taylor 			IBTF_DPRINTF_L2("fmr", "register needs remap");
2726*17a2b317SBill Taylor 		mutex_enter(&fmrpool->remap_lock);
2727*17a2b317SBill Taylor 		if (fmrpool->fmr_remap_list) {
2728*17a2b317SBill Taylor 			/* add to free list */
2729*17a2b317SBill Taylor 			*(fmrpool->fmr_free_list_tail) =
2730*17a2b317SBill Taylor 			    fmrpool->fmr_remap_list;
2731*17a2b317SBill Taylor 			fmrpool->fmr_remap_list = NULL;
2732*17a2b317SBill Taylor 			fmrpool->fmr_free_list_tail =
2733*17a2b317SBill Taylor 			    fmrpool->fmr_remap_list_tail;
27349e39c5baSBill Taylor 
2735*17a2b317SBill Taylor 			/* reset list */
2736*17a2b317SBill Taylor 			fmrpool->fmr_remap_list_tail = &fmrpool->fmr_remap_list;
2737*17a2b317SBill Taylor 			fmrpool->fmr_free_len += fmrpool->fmr_remap_len;
2738*17a2b317SBill Taylor 			fmrpool->fmr_remap_len = 0;
27399e39c5baSBill Taylor 		}
2740*17a2b317SBill Taylor 		mutex_exit(&fmrpool->remap_lock);
27419e39c5baSBill Taylor 	}
2742*17a2b317SBill Taylor 	if (fmrpool->fmr_free_list == NULL) {
2743*17a2b317SBill Taylor 		if (hermon_fmr_verbose & 2)
2744*17a2b317SBill Taylor 			IBTF_DPRINTF_L2("fmr", "register needs cleanup");
2745*17a2b317SBill Taylor 		hermon_fmr_cleanup(fmrpool);
2746*17a2b317SBill Taylor 	}
27479e39c5baSBill Taylor 
27489e39c5baSBill Taylor 	/* grab next free entry */
27499e39c5baSBill Taylor 	fmr = fmrpool->fmr_free_list;
27509e39c5baSBill Taylor 	if (fmr == NULL) {
2751c7facc54SBill Taylor 		IBTF_DPRINTF_L2("fmr", "WARNING: no free fmr resource");
2752*17a2b317SBill Taylor 		cmn_err(CE_CONT, "no free fmr resource\n");
27539e39c5baSBill Taylor 		mutex_exit(&fmrpool->fmr_lock);
27549e39c5baSBill Taylor 		return (IBT_INSUFF_RESOURCE);
27559e39c5baSBill Taylor 	}
27569e39c5baSBill Taylor 
2757*17a2b317SBill Taylor 	if ((fmrpool->fmr_free_list = fmr->fmr_next) == NULL)
2758*17a2b317SBill Taylor 		fmrpool->fmr_free_list_tail = &fmrpool->fmr_free_list;
27599e39c5baSBill Taylor 	fmr->fmr_next = NULL;
2760*17a2b317SBill Taylor 	fmrpool->fmr_stat_register++;
2761*17a2b317SBill Taylor 	mutex_exit(&fmrpool->fmr_lock);
27629e39c5baSBill Taylor 
2763*17a2b317SBill Taylor 	_NOTE(NOW_INVISIBLE_TO_OTHER_THREADS(*fmr))
27649e39c5baSBill Taylor 	status = hermon_mr_register_physical_fmr(state, mem_pattr, fmr->fmr,
27659e39c5baSBill Taylor 	    mem_desc_p);
27669e39c5baSBill Taylor 	if (status != DDI_SUCCESS) {
27679e39c5baSBill Taylor 		return (status);
27689e39c5baSBill Taylor 	}
2769c7facc54SBill Taylor 	_NOTE(NOW_INVISIBLE_TO_OTHER_THREADS(*fmr->fmr))
2770c7facc54SBill Taylor 	if (hermon_rdma_debug & 0x4)
2771c7facc54SBill Taylor 		IBTF_DPRINTF_L2("fmr", "  reg: mr %p  key %x",
2772c7facc54SBill Taylor 		    fmr->fmr, fmr->fmr->mr_rkey);
2773c7facc54SBill Taylor 	_NOTE(NOW_VISIBLE_TO_OTHER_THREADS(*fmr->fmr))
2774*17a2b317SBill Taylor 	if (fmr->fmr_remap_gen != fmrpool->fmr_remap_gen) {
2775*17a2b317SBill Taylor 		fmr->fmr_remap_gen = fmrpool->fmr_remap_gen;
2776*17a2b317SBill Taylor 		fmr->fmr_remaps = 0;
2777*17a2b317SBill Taylor 	}
27789e39c5baSBill Taylor 
27799e39c5baSBill Taylor 	fmr->fmr_remaps++;
27809e39c5baSBill Taylor 
27819e39c5baSBill Taylor 	*mr = (hermon_mrhdl_t)fmr->fmr;
27829e39c5baSBill Taylor 
27839e39c5baSBill Taylor 	return (DDI_SUCCESS);
27849e39c5baSBill Taylor }
27859e39c5baSBill Taylor 
27869e39c5baSBill Taylor /*
27879e39c5baSBill Taylor  * hermon_deregister_fmr()
27889e39c5baSBill Taylor  * Unmap FMR
27899e39c5baSBill Taylor  *    Context: Can be called from kernel context only.
27909e39c5baSBill Taylor  */
27919e39c5baSBill Taylor int
hermon_deregister_fmr(hermon_state_t * state,hermon_mrhdl_t mr)27929e39c5baSBill Taylor hermon_deregister_fmr(hermon_state_t *state, hermon_mrhdl_t mr)
27939e39c5baSBill Taylor {
27949e39c5baSBill Taylor 	hermon_fmrhdl_t		fmrpool;
2795*17a2b317SBill Taylor 	hermon_fmr_list_t	*fmr, **fmrlast;
2796*17a2b317SBill Taylor 	int			len;
27979e39c5baSBill Taylor 
27989e39c5baSBill Taylor 	fmr = mr->mr_fmr;
2799*17a2b317SBill Taylor 	_NOTE(NOW_INVISIBLE_TO_OTHER_THREADS(*fmr))
28009e39c5baSBill Taylor 	fmrpool = fmr->fmr_pool;
28019e39c5baSBill Taylor 
2802*17a2b317SBill Taylor 	/* mark as owned by software */
2803*17a2b317SBill Taylor 	_NOTE(NOW_INVISIBLE_TO_OTHER_THREADS(*(fmr->fmr)))
2804*17a2b317SBill Taylor 	*(uint8_t *)(fmr->fmr->mr_mptrsrcp->hr_addr) = 0xF0;
28059e39c5baSBill Taylor 
28069e39c5baSBill Taylor 	if (fmr->fmr_remaps <
28079e39c5baSBill Taylor 	    state->hs_cfg_profile->cp_fmr_max_remaps) {
2808*17a2b317SBill Taylor 		/* add to remap list */
2809c7facc54SBill Taylor 		_NOTE(NOW_INVISIBLE_TO_OTHER_THREADS(*(fmr->fmr)))
2810c7facc54SBill Taylor 		if (hermon_rdma_debug & 0x4)
2811c7facc54SBill Taylor 			IBTF_DPRINTF_L2("fmr", "dereg: mr %p  key %x",
2812c7facc54SBill Taylor 			    fmr->fmr, fmr->fmr->mr_rkey);
2813c7facc54SBill Taylor 		_NOTE(NOW_VISIBLE_TO_OTHER_THREADS(*(fmr->fmr)))
2814*17a2b317SBill Taylor 		mutex_enter(&fmrpool->remap_lock);
2815*17a2b317SBill Taylor 		fmr->fmr_next = NULL;
2816*17a2b317SBill Taylor 		*(fmrpool->fmr_remap_list_tail) = fmr;
2817*17a2b317SBill Taylor 		fmrpool->fmr_remap_list_tail = &fmr->fmr_next;
2818*17a2b317SBill Taylor 		fmrpool->fmr_remap_len++;
2819*17a2b317SBill Taylor 
2820*17a2b317SBill Taylor 		/* conditionally add remap list back to free list */
2821*17a2b317SBill Taylor 		fmrlast = NULL;
2822*17a2b317SBill Taylor 		if (fmrpool->fmr_remap_len >=
2823*17a2b317SBill Taylor 		    fmrpool->fmr_remap_watermark) {
2824*17a2b317SBill Taylor 			fmr = fmrpool->fmr_remap_list;
2825*17a2b317SBill Taylor 			fmrlast = fmrpool->fmr_remap_list_tail;
2826*17a2b317SBill Taylor 			len = fmrpool->fmr_remap_len;
2827*17a2b317SBill Taylor 			fmrpool->fmr_remap_len = 0;
2828*17a2b317SBill Taylor 			fmrpool->fmr_remap_list = NULL;
2829*17a2b317SBill Taylor 			fmrpool->fmr_remap_list_tail =
2830*17a2b317SBill Taylor 			    &fmrpool->fmr_remap_list;
2831*17a2b317SBill Taylor 		}
2832*17a2b317SBill Taylor 		mutex_exit(&fmrpool->remap_lock);
2833*17a2b317SBill Taylor 		if (fmrlast) {
2834*17a2b317SBill Taylor 			mutex_enter(&fmrpool->fmr_lock);
2835*17a2b317SBill Taylor 			*(fmrpool->fmr_free_list_tail) = fmr;
2836*17a2b317SBill Taylor 			fmrpool->fmr_free_list_tail = fmrlast;
2837*17a2b317SBill Taylor 			fmrpool->fmr_free_len += len;
2838*17a2b317SBill Taylor 			mutex_exit(&fmrpool->fmr_lock);
2839*17a2b317SBill Taylor 		}
28409e39c5baSBill Taylor 	} else {
28419e39c5baSBill Taylor 		/* add to dirty list */
2842c7facc54SBill Taylor 		_NOTE(NOW_INVISIBLE_TO_OTHER_THREADS(*(fmr->fmr)))
2843c7facc54SBill Taylor 		if (hermon_rdma_debug & 0x4)
2844c7facc54SBill Taylor 			IBTF_DPRINTF_L2("fmr", "dirty: mr %p  key %x",
2845c7facc54SBill Taylor 			    fmr->fmr, fmr->fmr->mr_rkey);
2846c7facc54SBill Taylor 		_NOTE(NOW_VISIBLE_TO_OTHER_THREADS(*(fmr->fmr)))
2847*17a2b317SBill Taylor 
2848*17a2b317SBill Taylor 		mutex_enter(&fmrpool->dirty_lock);
2849*17a2b317SBill Taylor 		fmr->fmr_next = NULL;
2850*17a2b317SBill Taylor 		*(fmrpool->fmr_dirty_list_tail) = fmr;
2851*17a2b317SBill Taylor 		fmrpool->fmr_dirty_list_tail = &fmr->fmr_next;
28529e39c5baSBill Taylor 		fmrpool->fmr_dirty_len++;
28539e39c5baSBill Taylor 
2854*17a2b317SBill Taylor 		if (fmrpool->fmr_dirty_len >=
2855*17a2b317SBill Taylor 		    fmrpool->fmr_dirty_watermark) {
2856*17a2b317SBill Taylor 			mutex_exit(&fmrpool->dirty_lock);
2857*17a2b317SBill Taylor 			mutex_enter(&fmrpool->fmr_lock);
2858*17a2b317SBill Taylor 			hermon_fmr_cleanup(fmrpool);
28599e39c5baSBill Taylor 			mutex_exit(&fmrpool->fmr_lock);
2860*17a2b317SBill Taylor 		} else
2861*17a2b317SBill Taylor 			mutex_exit(&fmrpool->dirty_lock);
28629e39c5baSBill Taylor 	}
28639e39c5baSBill Taylor 	return (DDI_SUCCESS);
28649e39c5baSBill Taylor }
28659e39c5baSBill Taylor 
28669e39c5baSBill Taylor /*
2867*17a2b317SBill Taylor  * hermon_fmr_cleanup()
2868*17a2b317SBill Taylor  *     Context: Called from any context.
28699e39c5baSBill Taylor  */
28709e39c5baSBill Taylor static void
hermon_fmr_cleanup(hermon_fmrhdl_t fmrpool)2871*17a2b317SBill Taylor hermon_fmr_cleanup(hermon_fmrhdl_t fmrpool)
28729e39c5baSBill Taylor {
28739e39c5baSBill Taylor 	int			status;
28749e39c5baSBill Taylor 
2875*17a2b317SBill Taylor 	ASSERT(MUTEX_HELD(&fmrpool->fmr_lock));
28769e39c5baSBill Taylor 
2877*17a2b317SBill Taylor 	if (fmrpool->fmr_stat_register == 0)
28789e39c5baSBill Taylor 		return;
2879*17a2b317SBill Taylor 
2880*17a2b317SBill Taylor 	fmrpool->fmr_stat_register = 0;
2881*17a2b317SBill Taylor 	membar_producer();
2882*17a2b317SBill Taylor 
2883*17a2b317SBill Taylor 	if (hermon_fmr_verbose)
2884*17a2b317SBill Taylor 		IBTF_DPRINTF_L2("fmr", "TPT_SYNC");
2885*17a2b317SBill Taylor 	status = hermon_sync_tpt_cmd_post(fmrpool->fmr_state,
2886*17a2b317SBill Taylor 	    HERMON_CMD_NOSLEEP_SPIN);
2887*17a2b317SBill Taylor 	if (status != HERMON_CMD_SUCCESS) {
2888*17a2b317SBill Taylor 		cmn_err(CE_WARN, "fmr SYNC_TPT failed(%x)\n", status);
28899e39c5baSBill Taylor 	}
2890*17a2b317SBill Taylor 	fmrpool->fmr_remap_gen++;
2891*17a2b317SBill Taylor 
2892*17a2b317SBill Taylor 	/* add everything back to the free list */
2893*17a2b317SBill Taylor 	mutex_enter(&fmrpool->dirty_lock);
2894*17a2b317SBill Taylor 	if (fmrpool->fmr_dirty_list) {
2895*17a2b317SBill Taylor 		/* add to free list */
2896*17a2b317SBill Taylor 		*(fmrpool->fmr_free_list_tail) = fmrpool->fmr_dirty_list;
2897*17a2b317SBill Taylor 		fmrpool->fmr_dirty_list = NULL;
2898*17a2b317SBill Taylor 		fmrpool->fmr_free_list_tail = fmrpool->fmr_dirty_list_tail;
2899*17a2b317SBill Taylor 
2900*17a2b317SBill Taylor 		/* reset list */
2901*17a2b317SBill Taylor 		fmrpool->fmr_dirty_list_tail = &fmrpool->fmr_dirty_list;
2902*17a2b317SBill Taylor 		fmrpool->fmr_free_len += fmrpool->fmr_dirty_len;
2903*17a2b317SBill Taylor 		fmrpool->fmr_dirty_len = 0;
2904*17a2b317SBill Taylor 	}
2905*17a2b317SBill Taylor 	mutex_exit(&fmrpool->dirty_lock);
2906*17a2b317SBill Taylor 
2907*17a2b317SBill Taylor 	mutex_enter(&fmrpool->remap_lock);
2908*17a2b317SBill Taylor 	if (fmrpool->fmr_remap_list) {
2909*17a2b317SBill Taylor 		/* add to free list */
2910*17a2b317SBill Taylor 		*(fmrpool->fmr_free_list_tail) = fmrpool->fmr_remap_list;
2911*17a2b317SBill Taylor 		fmrpool->fmr_remap_list = NULL;
2912*17a2b317SBill Taylor 		fmrpool->fmr_free_list_tail = fmrpool->fmr_remap_list_tail;
2913*17a2b317SBill Taylor 
2914*17a2b317SBill Taylor 		/* reset list */
2915*17a2b317SBill Taylor 		fmrpool->fmr_remap_list_tail = &fmrpool->fmr_remap_list;
2916*17a2b317SBill Taylor 		fmrpool->fmr_free_len += fmrpool->fmr_remap_len;
2917*17a2b317SBill Taylor 		fmrpool->fmr_remap_len = 0;
2918*17a2b317SBill Taylor 	}
2919*17a2b317SBill Taylor 	mutex_exit(&fmrpool->remap_lock);
29209e39c5baSBill Taylor 
29219e39c5baSBill Taylor 	if (fmrpool->fmr_flush_function != NULL) {
29229e39c5baSBill Taylor 		(void) fmrpool->fmr_flush_function(
29239e39c5baSBill Taylor 		    (ibc_fmr_pool_hdl_t)fmrpool,
29249e39c5baSBill Taylor 		    fmrpool->fmr_flush_arg);
29259e39c5baSBill Taylor 	}
29269e39c5baSBill Taylor }
2927