xref: /onnv-gate/usr/src/uts/common/io/ib/adapters/hermon/hermon_misc.c (revision 12965:b65a8427f8fe)
19517SBill.Taylor@Sun.COM /*
29517SBill.Taylor@Sun.COM  * CDDL HEADER START
39517SBill.Taylor@Sun.COM  *
49517SBill.Taylor@Sun.COM  * The contents of this file are subject to the terms of the
59517SBill.Taylor@Sun.COM  * Common Development and Distribution License (the "License").
69517SBill.Taylor@Sun.COM  * You may not use this file except in compliance with the License.
79517SBill.Taylor@Sun.COM  *
89517SBill.Taylor@Sun.COM  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
99517SBill.Taylor@Sun.COM  * or http://www.opensolaris.org/os/licensing.
109517SBill.Taylor@Sun.COM  * See the License for the specific language governing permissions
119517SBill.Taylor@Sun.COM  * and limitations under the License.
129517SBill.Taylor@Sun.COM  *
139517SBill.Taylor@Sun.COM  * When distributing Covered Code, include this CDDL HEADER in each
149517SBill.Taylor@Sun.COM  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
159517SBill.Taylor@Sun.COM  * If applicable, add the following below this CDDL HEADER, with the
169517SBill.Taylor@Sun.COM  * fields enclosed by brackets "[]" replaced with your own identifying
179517SBill.Taylor@Sun.COM  * information: Portions Copyright [yyyy] [name of copyright owner]
189517SBill.Taylor@Sun.COM  *
199517SBill.Taylor@Sun.COM  * CDDL HEADER END
209517SBill.Taylor@Sun.COM  */
219517SBill.Taylor@Sun.COM 
229517SBill.Taylor@Sun.COM /*
23*12965SWilliam.Taylor@Oracle.COM  * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
249517SBill.Taylor@Sun.COM  */
259517SBill.Taylor@Sun.COM 
269517SBill.Taylor@Sun.COM /*
279517SBill.Taylor@Sun.COM  * hermon_misc.c
289517SBill.Taylor@Sun.COM  *    Hermon Miscellaneous routines - Address Handle, Multicast, Protection
299517SBill.Taylor@Sun.COM  *    Domain, and port-related operations
309517SBill.Taylor@Sun.COM  *
319517SBill.Taylor@Sun.COM  *    Implements all the routines necessary for allocating, freeing, querying
329517SBill.Taylor@Sun.COM  *    and modifying Address Handles and Protection Domains.  Also implements
339517SBill.Taylor@Sun.COM  *    all the routines necessary for adding and removing Queue Pairs to/from
349517SBill.Taylor@Sun.COM  *    Multicast Groups.  Lastly, it implements the routines necessary for
359517SBill.Taylor@Sun.COM  *    port-related query and modify operations.
369517SBill.Taylor@Sun.COM  */
379517SBill.Taylor@Sun.COM 
389517SBill.Taylor@Sun.COM #include <sys/types.h>
399517SBill.Taylor@Sun.COM #include <sys/conf.h>
409517SBill.Taylor@Sun.COM #include <sys/ddi.h>
419517SBill.Taylor@Sun.COM #include <sys/sunddi.h>
429517SBill.Taylor@Sun.COM #include <sys/modctl.h>
439517SBill.Taylor@Sun.COM #include <sys/bitmap.h>
449517SBill.Taylor@Sun.COM #include <sys/sysmacros.h>
459517SBill.Taylor@Sun.COM 
469517SBill.Taylor@Sun.COM #include <sys/ib/adapters/hermon/hermon.h>
479517SBill.Taylor@Sun.COM 
4811972SBill.Taylor@Sun.COM extern int hermon_rdma_debug;
49*12965SWilliam.Taylor@Oracle.COM int hermon_fmr_verbose = 0;
509517SBill.Taylor@Sun.COM 
519517SBill.Taylor@Sun.COM static int hermon_mcg_qplist_add(hermon_state_t *state, hermon_mcghdl_t mcg,
529517SBill.Taylor@Sun.COM     hermon_hw_mcg_qp_list_t *mcg_qplist, hermon_qphdl_t qp, uint_t *qp_found);
539517SBill.Taylor@Sun.COM static int hermon_mcg_qplist_remove(hermon_mcghdl_t mcg,
549517SBill.Taylor@Sun.COM     hermon_hw_mcg_qp_list_t *mcg_qplist, hermon_qphdl_t qp);
559517SBill.Taylor@Sun.COM static void hermon_qp_mcg_refcnt_inc(hermon_qphdl_t qp);
569517SBill.Taylor@Sun.COM static void hermon_qp_mcg_refcnt_dec(hermon_qphdl_t qp);
579517SBill.Taylor@Sun.COM static uint_t hermon_mcg_walk_mgid_hash(hermon_state_t *state,
589517SBill.Taylor@Sun.COM     uint64_t start_indx, ib_gid_t mgid, uint_t *prev_indx);
599517SBill.Taylor@Sun.COM static void hermon_mcg_setup_new_hdr(hermon_mcghdl_t mcg,
609517SBill.Taylor@Sun.COM     hermon_hw_mcg_t *mcg_hdr, ib_gid_t mgid, hermon_rsrc_t *mcg_rsrc);
619517SBill.Taylor@Sun.COM static int hermon_mcg_hash_list_remove(hermon_state_t *state, uint_t curr_indx,
629517SBill.Taylor@Sun.COM     uint_t prev_indx, hermon_hw_mcg_t *mcg_entry);
639517SBill.Taylor@Sun.COM static int hermon_mcg_entry_invalidate(hermon_state_t *state,
649517SBill.Taylor@Sun.COM     hermon_hw_mcg_t *mcg_entry, uint_t indx);
659517SBill.Taylor@Sun.COM static int hermon_mgid_is_valid(ib_gid_t gid);
669517SBill.Taylor@Sun.COM static int hermon_mlid_is_valid(ib_lid_t lid);
67*12965SWilliam.Taylor@Oracle.COM static void hermon_fmr_cleanup(hermon_fmrhdl_t pool);
689517SBill.Taylor@Sun.COM 
699517SBill.Taylor@Sun.COM 
709517SBill.Taylor@Sun.COM #define	HERMON_MAX_DBR_PAGES_PER_USER	64
719517SBill.Taylor@Sun.COM #define	HERMON_DBR_KEY(index, page) \
729517SBill.Taylor@Sun.COM 	(((uint64_t)index) * HERMON_MAX_DBR_PAGES_PER_USER + (page))
739517SBill.Taylor@Sun.COM 
749517SBill.Taylor@Sun.COM static hermon_udbr_page_t *
hermon_dbr_new_user_page(hermon_state_t * state,uint_t index,uint_t page)759517SBill.Taylor@Sun.COM hermon_dbr_new_user_page(hermon_state_t *state, uint_t index,
769517SBill.Taylor@Sun.COM     uint_t page)
779517SBill.Taylor@Sun.COM {
789517SBill.Taylor@Sun.COM 	hermon_udbr_page_t *pagep;
799517SBill.Taylor@Sun.COM 	ddi_dma_attr_t dma_attr;
809517SBill.Taylor@Sun.COM 	uint_t cookiecnt;
8111586SBill.Taylor@Sun.COM 	int status;
829517SBill.Taylor@Sun.COM 	hermon_umap_db_entry_t *umapdb;
83*12965SWilliam.Taylor@Oracle.COM 	ulong_t pagesize = PAGESIZE;
849517SBill.Taylor@Sun.COM 
859517SBill.Taylor@Sun.COM 	pagep = kmem_alloc(sizeof (*pagep), KM_SLEEP);
869517SBill.Taylor@Sun.COM 	pagep->upg_index = page;
87*12965SWilliam.Taylor@Oracle.COM 	pagep->upg_nfree = pagesize / sizeof (hermon_dbr_t);
8811586SBill.Taylor@Sun.COM 
8911586SBill.Taylor@Sun.COM 	/* Allocate 1 bit per dbr for free/alloc management (0 => "free") */
90*12965SWilliam.Taylor@Oracle.COM 	pagep->upg_free = kmem_zalloc(pagesize / sizeof (hermon_dbr_t) / 8,
9111586SBill.Taylor@Sun.COM 	    KM_SLEEP);
92*12965SWilliam.Taylor@Oracle.COM 	pagep->upg_kvaddr = ddi_umem_alloc(pagesize, DDI_UMEM_SLEEP,
939517SBill.Taylor@Sun.COM 	    &pagep->upg_umemcookie); /* not HERMON_PAGESIZE here */
949517SBill.Taylor@Sun.COM 
959517SBill.Taylor@Sun.COM 	pagep->upg_buf = ddi_umem_iosetup(pagep->upg_umemcookie, 0,
96*12965SWilliam.Taylor@Oracle.COM 	    pagesize, B_WRITE, 0, 0, NULL, DDI_UMEM_SLEEP);
979517SBill.Taylor@Sun.COM 
989517SBill.Taylor@Sun.COM 	hermon_dma_attr_init(state, &dma_attr);
9911972SBill.Taylor@Sun.COM #ifdef	__sparc
10011972SBill.Taylor@Sun.COM 	if (state->hs_cfg_profile->cp_iommu_bypass == HERMON_BINDMEM_BYPASS)
10111972SBill.Taylor@Sun.COM 		dma_attr.dma_attr_flags = DDI_DMA_FORCE_PHYSICAL;
10211972SBill.Taylor@Sun.COM #endif
1039517SBill.Taylor@Sun.COM 	status = ddi_dma_alloc_handle(state->hs_dip, &dma_attr,
1049517SBill.Taylor@Sun.COM 	    DDI_DMA_SLEEP, NULL, &pagep->upg_dmahdl);
1059517SBill.Taylor@Sun.COM 	if (status != DDI_SUCCESS) {
1069517SBill.Taylor@Sun.COM 		IBTF_DPRINTF_L2("hermon", "hermon_new_user_page: "
1079517SBill.Taylor@Sun.COM 		    "ddi_dma_buf_bind_handle failed: %d", status);
1089517SBill.Taylor@Sun.COM 		return (NULL);
1099517SBill.Taylor@Sun.COM 	}
1109517SBill.Taylor@Sun.COM 	status = ddi_dma_buf_bind_handle(pagep->upg_dmahdl,
1119517SBill.Taylor@Sun.COM 	    pagep->upg_buf, DDI_DMA_RDWR | DDI_DMA_CONSISTENT,
1129517SBill.Taylor@Sun.COM 	    DDI_DMA_SLEEP, NULL, &pagep->upg_dmacookie, &cookiecnt);
1139517SBill.Taylor@Sun.COM 	if (status != DDI_SUCCESS) {
1149517SBill.Taylor@Sun.COM 		IBTF_DPRINTF_L2("hermon", "hermon_dbr_new_user_page: "
1159517SBill.Taylor@Sun.COM 		    "ddi_dma_buf_bind_handle failed: %d", status);
1169517SBill.Taylor@Sun.COM 		ddi_dma_free_handle(&pagep->upg_dmahdl);
1179517SBill.Taylor@Sun.COM 		return (NULL);
1189517SBill.Taylor@Sun.COM 	}
1199517SBill.Taylor@Sun.COM 	ASSERT(cookiecnt == 1);
1209517SBill.Taylor@Sun.COM 
1219517SBill.Taylor@Sun.COM 	/* create db entry for mmap */
1229517SBill.Taylor@Sun.COM 	umapdb = hermon_umap_db_alloc(state->hs_instance,
1239517SBill.Taylor@Sun.COM 	    HERMON_DBR_KEY(index, page), MLNX_UMAP_DBRMEM_RSRC,
1249517SBill.Taylor@Sun.COM 	    (uint64_t)(uintptr_t)pagep);
1259517SBill.Taylor@Sun.COM 	hermon_umap_db_add(umapdb);
1269517SBill.Taylor@Sun.COM 	return (pagep);
1279517SBill.Taylor@Sun.COM }
1289517SBill.Taylor@Sun.COM 
1299517SBill.Taylor@Sun.COM 
1309517SBill.Taylor@Sun.COM /*ARGSUSED*/
1319517SBill.Taylor@Sun.COM 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)1329517SBill.Taylor@Sun.COM hermon_user_dbr_alloc(hermon_state_t *state, uint_t index,
1339517SBill.Taylor@Sun.COM     ddi_acc_handle_t *acchdl, hermon_dbr_t **vdbr, uint64_t *pdbr,
1349517SBill.Taylor@Sun.COM     uint64_t *mapoffset)
1359517SBill.Taylor@Sun.COM {
1369517SBill.Taylor@Sun.COM 	hermon_user_dbr_t *udbr;
1379517SBill.Taylor@Sun.COM 	hermon_udbr_page_t *pagep;
1389517SBill.Taylor@Sun.COM 	uint_t next_page;
13911586SBill.Taylor@Sun.COM 	int dbr_index;
14011586SBill.Taylor@Sun.COM 	int i1, i2, i3, last;
14111586SBill.Taylor@Sun.COM 	uint64_t u64, mask;
1429517SBill.Taylor@Sun.COM 
1439517SBill.Taylor@Sun.COM 	mutex_enter(&state->hs_dbr_lock);
1449517SBill.Taylor@Sun.COM 	for (udbr = state->hs_user_dbr; udbr != NULL; udbr = udbr->udbr_link)
1459517SBill.Taylor@Sun.COM 		if (udbr->udbr_index == index)
1469517SBill.Taylor@Sun.COM 			break;
1479517SBill.Taylor@Sun.COM 	if (udbr == NULL) {
1489517SBill.Taylor@Sun.COM 		udbr = kmem_alloc(sizeof (*udbr), KM_SLEEP);
1499517SBill.Taylor@Sun.COM 		udbr->udbr_link = state->hs_user_dbr;
1509517SBill.Taylor@Sun.COM 		state->hs_user_dbr = udbr;
1519517SBill.Taylor@Sun.COM 		udbr->udbr_index = index;
1529517SBill.Taylor@Sun.COM 		udbr->udbr_pagep = NULL;
1539517SBill.Taylor@Sun.COM 	}
1549517SBill.Taylor@Sun.COM 	pagep = udbr->udbr_pagep;
1559517SBill.Taylor@Sun.COM 	next_page = (pagep == NULL) ? 0 : (pagep->upg_index + 1);
1569517SBill.Taylor@Sun.COM 	while (pagep != NULL)
1579517SBill.Taylor@Sun.COM 		if (pagep->upg_nfree > 0)
1589517SBill.Taylor@Sun.COM 			break;
1599517SBill.Taylor@Sun.COM 		else
1609517SBill.Taylor@Sun.COM 			pagep = pagep->upg_link;
1619517SBill.Taylor@Sun.COM 	if (pagep == NULL) {
1629517SBill.Taylor@Sun.COM 		pagep = hermon_dbr_new_user_page(state, index, next_page);
1639517SBill.Taylor@Sun.COM 		if (pagep == NULL) {
1649517SBill.Taylor@Sun.COM 			mutex_exit(&state->hs_dbr_lock);
1659517SBill.Taylor@Sun.COM 			return (DDI_FAILURE);
1669517SBill.Taylor@Sun.COM 		}
1679517SBill.Taylor@Sun.COM 		pagep->upg_link = udbr->udbr_pagep;
1689517SBill.Taylor@Sun.COM 		udbr->udbr_pagep = pagep;
1699517SBill.Taylor@Sun.COM 	}
17011586SBill.Taylor@Sun.COM 
17111586SBill.Taylor@Sun.COM 	/* Since nfree > 0, we're assured the loops below will succeed */
17211586SBill.Taylor@Sun.COM 
17311586SBill.Taylor@Sun.COM 	/* First, find a 64-bit (not ~0) that has a free dbr */
17411586SBill.Taylor@Sun.COM 	last = PAGESIZE / sizeof (uint64_t) / 64;
17511586SBill.Taylor@Sun.COM 	mask = ~0ull;
17611586SBill.Taylor@Sun.COM 	for (i1 = 0; i1 < last; i1++)
17711586SBill.Taylor@Sun.COM 		if ((pagep->upg_free[i1] & mask) != mask)
17811586SBill.Taylor@Sun.COM 			break;
17911586SBill.Taylor@Sun.COM 	u64 = pagep->upg_free[i1];
18011586SBill.Taylor@Sun.COM 
18111586SBill.Taylor@Sun.COM 	/* Second, find a byte (not 0xff) that has a free dbr */
18211586SBill.Taylor@Sun.COM 	last = sizeof (uint64_t) / sizeof (uint8_t);
18311586SBill.Taylor@Sun.COM 	for (i2 = 0, mask = 0xff; i2 < last; i2++, mask <<= 8)
18411586SBill.Taylor@Sun.COM 		if ((u64 & mask) != mask)
18511586SBill.Taylor@Sun.COM 			break;
18611586SBill.Taylor@Sun.COM 
18711586SBill.Taylor@Sun.COM 	/* Third, find a bit that is free (0) */
18811586SBill.Taylor@Sun.COM 	for (i3 = 0; i3 < sizeof (uint64_t) / sizeof (uint8_t); i3++)
18911586SBill.Taylor@Sun.COM 		if ((u64 & (1ul << (i3 + 8 * i2))) == 0)
19011586SBill.Taylor@Sun.COM 			break;
19111586SBill.Taylor@Sun.COM 
19211586SBill.Taylor@Sun.COM 	/* Mark it as allocated */
19311586SBill.Taylor@Sun.COM 	pagep->upg_free[i1] |= (1ul << (i3 + 8 * i2));
19411586SBill.Taylor@Sun.COM 
19511586SBill.Taylor@Sun.COM 	dbr_index = ((i1 * sizeof (uint64_t)) + i2) * sizeof (uint64_t) + i3;
1969517SBill.Taylor@Sun.COM 	pagep->upg_nfree--;
19711586SBill.Taylor@Sun.COM 	((uint64_t *)(void *)pagep->upg_kvaddr)[dbr_index] = 0;	/* clear dbr */
1989517SBill.Taylor@Sun.COM 	*mapoffset = ((HERMON_DBR_KEY(index, pagep->upg_index) <<
1999517SBill.Taylor@Sun.COM 	    MLNX_UMAP_RSRC_TYPE_SHIFT) | MLNX_UMAP_DBRMEM_RSRC) << PAGESHIFT;
20011586SBill.Taylor@Sun.COM 	*vdbr = (hermon_dbr_t *)((uint64_t *)(void *)pagep->upg_kvaddr +
20111586SBill.Taylor@Sun.COM 	    dbr_index);
20211586SBill.Taylor@Sun.COM 	*pdbr = pagep->upg_dmacookie.dmac_laddress + dbr_index *
20311586SBill.Taylor@Sun.COM 	    sizeof (uint64_t);
2049517SBill.Taylor@Sun.COM 
2059517SBill.Taylor@Sun.COM 	mutex_exit(&state->hs_dbr_lock);
2069517SBill.Taylor@Sun.COM 	return (DDI_SUCCESS);
2079517SBill.Taylor@Sun.COM }
2089517SBill.Taylor@Sun.COM 
2099517SBill.Taylor@Sun.COM static void
hermon_user_dbr_free(hermon_state_t * state,uint_t index,hermon_dbr_t * record)2109517SBill.Taylor@Sun.COM hermon_user_dbr_free(hermon_state_t *state, uint_t index, hermon_dbr_t *record)
2119517SBill.Taylor@Sun.COM {
2129517SBill.Taylor@Sun.COM 	hermon_user_dbr_t	*udbr;
2139517SBill.Taylor@Sun.COM 	hermon_udbr_page_t	*pagep;
2149517SBill.Taylor@Sun.COM 	caddr_t			kvaddr;
2159517SBill.Taylor@Sun.COM 	uint_t			dbr_index;
2169517SBill.Taylor@Sun.COM 	uint_t			max_free = PAGESIZE / sizeof (hermon_dbr_t);
21711586SBill.Taylor@Sun.COM 	int			i1, i2;
2189517SBill.Taylor@Sun.COM 
2199517SBill.Taylor@Sun.COM 	dbr_index = (uintptr_t)record & PAGEOFFSET; /* offset (not yet index) */
2209517SBill.Taylor@Sun.COM 	kvaddr = (caddr_t)record - dbr_index;
2219517SBill.Taylor@Sun.COM 	dbr_index /= sizeof (hermon_dbr_t); /* now it's the index */
2229517SBill.Taylor@Sun.COM 
2239517SBill.Taylor@Sun.COM 	mutex_enter(&state->hs_dbr_lock);
2249517SBill.Taylor@Sun.COM 	for (udbr = state->hs_user_dbr; udbr != NULL; udbr = udbr->udbr_link)
2259517SBill.Taylor@Sun.COM 		if (udbr->udbr_index == index)
2269517SBill.Taylor@Sun.COM 			break;
2279517SBill.Taylor@Sun.COM 	if (udbr == NULL) {
2289517SBill.Taylor@Sun.COM 		IBTF_DPRINTF_L2("hermon", "free user dbr: udbr struct not "
2299517SBill.Taylor@Sun.COM 		    "found for index %x", index);
2309517SBill.Taylor@Sun.COM 		mutex_exit(&state->hs_dbr_lock);
2319517SBill.Taylor@Sun.COM 		return;
2329517SBill.Taylor@Sun.COM 	}
2339517SBill.Taylor@Sun.COM 	for (pagep = udbr->udbr_pagep; pagep != NULL; pagep = pagep->upg_link)
2349517SBill.Taylor@Sun.COM 		if (pagep->upg_kvaddr == kvaddr)
2359517SBill.Taylor@Sun.COM 			break;
2369517SBill.Taylor@Sun.COM 	if (pagep == NULL) {
2379517SBill.Taylor@Sun.COM 		IBTF_DPRINTF_L2("hermon", "free user dbr: pagep struct not"
2389517SBill.Taylor@Sun.COM 		    " found for index %x, kvaddr %p, DBR index %x",
2399517SBill.Taylor@Sun.COM 		    index, kvaddr, dbr_index);
2409517SBill.Taylor@Sun.COM 		mutex_exit(&state->hs_dbr_lock);
2419517SBill.Taylor@Sun.COM 		return;
2429517SBill.Taylor@Sun.COM 	}
2439517SBill.Taylor@Sun.COM 	if (pagep->upg_nfree >= max_free) {
2449517SBill.Taylor@Sun.COM 		IBTF_DPRINTF_L2("hermon", "free user dbr: overflow: "
2459517SBill.Taylor@Sun.COM 		    "UCE index %x, DBR index %x", index, dbr_index);
2469517SBill.Taylor@Sun.COM 		mutex_exit(&state->hs_dbr_lock);
2479517SBill.Taylor@Sun.COM 		return;
2489517SBill.Taylor@Sun.COM 	}
2499517SBill.Taylor@Sun.COM 	ASSERT(dbr_index < max_free);
25011586SBill.Taylor@Sun.COM 	i1 = dbr_index / 64;
25111586SBill.Taylor@Sun.COM 	i2 = dbr_index % 64;
25211586SBill.Taylor@Sun.COM 	ASSERT((pagep->upg_free[i1] & (1ul << i2)) == (1ul << i2));
25311586SBill.Taylor@Sun.COM 	pagep->upg_free[i1] &= ~(1ul << i2);
2549517SBill.Taylor@Sun.COM 	pagep->upg_nfree++;
2559517SBill.Taylor@Sun.COM 	mutex_exit(&state->hs_dbr_lock);
2569517SBill.Taylor@Sun.COM }
2579517SBill.Taylor@Sun.COM 
2589517SBill.Taylor@Sun.COM /*
2599517SBill.Taylor@Sun.COM  * hermon_dbr_page_alloc()
2609517SBill.Taylor@Sun.COM  *	first page allocation - called from attach or open
2619517SBill.Taylor@Sun.COM  *	in this case, we want exactly one page per call, and aligned on a
2629517SBill.Taylor@Sun.COM  *	page - and may need to be mapped to the user for access
2639517SBill.Taylor@Sun.COM  */
2649517SBill.Taylor@Sun.COM int
hermon_dbr_page_alloc(hermon_state_t * state,hermon_dbr_info_t ** dinfo)2659517SBill.Taylor@Sun.COM hermon_dbr_page_alloc(hermon_state_t *state, hermon_dbr_info_t **dinfo)
2669517SBill.Taylor@Sun.COM {
2679517SBill.Taylor@Sun.COM 	int			status;
2689517SBill.Taylor@Sun.COM 	ddi_dma_handle_t	dma_hdl;
2699517SBill.Taylor@Sun.COM 	ddi_acc_handle_t	acc_hdl;
2709517SBill.Taylor@Sun.COM 	ddi_dma_attr_t		dma_attr;
2719517SBill.Taylor@Sun.COM 	ddi_dma_cookie_t	cookie;
2729517SBill.Taylor@Sun.COM 	uint_t			cookie_cnt;
2739517SBill.Taylor@Sun.COM 	int			i;
2749517SBill.Taylor@Sun.COM 	hermon_dbr_info_t 	*info;
27510027SGiri.Adari@Sun.COM 	caddr_t			dmaaddr;
2769517SBill.Taylor@Sun.COM 	uint64_t		dmalen;
277*12965SWilliam.Taylor@Oracle.COM 	ulong_t			pagesize = PAGESIZE;
2789517SBill.Taylor@Sun.COM 
2799517SBill.Taylor@Sun.COM 	info = kmem_zalloc(sizeof (hermon_dbr_info_t), KM_SLEEP);
2809517SBill.Taylor@Sun.COM 
2819517SBill.Taylor@Sun.COM 	/*
2829517SBill.Taylor@Sun.COM 	 * Initialize many of the default DMA attributes.  Then set additional
2839517SBill.Taylor@Sun.COM 	 * alignment restrictions if necessary for the dbr memory, meaning
2849517SBill.Taylor@Sun.COM 	 * page aligned.  Also use the configured value for IOMMU bypass
2859517SBill.Taylor@Sun.COM 	 */
2869517SBill.Taylor@Sun.COM 	hermon_dma_attr_init(state, &dma_attr);
287*12965SWilliam.Taylor@Oracle.COM 	dma_attr.dma_attr_align = pagesize;
2889517SBill.Taylor@Sun.COM 	dma_attr.dma_attr_sgllen = 1;	/* make sure only one cookie */
28911972SBill.Taylor@Sun.COM #ifdef	__sparc
29011972SBill.Taylor@Sun.COM 	if (state->hs_cfg_profile->cp_iommu_bypass == HERMON_BINDMEM_BYPASS)
29111972SBill.Taylor@Sun.COM 		dma_attr.dma_attr_flags = DDI_DMA_FORCE_PHYSICAL;
29211972SBill.Taylor@Sun.COM #endif
2939517SBill.Taylor@Sun.COM 
2949517SBill.Taylor@Sun.COM 	status = ddi_dma_alloc_handle(state->hs_dip, &dma_attr,
2959517SBill.Taylor@Sun.COM 	    DDI_DMA_SLEEP, NULL, &dma_hdl);
2969517SBill.Taylor@Sun.COM 	if (status != DDI_SUCCESS) {
2979517SBill.Taylor@Sun.COM 		kmem_free((void *)info, sizeof (hermon_dbr_info_t));
2989517SBill.Taylor@Sun.COM 		cmn_err(CE_NOTE, "dbr DMA handle alloc failed\n");
2999517SBill.Taylor@Sun.COM 		return (DDI_FAILURE);
3009517SBill.Taylor@Sun.COM 	}
3019517SBill.Taylor@Sun.COM 
302*12965SWilliam.Taylor@Oracle.COM 	status = ddi_dma_mem_alloc(dma_hdl, pagesize,
3039517SBill.Taylor@Sun.COM 	    &state->hs_reg_accattr, DDI_DMA_CONSISTENT, DDI_DMA_SLEEP,
30410027SGiri.Adari@Sun.COM 	    NULL, &dmaaddr, (size_t *)&dmalen, &acc_hdl);
3059517SBill.Taylor@Sun.COM 	if (status != DDI_SUCCESS)	{
3069517SBill.Taylor@Sun.COM 		ddi_dma_free_handle(&dma_hdl);
3079517SBill.Taylor@Sun.COM 		cmn_err(CE_CONT, "dbr DMA mem alloc failed(status %d)", status);
3089517SBill.Taylor@Sun.COM 		kmem_free((void *)info, sizeof (hermon_dbr_info_t));
3099517SBill.Taylor@Sun.COM 		return (DDI_FAILURE);
3109517SBill.Taylor@Sun.COM 	}
3119517SBill.Taylor@Sun.COM 
3129517SBill.Taylor@Sun.COM 	/* this memory won't be IB registered, so do the bind here */
3139517SBill.Taylor@Sun.COM 	status = ddi_dma_addr_bind_handle(dma_hdl, NULL,
31410027SGiri.Adari@Sun.COM 	    dmaaddr, (size_t)dmalen, DDI_DMA_RDWR |
3159517SBill.Taylor@Sun.COM 	    DDI_DMA_CONSISTENT, DDI_DMA_SLEEP, NULL, &cookie, &cookie_cnt);
3169517SBill.Taylor@Sun.COM 	if (status != DDI_SUCCESS) {
3179517SBill.Taylor@Sun.COM 		ddi_dma_mem_free(&acc_hdl);
3189517SBill.Taylor@Sun.COM 		ddi_dma_free_handle(&dma_hdl);
3199517SBill.Taylor@Sun.COM 		kmem_free((void *)info, sizeof (hermon_dbr_info_t));
3209517SBill.Taylor@Sun.COM 		cmn_err(CE_CONT, "dbr DMA bind handle failed (status %d)",
3219517SBill.Taylor@Sun.COM 		    status);
3229517SBill.Taylor@Sun.COM 		return (DDI_FAILURE);
3239517SBill.Taylor@Sun.COM 	}
3249517SBill.Taylor@Sun.COM 	*dinfo = info;		/* Pass back the pointer */
3259517SBill.Taylor@Sun.COM 
3269517SBill.Taylor@Sun.COM 	/* init the info structure with returned info */
3279517SBill.Taylor@Sun.COM 	info->dbr_dmahdl = dma_hdl;
3289517SBill.Taylor@Sun.COM 	info->dbr_acchdl = acc_hdl;
32910027SGiri.Adari@Sun.COM 	info->dbr_page   = (hermon_dbr_t *)(void *)dmaaddr;
33010027SGiri.Adari@Sun.COM 	info->dbr_link = NULL;
3319517SBill.Taylor@Sun.COM 	/* extract the phys addr from the cookie */
3329517SBill.Taylor@Sun.COM 	info->dbr_paddr = cookie.dmac_laddress;
33310027SGiri.Adari@Sun.COM 	info->dbr_firstfree = 0;
33410027SGiri.Adari@Sun.COM 	info->dbr_nfree = HERMON_NUM_DBR_PER_PAGE;
3359517SBill.Taylor@Sun.COM 	/* link all DBrs onto the free list */
3369517SBill.Taylor@Sun.COM 	for (i = 0; i < HERMON_NUM_DBR_PER_PAGE; i++) {
33710027SGiri.Adari@Sun.COM 		info->dbr_page[i] = i + 1;
3389517SBill.Taylor@Sun.COM 	}
3399517SBill.Taylor@Sun.COM 
3409517SBill.Taylor@Sun.COM 	return (DDI_SUCCESS);
3419517SBill.Taylor@Sun.COM }
3429517SBill.Taylor@Sun.COM 
3439517SBill.Taylor@Sun.COM 
3449517SBill.Taylor@Sun.COM /*
3459517SBill.Taylor@Sun.COM  * hermon_dbr_alloc()
3469517SBill.Taylor@Sun.COM  *	DBr record allocation - called from alloc cq/qp/srq
3479517SBill.Taylor@Sun.COM  *	will check for available dbrs in current
3489517SBill.Taylor@Sun.COM  *	page - if needed it will allocate another and link them
3499517SBill.Taylor@Sun.COM  */
3509517SBill.Taylor@Sun.COM 
3519517SBill.Taylor@Sun.COM 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)3529517SBill.Taylor@Sun.COM hermon_dbr_alloc(hermon_state_t *state, uint_t index, ddi_acc_handle_t *acchdl,
3539517SBill.Taylor@Sun.COM     hermon_dbr_t **vdbr, uint64_t *pdbr, uint64_t *mapoffset)
3549517SBill.Taylor@Sun.COM {
3559517SBill.Taylor@Sun.COM 	hermon_dbr_t		*record = NULL;
35610027SGiri.Adari@Sun.COM 	hermon_dbr_info_t	*info = NULL;
35710027SGiri.Adari@Sun.COM 	uint32_t		idx;
3589517SBill.Taylor@Sun.COM 	int			status;
3599517SBill.Taylor@Sun.COM 
3609517SBill.Taylor@Sun.COM 	if (index != state->hs_kernel_uar_index)
3619517SBill.Taylor@Sun.COM 		return (hermon_user_dbr_alloc(state, index, acchdl, vdbr, pdbr,
3629517SBill.Taylor@Sun.COM 		    mapoffset));
3639517SBill.Taylor@Sun.COM 
3649517SBill.Taylor@Sun.COM 	mutex_enter(&state->hs_dbr_lock);
36510027SGiri.Adari@Sun.COM 	for (info = state->hs_kern_dbr; info != NULL; info = info->dbr_link)
36610027SGiri.Adari@Sun.COM 		if (info->dbr_nfree != 0)
36710027SGiri.Adari@Sun.COM 			break;		/* found a page w/ one available */
36810027SGiri.Adari@Sun.COM 
36910027SGiri.Adari@Sun.COM 	if (info == NULL) {	/* did NOT find a page with one available */
37010027SGiri.Adari@Sun.COM 		status = hermon_dbr_page_alloc(state, &info);
3719517SBill.Taylor@Sun.COM 		if (status != DDI_SUCCESS) {
3729517SBill.Taylor@Sun.COM 			/* do error handling */
3739517SBill.Taylor@Sun.COM 			mutex_exit(&state->hs_dbr_lock);
3749517SBill.Taylor@Sun.COM 			return (DDI_FAILURE);
3759517SBill.Taylor@Sun.COM 		}
3769517SBill.Taylor@Sun.COM 		/* got a new page, so link it in. */
37710027SGiri.Adari@Sun.COM 		info->dbr_link = state->hs_kern_dbr;
37810027SGiri.Adari@Sun.COM 		state->hs_kern_dbr = info;
3799517SBill.Taylor@Sun.COM 	}
38010027SGiri.Adari@Sun.COM 	idx = info->dbr_firstfree;
38110027SGiri.Adari@Sun.COM 	record = info->dbr_page + idx;
38210027SGiri.Adari@Sun.COM 	info->dbr_firstfree = *record;
38310027SGiri.Adari@Sun.COM 	info->dbr_nfree--;
3849517SBill.Taylor@Sun.COM 	*record = 0;
3859517SBill.Taylor@Sun.COM 
38610027SGiri.Adari@Sun.COM 	*acchdl = info->dbr_acchdl;
3879517SBill.Taylor@Sun.COM 	*vdbr = record;
38810027SGiri.Adari@Sun.COM 	*pdbr = info->dbr_paddr + idx * sizeof (hermon_dbr_t);
3899517SBill.Taylor@Sun.COM 	mutex_exit(&state->hs_dbr_lock);
3909517SBill.Taylor@Sun.COM 	return (DDI_SUCCESS);
3919517SBill.Taylor@Sun.COM }
3929517SBill.Taylor@Sun.COM 
3939517SBill.Taylor@Sun.COM /*
3949517SBill.Taylor@Sun.COM  * hermon_dbr_free()
3959517SBill.Taylor@Sun.COM  *	DBr record deallocation - called from free cq/qp
3969517SBill.Taylor@Sun.COM  *	will update the counter in the header, and invalidate
3979517SBill.Taylor@Sun.COM  *	the dbr, but will NEVER free pages of dbrs - small
3989517SBill.Taylor@Sun.COM  *	price to pay, but userland access never will anyway
3999517SBill.Taylor@Sun.COM  */
4009517SBill.Taylor@Sun.COM void
hermon_dbr_free(hermon_state_t * state,uint_t indx,hermon_dbr_t * record)4019517SBill.Taylor@Sun.COM hermon_dbr_free(hermon_state_t *state, uint_t indx, hermon_dbr_t *record)
4029517SBill.Taylor@Sun.COM {
40310027SGiri.Adari@Sun.COM 	hermon_dbr_t		*page;
40410027SGiri.Adari@Sun.COM 	hermon_dbr_info_t	*info;
4059517SBill.Taylor@Sun.COM 
4069517SBill.Taylor@Sun.COM 	if (indx != state->hs_kernel_uar_index) {
4079517SBill.Taylor@Sun.COM 		hermon_user_dbr_free(state, indx, record);
4089517SBill.Taylor@Sun.COM 		return;
4099517SBill.Taylor@Sun.COM 	}
41010027SGiri.Adari@Sun.COM 	page = (hermon_dbr_t *)(uintptr_t)((uintptr_t)record & PAGEMASK);
4119517SBill.Taylor@Sun.COM 	mutex_enter(&state->hs_dbr_lock);
41210027SGiri.Adari@Sun.COM 	for (info = state->hs_kern_dbr; info != NULL; info = info->dbr_link)
41310027SGiri.Adari@Sun.COM 		if (info->dbr_page == page)
41410027SGiri.Adari@Sun.COM 			break;
41510027SGiri.Adari@Sun.COM 	ASSERT(info != NULL);
41610027SGiri.Adari@Sun.COM 	*record = info->dbr_firstfree;
41710027SGiri.Adari@Sun.COM 	info->dbr_firstfree = record - info->dbr_page;
41810027SGiri.Adari@Sun.COM 	info->dbr_nfree++;
4199517SBill.Taylor@Sun.COM 	mutex_exit(&state->hs_dbr_lock);
4209517SBill.Taylor@Sun.COM }
4219517SBill.Taylor@Sun.COM 
4229517SBill.Taylor@Sun.COM /*
4239517SBill.Taylor@Sun.COM  * hermon_dbr_kern_free()
4249517SBill.Taylor@Sun.COM  *    Context: Can be called only from detach context.
4259517SBill.Taylor@Sun.COM  *
4269517SBill.Taylor@Sun.COM  *	Free all kernel dbr pages.  This includes the freeing of all the dma
4279517SBill.Taylor@Sun.COM  *	resources acquired during the allocation of the pages.
4289517SBill.Taylor@Sun.COM  *
4299517SBill.Taylor@Sun.COM  *	Also, free all the user dbr pages.
4309517SBill.Taylor@Sun.COM  */
4319517SBill.Taylor@Sun.COM void
hermon_dbr_kern_free(hermon_state_t * state)4329517SBill.Taylor@Sun.COM hermon_dbr_kern_free(hermon_state_t *state)
4339517SBill.Taylor@Sun.COM {
43410027SGiri.Adari@Sun.COM 	hermon_dbr_info_t	*info, *link;
4359517SBill.Taylor@Sun.COM 	hermon_user_dbr_t	*udbr, *next;
4369517SBill.Taylor@Sun.COM 	hermon_udbr_page_t	*pagep, *nextp;
4379517SBill.Taylor@Sun.COM 	hermon_umap_db_entry_t	*umapdb;
4389517SBill.Taylor@Sun.COM 	int			instance, status;
4399517SBill.Taylor@Sun.COM 	uint64_t		value;
4409517SBill.Taylor@Sun.COM 	extern			hermon_umap_db_t hermon_userland_rsrc_db;
4419517SBill.Taylor@Sun.COM 
4429517SBill.Taylor@Sun.COM 	mutex_enter(&state->hs_dbr_lock);
44310027SGiri.Adari@Sun.COM 	for (info = state->hs_kern_dbr; info != NULL; info = link) {
44410027SGiri.Adari@Sun.COM 		(void) ddi_dma_unbind_handle(info->dbr_dmahdl);
44510027SGiri.Adari@Sun.COM 		ddi_dma_mem_free(&info->dbr_acchdl);	/* free page */
44610027SGiri.Adari@Sun.COM 		ddi_dma_free_handle(&info->dbr_dmahdl);
44710027SGiri.Adari@Sun.COM 		link = info->dbr_link;
44810027SGiri.Adari@Sun.COM 		kmem_free(info, sizeof (hermon_dbr_info_t));
4499517SBill.Taylor@Sun.COM 	}
4509517SBill.Taylor@Sun.COM 
4519517SBill.Taylor@Sun.COM 	udbr = state->hs_user_dbr;
4529517SBill.Taylor@Sun.COM 	instance = state->hs_instance;
4539517SBill.Taylor@Sun.COM 	mutex_enter(&hermon_userland_rsrc_db.hdl_umapdb_lock);
4549517SBill.Taylor@Sun.COM 	while (udbr != NULL) {
4559517SBill.Taylor@Sun.COM 		pagep = udbr->udbr_pagep;
4569517SBill.Taylor@Sun.COM 		while (pagep != NULL) {
4579517SBill.Taylor@Sun.COM 			/* probably need to remove "db" */
4589517SBill.Taylor@Sun.COM 			(void) ddi_dma_unbind_handle(pagep->upg_dmahdl);
4599517SBill.Taylor@Sun.COM 			ddi_dma_free_handle(&pagep->upg_dmahdl);
4609517SBill.Taylor@Sun.COM 			freerbuf(pagep->upg_buf);
4619517SBill.Taylor@Sun.COM 			ddi_umem_free(pagep->upg_umemcookie);
4629517SBill.Taylor@Sun.COM 			status = hermon_umap_db_find_nolock(instance,
4639517SBill.Taylor@Sun.COM 			    HERMON_DBR_KEY(udbr->udbr_index,
4649517SBill.Taylor@Sun.COM 			    pagep->upg_index), MLNX_UMAP_DBRMEM_RSRC,
4659517SBill.Taylor@Sun.COM 			    &value, HERMON_UMAP_DB_REMOVE, &umapdb);
4669517SBill.Taylor@Sun.COM 			if (status == DDI_SUCCESS)
4679517SBill.Taylor@Sun.COM 				hermon_umap_db_free(umapdb);
46811586SBill.Taylor@Sun.COM 			kmem_free(pagep->upg_free,
46911586SBill.Taylor@Sun.COM 			    PAGESIZE / sizeof (hermon_dbr_t) / 8);
4709517SBill.Taylor@Sun.COM 			nextp = pagep->upg_link;
4719517SBill.Taylor@Sun.COM 			kmem_free(pagep, sizeof (*pagep));
4729517SBill.Taylor@Sun.COM 			pagep = nextp;
4739517SBill.Taylor@Sun.COM 		}
4749517SBill.Taylor@Sun.COM 		next = udbr->udbr_link;
4759517SBill.Taylor@Sun.COM 		kmem_free(udbr, sizeof (*udbr));
4769517SBill.Taylor@Sun.COM 		udbr = next;
4779517SBill.Taylor@Sun.COM 	}
4789517SBill.Taylor@Sun.COM 	mutex_exit(&hermon_userland_rsrc_db.hdl_umapdb_lock);
4799517SBill.Taylor@Sun.COM 	mutex_exit(&state->hs_dbr_lock);
4809517SBill.Taylor@Sun.COM }
4819517SBill.Taylor@Sun.COM 
4829517SBill.Taylor@Sun.COM /*
4839517SBill.Taylor@Sun.COM  * hermon_ah_alloc()
4849517SBill.Taylor@Sun.COM  *    Context: Can be called only from user or kernel context.
4859517SBill.Taylor@Sun.COM  */
4869517SBill.Taylor@Sun.COM 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)4879517SBill.Taylor@Sun.COM hermon_ah_alloc(hermon_state_t *state, hermon_pdhdl_t pd,
4889517SBill.Taylor@Sun.COM     ibt_adds_vect_t *attr_p, hermon_ahhdl_t *ahhdl, uint_t sleepflag)
4899517SBill.Taylor@Sun.COM {
4909517SBill.Taylor@Sun.COM 	hermon_rsrc_t		*rsrc;
4919517SBill.Taylor@Sun.COM 	hermon_hw_udav_t	*udav;
4929517SBill.Taylor@Sun.COM 	hermon_ahhdl_t		ah;
4939517SBill.Taylor@Sun.COM 	int			status;
4949517SBill.Taylor@Sun.COM 
4959517SBill.Taylor@Sun.COM 	/*
4969517SBill.Taylor@Sun.COM 	 * Someday maybe the "ibt_adds_vect_t *attr_p" will be NULL to
4979517SBill.Taylor@Sun.COM 	 * indicate that we wish to allocate an "invalid" (i.e. empty)
4989517SBill.Taylor@Sun.COM 	 * address handle XXX
4999517SBill.Taylor@Sun.COM 	 */
5009517SBill.Taylor@Sun.COM 
5019517SBill.Taylor@Sun.COM 	/* Validate that specified port number is legal */
5029517SBill.Taylor@Sun.COM 	if (!hermon_portnum_is_valid(state, attr_p->av_port_num)) {
5039517SBill.Taylor@Sun.COM 		return (IBT_HCA_PORT_INVALID);
5049517SBill.Taylor@Sun.COM 	}
5059517SBill.Taylor@Sun.COM 
5069517SBill.Taylor@Sun.COM 	/*
5079517SBill.Taylor@Sun.COM 	 * Allocate the software structure for tracking the address handle
5089517SBill.Taylor@Sun.COM 	 * (i.e. the Hermon Address Handle struct).
5099517SBill.Taylor@Sun.COM 	 */
5109517SBill.Taylor@Sun.COM 	status = hermon_rsrc_alloc(state, HERMON_AHHDL, 1, sleepflag, &rsrc);
5119517SBill.Taylor@Sun.COM 	if (status != DDI_SUCCESS) {
5129517SBill.Taylor@Sun.COM 		return (IBT_INSUFF_RESOURCE);
5139517SBill.Taylor@Sun.COM 	}
5149517SBill.Taylor@Sun.COM 	ah = (hermon_ahhdl_t)rsrc->hr_addr;
5159517SBill.Taylor@Sun.COM 	_NOTE(NOW_INVISIBLE_TO_OTHER_THREADS(*ah))
5169517SBill.Taylor@Sun.COM 
5179517SBill.Taylor@Sun.COM 	/* Increment the reference count on the protection domain (PD) */
5189517SBill.Taylor@Sun.COM 	hermon_pd_refcnt_inc(pd);
5199517SBill.Taylor@Sun.COM 
5209517SBill.Taylor@Sun.COM 	udav = (hermon_hw_udav_t *)kmem_zalloc(sizeof (hermon_hw_udav_t),
5219517SBill.Taylor@Sun.COM 	    KM_SLEEP);
5229517SBill.Taylor@Sun.COM 	_NOTE(NOW_INVISIBLE_TO_OTHER_THREADS(*udav))
5239517SBill.Taylor@Sun.COM 
5249517SBill.Taylor@Sun.COM 	/*
5259517SBill.Taylor@Sun.COM 	 * Fill in the UDAV data. We first zero out the UDAV, then populate
5269517SBill.Taylor@Sun.COM 	 * it by then calling hermon_set_addr_path() to fill in the common
5279517SBill.Taylor@Sun.COM 	 * portions that can be pulled from the "ibt_adds_vect_t" passed in
5289517SBill.Taylor@Sun.COM 	 */
5299517SBill.Taylor@Sun.COM 	status = hermon_set_addr_path(state, attr_p,
5309517SBill.Taylor@Sun.COM 	    (hermon_hw_addr_path_t *)udav, HERMON_ADDRPATH_UDAV);
5319517SBill.Taylor@Sun.COM 	if (status != DDI_SUCCESS) {
5329517SBill.Taylor@Sun.COM 		hermon_pd_refcnt_dec(pd);
5339517SBill.Taylor@Sun.COM 		hermon_rsrc_free(state, &rsrc);
5349517SBill.Taylor@Sun.COM 		return (status);
5359517SBill.Taylor@Sun.COM 	}
5369517SBill.Taylor@Sun.COM 	udav->pd	= pd->pd_pdnum;
5379517SBill.Taylor@Sun.COM 	udav->sl	= attr_p->av_srvl;
5389517SBill.Taylor@Sun.COM 
5399517SBill.Taylor@Sun.COM 	/*
5409517SBill.Taylor@Sun.COM 	 * Fill in the rest of the Hermon Address Handle struct.
5419517SBill.Taylor@Sun.COM 	 *
5429517SBill.Taylor@Sun.COM 	 * NOTE: We are saving away a copy of the "av_dgid.gid_guid" field
5439517SBill.Taylor@Sun.COM 	 * here because we may need to return it later to the IBTF (as a
5449517SBill.Taylor@Sun.COM 	 * result of a subsequent query operation).  Unlike the other UDAV
5459517SBill.Taylor@Sun.COM 	 * parameters, the value of "av_dgid.gid_guid" is not always preserved.
5469517SBill.Taylor@Sun.COM 	 * The reason for this is described in hermon_set_addr_path().
5479517SBill.Taylor@Sun.COM 	 */
5489517SBill.Taylor@Sun.COM 	ah->ah_rsrcp	 = rsrc;
5499517SBill.Taylor@Sun.COM 	ah->ah_pdhdl	 = pd;
5509517SBill.Taylor@Sun.COM 	ah->ah_udav	 = udav;
5519517SBill.Taylor@Sun.COM 	ah->ah_save_guid = attr_p->av_dgid.gid_guid;
5529517SBill.Taylor@Sun.COM 	*ahhdl = ah;
5539517SBill.Taylor@Sun.COM 
5549517SBill.Taylor@Sun.COM 	return (DDI_SUCCESS);
5559517SBill.Taylor@Sun.COM }
5569517SBill.Taylor@Sun.COM 
5579517SBill.Taylor@Sun.COM 
5589517SBill.Taylor@Sun.COM /*
5599517SBill.Taylor@Sun.COM  * hermon_ah_free()
5609517SBill.Taylor@Sun.COM  *    Context: Can be called only from user or kernel context.
5619517SBill.Taylor@Sun.COM  */
5629517SBill.Taylor@Sun.COM /* ARGSUSED */
5639517SBill.Taylor@Sun.COM int
hermon_ah_free(hermon_state_t * state,hermon_ahhdl_t * ahhdl,uint_t sleepflag)5649517SBill.Taylor@Sun.COM hermon_ah_free(hermon_state_t *state, hermon_ahhdl_t *ahhdl, uint_t sleepflag)
5659517SBill.Taylor@Sun.COM {
5669517SBill.Taylor@Sun.COM 	hermon_rsrc_t		*rsrc;
5679517SBill.Taylor@Sun.COM 	hermon_pdhdl_t		pd;
5689517SBill.Taylor@Sun.COM 	hermon_ahhdl_t		ah;
5699517SBill.Taylor@Sun.COM 
5709517SBill.Taylor@Sun.COM 	/*
5719517SBill.Taylor@Sun.COM 	 * Pull all the necessary information from the Hermon Address Handle
5729517SBill.Taylor@Sun.COM 	 * struct.  This is necessary here because the resource for the
5739517SBill.Taylor@Sun.COM 	 * AH is going to be freed up as part of this operation.
5749517SBill.Taylor@Sun.COM 	 */
5759517SBill.Taylor@Sun.COM 	ah    = *ahhdl;
5769517SBill.Taylor@Sun.COM 	mutex_enter(&ah->ah_lock);
5779517SBill.Taylor@Sun.COM 	rsrc  = ah->ah_rsrcp;
5789517SBill.Taylor@Sun.COM 	pd    = ah->ah_pdhdl;
5799517SBill.Taylor@Sun.COM 	mutex_exit(&ah->ah_lock);
5809517SBill.Taylor@Sun.COM 	_NOTE(NOW_INVISIBLE_TO_OTHER_THREADS(*ah))
5819517SBill.Taylor@Sun.COM 
5829517SBill.Taylor@Sun.COM 	/* Free the UDAV memory */
5839517SBill.Taylor@Sun.COM 	kmem_free(ah->ah_udav, sizeof (hermon_hw_udav_t));
5849517SBill.Taylor@Sun.COM 
5859517SBill.Taylor@Sun.COM 	/* Decrement the reference count on the protection domain (PD) */
5869517SBill.Taylor@Sun.COM 	hermon_pd_refcnt_dec(pd);
5879517SBill.Taylor@Sun.COM 
5889517SBill.Taylor@Sun.COM 	/* Free the Hermon Address Handle structure */
5899517SBill.Taylor@Sun.COM 	hermon_rsrc_free(state, &rsrc);
5909517SBill.Taylor@Sun.COM 
5919517SBill.Taylor@Sun.COM 	/* Set the ahhdl pointer to NULL and return success */
5929517SBill.Taylor@Sun.COM 	*ahhdl = NULL;
5939517SBill.Taylor@Sun.COM 
5949517SBill.Taylor@Sun.COM 	return (DDI_SUCCESS);
5959517SBill.Taylor@Sun.COM }
5969517SBill.Taylor@Sun.COM 
5979517SBill.Taylor@Sun.COM 
5989517SBill.Taylor@Sun.COM /*
5999517SBill.Taylor@Sun.COM  * hermon_ah_query()
6009517SBill.Taylor@Sun.COM  *    Context: Can be called from interrupt or base context.
6019517SBill.Taylor@Sun.COM  */
6029517SBill.Taylor@Sun.COM /* ARGSUSED */
6039517SBill.Taylor@Sun.COM int
hermon_ah_query(hermon_state_t * state,hermon_ahhdl_t ah,hermon_pdhdl_t * pd,ibt_adds_vect_t * attr_p)6049517SBill.Taylor@Sun.COM hermon_ah_query(hermon_state_t *state, hermon_ahhdl_t ah, hermon_pdhdl_t *pd,
6059517SBill.Taylor@Sun.COM     ibt_adds_vect_t *attr_p)
6069517SBill.Taylor@Sun.COM {
6079517SBill.Taylor@Sun.COM 	mutex_enter(&ah->ah_lock);
6089517SBill.Taylor@Sun.COM 	_NOTE(NOW_INVISIBLE_TO_OTHER_THREADS(*attr_p))
6099517SBill.Taylor@Sun.COM 
6109517SBill.Taylor@Sun.COM 	/*
6119517SBill.Taylor@Sun.COM 	 * Pull the PD and UDAV from the Hermon Address Handle structure
6129517SBill.Taylor@Sun.COM 	 */
6139517SBill.Taylor@Sun.COM 	*pd = ah->ah_pdhdl;
6149517SBill.Taylor@Sun.COM 
6159517SBill.Taylor@Sun.COM 	/*
6169517SBill.Taylor@Sun.COM 	 * Fill in "ibt_adds_vect_t".  We call hermon_get_addr_path() to fill
6179517SBill.Taylor@Sun.COM 	 * the common portions that can be pulled from the UDAV we pass in.
6189517SBill.Taylor@Sun.COM 	 *
6199517SBill.Taylor@Sun.COM 	 * NOTE: We will also fill the "av_dgid.gid_guid" field from the
6209517SBill.Taylor@Sun.COM 	 * "ah_save_guid" field we have previously saved away.  The reason
6219517SBill.Taylor@Sun.COM 	 * for this is described in hermon_ah_alloc() and hermon_ah_modify().
6229517SBill.Taylor@Sun.COM 	 */
6239517SBill.Taylor@Sun.COM 	hermon_get_addr_path(state, (hermon_hw_addr_path_t *)ah->ah_udav,
6249517SBill.Taylor@Sun.COM 	    attr_p, HERMON_ADDRPATH_UDAV);
6259517SBill.Taylor@Sun.COM 
6269517SBill.Taylor@Sun.COM 	attr_p->av_dgid.gid_guid = ah->ah_save_guid;
6279517SBill.Taylor@Sun.COM 
6289517SBill.Taylor@Sun.COM 	mutex_exit(&ah->ah_lock);
6299517SBill.Taylor@Sun.COM 	return (DDI_SUCCESS);
6309517SBill.Taylor@Sun.COM }
6319517SBill.Taylor@Sun.COM 
6329517SBill.Taylor@Sun.COM 
6339517SBill.Taylor@Sun.COM /*
6349517SBill.Taylor@Sun.COM  * hermon_ah_modify()
6359517SBill.Taylor@Sun.COM  *    Context: Can be called from interrupt or base context.
6369517SBill.Taylor@Sun.COM  */
6379517SBill.Taylor@Sun.COM /* ARGSUSED */
6389517SBill.Taylor@Sun.COM int
hermon_ah_modify(hermon_state_t * state,hermon_ahhdl_t ah,ibt_adds_vect_t * attr_p)6399517SBill.Taylor@Sun.COM hermon_ah_modify(hermon_state_t *state, hermon_ahhdl_t ah,
6409517SBill.Taylor@Sun.COM     ibt_adds_vect_t *attr_p)
6419517SBill.Taylor@Sun.COM {
6429517SBill.Taylor@Sun.COM 	hermon_hw_udav_t	old_udav;
6439517SBill.Taylor@Sun.COM 	uint64_t		data_old;
6449517SBill.Taylor@Sun.COM 	int			status, size, i;
6459517SBill.Taylor@Sun.COM 
6469517SBill.Taylor@Sun.COM 	/* Validate that specified port number is legal */
6479517SBill.Taylor@Sun.COM 	if (!hermon_portnum_is_valid(state, attr_p->av_port_num)) {
6489517SBill.Taylor@Sun.COM 		return (IBT_HCA_PORT_INVALID);
6499517SBill.Taylor@Sun.COM 	}
6509517SBill.Taylor@Sun.COM 
6519517SBill.Taylor@Sun.COM 	mutex_enter(&ah->ah_lock);
6529517SBill.Taylor@Sun.COM 
6539517SBill.Taylor@Sun.COM 	/* Save a copy of the current UDAV data in old_udav. */
6549517SBill.Taylor@Sun.COM 	bcopy(ah->ah_udav, &old_udav, sizeof (hermon_hw_udav_t));
6559517SBill.Taylor@Sun.COM 
6569517SBill.Taylor@Sun.COM 	/*
6579517SBill.Taylor@Sun.COM 	 * Fill in the new UDAV with the caller's data, passed in via the
6589517SBill.Taylor@Sun.COM 	 * "ibt_adds_vect_t" structure.
6599517SBill.Taylor@Sun.COM 	 *
6609517SBill.Taylor@Sun.COM 	 * NOTE: We also need to save away a copy of the "av_dgid.gid_guid"
6619517SBill.Taylor@Sun.COM 	 * field here (just as we did during hermon_ah_alloc()) because we
6629517SBill.Taylor@Sun.COM 	 * may need to return it later to the IBTF (as a result of a
6639517SBill.Taylor@Sun.COM 	 * subsequent query operation).  As explained in hermon_ah_alloc(),
6649517SBill.Taylor@Sun.COM 	 * unlike the other UDAV parameters, the value of "av_dgid.gid_guid"
6659517SBill.Taylor@Sun.COM 	 * is not always preserved. The reason for this is described in
6669517SBill.Taylor@Sun.COM 	 * hermon_set_addr_path().
6679517SBill.Taylor@Sun.COM 	 */
6689517SBill.Taylor@Sun.COM 	status = hermon_set_addr_path(state, attr_p,
6699517SBill.Taylor@Sun.COM 	    (hermon_hw_addr_path_t *)ah->ah_udav, HERMON_ADDRPATH_UDAV);
6709517SBill.Taylor@Sun.COM 	if (status != DDI_SUCCESS) {
6719517SBill.Taylor@Sun.COM 		mutex_exit(&ah->ah_lock);
6729517SBill.Taylor@Sun.COM 		return (status);
6739517SBill.Taylor@Sun.COM 	}
6749517SBill.Taylor@Sun.COM 	ah->ah_save_guid = attr_p->av_dgid.gid_guid;
6759517SBill.Taylor@Sun.COM 	_NOTE(NOW_INVISIBLE_TO_OTHER_THREADS(*(ah->ah_udav)))
6769517SBill.Taylor@Sun.COM 	ah->ah_udav->sl  = attr_p->av_srvl;
6779517SBill.Taylor@Sun.COM 
6789517SBill.Taylor@Sun.COM 	/*
6799517SBill.Taylor@Sun.COM 	 * Copy changes into the new UDAV.
6809517SBill.Taylor@Sun.COM 	 *    Note:  We copy in 64-bit chunks.  For the first two of these
6819517SBill.Taylor@Sun.COM 	 *    chunks it is necessary to read the current contents of the
6829517SBill.Taylor@Sun.COM 	 *    UDAV, mask off the modifiable portions (maintaining any
6839517SBill.Taylor@Sun.COM 	 *    of the "reserved" portions), and then mask on the new data.
6849517SBill.Taylor@Sun.COM 	 */
6859517SBill.Taylor@Sun.COM 	size = sizeof (hermon_hw_udav_t) >> 3;
6869517SBill.Taylor@Sun.COM 	for (i = 0; i < size; i++) {
6879517SBill.Taylor@Sun.COM 		data_old = ((uint64_t *)&old_udav)[i];
6889517SBill.Taylor@Sun.COM 
6899517SBill.Taylor@Sun.COM 		/*
6909517SBill.Taylor@Sun.COM 		 * Apply mask to change only the relevant values.
6919517SBill.Taylor@Sun.COM 		 */
6929517SBill.Taylor@Sun.COM 		if (i == 0) {
6939517SBill.Taylor@Sun.COM 			data_old = data_old & HERMON_UDAV_MODIFY_MASK0;
6949517SBill.Taylor@Sun.COM 		} else if (i == 1) {
6959517SBill.Taylor@Sun.COM 			data_old = data_old & HERMON_UDAV_MODIFY_MASK1;
6969517SBill.Taylor@Sun.COM 		} else {
6979517SBill.Taylor@Sun.COM 			data_old = 0;
6989517SBill.Taylor@Sun.COM 		}
6999517SBill.Taylor@Sun.COM 
7009517SBill.Taylor@Sun.COM 		/* Store the updated values to the UDAV */
7019517SBill.Taylor@Sun.COM 		((uint64_t *)ah->ah_udav)[i] |= data_old;
7029517SBill.Taylor@Sun.COM 	}
7039517SBill.Taylor@Sun.COM 
7049517SBill.Taylor@Sun.COM 	/*
7059517SBill.Taylor@Sun.COM 	 * Put the valid PD number back into the UDAV entry, as it
7069517SBill.Taylor@Sun.COM 	 * might have been clobbered above.
7079517SBill.Taylor@Sun.COM 	 */
7089517SBill.Taylor@Sun.COM 	ah->ah_udav->pd = old_udav.pd;
7099517SBill.Taylor@Sun.COM 
7109517SBill.Taylor@Sun.COM 
7119517SBill.Taylor@Sun.COM 	mutex_exit(&ah->ah_lock);
7129517SBill.Taylor@Sun.COM 	return (DDI_SUCCESS);
7139517SBill.Taylor@Sun.COM }
7149517SBill.Taylor@Sun.COM 
7159517SBill.Taylor@Sun.COM /*
7169517SBill.Taylor@Sun.COM  * hermon_mcg_attach()
7179517SBill.Taylor@Sun.COM  *    Context: Can be called only from user or kernel context.
7189517SBill.Taylor@Sun.COM  */
7199517SBill.Taylor@Sun.COM int
hermon_mcg_attach(hermon_state_t * state,hermon_qphdl_t qp,ib_gid_t gid,ib_lid_t lid)7209517SBill.Taylor@Sun.COM hermon_mcg_attach(hermon_state_t *state, hermon_qphdl_t qp, ib_gid_t gid,
7219517SBill.Taylor@Sun.COM     ib_lid_t lid)
7229517SBill.Taylor@Sun.COM {
7239517SBill.Taylor@Sun.COM 	hermon_rsrc_t		*rsrc;
7249517SBill.Taylor@Sun.COM 	hermon_hw_mcg_t		*mcg_entry;
7259517SBill.Taylor@Sun.COM 	hermon_hw_mcg_qp_list_t	*mcg_entry_qplist;
7269517SBill.Taylor@Sun.COM 	hermon_mcghdl_t		mcg, newmcg;
7279517SBill.Taylor@Sun.COM 	uint64_t		mgid_hash;
7289517SBill.Taylor@Sun.COM 	uint32_t		end_indx;
7299517SBill.Taylor@Sun.COM 	int			status;
7309517SBill.Taylor@Sun.COM 	uint_t			qp_found;
7319517SBill.Taylor@Sun.COM 
7329517SBill.Taylor@Sun.COM 	/*
7339517SBill.Taylor@Sun.COM 	 * It is only allowed to attach MCG to UD queue pairs.  Verify
7349517SBill.Taylor@Sun.COM 	 * that the intended QP is of the appropriate transport type
7359517SBill.Taylor@Sun.COM 	 */
7369517SBill.Taylor@Sun.COM 	if (qp->qp_serv_type != HERMON_QP_UD) {
7379517SBill.Taylor@Sun.COM 		return (IBT_QP_SRV_TYPE_INVALID);
7389517SBill.Taylor@Sun.COM 	}
7399517SBill.Taylor@Sun.COM 
7409517SBill.Taylor@Sun.COM 	/*
7419517SBill.Taylor@Sun.COM 	 * Check for invalid Multicast DLID.  Specifically, all Multicast
7429517SBill.Taylor@Sun.COM 	 * LIDs should be within a well defined range.  If the specified LID
7439517SBill.Taylor@Sun.COM 	 * is outside of that range, then return an error.
7449517SBill.Taylor@Sun.COM 	 */
7459517SBill.Taylor@Sun.COM 	if (hermon_mlid_is_valid(lid) == 0) {
7469517SBill.Taylor@Sun.COM 		return (IBT_MC_MLID_INVALID);
7479517SBill.Taylor@Sun.COM 	}
7489517SBill.Taylor@Sun.COM 	/*
7499517SBill.Taylor@Sun.COM 	 * Check for invalid Multicast GID.  All Multicast GIDs should have
7509517SBill.Taylor@Sun.COM 	 * a well-defined pattern of bits and flags that are allowable.  If
7519517SBill.Taylor@Sun.COM 	 * the specified GID does not meet the criteria, then return an error.
7529517SBill.Taylor@Sun.COM 	 */
7539517SBill.Taylor@Sun.COM 	if (hermon_mgid_is_valid(gid) == 0) {
7549517SBill.Taylor@Sun.COM 		return (IBT_MC_MGID_INVALID);
7559517SBill.Taylor@Sun.COM 	}
7569517SBill.Taylor@Sun.COM 
7579517SBill.Taylor@Sun.COM 	/*
7589517SBill.Taylor@Sun.COM 	 * Compute the MGID hash value.  Since the MCG table is arranged as
7599517SBill.Taylor@Sun.COM 	 * a number of separate hash chains, this operation converts the
7609517SBill.Taylor@Sun.COM 	 * specified MGID into the starting index of an entry in the hash
7619517SBill.Taylor@Sun.COM 	 * table (i.e. the index for the start of the appropriate hash chain).
7629517SBill.Taylor@Sun.COM 	 * Subsequent operations below will walk the chain searching for the
7639517SBill.Taylor@Sun.COM 	 * right place to add this new QP.
7649517SBill.Taylor@Sun.COM 	 */
7659517SBill.Taylor@Sun.COM 	status = hermon_mgid_hash_cmd_post(state, gid.gid_prefix, gid.gid_guid,
7669517SBill.Taylor@Sun.COM 	    &mgid_hash, HERMON_SLEEPFLAG_FOR_CONTEXT());
7679517SBill.Taylor@Sun.COM 	if (status != HERMON_CMD_SUCCESS) {
7689517SBill.Taylor@Sun.COM 		cmn_err(CE_CONT, "Hermon: MGID_HASH command failed: %08x\n",
7699517SBill.Taylor@Sun.COM 		    status);
7709517SBill.Taylor@Sun.COM 		if (status == HERMON_CMD_INVALID_STATUS) {
7719517SBill.Taylor@Sun.COM 			hermon_fm_ereport(state, HCA_SYS_ERR, HCA_ERR_SRV_LOST);
7729517SBill.Taylor@Sun.COM 		}
7739517SBill.Taylor@Sun.COM 		return (ibc_get_ci_failure(0));
7749517SBill.Taylor@Sun.COM 	}
7759517SBill.Taylor@Sun.COM 
7769517SBill.Taylor@Sun.COM 	/*
7779517SBill.Taylor@Sun.COM 	 * Grab the multicast group mutex.  Then grab the pre-allocated
7789517SBill.Taylor@Sun.COM 	 * temporary buffer used for holding and/or modifying MCG entries.
7799517SBill.Taylor@Sun.COM 	 * Zero out the temporary MCG entry before we begin.
7809517SBill.Taylor@Sun.COM 	 */
7819517SBill.Taylor@Sun.COM 	mutex_enter(&state->hs_mcglock);
7829517SBill.Taylor@Sun.COM 	mcg_entry = state->hs_mcgtmp;
7839517SBill.Taylor@Sun.COM 	mcg_entry_qplist = HERMON_MCG_GET_QPLIST_PTR(mcg_entry);
7849517SBill.Taylor@Sun.COM 	bzero(mcg_entry, HERMON_MCGMEM_SZ(state));
7859517SBill.Taylor@Sun.COM 
7869517SBill.Taylor@Sun.COM 	/*
7879517SBill.Taylor@Sun.COM 	 * Walk through the array of MCG entries starting at "mgid_hash".
7889517SBill.Taylor@Sun.COM 	 * Try to find the appropriate place for this new QP to be added.
7899517SBill.Taylor@Sun.COM 	 * This could happen when the first entry of the chain has MGID == 0
7909517SBill.Taylor@Sun.COM 	 * (which means that the hash chain is empty), or because we find
7919517SBill.Taylor@Sun.COM 	 * an entry with the same MGID (in which case we'll add the QP to
7929517SBill.Taylor@Sun.COM 	 * that MCG), or because we come to the end of the chain (in which
7939517SBill.Taylor@Sun.COM 	 * case this is the first QP being added to the multicast group that
7949517SBill.Taylor@Sun.COM 	 * corresponds to the MGID.  The hermon_mcg_walk_mgid_hash() routine
7959517SBill.Taylor@Sun.COM 	 * walks the list and returns an index into the MCG table.  The entry
7969517SBill.Taylor@Sun.COM 	 * at this index is then checked to determine which case we have
7979517SBill.Taylor@Sun.COM 	 * fallen into (see below).  Note:  We are using the "shadow" MCG
7989517SBill.Taylor@Sun.COM 	 * list (of hermon_mcg_t structs) for this lookup because the real
7999517SBill.Taylor@Sun.COM 	 * MCG entries are in hardware (and the lookup process would be much
8009517SBill.Taylor@Sun.COM 	 * more time consuming).
8019517SBill.Taylor@Sun.COM 	 */
8029517SBill.Taylor@Sun.COM 	end_indx = hermon_mcg_walk_mgid_hash(state, mgid_hash, gid, NULL);
8039517SBill.Taylor@Sun.COM 	mcg	 = &state->hs_mcghdl[end_indx];
8049517SBill.Taylor@Sun.COM 
8059517SBill.Taylor@Sun.COM 	/*
8069517SBill.Taylor@Sun.COM 	 * If MGID == 0, then the hash chain is empty.  Just fill in the
8079517SBill.Taylor@Sun.COM 	 * current entry.  Note:  No need to allocate an MCG table entry
8089517SBill.Taylor@Sun.COM 	 * as all the hash chain "heads" are already preallocated.
8099517SBill.Taylor@Sun.COM 	 */
8109517SBill.Taylor@Sun.COM 	if ((mcg->mcg_mgid_h == 0) && (mcg->mcg_mgid_l == 0)) {
8119517SBill.Taylor@Sun.COM 
8129517SBill.Taylor@Sun.COM 		/* Fill in the current entry in the "shadow" MCG list */
8139517SBill.Taylor@Sun.COM 		hermon_mcg_setup_new_hdr(mcg, mcg_entry, gid, NULL);
8149517SBill.Taylor@Sun.COM 
8159517SBill.Taylor@Sun.COM 		/*
8169517SBill.Taylor@Sun.COM 		 * Try to add the new QP number to the list.  This (and the
8179517SBill.Taylor@Sun.COM 		 * above) routine fills in a temporary MCG.  The "mcg_entry"
8189517SBill.Taylor@Sun.COM 		 * and "mcg_entry_qplist" pointers simply point to different
8199517SBill.Taylor@Sun.COM 		 * offsets within the same temporary copy of the MCG (for
8209517SBill.Taylor@Sun.COM 		 * convenience).  Note:  If this fails, we need to invalidate
8219517SBill.Taylor@Sun.COM 		 * the entries we've already put into the "shadow" list entry
8229517SBill.Taylor@Sun.COM 		 * above.
8239517SBill.Taylor@Sun.COM 		 */
8249517SBill.Taylor@Sun.COM 		status = hermon_mcg_qplist_add(state, mcg, mcg_entry_qplist, qp,
8259517SBill.Taylor@Sun.COM 		    &qp_found);
8269517SBill.Taylor@Sun.COM 		if (status != DDI_SUCCESS) {
8279517SBill.Taylor@Sun.COM 			bzero(mcg, sizeof (struct hermon_sw_mcg_list_s));
8289517SBill.Taylor@Sun.COM 			mutex_exit(&state->hs_mcglock);
8299517SBill.Taylor@Sun.COM 			return (status);
8309517SBill.Taylor@Sun.COM 		}
8319517SBill.Taylor@Sun.COM 		if (!qp_found)
8329517SBill.Taylor@Sun.COM 			mcg_entry->member_cnt = (mcg->mcg_num_qps + 1);
8339517SBill.Taylor@Sun.COM 			    /* set the member count */
8349517SBill.Taylor@Sun.COM 
8359517SBill.Taylor@Sun.COM 		/*
8369517SBill.Taylor@Sun.COM 		 * Once the temporary MCG has been filled in, write the entry
8379517SBill.Taylor@Sun.COM 		 * into the appropriate location in the Hermon MCG entry table.
8389517SBill.Taylor@Sun.COM 		 * If it's successful, then drop the lock and return success.
8399517SBill.Taylor@Sun.COM 		 * Note: In general, this operation shouldn't fail.  If it
8409517SBill.Taylor@Sun.COM 		 * does, then it is an indication that something (probably in
8419517SBill.Taylor@Sun.COM 		 * HW, but maybe in SW) has gone seriously wrong.  We still
8429517SBill.Taylor@Sun.COM 		 * want to zero out the entries that we've filled in above
8439517SBill.Taylor@Sun.COM 		 * (in the hermon_mcg_setup_new_hdr() routine).
8449517SBill.Taylor@Sun.COM 		 */
8459517SBill.Taylor@Sun.COM 		status = hermon_write_mgm_cmd_post(state, mcg_entry, end_indx,
8469517SBill.Taylor@Sun.COM 		    HERMON_CMD_NOSLEEP_SPIN);
8479517SBill.Taylor@Sun.COM 		if (status != HERMON_CMD_SUCCESS) {
8489517SBill.Taylor@Sun.COM 			bzero(mcg, sizeof (struct hermon_sw_mcg_list_s));
8499517SBill.Taylor@Sun.COM 			mutex_exit(&state->hs_mcglock);
8509517SBill.Taylor@Sun.COM 			HERMON_WARNING(state, "failed to write MCG entry");
8519517SBill.Taylor@Sun.COM 			cmn_err(CE_CONT, "Hermon: WRITE_MGM command failed: "
8529517SBill.Taylor@Sun.COM 			    "%08x\n", status);
8539517SBill.Taylor@Sun.COM 			if (status == HERMON_CMD_INVALID_STATUS) {
8549517SBill.Taylor@Sun.COM 				hermon_fm_ereport(state, HCA_SYS_ERR,
8559517SBill.Taylor@Sun.COM 				    HCA_ERR_SRV_LOST);
8569517SBill.Taylor@Sun.COM 			}
8579517SBill.Taylor@Sun.COM 			return (ibc_get_ci_failure(0));
8589517SBill.Taylor@Sun.COM 		}
8599517SBill.Taylor@Sun.COM 
8609517SBill.Taylor@Sun.COM 		/*
8619517SBill.Taylor@Sun.COM 		 * Now that we know all the Hermon firmware accesses have been
8629517SBill.Taylor@Sun.COM 		 * successful, we update the "shadow" MCG entry by incrementing
8639517SBill.Taylor@Sun.COM 		 * the "number of attached QPs" count.
8649517SBill.Taylor@Sun.COM 		 *
8659517SBill.Taylor@Sun.COM 		 * We increment only if the QP is not already part of the
8669517SBill.Taylor@Sun.COM 		 * MCG by checking the 'qp_found' flag returned from the
8679517SBill.Taylor@Sun.COM 		 * qplist_add above.
8689517SBill.Taylor@Sun.COM 		 */
8699517SBill.Taylor@Sun.COM 		if (!qp_found) {
8709517SBill.Taylor@Sun.COM 			mcg->mcg_num_qps++;
8719517SBill.Taylor@Sun.COM 
8729517SBill.Taylor@Sun.COM 			/*
8739517SBill.Taylor@Sun.COM 			 * Increment the refcnt for this QP.  Because the QP
8749517SBill.Taylor@Sun.COM 			 * was added to this MCG, the refcnt must be
8759517SBill.Taylor@Sun.COM 			 * incremented.
8769517SBill.Taylor@Sun.COM 			 */
8779517SBill.Taylor@Sun.COM 			hermon_qp_mcg_refcnt_inc(qp);
8789517SBill.Taylor@Sun.COM 		}
8799517SBill.Taylor@Sun.COM 
8809517SBill.Taylor@Sun.COM 		/*
8819517SBill.Taylor@Sun.COM 		 * We drop the lock and return success.
8829517SBill.Taylor@Sun.COM 		 */
8839517SBill.Taylor@Sun.COM 		mutex_exit(&state->hs_mcglock);
8849517SBill.Taylor@Sun.COM 		return (DDI_SUCCESS);
8859517SBill.Taylor@Sun.COM 	}
8869517SBill.Taylor@Sun.COM 
8879517SBill.Taylor@Sun.COM 	/*
8889517SBill.Taylor@Sun.COM 	 * If the specified MGID matches the MGID in the current entry, then
8899517SBill.Taylor@Sun.COM 	 * we need to try to add the QP to the current MCG entry.  In this
8909517SBill.Taylor@Sun.COM 	 * case, it means that we need to read the existing MCG entry (into
8919517SBill.Taylor@Sun.COM 	 * the temporary MCG), add the new QP number to the temporary entry
8929517SBill.Taylor@Sun.COM 	 * (using the same method we used above), and write the entry back
8939517SBill.Taylor@Sun.COM 	 * to the hardware (same as above).
8949517SBill.Taylor@Sun.COM 	 */
8959517SBill.Taylor@Sun.COM 	if ((mcg->mcg_mgid_h == gid.gid_prefix) &&
8969517SBill.Taylor@Sun.COM 	    (mcg->mcg_mgid_l == gid.gid_guid)) {
8979517SBill.Taylor@Sun.COM 
8989517SBill.Taylor@Sun.COM 		/*
8999517SBill.Taylor@Sun.COM 		 * Read the current MCG entry into the temporary MCG.  Note:
9009517SBill.Taylor@Sun.COM 		 * In general, this operation shouldn't fail.  If it does,
9019517SBill.Taylor@Sun.COM 		 * then it is an indication that something (probably in HW,
9029517SBill.Taylor@Sun.COM 		 * but maybe in SW) has gone seriously wrong.
9039517SBill.Taylor@Sun.COM 		 */
9049517SBill.Taylor@Sun.COM 		status = hermon_read_mgm_cmd_post(state, mcg_entry, end_indx,
9059517SBill.Taylor@Sun.COM 		    HERMON_CMD_NOSLEEP_SPIN);
9069517SBill.Taylor@Sun.COM 		if (status != HERMON_CMD_SUCCESS) {
9079517SBill.Taylor@Sun.COM 			mutex_exit(&state->hs_mcglock);
9089517SBill.Taylor@Sun.COM 			HERMON_WARNING(state, "failed to read MCG entry");
9099517SBill.Taylor@Sun.COM 			cmn_err(CE_CONT, "Hermon: READ_MGM command failed: "
9109517SBill.Taylor@Sun.COM 			    "%08x\n", status);
9119517SBill.Taylor@Sun.COM 			if (status == HERMON_CMD_INVALID_STATUS) {
9129517SBill.Taylor@Sun.COM 				hermon_fm_ereport(state, HCA_SYS_ERR,
9139517SBill.Taylor@Sun.COM 				    HCA_ERR_SRV_LOST);
9149517SBill.Taylor@Sun.COM 			}
9159517SBill.Taylor@Sun.COM 			return (ibc_get_ci_failure(0));
9169517SBill.Taylor@Sun.COM 		}
9179517SBill.Taylor@Sun.COM 
9189517SBill.Taylor@Sun.COM 		/*
9199517SBill.Taylor@Sun.COM 		 * Try to add the new QP number to the list.  This routine
9209517SBill.Taylor@Sun.COM 		 * fills in the necessary pieces of the temporary MCG.  The
9219517SBill.Taylor@Sun.COM 		 * "mcg_entry_qplist" pointer is used to point to the portion
9229517SBill.Taylor@Sun.COM 		 * of the temporary MCG that holds the QP numbers.
9239517SBill.Taylor@Sun.COM 		 *
9249517SBill.Taylor@Sun.COM 		 * Note: hermon_mcg_qplist_add() returns SUCCESS if it
9259517SBill.Taylor@Sun.COM 		 * already found the QP in the list.  In this case, the QP is
9269517SBill.Taylor@Sun.COM 		 * not added on to the list again.  Check the flag 'qp_found'
9279517SBill.Taylor@Sun.COM 		 * if this value is needed to be known.
9289517SBill.Taylor@Sun.COM 		 *
9299517SBill.Taylor@Sun.COM 		 */
9309517SBill.Taylor@Sun.COM 		status = hermon_mcg_qplist_add(state, mcg, mcg_entry_qplist, qp,
9319517SBill.Taylor@Sun.COM 		    &qp_found);
9329517SBill.Taylor@Sun.COM 		if (status != DDI_SUCCESS) {
9339517SBill.Taylor@Sun.COM 			mutex_exit(&state->hs_mcglock);
9349517SBill.Taylor@Sun.COM 			return (status);
9359517SBill.Taylor@Sun.COM 		}
9369517SBill.Taylor@Sun.COM 		if (!qp_found)
9379517SBill.Taylor@Sun.COM 			mcg_entry->member_cnt = (mcg->mcg_num_qps + 1);
9389517SBill.Taylor@Sun.COM 			    /* set the member count */
9399517SBill.Taylor@Sun.COM 
9409517SBill.Taylor@Sun.COM 		/*
9419517SBill.Taylor@Sun.COM 		 * Once the temporary MCG has been updated, write the entry
9429517SBill.Taylor@Sun.COM 		 * into the appropriate location in the Hermon MCG entry table.
9439517SBill.Taylor@Sun.COM 		 * If it's successful, then drop the lock and return success.
9449517SBill.Taylor@Sun.COM 		 * Note: In general, this operation shouldn't fail.  If it
9459517SBill.Taylor@Sun.COM 		 * does, then it is an indication that something (probably in
9469517SBill.Taylor@Sun.COM 		 * HW, but maybe in SW) has gone seriously wrong.
9479517SBill.Taylor@Sun.COM 		 */
9489517SBill.Taylor@Sun.COM 		status = hermon_write_mgm_cmd_post(state, mcg_entry, end_indx,
9499517SBill.Taylor@Sun.COM 		    HERMON_CMD_NOSLEEP_SPIN);
9509517SBill.Taylor@Sun.COM 		if (status != HERMON_CMD_SUCCESS) {
9519517SBill.Taylor@Sun.COM 			mutex_exit(&state->hs_mcglock);
9529517SBill.Taylor@Sun.COM 			HERMON_WARNING(state, "failed to write MCG entry");
9539517SBill.Taylor@Sun.COM 			cmn_err(CE_CONT, "Hermon: WRITE_MGM command failed: "
9549517SBill.Taylor@Sun.COM 			    "%08x\n", status);
9559517SBill.Taylor@Sun.COM 			if (status == HERMON_CMD_INVALID_STATUS) {
9569517SBill.Taylor@Sun.COM 				hermon_fm_ereport(state, HCA_SYS_ERR,
9579517SBill.Taylor@Sun.COM 				    HCA_ERR_SRV_LOST);
9589517SBill.Taylor@Sun.COM 			}
9599517SBill.Taylor@Sun.COM 			return (ibc_get_ci_failure(0));
9609517SBill.Taylor@Sun.COM 		}
9619517SBill.Taylor@Sun.COM 
9629517SBill.Taylor@Sun.COM 		/*
9639517SBill.Taylor@Sun.COM 		 * Now that we know all the Hermon firmware accesses have been
9649517SBill.Taylor@Sun.COM 		 * successful, we update the current "shadow" MCG entry by
9659517SBill.Taylor@Sun.COM 		 * incrementing the "number of attached QPs" count.
9669517SBill.Taylor@Sun.COM 		 *
9679517SBill.Taylor@Sun.COM 		 * We increment only if the QP is not already part of the
9689517SBill.Taylor@Sun.COM 		 * MCG by checking the 'qp_found' flag returned
9699517SBill.Taylor@Sun.COM 		 * hermon_mcg_walk_mgid_hashfrom the qplist_add above.
9709517SBill.Taylor@Sun.COM 		 */
9719517SBill.Taylor@Sun.COM 		if (!qp_found) {
9729517SBill.Taylor@Sun.COM 			mcg->mcg_num_qps++;
9739517SBill.Taylor@Sun.COM 
9749517SBill.Taylor@Sun.COM 			/*
9759517SBill.Taylor@Sun.COM 			 * Increment the refcnt for this QP.  Because the QP
9769517SBill.Taylor@Sun.COM 			 * was added to this MCG, the refcnt must be
9779517SBill.Taylor@Sun.COM 			 * incremented.
9789517SBill.Taylor@Sun.COM 			 */
9799517SBill.Taylor@Sun.COM 			hermon_qp_mcg_refcnt_inc(qp);
9809517SBill.Taylor@Sun.COM 		}
9819517SBill.Taylor@Sun.COM 
9829517SBill.Taylor@Sun.COM 		/*
9839517SBill.Taylor@Sun.COM 		 * We drop the lock and return success.
9849517SBill.Taylor@Sun.COM 		 */
9859517SBill.Taylor@Sun.COM 		mutex_exit(&state->hs_mcglock);
9869517SBill.Taylor@Sun.COM 		return (DDI_SUCCESS);
9879517SBill.Taylor@Sun.COM 	}
9889517SBill.Taylor@Sun.COM 
9899517SBill.Taylor@Sun.COM 	/*
9909517SBill.Taylor@Sun.COM 	 * If we've reached here, then we're at the end of the hash chain.
9919517SBill.Taylor@Sun.COM 	 * We need to allocate a new MCG entry, fill it in, write it to Hermon,
9929517SBill.Taylor@Sun.COM 	 * and update the previous entry to link the new one to the end of the
9939517SBill.Taylor@Sun.COM 	 * chain.
9949517SBill.Taylor@Sun.COM 	 */
9959517SBill.Taylor@Sun.COM 
9969517SBill.Taylor@Sun.COM 	/*
9979517SBill.Taylor@Sun.COM 	 * Allocate an MCG table entry.  This will be filled in with all
9989517SBill.Taylor@Sun.COM 	 * the necessary parameters to define the multicast group.  Then it
9999517SBill.Taylor@Sun.COM 	 * will be written to the hardware in the next-to-last step below.
10009517SBill.Taylor@Sun.COM 	 */
10019517SBill.Taylor@Sun.COM 	status = hermon_rsrc_alloc(state, HERMON_MCG, 1, HERMON_NOSLEEP, &rsrc);
10029517SBill.Taylor@Sun.COM 	if (status != DDI_SUCCESS) {
10039517SBill.Taylor@Sun.COM 		mutex_exit(&state->hs_mcglock);
10049517SBill.Taylor@Sun.COM 		return (IBT_INSUFF_RESOURCE);
10059517SBill.Taylor@Sun.COM 	}
10069517SBill.Taylor@Sun.COM 
10079517SBill.Taylor@Sun.COM 	/*
10089517SBill.Taylor@Sun.COM 	 * Fill in the new entry in the "shadow" MCG list.  Note:  Just as
10099517SBill.Taylor@Sun.COM 	 * it does above, hermon_mcg_setup_new_hdr() also fills in a portion
10109517SBill.Taylor@Sun.COM 	 * of the temporary MCG entry (the rest of which will be filled in by
10119517SBill.Taylor@Sun.COM 	 * hermon_mcg_qplist_add() below)
10129517SBill.Taylor@Sun.COM 	 */
10139517SBill.Taylor@Sun.COM 	newmcg = &state->hs_mcghdl[rsrc->hr_indx];
10149517SBill.Taylor@Sun.COM 	hermon_mcg_setup_new_hdr(newmcg, mcg_entry, gid, rsrc);
10159517SBill.Taylor@Sun.COM 
10169517SBill.Taylor@Sun.COM 	/*
10179517SBill.Taylor@Sun.COM 	 * Try to add the new QP number to the list.  This routine fills in
10189517SBill.Taylor@Sun.COM 	 * the final necessary pieces of the temporary MCG.  The
10199517SBill.Taylor@Sun.COM 	 * "mcg_entry_qplist" pointer is used to point to the portion of the
10209517SBill.Taylor@Sun.COM 	 * temporary MCG that holds the QP numbers.  If we fail here, we
10219517SBill.Taylor@Sun.COM 	 * must undo the previous resource allocation.
10229517SBill.Taylor@Sun.COM 	 *
10239517SBill.Taylor@Sun.COM 	 * Note: hermon_mcg_qplist_add() can we return SUCCESS if it already
10249517SBill.Taylor@Sun.COM 	 * found the QP in the list.  In this case, the QP is not added on to
10259517SBill.Taylor@Sun.COM 	 * the list again.  Check the flag 'qp_found' if this value is needed
10269517SBill.Taylor@Sun.COM 	 * to be known.
10279517SBill.Taylor@Sun.COM 	 */
10289517SBill.Taylor@Sun.COM 	status = hermon_mcg_qplist_add(state, newmcg, mcg_entry_qplist, qp,
10299517SBill.Taylor@Sun.COM 	    &qp_found);
10309517SBill.Taylor@Sun.COM 	if (status != DDI_SUCCESS) {
10319517SBill.Taylor@Sun.COM 		bzero(newmcg, sizeof (struct hermon_sw_mcg_list_s));
10329517SBill.Taylor@Sun.COM 		hermon_rsrc_free(state, &rsrc);
10339517SBill.Taylor@Sun.COM 		mutex_exit(&state->hs_mcglock);
10349517SBill.Taylor@Sun.COM 		return (status);
10359517SBill.Taylor@Sun.COM 	}
10369517SBill.Taylor@Sun.COM 	mcg_entry->member_cnt = (newmcg->mcg_num_qps + 1);
10379517SBill.Taylor@Sun.COM 	    /* set the member count */
10389517SBill.Taylor@Sun.COM 
10399517SBill.Taylor@Sun.COM 	/*
10409517SBill.Taylor@Sun.COM 	 * Once the temporary MCG has been updated, write the entry into the
10419517SBill.Taylor@Sun.COM 	 * appropriate location in the Hermon MCG entry table.  If this is
10429517SBill.Taylor@Sun.COM 	 * successful, then we need to chain the previous entry to this one.
10439517SBill.Taylor@Sun.COM 	 * Note: In general, this operation shouldn't fail.  If it does, then
10449517SBill.Taylor@Sun.COM 	 * it is an indication that something (probably in HW, but maybe in
10459517SBill.Taylor@Sun.COM 	 * SW) has gone seriously wrong.
10469517SBill.Taylor@Sun.COM 	 */
10479517SBill.Taylor@Sun.COM 	status = hermon_write_mgm_cmd_post(state, mcg_entry, rsrc->hr_indx,
10489517SBill.Taylor@Sun.COM 	    HERMON_CMD_NOSLEEP_SPIN);
10499517SBill.Taylor@Sun.COM 	if (status != HERMON_CMD_SUCCESS) {
10509517SBill.Taylor@Sun.COM 		bzero(newmcg, sizeof (struct hermon_sw_mcg_list_s));
10519517SBill.Taylor@Sun.COM 		hermon_rsrc_free(state, &rsrc);
10529517SBill.Taylor@Sun.COM 		mutex_exit(&state->hs_mcglock);
10539517SBill.Taylor@Sun.COM 		HERMON_WARNING(state, "failed to write MCG entry");
10549517SBill.Taylor@Sun.COM 		cmn_err(CE_CONT, "Hermon: WRITE_MGM command failed: %08x\n",
10559517SBill.Taylor@Sun.COM 		    status);
10569517SBill.Taylor@Sun.COM 		if (status == HERMON_CMD_INVALID_STATUS) {
10579517SBill.Taylor@Sun.COM 			hermon_fm_ereport(state, HCA_SYS_ERR, HCA_ERR_SRV_LOST);
10589517SBill.Taylor@Sun.COM 		}
10599517SBill.Taylor@Sun.COM 		return (ibc_get_ci_failure(0));
10609517SBill.Taylor@Sun.COM 	}
10619517SBill.Taylor@Sun.COM 
10629517SBill.Taylor@Sun.COM 	/*
10639517SBill.Taylor@Sun.COM 	 * Now read the current MCG entry (the one previously at the end of
10649517SBill.Taylor@Sun.COM 	 * hash chain) into the temporary MCG.  We are going to update its
10659517SBill.Taylor@Sun.COM 	 * "next_gid_indx" now and write the entry back to the MCG table.
10669517SBill.Taylor@Sun.COM 	 * Note:  In general, this operation shouldn't fail.  If it does, then
10679517SBill.Taylor@Sun.COM 	 * it is an indication that something (probably in HW, but maybe in SW)
10689517SBill.Taylor@Sun.COM 	 * has gone seriously wrong.  We will free up the MCG entry resource,
10699517SBill.Taylor@Sun.COM 	 * but we will not undo the previously written MCG entry in the HW.
10709517SBill.Taylor@Sun.COM 	 * This is OK, though, because the MCG entry is not currently attached
10719517SBill.Taylor@Sun.COM 	 * to any hash chain.
10729517SBill.Taylor@Sun.COM 	 */
10739517SBill.Taylor@Sun.COM 	status = hermon_read_mgm_cmd_post(state, mcg_entry, end_indx,
10749517SBill.Taylor@Sun.COM 	    HERMON_CMD_NOSLEEP_SPIN);
10759517SBill.Taylor@Sun.COM 	if (status != HERMON_CMD_SUCCESS) {
10769517SBill.Taylor@Sun.COM 		bzero(newmcg, sizeof (struct hermon_sw_mcg_list_s));
10779517SBill.Taylor@Sun.COM 		hermon_rsrc_free(state, &rsrc);
10789517SBill.Taylor@Sun.COM 		mutex_exit(&state->hs_mcglock);
10799517SBill.Taylor@Sun.COM 		HERMON_WARNING(state, "failed to read MCG entry");
10809517SBill.Taylor@Sun.COM 		cmn_err(CE_CONT, "Hermon: READ_MGM command failed: %08x\n",
10819517SBill.Taylor@Sun.COM 		    status);
10829517SBill.Taylor@Sun.COM 		if (status == HERMON_CMD_INVALID_STATUS) {
10839517SBill.Taylor@Sun.COM 			hermon_fm_ereport(state, HCA_SYS_ERR, HCA_ERR_SRV_LOST);
10849517SBill.Taylor@Sun.COM 		}
10859517SBill.Taylor@Sun.COM 		return (ibc_get_ci_failure(0));
10869517SBill.Taylor@Sun.COM 	}
10879517SBill.Taylor@Sun.COM 
10889517SBill.Taylor@Sun.COM 	/*
10899517SBill.Taylor@Sun.COM 	 * Finally, we update the "next_gid_indx" field in the temporary MCG
10909517SBill.Taylor@Sun.COM 	 * and attempt to write the entry back into the Hermon MCG table.  If
10919517SBill.Taylor@Sun.COM 	 * this succeeds, then we update the "shadow" list to reflect the
10929517SBill.Taylor@Sun.COM 	 * change, drop the lock, and return success.  Note:  In general, this
10939517SBill.Taylor@Sun.COM 	 * operation shouldn't fail.  If it does, then it is an indication
10949517SBill.Taylor@Sun.COM 	 * that something (probably in HW, but maybe in SW) has gone seriously
10959517SBill.Taylor@Sun.COM 	 * wrong.  Just as we do above, we will free up the MCG entry resource,
10969517SBill.Taylor@Sun.COM 	 * but we will not try to undo the previously written MCG entry.  This
10979517SBill.Taylor@Sun.COM 	 * is OK, though, because (since we failed here to update the end of
10989517SBill.Taylor@Sun.COM 	 * the chain) that other entry is not currently attached to any chain.
10999517SBill.Taylor@Sun.COM 	 */
11009517SBill.Taylor@Sun.COM 	mcg_entry->next_gid_indx = rsrc->hr_indx;
11019517SBill.Taylor@Sun.COM 	status = hermon_write_mgm_cmd_post(state, mcg_entry, end_indx,
11029517SBill.Taylor@Sun.COM 	    HERMON_CMD_NOSLEEP_SPIN);
11039517SBill.Taylor@Sun.COM 	if (status != HERMON_CMD_SUCCESS) {
11049517SBill.Taylor@Sun.COM 		bzero(newmcg, sizeof (struct hermon_sw_mcg_list_s));
11059517SBill.Taylor@Sun.COM 		hermon_rsrc_free(state, &rsrc);
11069517SBill.Taylor@Sun.COM 		mutex_exit(&state->hs_mcglock);
11079517SBill.Taylor@Sun.COM 		HERMON_WARNING(state, "failed to write MCG entry");
11089517SBill.Taylor@Sun.COM 		cmn_err(CE_CONT, "Hermon: WRITE_MGM command failed: %08x\n",
11099517SBill.Taylor@Sun.COM 		    status);
11109517SBill.Taylor@Sun.COM 		if (status == HERMON_CMD_INVALID_STATUS) {
11119517SBill.Taylor@Sun.COM 			hermon_fm_ereport(state, HCA_SYS_ERR, HCA_ERR_SRV_LOST);
11129517SBill.Taylor@Sun.COM 		}
11139517SBill.Taylor@Sun.COM 		return (ibc_get_ci_failure(0));
11149517SBill.Taylor@Sun.COM 	}
11159517SBill.Taylor@Sun.COM 	mcg = &state->hs_mcghdl[end_indx];
11169517SBill.Taylor@Sun.COM 	mcg->mcg_next_indx = rsrc->hr_indx;
11179517SBill.Taylor@Sun.COM 
11189517SBill.Taylor@Sun.COM 	/*
11199517SBill.Taylor@Sun.COM 	 * Now that we know all the Hermon firmware accesses have been
11209517SBill.Taylor@Sun.COM 	 * successful, we update the new "shadow" MCG entry by incrementing
11219517SBill.Taylor@Sun.COM 	 * the "number of attached QPs" count.  Then we drop the lock and
11229517SBill.Taylor@Sun.COM 	 * return success.
11239517SBill.Taylor@Sun.COM 	 */
11249517SBill.Taylor@Sun.COM 	newmcg->mcg_num_qps++;
11259517SBill.Taylor@Sun.COM 
11269517SBill.Taylor@Sun.COM 	/*
11279517SBill.Taylor@Sun.COM 	 * Increment the refcnt for this QP.  Because the QP
11289517SBill.Taylor@Sun.COM 	 * was added to this MCG, the refcnt must be
11299517SBill.Taylor@Sun.COM 	 * incremented.
11309517SBill.Taylor@Sun.COM 	 */
11319517SBill.Taylor@Sun.COM 	hermon_qp_mcg_refcnt_inc(qp);
11329517SBill.Taylor@Sun.COM 
11339517SBill.Taylor@Sun.COM 	mutex_exit(&state->hs_mcglock);
11349517SBill.Taylor@Sun.COM 	return (DDI_SUCCESS);
11359517SBill.Taylor@Sun.COM }
11369517SBill.Taylor@Sun.COM 
11379517SBill.Taylor@Sun.COM 
11389517SBill.Taylor@Sun.COM /*
11399517SBill.Taylor@Sun.COM  * hermon_mcg_detach()
11409517SBill.Taylor@Sun.COM  *    Context: Can be called only from user or kernel context.
11419517SBill.Taylor@Sun.COM  */
11429517SBill.Taylor@Sun.COM int
hermon_mcg_detach(hermon_state_t * state,hermon_qphdl_t qp,ib_gid_t gid,ib_lid_t lid)11439517SBill.Taylor@Sun.COM hermon_mcg_detach(hermon_state_t *state, hermon_qphdl_t qp, ib_gid_t gid,
11449517SBill.Taylor@Sun.COM     ib_lid_t lid)
11459517SBill.Taylor@Sun.COM {
11469517SBill.Taylor@Sun.COM 	hermon_hw_mcg_t		*mcg_entry;
11479517SBill.Taylor@Sun.COM 	hermon_hw_mcg_qp_list_t	*mcg_entry_qplist;
11489517SBill.Taylor@Sun.COM 	hermon_mcghdl_t		mcg;
11499517SBill.Taylor@Sun.COM 	uint64_t		mgid_hash;
11509517SBill.Taylor@Sun.COM 	uint32_t		end_indx, prev_indx;
11519517SBill.Taylor@Sun.COM 	int			status;
11529517SBill.Taylor@Sun.COM 
11539517SBill.Taylor@Sun.COM 	/*
11549517SBill.Taylor@Sun.COM 	 * Check for invalid Multicast DLID.  Specifically, all Multicast
11559517SBill.Taylor@Sun.COM 	 * LIDs should be within a well defined range.  If the specified LID
11569517SBill.Taylor@Sun.COM 	 * is outside of that range, then return an error.
11579517SBill.Taylor@Sun.COM 	 */
11589517SBill.Taylor@Sun.COM 	if (hermon_mlid_is_valid(lid) == 0) {
11599517SBill.Taylor@Sun.COM 		return (IBT_MC_MLID_INVALID);
11609517SBill.Taylor@Sun.COM 	}
11619517SBill.Taylor@Sun.COM 
11629517SBill.Taylor@Sun.COM 	/*
11639517SBill.Taylor@Sun.COM 	 * Compute the MGID hash value.  As described above, the MCG table is
11649517SBill.Taylor@Sun.COM 	 * arranged as a number of separate hash chains.  This operation
11659517SBill.Taylor@Sun.COM 	 * converts the specified MGID into the starting index of an entry in
11669517SBill.Taylor@Sun.COM 	 * the hash table (i.e. the index for the start of the appropriate
11679517SBill.Taylor@Sun.COM 	 * hash chain).  Subsequent operations below will walk the chain
11689517SBill.Taylor@Sun.COM 	 * searching for a matching entry from which to attempt to remove
11699517SBill.Taylor@Sun.COM 	 * the specified QP.
11709517SBill.Taylor@Sun.COM 	 */
11719517SBill.Taylor@Sun.COM 	status = hermon_mgid_hash_cmd_post(state, gid.gid_prefix, gid.gid_guid,
11729517SBill.Taylor@Sun.COM 	    &mgid_hash, HERMON_SLEEPFLAG_FOR_CONTEXT());
11739517SBill.Taylor@Sun.COM 	if (status != HERMON_CMD_SUCCESS) {
11749517SBill.Taylor@Sun.COM 		cmn_err(CE_CONT, "Hermon: MGID_HASH command failed: %08x\n",
11759517SBill.Taylor@Sun.COM 		    status);
11769517SBill.Taylor@Sun.COM 		if (status == HERMON_CMD_INVALID_STATUS) {
11779517SBill.Taylor@Sun.COM 			hermon_fm_ereport(state, HCA_SYS_ERR, HCA_ERR_SRV_LOST);
11789517SBill.Taylor@Sun.COM 		}
11799517SBill.Taylor@Sun.COM 		return (ibc_get_ci_failure(0));
11809517SBill.Taylor@Sun.COM 	}
11819517SBill.Taylor@Sun.COM 
11829517SBill.Taylor@Sun.COM 	/*
11839517SBill.Taylor@Sun.COM 	 * Grab the multicast group mutex.  Then grab the pre-allocated
11849517SBill.Taylor@Sun.COM 	 * temporary buffer used for holding and/or modifying MCG entries.
11859517SBill.Taylor@Sun.COM 	 */
11869517SBill.Taylor@Sun.COM 	mutex_enter(&state->hs_mcglock);
11879517SBill.Taylor@Sun.COM 	mcg_entry = state->hs_mcgtmp;
11889517SBill.Taylor@Sun.COM 	mcg_entry_qplist = HERMON_MCG_GET_QPLIST_PTR(mcg_entry);
11899517SBill.Taylor@Sun.COM 
11909517SBill.Taylor@Sun.COM 	/*
11919517SBill.Taylor@Sun.COM 	 * Walk through the array of MCG entries starting at "mgid_hash".
11929517SBill.Taylor@Sun.COM 	 * Try to find an MCG entry with a matching MGID.  The
11939517SBill.Taylor@Sun.COM 	 * hermon_mcg_walk_mgid_hash() routine walks the list and returns an
11949517SBill.Taylor@Sun.COM 	 * index into the MCG table.  The entry at this index is checked to
11959517SBill.Taylor@Sun.COM 	 * determine whether it is a match or not.  If it is a match, then
11969517SBill.Taylor@Sun.COM 	 * we continue on to attempt to remove the QP from the MCG.  If it
11979517SBill.Taylor@Sun.COM 	 * is not a match (or not a valid MCG entry), then we return an error.
11989517SBill.Taylor@Sun.COM 	 */
11999517SBill.Taylor@Sun.COM 	end_indx = hermon_mcg_walk_mgid_hash(state, mgid_hash, gid, &prev_indx);
12009517SBill.Taylor@Sun.COM 	mcg	 = &state->hs_mcghdl[end_indx];
12019517SBill.Taylor@Sun.COM 
12029517SBill.Taylor@Sun.COM 	/*
12039517SBill.Taylor@Sun.COM 	 * If MGID == 0 (the hash chain is empty) or if the specified MGID
12049517SBill.Taylor@Sun.COM 	 * does not match the MGID in the current entry, then return
12059517SBill.Taylor@Sun.COM 	 * IBT_MC_MGID_INVALID (to indicate that the specified MGID is not
12069517SBill.Taylor@Sun.COM 	 * valid).
12079517SBill.Taylor@Sun.COM 	 */
12089517SBill.Taylor@Sun.COM 	if (((mcg->mcg_mgid_h == 0) && (mcg->mcg_mgid_l == 0)) ||
12099517SBill.Taylor@Sun.COM 	    ((mcg->mcg_mgid_h != gid.gid_prefix) ||
12109517SBill.Taylor@Sun.COM 	    (mcg->mcg_mgid_l != gid.gid_guid))) {
12119517SBill.Taylor@Sun.COM 		mutex_exit(&state->hs_mcglock);
12129517SBill.Taylor@Sun.COM 		return (IBT_MC_MGID_INVALID);
12139517SBill.Taylor@Sun.COM 	}
12149517SBill.Taylor@Sun.COM 
12159517SBill.Taylor@Sun.COM 	/*
12169517SBill.Taylor@Sun.COM 	 * Read the current MCG entry into the temporary MCG.  Note: In
12179517SBill.Taylor@Sun.COM 	 * general, this operation shouldn't fail.  If it does, then it is
12189517SBill.Taylor@Sun.COM 	 * an indication that something (probably in HW, but maybe in SW)
12199517SBill.Taylor@Sun.COM 	 * has gone seriously wrong.
12209517SBill.Taylor@Sun.COM 	 */
12219517SBill.Taylor@Sun.COM 	status = hermon_read_mgm_cmd_post(state, mcg_entry, end_indx,
12229517SBill.Taylor@Sun.COM 	    HERMON_CMD_NOSLEEP_SPIN);
12239517SBill.Taylor@Sun.COM 	if (status != HERMON_CMD_SUCCESS) {
12249517SBill.Taylor@Sun.COM 		mutex_exit(&state->hs_mcglock);
12259517SBill.Taylor@Sun.COM 		HERMON_WARNING(state, "failed to read MCG entry");
12269517SBill.Taylor@Sun.COM 		cmn_err(CE_CONT, "Hermon: READ_MGM command failed: %08x\n",
12279517SBill.Taylor@Sun.COM 		    status);
12289517SBill.Taylor@Sun.COM 		if (status == HERMON_CMD_INVALID_STATUS) {
12299517SBill.Taylor@Sun.COM 			hermon_fm_ereport(state, HCA_SYS_ERR, HCA_ERR_SRV_LOST);
12309517SBill.Taylor@Sun.COM 		}
12319517SBill.Taylor@Sun.COM 		return (ibc_get_ci_failure(0));
12329517SBill.Taylor@Sun.COM 	}
12339517SBill.Taylor@Sun.COM 
12349517SBill.Taylor@Sun.COM 	/*
12359517SBill.Taylor@Sun.COM 	 * Search the QP number list for a match.  If a match is found, then
12369517SBill.Taylor@Sun.COM 	 * remove the entry from the QP list.  Otherwise, if no match is found,
12379517SBill.Taylor@Sun.COM 	 * return an error.
12389517SBill.Taylor@Sun.COM 	 */
12399517SBill.Taylor@Sun.COM 	status = hermon_mcg_qplist_remove(mcg, mcg_entry_qplist, qp);
12409517SBill.Taylor@Sun.COM 	if (status != DDI_SUCCESS) {
12419517SBill.Taylor@Sun.COM 		mutex_exit(&state->hs_mcglock);
12429517SBill.Taylor@Sun.COM 		return (status);
12439517SBill.Taylor@Sun.COM 	}
12449517SBill.Taylor@Sun.COM 
12459517SBill.Taylor@Sun.COM 	/*
12469517SBill.Taylor@Sun.COM 	 * Decrement the MCG count for this QP.  When the 'qp_mcg'
12479517SBill.Taylor@Sun.COM 	 * field becomes 0, then this QP is no longer a member of any
12489517SBill.Taylor@Sun.COM 	 * MCG.
12499517SBill.Taylor@Sun.COM 	 */
12509517SBill.Taylor@Sun.COM 	hermon_qp_mcg_refcnt_dec(qp);
12519517SBill.Taylor@Sun.COM 
12529517SBill.Taylor@Sun.COM 	/*
12539517SBill.Taylor@Sun.COM 	 * If the current MCG's QP number list is about to be made empty
12549517SBill.Taylor@Sun.COM 	 * ("mcg_num_qps" == 1), then remove the entry itself from the hash
12559517SBill.Taylor@Sun.COM 	 * chain.  Otherwise, just write the updated MCG entry back to the
12569517SBill.Taylor@Sun.COM 	 * hardware.  In either case, once we successfully update the hardware
12579517SBill.Taylor@Sun.COM 	 * chain, then we decrement the "shadow" list entry's "mcg_num_qps"
12589517SBill.Taylor@Sun.COM 	 * count (or zero out the entire "shadow" list entry) before returning
12599517SBill.Taylor@Sun.COM 	 * success.  Note:  Zeroing out the "shadow" list entry is done
12609517SBill.Taylor@Sun.COM 	 * inside of hermon_mcg_hash_list_remove().
12619517SBill.Taylor@Sun.COM 	 */
12629517SBill.Taylor@Sun.COM 	if (mcg->mcg_num_qps == 1) {
12639517SBill.Taylor@Sun.COM 
12649517SBill.Taylor@Sun.COM 		/* Remove an MCG entry from the hash chain */
12659517SBill.Taylor@Sun.COM 		status = hermon_mcg_hash_list_remove(state, end_indx, prev_indx,
12669517SBill.Taylor@Sun.COM 		    mcg_entry);
12679517SBill.Taylor@Sun.COM 		if (status != DDI_SUCCESS) {
12689517SBill.Taylor@Sun.COM 			mutex_exit(&state->hs_mcglock);
12699517SBill.Taylor@Sun.COM 			return (status);
12709517SBill.Taylor@Sun.COM 		}
12719517SBill.Taylor@Sun.COM 
12729517SBill.Taylor@Sun.COM 	} else {
12739517SBill.Taylor@Sun.COM 		/*
12749517SBill.Taylor@Sun.COM 		 * Write the updated MCG entry back to the Hermon MCG table.
12759517SBill.Taylor@Sun.COM 		 * If this succeeds, then we update the "shadow" list to
12769517SBill.Taylor@Sun.COM 		 * reflect the change (i.e. decrement the "mcg_num_qps"),
12779517SBill.Taylor@Sun.COM 		 * drop the lock, and return success.  Note:  In general,
12789517SBill.Taylor@Sun.COM 		 * this operation shouldn't fail.  If it does, then it is an
12799517SBill.Taylor@Sun.COM 		 * indication that something (probably in HW, but maybe in SW)
12809517SBill.Taylor@Sun.COM 		 * has gone seriously wrong.
12819517SBill.Taylor@Sun.COM 		 */
12829517SBill.Taylor@Sun.COM 		mcg_entry->member_cnt = (mcg->mcg_num_qps - 1);
12839517SBill.Taylor@Sun.COM 		status = hermon_write_mgm_cmd_post(state, mcg_entry, end_indx,
12849517SBill.Taylor@Sun.COM 		    HERMON_CMD_NOSLEEP_SPIN);
12859517SBill.Taylor@Sun.COM 		if (status != HERMON_CMD_SUCCESS) {
12869517SBill.Taylor@Sun.COM 			mutex_exit(&state->hs_mcglock);
12879517SBill.Taylor@Sun.COM 			HERMON_WARNING(state, "failed to write MCG entry");
12889517SBill.Taylor@Sun.COM 			cmn_err(CE_CONT, "Hermon: WRITE_MGM command failed: "
12899517SBill.Taylor@Sun.COM 			    "%08x\n", status);
12909517SBill.Taylor@Sun.COM 			if (status == HERMON_CMD_INVALID_STATUS) {
12919517SBill.Taylor@Sun.COM 				hermon_fm_ereport(state, HCA_SYS_ERR,
12929517SBill.Taylor@Sun.COM 				    HCA_ERR_SRV_LOST);
12939517SBill.Taylor@Sun.COM 			}
12949517SBill.Taylor@Sun.COM 			return (ibc_get_ci_failure(0));
12959517SBill.Taylor@Sun.COM 		}
12969517SBill.Taylor@Sun.COM 		mcg->mcg_num_qps--;
12979517SBill.Taylor@Sun.COM 	}
12989517SBill.Taylor@Sun.COM 
12999517SBill.Taylor@Sun.COM 	mutex_exit(&state->hs_mcglock);
13009517SBill.Taylor@Sun.COM 	return (DDI_SUCCESS);
13019517SBill.Taylor@Sun.COM }
13029517SBill.Taylor@Sun.COM 
13039517SBill.Taylor@Sun.COM /*
13049517SBill.Taylor@Sun.COM  * hermon_qp_mcg_refcnt_inc()
13059517SBill.Taylor@Sun.COM  *    Context: Can be called from interrupt or base context.
13069517SBill.Taylor@Sun.COM  */
13079517SBill.Taylor@Sun.COM static void
hermon_qp_mcg_refcnt_inc(hermon_qphdl_t qp)13089517SBill.Taylor@Sun.COM hermon_qp_mcg_refcnt_inc(hermon_qphdl_t qp)
13099517SBill.Taylor@Sun.COM {
13109517SBill.Taylor@Sun.COM 	/* Increment the QP's MCG reference count */
13119517SBill.Taylor@Sun.COM 	mutex_enter(&qp->qp_lock);
13129517SBill.Taylor@Sun.COM 	qp->qp_mcg_refcnt++;
13139517SBill.Taylor@Sun.COM 	mutex_exit(&qp->qp_lock);
13149517SBill.Taylor@Sun.COM }
13159517SBill.Taylor@Sun.COM 
13169517SBill.Taylor@Sun.COM 
13179517SBill.Taylor@Sun.COM /*
13189517SBill.Taylor@Sun.COM  * hermon_qp_mcg_refcnt_dec()
13199517SBill.Taylor@Sun.COM  *    Context: Can be called from interrupt or base context.
13209517SBill.Taylor@Sun.COM  */
13219517SBill.Taylor@Sun.COM static void
hermon_qp_mcg_refcnt_dec(hermon_qphdl_t qp)13229517SBill.Taylor@Sun.COM hermon_qp_mcg_refcnt_dec(hermon_qphdl_t qp)
13239517SBill.Taylor@Sun.COM {
13249517SBill.Taylor@Sun.COM 	/* Decrement the QP's MCG reference count */
13259517SBill.Taylor@Sun.COM 	mutex_enter(&qp->qp_lock);
13269517SBill.Taylor@Sun.COM 	qp->qp_mcg_refcnt--;
13279517SBill.Taylor@Sun.COM 	mutex_exit(&qp->qp_lock);
13289517SBill.Taylor@Sun.COM }
13299517SBill.Taylor@Sun.COM 
13309517SBill.Taylor@Sun.COM 
13319517SBill.Taylor@Sun.COM /*
13329517SBill.Taylor@Sun.COM  * hermon_mcg_qplist_add()
13339517SBill.Taylor@Sun.COM  *    Context: Can be called from interrupt or base context.
13349517SBill.Taylor@Sun.COM  */
13359517SBill.Taylor@Sun.COM 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)13369517SBill.Taylor@Sun.COM hermon_mcg_qplist_add(hermon_state_t *state, hermon_mcghdl_t mcg,
13379517SBill.Taylor@Sun.COM     hermon_hw_mcg_qp_list_t *mcg_qplist, hermon_qphdl_t qp,
13389517SBill.Taylor@Sun.COM     uint_t *qp_found)
13399517SBill.Taylor@Sun.COM {
13409517SBill.Taylor@Sun.COM 	uint_t		qplist_indx;
13419517SBill.Taylor@Sun.COM 
13429517SBill.Taylor@Sun.COM 	ASSERT(MUTEX_HELD(&state->hs_mcglock));
13439517SBill.Taylor@Sun.COM 
13449517SBill.Taylor@Sun.COM 	qplist_indx = mcg->mcg_num_qps;
13459517SBill.Taylor@Sun.COM 
13469517SBill.Taylor@Sun.COM 	/*
13479517SBill.Taylor@Sun.COM 	 * Determine if we have exceeded the maximum number of QP per
13489517SBill.Taylor@Sun.COM 	 * multicast group.  If we have, then return an error
13499517SBill.Taylor@Sun.COM 	 */
13509517SBill.Taylor@Sun.COM 	if (qplist_indx >= state->hs_cfg_profile->cp_num_qp_per_mcg) {
13519517SBill.Taylor@Sun.COM 		return (IBT_HCA_MCG_QP_EXCEEDED);
13529517SBill.Taylor@Sun.COM 	}
13539517SBill.Taylor@Sun.COM 
13549517SBill.Taylor@Sun.COM 	/*
13559517SBill.Taylor@Sun.COM 	 * Determine if the QP is already attached to this MCG table.  If it
13569517SBill.Taylor@Sun.COM 	 * is, then we break out and treat this operation as a NO-OP
13579517SBill.Taylor@Sun.COM 	 */
13589517SBill.Taylor@Sun.COM 	for (qplist_indx = 0; qplist_indx < mcg->mcg_num_qps;
13599517SBill.Taylor@Sun.COM 	    qplist_indx++) {
13609517SBill.Taylor@Sun.COM 		if (mcg_qplist[qplist_indx].qpn == qp->qp_qpnum) {
13619517SBill.Taylor@Sun.COM 			break;
13629517SBill.Taylor@Sun.COM 		}
13639517SBill.Taylor@Sun.COM 	}
13649517SBill.Taylor@Sun.COM 
13659517SBill.Taylor@Sun.COM 	/*
13669517SBill.Taylor@Sun.COM 	 * If the QP was already on the list, set 'qp_found' to TRUE.  We still
13679517SBill.Taylor@Sun.COM 	 * return SUCCESS in this case, but the qplist will not have been
13689517SBill.Taylor@Sun.COM 	 * updated because the QP was already on the list.
13699517SBill.Taylor@Sun.COM 	 */
13709517SBill.Taylor@Sun.COM 	if (qplist_indx < mcg->mcg_num_qps) {
13719517SBill.Taylor@Sun.COM 		*qp_found = 1;
13729517SBill.Taylor@Sun.COM 	} else {
13739517SBill.Taylor@Sun.COM 		/*
13749517SBill.Taylor@Sun.COM 		 * Otherwise, append the new QP number to the end of the
13759517SBill.Taylor@Sun.COM 		 * current QP list.  Note: We will increment the "mcg_num_qps"
13769517SBill.Taylor@Sun.COM 		 * field on the "shadow" MCG list entry later (after we know
13779517SBill.Taylor@Sun.COM 		 * that all necessary Hermon firmware accesses have been
13789517SBill.Taylor@Sun.COM 		 * successful).
13799517SBill.Taylor@Sun.COM 		 *
13809517SBill.Taylor@Sun.COM 		 * Set 'qp_found' to 0 so we know the QP was added on to the
13819517SBill.Taylor@Sun.COM 		 * list for sure.
13829517SBill.Taylor@Sun.COM 		 */
13839517SBill.Taylor@Sun.COM 		mcg_qplist[qplist_indx].qpn =
13849517SBill.Taylor@Sun.COM 		    (qp->qp_qpnum | HERMON_MCG_QPN_BLOCK_LB);
13859517SBill.Taylor@Sun.COM 		*qp_found = 0;
13869517SBill.Taylor@Sun.COM 	}
13879517SBill.Taylor@Sun.COM 
13889517SBill.Taylor@Sun.COM 	return (DDI_SUCCESS);
13899517SBill.Taylor@Sun.COM }
13909517SBill.Taylor@Sun.COM 
13919517SBill.Taylor@Sun.COM 
13929517SBill.Taylor@Sun.COM 
13939517SBill.Taylor@Sun.COM /*
13949517SBill.Taylor@Sun.COM  * hermon_mcg_qplist_remove()
13959517SBill.Taylor@Sun.COM  *    Context: Can be called from interrupt or base context.
13969517SBill.Taylor@Sun.COM  */
13979517SBill.Taylor@Sun.COM static int
hermon_mcg_qplist_remove(hermon_mcghdl_t mcg,hermon_hw_mcg_qp_list_t * mcg_qplist,hermon_qphdl_t qp)13989517SBill.Taylor@Sun.COM hermon_mcg_qplist_remove(hermon_mcghdl_t mcg,
13999517SBill.Taylor@Sun.COM     hermon_hw_mcg_qp_list_t *mcg_qplist, hermon_qphdl_t qp)
14009517SBill.Taylor@Sun.COM {
14019517SBill.Taylor@Sun.COM 	uint_t		i, qplist_indx;
14029517SBill.Taylor@Sun.COM 
14039517SBill.Taylor@Sun.COM 	/*
14049517SBill.Taylor@Sun.COM 	 * Search the MCG QP list for a matching QPN.  When
14059517SBill.Taylor@Sun.COM 	 * it's found, we swap the last entry with the current
14069517SBill.Taylor@Sun.COM 	 * one, set the last entry to zero, decrement the last
14079517SBill.Taylor@Sun.COM 	 * entry, and return.  If it's not found, then it's
14089517SBill.Taylor@Sun.COM 	 * and error.
14099517SBill.Taylor@Sun.COM 	 */
14109517SBill.Taylor@Sun.COM 	qplist_indx = mcg->mcg_num_qps;
14119517SBill.Taylor@Sun.COM 	for (i = 0; i < qplist_indx; i++) {
14129517SBill.Taylor@Sun.COM 		if (mcg_qplist[i].qpn == qp->qp_qpnum) {
14139517SBill.Taylor@Sun.COM 			mcg_qplist[i] = mcg_qplist[qplist_indx - 1];
14149517SBill.Taylor@Sun.COM 			mcg_qplist[qplist_indx - 1].qpn = 0;
14159517SBill.Taylor@Sun.COM 
14169517SBill.Taylor@Sun.COM 			return (DDI_SUCCESS);
14179517SBill.Taylor@Sun.COM 		}
14189517SBill.Taylor@Sun.COM 	}
14199517SBill.Taylor@Sun.COM 
14209517SBill.Taylor@Sun.COM 	return (IBT_QP_HDL_INVALID);
14219517SBill.Taylor@Sun.COM }
14229517SBill.Taylor@Sun.COM 
14239517SBill.Taylor@Sun.COM 
14249517SBill.Taylor@Sun.COM /*
14259517SBill.Taylor@Sun.COM  * hermon_mcg_walk_mgid_hash()
14269517SBill.Taylor@Sun.COM  *    Context: Can be called from interrupt or base context.
14279517SBill.Taylor@Sun.COM  */
14289517SBill.Taylor@Sun.COM static uint_t
hermon_mcg_walk_mgid_hash(hermon_state_t * state,uint64_t start_indx,ib_gid_t mgid,uint_t * p_indx)14299517SBill.Taylor@Sun.COM hermon_mcg_walk_mgid_hash(hermon_state_t *state, uint64_t start_indx,
14309517SBill.Taylor@Sun.COM     ib_gid_t mgid, uint_t *p_indx)
14319517SBill.Taylor@Sun.COM {
14329517SBill.Taylor@Sun.COM 	hermon_mcghdl_t	curr_mcghdl;
14339517SBill.Taylor@Sun.COM 	uint_t		curr_indx, prev_indx;
14349517SBill.Taylor@Sun.COM 
14359517SBill.Taylor@Sun.COM 	ASSERT(MUTEX_HELD(&state->hs_mcglock));
14369517SBill.Taylor@Sun.COM 
14379517SBill.Taylor@Sun.COM 	/* Start at the head of the hash chain */
14389517SBill.Taylor@Sun.COM 	curr_indx   = (uint_t)start_indx;
14399517SBill.Taylor@Sun.COM 	prev_indx   = curr_indx;
14409517SBill.Taylor@Sun.COM 	curr_mcghdl = &state->hs_mcghdl[curr_indx];
14419517SBill.Taylor@Sun.COM 
14429517SBill.Taylor@Sun.COM 	/* If the first entry in the chain has MGID == 0, then stop */
14439517SBill.Taylor@Sun.COM 	if ((curr_mcghdl->mcg_mgid_h == 0) &&
14449517SBill.Taylor@Sun.COM 	    (curr_mcghdl->mcg_mgid_l == 0)) {
14459517SBill.Taylor@Sun.COM 		goto end_mgid_hash_walk;
14469517SBill.Taylor@Sun.COM 	}
14479517SBill.Taylor@Sun.COM 
14489517SBill.Taylor@Sun.COM 	/* If the first entry in the chain matches the MGID, then stop */
14499517SBill.Taylor@Sun.COM 	if ((curr_mcghdl->mcg_mgid_h == mgid.gid_prefix) &&
14509517SBill.Taylor@Sun.COM 	    (curr_mcghdl->mcg_mgid_l == mgid.gid_guid)) {
14519517SBill.Taylor@Sun.COM 		goto end_mgid_hash_walk;
14529517SBill.Taylor@Sun.COM 	}
14539517SBill.Taylor@Sun.COM 
14549517SBill.Taylor@Sun.COM 	/* Otherwise, walk the hash chain looking for a match */
14559517SBill.Taylor@Sun.COM 	while (curr_mcghdl->mcg_next_indx != 0) {
14569517SBill.Taylor@Sun.COM 		prev_indx = curr_indx;
14579517SBill.Taylor@Sun.COM 		curr_indx = curr_mcghdl->mcg_next_indx;
14589517SBill.Taylor@Sun.COM 		curr_mcghdl = &state->hs_mcghdl[curr_indx];
14599517SBill.Taylor@Sun.COM 
14609517SBill.Taylor@Sun.COM 		if ((curr_mcghdl->mcg_mgid_h == mgid.gid_prefix) &&
14619517SBill.Taylor@Sun.COM 		    (curr_mcghdl->mcg_mgid_l == mgid.gid_guid)) {
14629517SBill.Taylor@Sun.COM 			break;
14639517SBill.Taylor@Sun.COM 		}
14649517SBill.Taylor@Sun.COM 	}
14659517SBill.Taylor@Sun.COM 
14669517SBill.Taylor@Sun.COM end_mgid_hash_walk:
14679517SBill.Taylor@Sun.COM 	/*
14689517SBill.Taylor@Sun.COM 	 * If necessary, return the index of the previous entry too.  This
14699517SBill.Taylor@Sun.COM 	 * is primarily used for detaching a QP from a multicast group.  It
14709517SBill.Taylor@Sun.COM 	 * may be necessary, in that case, to delete an MCG entry from the
14719517SBill.Taylor@Sun.COM 	 * hash chain and having the index of the previous entry is helpful.
14729517SBill.Taylor@Sun.COM 	 */
14739517SBill.Taylor@Sun.COM 	if (p_indx != NULL) {
14749517SBill.Taylor@Sun.COM 		*p_indx = prev_indx;
14759517SBill.Taylor@Sun.COM 	}
14769517SBill.Taylor@Sun.COM 	return (curr_indx);
14779517SBill.Taylor@Sun.COM }
14789517SBill.Taylor@Sun.COM 
14799517SBill.Taylor@Sun.COM 
14809517SBill.Taylor@Sun.COM /*
14819517SBill.Taylor@Sun.COM  * hermon_mcg_setup_new_hdr()
14829517SBill.Taylor@Sun.COM  *    Context: Can be called from interrupt or base context.
14839517SBill.Taylor@Sun.COM  */
14849517SBill.Taylor@Sun.COM 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)14859517SBill.Taylor@Sun.COM hermon_mcg_setup_new_hdr(hermon_mcghdl_t mcg, hermon_hw_mcg_t *mcg_hdr,
14869517SBill.Taylor@Sun.COM     ib_gid_t mgid, hermon_rsrc_t *mcg_rsrc)
14879517SBill.Taylor@Sun.COM {
14889517SBill.Taylor@Sun.COM 	/*
14899517SBill.Taylor@Sun.COM 	 * Fill in the fields of the "shadow" entry used by software
14909517SBill.Taylor@Sun.COM 	 * to track MCG hardware entry
14919517SBill.Taylor@Sun.COM 	 */
14929517SBill.Taylor@Sun.COM 	mcg->mcg_mgid_h	   = mgid.gid_prefix;
14939517SBill.Taylor@Sun.COM 	mcg->mcg_mgid_l	   = mgid.gid_guid;
14949517SBill.Taylor@Sun.COM 	mcg->mcg_rsrcp	   = mcg_rsrc;
14959517SBill.Taylor@Sun.COM 	mcg->mcg_next_indx = 0;
14969517SBill.Taylor@Sun.COM 	mcg->mcg_num_qps   = 0;
14979517SBill.Taylor@Sun.COM 
14989517SBill.Taylor@Sun.COM 	/*
14999517SBill.Taylor@Sun.COM 	 * Fill the header fields of the MCG entry (in the temporary copy)
15009517SBill.Taylor@Sun.COM 	 */
15019517SBill.Taylor@Sun.COM 	mcg_hdr->mgid_h		= mgid.gid_prefix;
15029517SBill.Taylor@Sun.COM 	mcg_hdr->mgid_l		= mgid.gid_guid;
15039517SBill.Taylor@Sun.COM 	mcg_hdr->next_gid_indx	= 0;
15049517SBill.Taylor@Sun.COM }
15059517SBill.Taylor@Sun.COM 
15069517SBill.Taylor@Sun.COM 
15079517SBill.Taylor@Sun.COM /*
15089517SBill.Taylor@Sun.COM  * hermon_mcg_hash_list_remove()
15099517SBill.Taylor@Sun.COM  *    Context: Can be called only from user or kernel context.
15109517SBill.Taylor@Sun.COM  */
15119517SBill.Taylor@Sun.COM 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)15129517SBill.Taylor@Sun.COM hermon_mcg_hash_list_remove(hermon_state_t *state, uint_t curr_indx,
15139517SBill.Taylor@Sun.COM     uint_t prev_indx, hermon_hw_mcg_t *mcg_entry)
15149517SBill.Taylor@Sun.COM {
15159517SBill.Taylor@Sun.COM 	hermon_mcghdl_t		curr_mcg, prev_mcg, next_mcg;
15169517SBill.Taylor@Sun.COM 	uint_t			next_indx;
15179517SBill.Taylor@Sun.COM 	int			status;
15189517SBill.Taylor@Sun.COM 
15199517SBill.Taylor@Sun.COM 	/* Get the pointer to "shadow" list for current entry */
15209517SBill.Taylor@Sun.COM 	curr_mcg = &state->hs_mcghdl[curr_indx];
15219517SBill.Taylor@Sun.COM 
15229517SBill.Taylor@Sun.COM 	/*
15239517SBill.Taylor@Sun.COM 	 * If this is the first entry on a hash chain, then attempt to replace
15249517SBill.Taylor@Sun.COM 	 * the entry with the next entry on the chain.  If there are no
15259517SBill.Taylor@Sun.COM 	 * subsequent entries on the chain, then this is the only entry and
15269517SBill.Taylor@Sun.COM 	 * should be invalidated.
15279517SBill.Taylor@Sun.COM 	 */
15289517SBill.Taylor@Sun.COM 	if (curr_indx == prev_indx) {
15299517SBill.Taylor@Sun.COM 
15309517SBill.Taylor@Sun.COM 		/*
15319517SBill.Taylor@Sun.COM 		 * If this is the only entry on the chain, then invalidate it.
15329517SBill.Taylor@Sun.COM 		 * Note:  Invalidating an MCG entry means writing all zeros
15339517SBill.Taylor@Sun.COM 		 * to the entry.  This is only necessary for those MCG
15349517SBill.Taylor@Sun.COM 		 * entries that are the "head" entries of the individual hash
15359517SBill.Taylor@Sun.COM 		 * chains.  Regardless of whether this operation returns
15369517SBill.Taylor@Sun.COM 		 * success or failure, return that result to the caller.
15379517SBill.Taylor@Sun.COM 		 */
15389517SBill.Taylor@Sun.COM 		next_indx = curr_mcg->mcg_next_indx;
15399517SBill.Taylor@Sun.COM 		if (next_indx == 0) {
15409517SBill.Taylor@Sun.COM 			status = hermon_mcg_entry_invalidate(state, mcg_entry,
15419517SBill.Taylor@Sun.COM 			    curr_indx);
15429517SBill.Taylor@Sun.COM 			bzero(curr_mcg, sizeof (struct hermon_sw_mcg_list_s));
15439517SBill.Taylor@Sun.COM 			return (status);
15449517SBill.Taylor@Sun.COM 		}
15459517SBill.Taylor@Sun.COM 
15469517SBill.Taylor@Sun.COM 		/*
15479517SBill.Taylor@Sun.COM 		 * Otherwise, this is just the first entry on the chain, so
15489517SBill.Taylor@Sun.COM 		 * grab the next one
15499517SBill.Taylor@Sun.COM 		 */
15509517SBill.Taylor@Sun.COM 		next_mcg = &state->hs_mcghdl[next_indx];
15519517SBill.Taylor@Sun.COM 
15529517SBill.Taylor@Sun.COM 		/*
15539517SBill.Taylor@Sun.COM 		 * Read the next MCG entry into the temporary MCG.  Note:
15549517SBill.Taylor@Sun.COM 		 * In general, this operation shouldn't fail.  If it does,
15559517SBill.Taylor@Sun.COM 		 * then it is an indication that something (probably in HW,
15569517SBill.Taylor@Sun.COM 		 * but maybe in SW) has gone seriously wrong.
15579517SBill.Taylor@Sun.COM 		 */
15589517SBill.Taylor@Sun.COM 		status = hermon_read_mgm_cmd_post(state, mcg_entry, next_indx,
15599517SBill.Taylor@Sun.COM 		    HERMON_CMD_NOSLEEP_SPIN);
15609517SBill.Taylor@Sun.COM 		if (status != HERMON_CMD_SUCCESS) {
15619517SBill.Taylor@Sun.COM 			HERMON_WARNING(state, "failed to read MCG entry");
15629517SBill.Taylor@Sun.COM 			cmn_err(CE_CONT, "Hermon: READ_MGM command failed: "
15639517SBill.Taylor@Sun.COM 			    "%08x\n", status);
15649517SBill.Taylor@Sun.COM 			if (status == HERMON_CMD_INVALID_STATUS) {
15659517SBill.Taylor@Sun.COM 				hermon_fm_ereport(state, HCA_SYS_ERR,
15669517SBill.Taylor@Sun.COM 				    HCA_ERR_SRV_LOST);
15679517SBill.Taylor@Sun.COM 			}
15689517SBill.Taylor@Sun.COM 			return (ibc_get_ci_failure(0));
15699517SBill.Taylor@Sun.COM 		}
15709517SBill.Taylor@Sun.COM 
15719517SBill.Taylor@Sun.COM 		/*
15729517SBill.Taylor@Sun.COM 		 * Copy/Write the temporary MCG back to the hardware MCG list
15739517SBill.Taylor@Sun.COM 		 * using the current index.  This essentially removes the
15749517SBill.Taylor@Sun.COM 		 * current MCG entry from the list by writing over it with
15759517SBill.Taylor@Sun.COM 		 * the next one.  If this is successful, then we can do the
15769517SBill.Taylor@Sun.COM 		 * same operation for the "shadow" list.  And we can also
15779517SBill.Taylor@Sun.COM 		 * free up the Hermon MCG entry resource that was associated
15789517SBill.Taylor@Sun.COM 		 * with the (old) next entry.  Note:  In general, this
15799517SBill.Taylor@Sun.COM 		 * operation shouldn't fail.  If it does, then it is an
15809517SBill.Taylor@Sun.COM 		 * indication that something (probably in HW, but maybe in SW)
15819517SBill.Taylor@Sun.COM 		 * has gone seriously wrong.
15829517SBill.Taylor@Sun.COM 		 */
15839517SBill.Taylor@Sun.COM 		status = hermon_write_mgm_cmd_post(state, mcg_entry, curr_indx,
15849517SBill.Taylor@Sun.COM 		    HERMON_CMD_NOSLEEP_SPIN);
15859517SBill.Taylor@Sun.COM 		if (status != HERMON_CMD_SUCCESS) {
15869517SBill.Taylor@Sun.COM 			HERMON_WARNING(state, "failed to write MCG entry");
15879517SBill.Taylor@Sun.COM 			cmn_err(CE_CONT, "Hermon: WRITE_MGM command failed: "
15889517SBill.Taylor@Sun.COM 			    "%08x\n", status);
15899517SBill.Taylor@Sun.COM 			if (status == HERMON_CMD_INVALID_STATUS) {
15909517SBill.Taylor@Sun.COM 				hermon_fm_ereport(state, HCA_SYS_ERR,
15919517SBill.Taylor@Sun.COM 				    HCA_ERR_SRV_LOST);
15929517SBill.Taylor@Sun.COM 			}
15939517SBill.Taylor@Sun.COM 			return (ibc_get_ci_failure(0));
15949517SBill.Taylor@Sun.COM 		}
15959517SBill.Taylor@Sun.COM 
15969517SBill.Taylor@Sun.COM 		/*
15979517SBill.Taylor@Sun.COM 		 * Copy all the software tracking information from the next
15989517SBill.Taylor@Sun.COM 		 * entry on the "shadow" MCG list into the current entry on
15999517SBill.Taylor@Sun.COM 		 * the list.  Then invalidate (zero out) the other "shadow"
16009517SBill.Taylor@Sun.COM 		 * list entry.
16019517SBill.Taylor@Sun.COM 		 */
16029517SBill.Taylor@Sun.COM 		bcopy(next_mcg, curr_mcg, sizeof (struct hermon_sw_mcg_list_s));
16039517SBill.Taylor@Sun.COM 		bzero(next_mcg, sizeof (struct hermon_sw_mcg_list_s));
16049517SBill.Taylor@Sun.COM 
16059517SBill.Taylor@Sun.COM 		/*
16069517SBill.Taylor@Sun.COM 		 * Free up the Hermon MCG entry resource used by the "next"
16079517SBill.Taylor@Sun.COM 		 * MCG entry.  That resource is no longer needed by any
16089517SBill.Taylor@Sun.COM 		 * MCG entry which is first on a hash chain (like the "next"
16099517SBill.Taylor@Sun.COM 		 * entry has just become).
16109517SBill.Taylor@Sun.COM 		 */
16119517SBill.Taylor@Sun.COM 		hermon_rsrc_free(state, &curr_mcg->mcg_rsrcp);
16129517SBill.Taylor@Sun.COM 
16139517SBill.Taylor@Sun.COM 		return (DDI_SUCCESS);
16149517SBill.Taylor@Sun.COM 	}
16159517SBill.Taylor@Sun.COM 
16169517SBill.Taylor@Sun.COM 	/*
16179517SBill.Taylor@Sun.COM 	 * Else if this is the last entry on the hash chain (or a middle
16189517SBill.Taylor@Sun.COM 	 * entry, then we update the previous entry's "next_gid_index" field
16199517SBill.Taylor@Sun.COM 	 * to make it point instead to the next entry on the chain.  By
16209517SBill.Taylor@Sun.COM 	 * skipping over the removed entry in this way, we can then free up
16219517SBill.Taylor@Sun.COM 	 * any resources associated with the current entry.  Note:  We don't
16229517SBill.Taylor@Sun.COM 	 * need to invalidate the "skipped over" hardware entry because it
16239517SBill.Taylor@Sun.COM 	 * will no be longer connected to any hash chains, and if/when it is
16249517SBill.Taylor@Sun.COM 	 * finally re-used, it will be written with entirely new values.
16259517SBill.Taylor@Sun.COM 	 */
16269517SBill.Taylor@Sun.COM 
16279517SBill.Taylor@Sun.COM 	/*
16289517SBill.Taylor@Sun.COM 	 * Read the next MCG entry into the temporary MCG.  Note:  In general,
16299517SBill.Taylor@Sun.COM 	 * this operation shouldn't fail.  If it does, then it is an
16309517SBill.Taylor@Sun.COM 	 * indication that something (probably in HW, but maybe in SW) has
16319517SBill.Taylor@Sun.COM 	 * gone seriously wrong.
16329517SBill.Taylor@Sun.COM 	 */
16339517SBill.Taylor@Sun.COM 	status = hermon_read_mgm_cmd_post(state, mcg_entry, prev_indx,
16349517SBill.Taylor@Sun.COM 	    HERMON_CMD_NOSLEEP_SPIN);
16359517SBill.Taylor@Sun.COM 	if (status != HERMON_CMD_SUCCESS) {
16369517SBill.Taylor@Sun.COM 		HERMON_WARNING(state, "failed to read MCG entry");
16379517SBill.Taylor@Sun.COM 		cmn_err(CE_CONT, "Hermon: READ_MGM command failed: %08x\n",
16389517SBill.Taylor@Sun.COM 		    status);
16399517SBill.Taylor@Sun.COM 		if (status == HERMON_CMD_INVALID_STATUS) {
16409517SBill.Taylor@Sun.COM 			hermon_fm_ereport(state, HCA_SYS_ERR, HCA_ERR_SRV_LOST);
16419517SBill.Taylor@Sun.COM 		}
16429517SBill.Taylor@Sun.COM 		return (ibc_get_ci_failure(0));
16439517SBill.Taylor@Sun.COM 	}
16449517SBill.Taylor@Sun.COM 
16459517SBill.Taylor@Sun.COM 	/*
16469517SBill.Taylor@Sun.COM 	 * Finally, we update the "next_gid_indx" field in the temporary MCG
16479517SBill.Taylor@Sun.COM 	 * and attempt to write the entry back into the Hermon MCG table.  If
16489517SBill.Taylor@Sun.COM 	 * this succeeds, then we update the "shadow" list to reflect the
16499517SBill.Taylor@Sun.COM 	 * change, free up the Hermon MCG entry resource that was associated
16509517SBill.Taylor@Sun.COM 	 * with the current entry, and return success.  Note:  In general,
16519517SBill.Taylor@Sun.COM 	 * this operation shouldn't fail.  If it does, then it is an indication
16529517SBill.Taylor@Sun.COM 	 * that something (probably in HW, but maybe in SW) has gone seriously
16539517SBill.Taylor@Sun.COM 	 * wrong.
16549517SBill.Taylor@Sun.COM 	 */
16559517SBill.Taylor@Sun.COM 	mcg_entry->next_gid_indx = curr_mcg->mcg_next_indx;
16569517SBill.Taylor@Sun.COM 	status = hermon_write_mgm_cmd_post(state, mcg_entry, prev_indx,
16579517SBill.Taylor@Sun.COM 	    HERMON_CMD_NOSLEEP_SPIN);
16589517SBill.Taylor@Sun.COM 	if (status != HERMON_CMD_SUCCESS) {
16599517SBill.Taylor@Sun.COM 		HERMON_WARNING(state, "failed to write MCG entry");
16609517SBill.Taylor@Sun.COM 		cmn_err(CE_CONT, "Hermon: WRITE_MGM command failed: %08x\n",
16619517SBill.Taylor@Sun.COM 		    status);
16629517SBill.Taylor@Sun.COM 		if (status == HERMON_CMD_INVALID_STATUS) {
16639517SBill.Taylor@Sun.COM 			hermon_fm_ereport(state, HCA_SYS_ERR,
16649517SBill.Taylor@Sun.COM 			    HCA_ERR_SRV_LOST);
16659517SBill.Taylor@Sun.COM 		}
16669517SBill.Taylor@Sun.COM 		return (ibc_get_ci_failure(0));
16679517SBill.Taylor@Sun.COM 	}
16689517SBill.Taylor@Sun.COM 
16699517SBill.Taylor@Sun.COM 	/*
16709517SBill.Taylor@Sun.COM 	 * Get the pointer to the "shadow" MCG list entry for the previous
16719517SBill.Taylor@Sun.COM 	 * MCG.  Update its "mcg_next_indx" to point to the next entry
16729517SBill.Taylor@Sun.COM 	 * the one after the current entry. Note:  This next index may be
16739517SBill.Taylor@Sun.COM 	 * zero, indicating the end of the list.
16749517SBill.Taylor@Sun.COM 	 */
16759517SBill.Taylor@Sun.COM 	prev_mcg = &state->hs_mcghdl[prev_indx];
16769517SBill.Taylor@Sun.COM 	prev_mcg->mcg_next_indx = curr_mcg->mcg_next_indx;
16779517SBill.Taylor@Sun.COM 
16789517SBill.Taylor@Sun.COM 	/*
16799517SBill.Taylor@Sun.COM 	 * Free up the Hermon MCG entry resource used by the current entry.
16809517SBill.Taylor@Sun.COM 	 * This resource is no longer needed because the chain now skips over
16819517SBill.Taylor@Sun.COM 	 * the current entry.  Then invalidate (zero out) the current "shadow"
16829517SBill.Taylor@Sun.COM 	 * list entry.
16839517SBill.Taylor@Sun.COM 	 */
16849517SBill.Taylor@Sun.COM 	hermon_rsrc_free(state, &curr_mcg->mcg_rsrcp);
16859517SBill.Taylor@Sun.COM 	bzero(curr_mcg, sizeof (struct hermon_sw_mcg_list_s));
16869517SBill.Taylor@Sun.COM 
16879517SBill.Taylor@Sun.COM 	return (DDI_SUCCESS);
16889517SBill.Taylor@Sun.COM }
16899517SBill.Taylor@Sun.COM 
16909517SBill.Taylor@Sun.COM 
16919517SBill.Taylor@Sun.COM /*
16929517SBill.Taylor@Sun.COM  * hermon_mcg_entry_invalidate()
16939517SBill.Taylor@Sun.COM  *    Context: Can be called only from user or kernel context.
16949517SBill.Taylor@Sun.COM  */
16959517SBill.Taylor@Sun.COM static int
hermon_mcg_entry_invalidate(hermon_state_t * state,hermon_hw_mcg_t * mcg_entry,uint_t indx)16969517SBill.Taylor@Sun.COM hermon_mcg_entry_invalidate(hermon_state_t *state, hermon_hw_mcg_t *mcg_entry,
16979517SBill.Taylor@Sun.COM     uint_t indx)
16989517SBill.Taylor@Sun.COM {
16999517SBill.Taylor@Sun.COM 	int		status;
17009517SBill.Taylor@Sun.COM 
17019517SBill.Taylor@Sun.COM 	/*
17029517SBill.Taylor@Sun.COM 	 * Invalidate the hardware MCG entry by zeroing out this temporary
17039517SBill.Taylor@Sun.COM 	 * MCG and writing it the the hardware.  Note: In general, this
17049517SBill.Taylor@Sun.COM 	 * operation shouldn't fail.  If it does, then it is an indication
17059517SBill.Taylor@Sun.COM 	 * that something (probably in HW, but maybe in SW) has gone seriously
17069517SBill.Taylor@Sun.COM 	 * wrong.
17079517SBill.Taylor@Sun.COM 	 */
17089517SBill.Taylor@Sun.COM 	bzero(mcg_entry, HERMON_MCGMEM_SZ(state));
17099517SBill.Taylor@Sun.COM 	status = hermon_write_mgm_cmd_post(state, mcg_entry, indx,
17109517SBill.Taylor@Sun.COM 	    HERMON_CMD_NOSLEEP_SPIN);
17119517SBill.Taylor@Sun.COM 	if (status != HERMON_CMD_SUCCESS) {
17129517SBill.Taylor@Sun.COM 		HERMON_WARNING(state, "failed to write MCG entry");
17139517SBill.Taylor@Sun.COM 		cmn_err(CE_CONT, "Hermon: WRITE_MGM command failed: %08x\n",
17149517SBill.Taylor@Sun.COM 		    status);
17159517SBill.Taylor@Sun.COM 		if (status == HERMON_CMD_INVALID_STATUS) {
17169517SBill.Taylor@Sun.COM 			hermon_fm_ereport(state, HCA_SYS_ERR, HCA_ERR_SRV_LOST);
17179517SBill.Taylor@Sun.COM 		}
17189517SBill.Taylor@Sun.COM 		return (ibc_get_ci_failure(0));
17199517SBill.Taylor@Sun.COM 	}
17209517SBill.Taylor@Sun.COM 
17219517SBill.Taylor@Sun.COM 	return (DDI_SUCCESS);
17229517SBill.Taylor@Sun.COM }
17239517SBill.Taylor@Sun.COM 
17249517SBill.Taylor@Sun.COM 
17259517SBill.Taylor@Sun.COM /*
17269517SBill.Taylor@Sun.COM  * hermon_mgid_is_valid()
17279517SBill.Taylor@Sun.COM  *    Context: Can be called from interrupt or base context.
17289517SBill.Taylor@Sun.COM  */
17299517SBill.Taylor@Sun.COM static int
hermon_mgid_is_valid(ib_gid_t gid)17309517SBill.Taylor@Sun.COM hermon_mgid_is_valid(ib_gid_t gid)
17319517SBill.Taylor@Sun.COM {
17329517SBill.Taylor@Sun.COM 	uint_t		topbits, flags, scope;
17339517SBill.Taylor@Sun.COM 
17349517SBill.Taylor@Sun.COM 	/*
17359517SBill.Taylor@Sun.COM 	 * According to IBA 1.1 specification (section 4.1.1) a valid
17369517SBill.Taylor@Sun.COM 	 * "multicast GID" must have its top eight bits set to all ones
17379517SBill.Taylor@Sun.COM 	 */
17389517SBill.Taylor@Sun.COM 	topbits = (gid.gid_prefix >> HERMON_MCG_TOPBITS_SHIFT) &
17399517SBill.Taylor@Sun.COM 	    HERMON_MCG_TOPBITS_MASK;
17409517SBill.Taylor@Sun.COM 	if (topbits != HERMON_MCG_TOPBITS) {
17419517SBill.Taylor@Sun.COM 		return (0);
17429517SBill.Taylor@Sun.COM 	}
17439517SBill.Taylor@Sun.COM 
17449517SBill.Taylor@Sun.COM 	/*
17459517SBill.Taylor@Sun.COM 	 * The next 4 bits are the "flag" bits.  These are valid only
17469517SBill.Taylor@Sun.COM 	 * if they are "0" (which correspond to permanently assigned/
17479517SBill.Taylor@Sun.COM 	 * "well-known" multicast GIDs) or "1" (for so-called "transient"
17489517SBill.Taylor@Sun.COM 	 * multicast GIDs).  All other values are reserved.
17499517SBill.Taylor@Sun.COM 	 */
17509517SBill.Taylor@Sun.COM 	flags = (gid.gid_prefix >> HERMON_MCG_FLAGS_SHIFT) &
17519517SBill.Taylor@Sun.COM 	    HERMON_MCG_FLAGS_MASK;
17529517SBill.Taylor@Sun.COM 	if (!((flags == HERMON_MCG_FLAGS_PERM) ||
17539517SBill.Taylor@Sun.COM 	    (flags == HERMON_MCG_FLAGS_NONPERM))) {
17549517SBill.Taylor@Sun.COM 		return (0);
17559517SBill.Taylor@Sun.COM 	}
17569517SBill.Taylor@Sun.COM 
17579517SBill.Taylor@Sun.COM 	/*
17589517SBill.Taylor@Sun.COM 	 * The next 4 bits are the "scope" bits.  These are valid only
17599517SBill.Taylor@Sun.COM 	 * if they are "2" (Link-local), "5" (Site-local), "8"
17609517SBill.Taylor@Sun.COM 	 * (Organization-local) or "E" (Global).  All other values
17619517SBill.Taylor@Sun.COM 	 * are reserved (or currently unassigned).
17629517SBill.Taylor@Sun.COM 	 */
17639517SBill.Taylor@Sun.COM 	scope = (gid.gid_prefix >> HERMON_MCG_SCOPE_SHIFT) &
17649517SBill.Taylor@Sun.COM 	    HERMON_MCG_SCOPE_MASK;
17659517SBill.Taylor@Sun.COM 	if (!((scope == HERMON_MCG_SCOPE_LINKLOC) ||
17669517SBill.Taylor@Sun.COM 	    (scope == HERMON_MCG_SCOPE_SITELOC)	 ||
17679517SBill.Taylor@Sun.COM 	    (scope == HERMON_MCG_SCOPE_ORGLOC)	 ||
17689517SBill.Taylor@Sun.COM 	    (scope == HERMON_MCG_SCOPE_GLOBAL))) {
17699517SBill.Taylor@Sun.COM 		return (0);
17709517SBill.Taylor@Sun.COM 	}
17719517SBill.Taylor@Sun.COM 
17729517SBill.Taylor@Sun.COM 	/*
17739517SBill.Taylor@Sun.COM 	 * If it passes all of the above checks, then we will consider it
17749517SBill.Taylor@Sun.COM 	 * a valid multicast GID.
17759517SBill.Taylor@Sun.COM 	 */
17769517SBill.Taylor@Sun.COM 	return (1);
17779517SBill.Taylor@Sun.COM }
17789517SBill.Taylor@Sun.COM 
17799517SBill.Taylor@Sun.COM 
17809517SBill.Taylor@Sun.COM /*
17819517SBill.Taylor@Sun.COM  * hermon_mlid_is_valid()
17829517SBill.Taylor@Sun.COM  *    Context: Can be called from interrupt or base context.
17839517SBill.Taylor@Sun.COM  */
17849517SBill.Taylor@Sun.COM static int
hermon_mlid_is_valid(ib_lid_t lid)17859517SBill.Taylor@Sun.COM hermon_mlid_is_valid(ib_lid_t lid)
17869517SBill.Taylor@Sun.COM {
17879517SBill.Taylor@Sun.COM 	/*
17889517SBill.Taylor@Sun.COM 	 * According to IBA 1.1 specification (section 4.1.1) a valid
17899517SBill.Taylor@Sun.COM 	 * "multicast DLID" must be between 0xC000 and 0xFFFE.
17909517SBill.Taylor@Sun.COM 	 */
17919517SBill.Taylor@Sun.COM 	if ((lid < IB_LID_MC_FIRST) || (lid > IB_LID_MC_LAST)) {
17929517SBill.Taylor@Sun.COM 		return (0);
17939517SBill.Taylor@Sun.COM 	}
17949517SBill.Taylor@Sun.COM 
17959517SBill.Taylor@Sun.COM 	return (1);
17969517SBill.Taylor@Sun.COM }
17979517SBill.Taylor@Sun.COM 
17989517SBill.Taylor@Sun.COM 
17999517SBill.Taylor@Sun.COM /*
18009517SBill.Taylor@Sun.COM  * hermon_pd_alloc()
18019517SBill.Taylor@Sun.COM  *    Context: Can be called only from user or kernel context.
18029517SBill.Taylor@Sun.COM  */
18039517SBill.Taylor@Sun.COM int
hermon_pd_alloc(hermon_state_t * state,hermon_pdhdl_t * pdhdl,uint_t sleepflag)18049517SBill.Taylor@Sun.COM hermon_pd_alloc(hermon_state_t *state, hermon_pdhdl_t *pdhdl, uint_t sleepflag)
18059517SBill.Taylor@Sun.COM {
18069517SBill.Taylor@Sun.COM 	hermon_rsrc_t	*rsrc;
18079517SBill.Taylor@Sun.COM 	hermon_pdhdl_t	pd;
18089517SBill.Taylor@Sun.COM 	int		status;
18099517SBill.Taylor@Sun.COM 
18109517SBill.Taylor@Sun.COM 	/*
18119517SBill.Taylor@Sun.COM 	 * Allocate the software structure for tracking the protection domain
18129517SBill.Taylor@Sun.COM 	 * (i.e. the Hermon Protection Domain handle).  By default each PD
18139517SBill.Taylor@Sun.COM 	 * structure will have a unique PD number assigned to it.  All that
18149517SBill.Taylor@Sun.COM 	 * is necessary is for software to initialize the PD reference count
18159517SBill.Taylor@Sun.COM 	 * (to zero) and return success.
18169517SBill.Taylor@Sun.COM 	 */
18179517SBill.Taylor@Sun.COM 	status = hermon_rsrc_alloc(state, HERMON_PDHDL, 1, sleepflag, &rsrc);
18189517SBill.Taylor@Sun.COM 	if (status != DDI_SUCCESS) {
18199517SBill.Taylor@Sun.COM 		return (IBT_INSUFF_RESOURCE);
18209517SBill.Taylor@Sun.COM 	}
18219517SBill.Taylor@Sun.COM 	pd = (hermon_pdhdl_t)rsrc->hr_addr;
18229517SBill.Taylor@Sun.COM 	_NOTE(NOW_INVISIBLE_TO_OTHER_THREADS(*pd))
18239517SBill.Taylor@Sun.COM 
18249517SBill.Taylor@Sun.COM 	pd->pd_refcnt = 0;
18259517SBill.Taylor@Sun.COM 	*pdhdl = pd;
18269517SBill.Taylor@Sun.COM 
18279517SBill.Taylor@Sun.COM 	return (DDI_SUCCESS);
18289517SBill.Taylor@Sun.COM }
18299517SBill.Taylor@Sun.COM 
18309517SBill.Taylor@Sun.COM 
18319517SBill.Taylor@Sun.COM /*
18329517SBill.Taylor@Sun.COM  * hermon_pd_free()
18339517SBill.Taylor@Sun.COM  *    Context: Can be called only from user or kernel context.
18349517SBill.Taylor@Sun.COM  */
18359517SBill.Taylor@Sun.COM int
hermon_pd_free(hermon_state_t * state,hermon_pdhdl_t * pdhdl)18369517SBill.Taylor@Sun.COM hermon_pd_free(hermon_state_t *state, hermon_pdhdl_t *pdhdl)
18379517SBill.Taylor@Sun.COM {
18389517SBill.Taylor@Sun.COM 	hermon_rsrc_t	*rsrc;
18399517SBill.Taylor@Sun.COM 	hermon_pdhdl_t	pd;
18409517SBill.Taylor@Sun.COM 
18419517SBill.Taylor@Sun.COM 	/*
18429517SBill.Taylor@Sun.COM 	 * Pull all the necessary information from the Hermon Protection Domain
18439517SBill.Taylor@Sun.COM 	 * handle.  This is necessary here because the resource for the
18449517SBill.Taylor@Sun.COM 	 * PD is going to be freed up as part of this operation.
18459517SBill.Taylor@Sun.COM 	 */
18469517SBill.Taylor@Sun.COM 	pd   = *pdhdl;
18479517SBill.Taylor@Sun.COM 	_NOTE(NOW_INVISIBLE_TO_OTHER_THREADS(*pd))
18489517SBill.Taylor@Sun.COM 	rsrc = pd->pd_rsrcp;
18499517SBill.Taylor@Sun.COM 
18509517SBill.Taylor@Sun.COM 	/*
18519517SBill.Taylor@Sun.COM 	 * Check the PD reference count.  If the reference count is non-zero,
18529517SBill.Taylor@Sun.COM 	 * then it means that this protection domain is still referenced by
18539517SBill.Taylor@Sun.COM 	 * some memory region, queue pair, address handle, or other IB object
18549517SBill.Taylor@Sun.COM 	 * If it is non-zero, then return an error.  Otherwise, free the
18559517SBill.Taylor@Sun.COM 	 * Hermon resource and return success.
18569517SBill.Taylor@Sun.COM 	 */
18579517SBill.Taylor@Sun.COM 	if (pd->pd_refcnt != 0) {
18589517SBill.Taylor@Sun.COM 		return (IBT_PD_IN_USE);
18599517SBill.Taylor@Sun.COM 	}
18609517SBill.Taylor@Sun.COM 
18619517SBill.Taylor@Sun.COM 	/* Free the Hermon Protection Domain handle */
18629517SBill.Taylor@Sun.COM 	hermon_rsrc_free(state, &rsrc);
18639517SBill.Taylor@Sun.COM 
18649517SBill.Taylor@Sun.COM 	/* Set the pdhdl pointer to NULL and return success */
18659517SBill.Taylor@Sun.COM 	*pdhdl = (hermon_pdhdl_t)NULL;
18669517SBill.Taylor@Sun.COM 
18679517SBill.Taylor@Sun.COM 	return (DDI_SUCCESS);
18689517SBill.Taylor@Sun.COM }
18699517SBill.Taylor@Sun.COM 
18709517SBill.Taylor@Sun.COM 
18719517SBill.Taylor@Sun.COM /*
18729517SBill.Taylor@Sun.COM  * hermon_pd_refcnt_inc()
18739517SBill.Taylor@Sun.COM  *    Context: Can be called from interrupt or base context.
18749517SBill.Taylor@Sun.COM  */
18759517SBill.Taylor@Sun.COM void
hermon_pd_refcnt_inc(hermon_pdhdl_t pd)18769517SBill.Taylor@Sun.COM hermon_pd_refcnt_inc(hermon_pdhdl_t pd)
18779517SBill.Taylor@Sun.COM {
18789517SBill.Taylor@Sun.COM 	/* Increment the protection domain's reference count */
18799517SBill.Taylor@Sun.COM 	atomic_inc_32(&pd->pd_refcnt);
18809517SBill.Taylor@Sun.COM }
18819517SBill.Taylor@Sun.COM 
18829517SBill.Taylor@Sun.COM 
18839517SBill.Taylor@Sun.COM /*
18849517SBill.Taylor@Sun.COM  * hermon_pd_refcnt_dec()
18859517SBill.Taylor@Sun.COM  *    Context: Can be called from interrupt or base context.
18869517SBill.Taylor@Sun.COM  */
18879517SBill.Taylor@Sun.COM void
hermon_pd_refcnt_dec(hermon_pdhdl_t pd)18889517SBill.Taylor@Sun.COM hermon_pd_refcnt_dec(hermon_pdhdl_t pd)
18899517SBill.Taylor@Sun.COM {
18909517SBill.Taylor@Sun.COM 	/* Decrement the protection domain's reference count */
18919517SBill.Taylor@Sun.COM 	atomic_dec_32(&pd->pd_refcnt);
18929517SBill.Taylor@Sun.COM }
18939517SBill.Taylor@Sun.COM 
18949517SBill.Taylor@Sun.COM 
18959517SBill.Taylor@Sun.COM /*
18969517SBill.Taylor@Sun.COM  * hermon_port_query()
18979517SBill.Taylor@Sun.COM  *    Context: Can be called only from user or kernel context.
18989517SBill.Taylor@Sun.COM  */
18999517SBill.Taylor@Sun.COM int
hermon_port_query(hermon_state_t * state,uint_t port,ibt_hca_portinfo_t * pi)19009517SBill.Taylor@Sun.COM hermon_port_query(hermon_state_t *state, uint_t port, ibt_hca_portinfo_t *pi)
19019517SBill.Taylor@Sun.COM {
19029517SBill.Taylor@Sun.COM 	sm_portinfo_t		portinfo;
19039517SBill.Taylor@Sun.COM 	sm_guidinfo_t		guidinfo;
19049517SBill.Taylor@Sun.COM 	sm_pkey_table_t		pkeytable;
19059517SBill.Taylor@Sun.COM 	ib_gid_t		*sgid;
19069517SBill.Taylor@Sun.COM 	uint_t			sgid_max, pkey_max, tbl_size;
19079517SBill.Taylor@Sun.COM 	int			i, j, indx, status;
19089517SBill.Taylor@Sun.COM 	ib_pkey_t		*pkeyp;
19099517SBill.Taylor@Sun.COM 	ib_guid_t		*guidp;
19109517SBill.Taylor@Sun.COM 
19119517SBill.Taylor@Sun.COM 	_NOTE(NOW_INVISIBLE_TO_OTHER_THREADS(*pi))
19129517SBill.Taylor@Sun.COM 	_NOTE(NOW_INVISIBLE_TO_OTHER_THREADS(*state))
19139517SBill.Taylor@Sun.COM 
19149517SBill.Taylor@Sun.COM 	/* Validate that specified port number is legal */
19159517SBill.Taylor@Sun.COM 	if (!hermon_portnum_is_valid(state, port)) {
19169517SBill.Taylor@Sun.COM 		return (IBT_HCA_PORT_INVALID);
19179517SBill.Taylor@Sun.COM 	}
19189517SBill.Taylor@Sun.COM 	pkeyp = state->hs_pkey[port - 1];
19199517SBill.Taylor@Sun.COM 	guidp = state->hs_guid[port - 1];
19209517SBill.Taylor@Sun.COM 
19219517SBill.Taylor@Sun.COM 	/*
19229517SBill.Taylor@Sun.COM 	 * We use the Hermon MAD_IFC command to post a GetPortInfo MAD
19239517SBill.Taylor@Sun.COM 	 * to the firmware (for the specified port number).  This returns
19249517SBill.Taylor@Sun.COM 	 * a full PortInfo MAD (in "portinfo") which we subsequently
19259517SBill.Taylor@Sun.COM 	 * parse to fill in the "ibt_hca_portinfo_t" structure returned
19269517SBill.Taylor@Sun.COM 	 * to the IBTF.
19279517SBill.Taylor@Sun.COM 	 */
19289517SBill.Taylor@Sun.COM 	status = hermon_getportinfo_cmd_post(state, port,
19299517SBill.Taylor@Sun.COM 	    HERMON_SLEEPFLAG_FOR_CONTEXT(), &portinfo);
19309517SBill.Taylor@Sun.COM 	if (status != HERMON_CMD_SUCCESS) {
19319517SBill.Taylor@Sun.COM 		cmn_err(CE_CONT, "Hermon: GetPortInfo (port %02d) command "
19329517SBill.Taylor@Sun.COM 		    "failed: %08x\n", port, status);
19339517SBill.Taylor@Sun.COM 		if (status == HERMON_CMD_INVALID_STATUS) {
19349517SBill.Taylor@Sun.COM 			hermon_fm_ereport(state, HCA_SYS_ERR, HCA_ERR_SRV_LOST);
19359517SBill.Taylor@Sun.COM 		}
19369517SBill.Taylor@Sun.COM 		return (ibc_get_ci_failure(0));
19379517SBill.Taylor@Sun.COM 	}
19389517SBill.Taylor@Sun.COM 
19399517SBill.Taylor@Sun.COM 	/*
19409517SBill.Taylor@Sun.COM 	 * Parse the PortInfo MAD and fill in the IBTF structure
19419517SBill.Taylor@Sun.COM 	 */
19429517SBill.Taylor@Sun.COM 	pi->p_base_lid		= portinfo.LID;
19439517SBill.Taylor@Sun.COM 	pi->p_qkey_violations	= portinfo.Q_KeyViolations;
19449517SBill.Taylor@Sun.COM 	pi->p_pkey_violations	= portinfo.P_KeyViolations;
19459517SBill.Taylor@Sun.COM 	pi->p_sm_sl		= portinfo.MasterSMSL;
19469517SBill.Taylor@Sun.COM 	pi->p_sm_lid		= portinfo.MasterSMLID;
19479517SBill.Taylor@Sun.COM 	pi->p_linkstate		= portinfo.PortState;
19489517SBill.Taylor@Sun.COM 	pi->p_port_num		= portinfo.LocalPortNum;
19499673SBill.Taylor@Sun.COM 	pi->p_phys_state	= portinfo.PortPhysicalState;
19509673SBill.Taylor@Sun.COM 	pi->p_width_supported	= portinfo.LinkWidthSupported;
19519673SBill.Taylor@Sun.COM 	pi->p_width_enabled	= portinfo.LinkWidthEnabled;
19529673SBill.Taylor@Sun.COM 	pi->p_width_active	= portinfo.LinkWidthActive;
19539673SBill.Taylor@Sun.COM 	pi->p_speed_supported	= portinfo.LinkSpeedSupported;
19549673SBill.Taylor@Sun.COM 	pi->p_speed_enabled	= portinfo.LinkSpeedEnabled;
19559673SBill.Taylor@Sun.COM 	pi->p_speed_active	= portinfo.LinkSpeedActive;
19569517SBill.Taylor@Sun.COM 	pi->p_mtu		= portinfo.MTUCap;
19579517SBill.Taylor@Sun.COM 	pi->p_lmc		= portinfo.LMC;
19589517SBill.Taylor@Sun.COM 	pi->p_max_vl		= portinfo.VLCap;
19599517SBill.Taylor@Sun.COM 	pi->p_subnet_timeout	= portinfo.SubnetTimeOut;
19609517SBill.Taylor@Sun.COM 	pi->p_msg_sz		= ((uint32_t)1 << HERMON_QP_LOG_MAX_MSGSZ);
19619517SBill.Taylor@Sun.COM 	tbl_size = state->hs_cfg_profile->cp_log_max_gidtbl;
19629517SBill.Taylor@Sun.COM 	pi->p_sgid_tbl_sz	= (1 << tbl_size);
19639517SBill.Taylor@Sun.COM 	tbl_size = state->hs_cfg_profile->cp_log_max_pkeytbl;
19649517SBill.Taylor@Sun.COM 	pi->p_pkey_tbl_sz	= (1 << tbl_size);
19659517SBill.Taylor@Sun.COM 	state->hs_sn_prefix[port - 1] = portinfo.GidPrefix;
19669517SBill.Taylor@Sun.COM 
19679517SBill.Taylor@Sun.COM 	/*
19689517SBill.Taylor@Sun.COM 	 * Convert InfiniBand-defined port capability flags to the format
19699517SBill.Taylor@Sun.COM 	 * specified by the IBTF
19709517SBill.Taylor@Sun.COM 	 */
19719517SBill.Taylor@Sun.COM 	if (portinfo.CapabilityMask & SM_CAP_MASK_IS_SM)
19729517SBill.Taylor@Sun.COM 		pi->p_capabilities |= IBT_PORT_CAP_SM;
19739517SBill.Taylor@Sun.COM 	if (portinfo.CapabilityMask & SM_CAP_MASK_IS_SM_DISABLED)
19749517SBill.Taylor@Sun.COM 		pi->p_capabilities |= IBT_PORT_CAP_SM_DISABLED;
19759517SBill.Taylor@Sun.COM 	if (portinfo.CapabilityMask & SM_CAP_MASK_IS_SNMP_SUPPD)
19769517SBill.Taylor@Sun.COM 		pi->p_capabilities |= IBT_PORT_CAP_SNMP_TUNNEL;
19779517SBill.Taylor@Sun.COM 	if (portinfo.CapabilityMask & SM_CAP_MASK_IS_DM_SUPPD)
19789517SBill.Taylor@Sun.COM 		pi->p_capabilities |= IBT_PORT_CAP_DM;
19799517SBill.Taylor@Sun.COM 	if (portinfo.CapabilityMask & SM_CAP_MASK_IS_VM_SUPPD)
19809517SBill.Taylor@Sun.COM 		pi->p_capabilities |= IBT_PORT_CAP_VENDOR;
19819891SRajkumar.Sivaprakasam@Sun.COM 	if (portinfo.CapabilityMask & SM_CAP_MASK_IS_CLNT_REREG_SUPPD)
19829891SRajkumar.Sivaprakasam@Sun.COM 		pi->p_capabilities |= IBT_PORT_CAP_CLNT_REREG;
19839517SBill.Taylor@Sun.COM 
19849517SBill.Taylor@Sun.COM 	/*
19859517SBill.Taylor@Sun.COM 	 * Fill in the SGID table.  Since the only access to the Hermon
19869517SBill.Taylor@Sun.COM 	 * GID tables is through the firmware's MAD_IFC interface, we
19879517SBill.Taylor@Sun.COM 	 * post as many GetGUIDInfo MADs as necessary to read in the entire
19889517SBill.Taylor@Sun.COM 	 * contents of the SGID table (for the specified port).  Note:  The
19899517SBill.Taylor@Sun.COM 	 * GetGUIDInfo command only gets eight GUIDs per operation.  These
19909517SBill.Taylor@Sun.COM 	 * GUIDs are then appended to the GID prefix for the port (from the
19919517SBill.Taylor@Sun.COM 	 * GetPortInfo above) to form the entire SGID table.
19929517SBill.Taylor@Sun.COM 	 */
19939517SBill.Taylor@Sun.COM 	for (i = 0; i < pi->p_sgid_tbl_sz; i += 8) {
19949517SBill.Taylor@Sun.COM 		status = hermon_getguidinfo_cmd_post(state, port, i >> 3,
19959517SBill.Taylor@Sun.COM 		    HERMON_SLEEPFLAG_FOR_CONTEXT(), &guidinfo);
19969517SBill.Taylor@Sun.COM 		if (status != HERMON_CMD_SUCCESS) {
19979517SBill.Taylor@Sun.COM 			cmn_err(CE_CONT, "Hermon: GetGUIDInfo (port %02d) "
19989517SBill.Taylor@Sun.COM 			    "command failed: %08x\n", port, status);
19999517SBill.Taylor@Sun.COM 			if (status == HERMON_CMD_INVALID_STATUS) {
20009517SBill.Taylor@Sun.COM 				hermon_fm_ereport(state, HCA_SYS_ERR,
20019517SBill.Taylor@Sun.COM 				    HCA_ERR_SRV_LOST);
20029517SBill.Taylor@Sun.COM 			}
20039517SBill.Taylor@Sun.COM 			return (ibc_get_ci_failure(0));
20049517SBill.Taylor@Sun.COM 		}
20059517SBill.Taylor@Sun.COM 
20069517SBill.Taylor@Sun.COM 		/* Figure out how many of the entries are valid */
20079517SBill.Taylor@Sun.COM 		sgid_max = min((pi->p_sgid_tbl_sz - i), 8);
20089517SBill.Taylor@Sun.COM 		for (j = 0; j < sgid_max; j++) {
20099517SBill.Taylor@Sun.COM 			indx = (i + j);
20109517SBill.Taylor@Sun.COM 			sgid = &pi->p_sgid_tbl[indx];
20119517SBill.Taylor@Sun.COM 			_NOTE(NOW_INVISIBLE_TO_OTHER_THREADS(*sgid))
20129517SBill.Taylor@Sun.COM 			sgid->gid_prefix = portinfo.GidPrefix;
20139517SBill.Taylor@Sun.COM 			guidp[indx] = sgid->gid_guid =
20149517SBill.Taylor@Sun.COM 			    guidinfo.GUIDBlocks[j];
20159517SBill.Taylor@Sun.COM 		}
20169517SBill.Taylor@Sun.COM 	}
20179517SBill.Taylor@Sun.COM 
20189517SBill.Taylor@Sun.COM 	/*
20199517SBill.Taylor@Sun.COM 	 * Fill in the PKey table.  Just as for the GID tables above, the
20209517SBill.Taylor@Sun.COM 	 * only access to the Hermon PKey tables is through the firmware's
20219517SBill.Taylor@Sun.COM 	 * MAD_IFC interface.  We post as many GetPKeyTable MADs as necessary
20229517SBill.Taylor@Sun.COM 	 * to read in the entire contents of the PKey table (for the specified
20239517SBill.Taylor@Sun.COM 	 * port).  Note:  The GetPKeyTable command only gets 32 PKeys per
20249517SBill.Taylor@Sun.COM 	 * operation.
20259517SBill.Taylor@Sun.COM 	 */
20269517SBill.Taylor@Sun.COM 	for (i = 0; i < pi->p_pkey_tbl_sz; i += 32) {
20279517SBill.Taylor@Sun.COM 		status = hermon_getpkeytable_cmd_post(state, port, i,
20289517SBill.Taylor@Sun.COM 		    HERMON_SLEEPFLAG_FOR_CONTEXT(), &pkeytable);
20299517SBill.Taylor@Sun.COM 		if (status != HERMON_CMD_SUCCESS) {
20309517SBill.Taylor@Sun.COM 			cmn_err(CE_CONT, "Hermon: GetPKeyTable (port %02d) "
20319517SBill.Taylor@Sun.COM 			    "command failed: %08x\n", port, status);
20329517SBill.Taylor@Sun.COM 			if (status == HERMON_CMD_INVALID_STATUS) {
20339517SBill.Taylor@Sun.COM 				hermon_fm_ereport(state, HCA_SYS_ERR,
20349517SBill.Taylor@Sun.COM 				    HCA_ERR_SRV_LOST);
20359517SBill.Taylor@Sun.COM 			}
20369517SBill.Taylor@Sun.COM 			return (ibc_get_ci_failure(0));
20379517SBill.Taylor@Sun.COM 		}
20389517SBill.Taylor@Sun.COM 
20399517SBill.Taylor@Sun.COM 		/* Figure out how many of the entries are valid */
20409517SBill.Taylor@Sun.COM 		pkey_max = min((pi->p_pkey_tbl_sz - i), 32);
20419517SBill.Taylor@Sun.COM 		for (j = 0; j < pkey_max; j++) {
20429517SBill.Taylor@Sun.COM 			indx = (i + j);
20439517SBill.Taylor@Sun.COM 			pkeyp[indx] = pi->p_pkey_tbl[indx] =
20449517SBill.Taylor@Sun.COM 			    pkeytable.P_KeyTableBlocks[j];
20459517SBill.Taylor@Sun.COM 		}
20469517SBill.Taylor@Sun.COM 	}
20479517SBill.Taylor@Sun.COM 
20489517SBill.Taylor@Sun.COM 	return (DDI_SUCCESS);
20499517SBill.Taylor@Sun.COM }
20509517SBill.Taylor@Sun.COM 
20519517SBill.Taylor@Sun.COM 
20529517SBill.Taylor@Sun.COM /*
20539517SBill.Taylor@Sun.COM  * hermon_port_modify()
20549517SBill.Taylor@Sun.COM  *    Context: Can be called only from user or kernel context.
20559517SBill.Taylor@Sun.COM  */
20569517SBill.Taylor@Sun.COM /* ARGSUSED */
20579517SBill.Taylor@Sun.COM int
hermon_port_modify(hermon_state_t * state,uint8_t port,ibt_port_modify_flags_t flags,uint8_t init_type)20589517SBill.Taylor@Sun.COM hermon_port_modify(hermon_state_t *state, uint8_t port,
20599517SBill.Taylor@Sun.COM     ibt_port_modify_flags_t flags, uint8_t init_type)
20609517SBill.Taylor@Sun.COM {
20619517SBill.Taylor@Sun.COM 	sm_portinfo_t		portinfo;
20629517SBill.Taylor@Sun.COM 	uint32_t		capmask;
20639517SBill.Taylor@Sun.COM 	int			status;
20649517SBill.Taylor@Sun.COM 	hermon_hw_set_port_t	set_port;
20659517SBill.Taylor@Sun.COM 
20669517SBill.Taylor@Sun.COM 	/*
20679517SBill.Taylor@Sun.COM 	 * Return an error if either of the unsupported flags are set
20689517SBill.Taylor@Sun.COM 	 */
20699517SBill.Taylor@Sun.COM 	if ((flags & IBT_PORT_SHUTDOWN) ||
20709517SBill.Taylor@Sun.COM 	    (flags & IBT_PORT_SET_INIT_TYPE)) {
20719517SBill.Taylor@Sun.COM 		return (IBT_NOT_SUPPORTED);
20729517SBill.Taylor@Sun.COM 	}
20739517SBill.Taylor@Sun.COM 
20749517SBill.Taylor@Sun.COM 	bzero(&set_port, sizeof (set_port));
20759517SBill.Taylor@Sun.COM 
20769517SBill.Taylor@Sun.COM 	/*
20779517SBill.Taylor@Sun.COM 	 * Determine whether we are trying to reset the QKey counter
20789517SBill.Taylor@Sun.COM 	 */
20799517SBill.Taylor@Sun.COM 	if (flags & IBT_PORT_RESET_QKEY)
20809517SBill.Taylor@Sun.COM 		set_port.rqk = 1;
20819517SBill.Taylor@Sun.COM 
20829517SBill.Taylor@Sun.COM 	/* Validate that specified port number is legal */
20839517SBill.Taylor@Sun.COM 	if (!hermon_portnum_is_valid(state, port)) {
20849517SBill.Taylor@Sun.COM 		return (IBT_HCA_PORT_INVALID);
20859517SBill.Taylor@Sun.COM 	}
20869517SBill.Taylor@Sun.COM 
20879517SBill.Taylor@Sun.COM 	/*
20889517SBill.Taylor@Sun.COM 	 * Use the Hermon MAD_IFC command to post a GetPortInfo MAD to the
20899517SBill.Taylor@Sun.COM 	 * firmware (for the specified port number).  This returns a full
20909517SBill.Taylor@Sun.COM 	 * PortInfo MAD (in "portinfo") from which we pull the current
20919517SBill.Taylor@Sun.COM 	 * capability mask.  We then modify the capability mask as directed
20929517SBill.Taylor@Sun.COM 	 * by the "pmod_flags" field, and write the updated capability mask
20939517SBill.Taylor@Sun.COM 	 * using the Hermon SET_IB command (below).
20949517SBill.Taylor@Sun.COM 	 */
20959517SBill.Taylor@Sun.COM 	status = hermon_getportinfo_cmd_post(state, port,
20969517SBill.Taylor@Sun.COM 	    HERMON_SLEEPFLAG_FOR_CONTEXT(), &portinfo);
20979517SBill.Taylor@Sun.COM 	if (status != HERMON_CMD_SUCCESS) {
20989517SBill.Taylor@Sun.COM 		if (status == HERMON_CMD_INVALID_STATUS) {
20999517SBill.Taylor@Sun.COM 			hermon_fm_ereport(state, HCA_SYS_ERR, HCA_ERR_SRV_LOST);
21009517SBill.Taylor@Sun.COM 		}
21019517SBill.Taylor@Sun.COM 		return (ibc_get_ci_failure(0));
21029517SBill.Taylor@Sun.COM 	}
21039517SBill.Taylor@Sun.COM 
21049517SBill.Taylor@Sun.COM 	/*
21059517SBill.Taylor@Sun.COM 	 * Convert InfiniBand-defined port capability flags to the format
21069517SBill.Taylor@Sun.COM 	 * specified by the IBTF.  Specifically, we modify the capability
21079517SBill.Taylor@Sun.COM 	 * mask based on the specified values.
21089517SBill.Taylor@Sun.COM 	 */
21099517SBill.Taylor@Sun.COM 	capmask = portinfo.CapabilityMask;
21109517SBill.Taylor@Sun.COM 
21119517SBill.Taylor@Sun.COM 	if (flags & IBT_PORT_RESET_SM)
21129517SBill.Taylor@Sun.COM 		capmask &= ~SM_CAP_MASK_IS_SM;
21139517SBill.Taylor@Sun.COM 	else if (flags & IBT_PORT_SET_SM)
21149517SBill.Taylor@Sun.COM 		capmask |= SM_CAP_MASK_IS_SM;
21159517SBill.Taylor@Sun.COM 
21169517SBill.Taylor@Sun.COM 	if (flags & IBT_PORT_RESET_SNMP)
21179517SBill.Taylor@Sun.COM 		capmask &= ~SM_CAP_MASK_IS_SNMP_SUPPD;
21189517SBill.Taylor@Sun.COM 	else if (flags & IBT_PORT_SET_SNMP)
21199517SBill.Taylor@Sun.COM 		capmask |= SM_CAP_MASK_IS_SNMP_SUPPD;
21209517SBill.Taylor@Sun.COM 
21219517SBill.Taylor@Sun.COM 	if (flags & IBT_PORT_RESET_DEVMGT)
21229517SBill.Taylor@Sun.COM 		capmask &= ~SM_CAP_MASK_IS_DM_SUPPD;
21239517SBill.Taylor@Sun.COM 	else if (flags & IBT_PORT_SET_DEVMGT)
21249517SBill.Taylor@Sun.COM 		capmask |= SM_CAP_MASK_IS_DM_SUPPD;
21259517SBill.Taylor@Sun.COM 
21269517SBill.Taylor@Sun.COM 	if (flags & IBT_PORT_RESET_VENDOR)
21279517SBill.Taylor@Sun.COM 		capmask &= ~SM_CAP_MASK_IS_VM_SUPPD;
21289517SBill.Taylor@Sun.COM 	else if (flags & IBT_PORT_SET_VENDOR)
21299517SBill.Taylor@Sun.COM 		capmask |= SM_CAP_MASK_IS_VM_SUPPD;
21309517SBill.Taylor@Sun.COM 
21319517SBill.Taylor@Sun.COM 	set_port.cap_mask = capmask;
21329517SBill.Taylor@Sun.COM 
21339517SBill.Taylor@Sun.COM 	/*
21349517SBill.Taylor@Sun.COM 	 * Use the Hermon SET_PORT command to update the capability mask and
21359517SBill.Taylor@Sun.COM 	 * (possibly) reset the QKey violation counter for the specified port.
21369517SBill.Taylor@Sun.COM 	 * Note: In general, this operation shouldn't fail.  If it does, then
21379517SBill.Taylor@Sun.COM 	 * it is an indication that something (probably in HW, but maybe in
21389517SBill.Taylor@Sun.COM 	 * SW) has gone seriously wrong.
21399517SBill.Taylor@Sun.COM 	 */
21409517SBill.Taylor@Sun.COM 	status = hermon_set_port_cmd_post(state, &set_port, port,
21419517SBill.Taylor@Sun.COM 	    HERMON_SLEEPFLAG_FOR_CONTEXT());
21429517SBill.Taylor@Sun.COM 	if (status != HERMON_CMD_SUCCESS) {
21439517SBill.Taylor@Sun.COM 		HERMON_WARNING(state, "failed to modify port capabilities");
21449517SBill.Taylor@Sun.COM 		cmn_err(CE_CONT, "Hermon: SET_IB (port %02d) command failed: "
21459517SBill.Taylor@Sun.COM 		    "%08x\n", port, status);
21469517SBill.Taylor@Sun.COM 		if (status == HERMON_CMD_INVALID_STATUS) {
21479517SBill.Taylor@Sun.COM 			hermon_fm_ereport(state, HCA_SYS_ERR, HCA_ERR_SRV_LOST);
21489517SBill.Taylor@Sun.COM 		}
21499517SBill.Taylor@Sun.COM 		return (ibc_get_ci_failure(0));
21509517SBill.Taylor@Sun.COM 	}
21519517SBill.Taylor@Sun.COM 
21529517SBill.Taylor@Sun.COM 	return (DDI_SUCCESS);
21539517SBill.Taylor@Sun.COM }
21549517SBill.Taylor@Sun.COM 
21559517SBill.Taylor@Sun.COM 
21569517SBill.Taylor@Sun.COM /*
21579517SBill.Taylor@Sun.COM  * hermon_set_addr_path()
21589517SBill.Taylor@Sun.COM  *    Context: Can be called from interrupt or base context.
21599517SBill.Taylor@Sun.COM  *
21609517SBill.Taylor@Sun.COM  * Note: This routine is used for two purposes.  It is used to fill in the
21619517SBill.Taylor@Sun.COM  * Hermon UDAV fields, and it is used to fill in the address path information
21629517SBill.Taylor@Sun.COM  * for QPs.  Because the two Hermon structures are similar, common fields can
21639517SBill.Taylor@Sun.COM  * be filled in here.  Because they are different, however, we pass
21649517SBill.Taylor@Sun.COM  * an additional flag to indicate which type is being filled and do each one
21659517SBill.Taylor@Sun.COM  * uniquely
21669517SBill.Taylor@Sun.COM  */
21679517SBill.Taylor@Sun.COM 
21689517SBill.Taylor@Sun.COM int hermon_srate_override = -1;	/* allows ease of testing */
21699517SBill.Taylor@Sun.COM 
21709517SBill.Taylor@Sun.COM int
hermon_set_addr_path(hermon_state_t * state,ibt_adds_vect_t * av,hermon_hw_addr_path_t * path,uint_t type)21719517SBill.Taylor@Sun.COM hermon_set_addr_path(hermon_state_t *state, ibt_adds_vect_t *av,
21729517SBill.Taylor@Sun.COM     hermon_hw_addr_path_t *path, uint_t type)
21739517SBill.Taylor@Sun.COM {
21749517SBill.Taylor@Sun.COM 	uint_t		gidtbl_sz;
21759517SBill.Taylor@Sun.COM 	hermon_hw_udav_t *udav;
21769517SBill.Taylor@Sun.COM 
21779517SBill.Taylor@Sun.COM 	_NOTE(NOW_INVISIBLE_TO_OTHER_THREADS(*av))
21789517SBill.Taylor@Sun.COM 	_NOTE(NOW_INVISIBLE_TO_OTHER_THREADS(*path))
21799517SBill.Taylor@Sun.COM 
21809517SBill.Taylor@Sun.COM 	udav = (hermon_hw_udav_t *)(void *)path;
21819517SBill.Taylor@Sun.COM 	_NOTE(NOW_INVISIBLE_TO_OTHER_THREADS(*udav))
21829517SBill.Taylor@Sun.COM 	path->mlid	= av->av_src_path;
21839517SBill.Taylor@Sun.COM 	path->rlid	= av->av_dlid;
21849517SBill.Taylor@Sun.COM 
21859580SBill.Taylor@Sun.COM 	switch (av->av_srate) {
21869580SBill.Taylor@Sun.COM 	case IBT_SRATE_2:	/* 1xSDR-2.5Gb/s injection rate */
21879580SBill.Taylor@Sun.COM 		path->max_stat_rate = 7; break;
21889580SBill.Taylor@Sun.COM 	case IBT_SRATE_10:	/* 4xSDR-10.0Gb/s injection rate */
21899580SBill.Taylor@Sun.COM 		path->max_stat_rate = 8; break;
21909580SBill.Taylor@Sun.COM 	case IBT_SRATE_30:	/* 12xSDR-30Gb/s injection rate */
21919580SBill.Taylor@Sun.COM 		path->max_stat_rate = 9; break;
21929580SBill.Taylor@Sun.COM 	case IBT_SRATE_5:	/* 1xDDR-5Gb/s injection rate */
21939580SBill.Taylor@Sun.COM 		path->max_stat_rate = 10; break;
21949580SBill.Taylor@Sun.COM 	case IBT_SRATE_20:	/* 4xDDR-20Gb/s injection rate */
21959580SBill.Taylor@Sun.COM 		path->max_stat_rate = 11; break;
21969580SBill.Taylor@Sun.COM 	case IBT_SRATE_40:	/* 4xQDR-40Gb/s injection rate */
21979580SBill.Taylor@Sun.COM 		path->max_stat_rate = 12; break;
21989580SBill.Taylor@Sun.COM 	case IBT_SRATE_60:	/* 12xDDR-60Gb/s injection rate */
21999580SBill.Taylor@Sun.COM 		path->max_stat_rate = 13; break;
22009580SBill.Taylor@Sun.COM 	case IBT_SRATE_80:	/* 8xQDR-80Gb/s injection rate */
22019580SBill.Taylor@Sun.COM 		path->max_stat_rate = 14; break;
22029580SBill.Taylor@Sun.COM 	case IBT_SRATE_120:	/* 12xQDR-120Gb/s injection rate */
22039580SBill.Taylor@Sun.COM 		path->max_stat_rate = 15; break;
22049580SBill.Taylor@Sun.COM 	case IBT_SRATE_NOT_SPECIFIED:	/* Max */
22059580SBill.Taylor@Sun.COM 		path->max_stat_rate = 0; break;
22069580SBill.Taylor@Sun.COM 	default:
22079517SBill.Taylor@Sun.COM 		return (IBT_STATIC_RATE_INVALID);
22089517SBill.Taylor@Sun.COM 	}
22099517SBill.Taylor@Sun.COM 	if (hermon_srate_override != -1) /* for evaluating HCA firmware */
22109517SBill.Taylor@Sun.COM 		path->max_stat_rate = hermon_srate_override;
22119517SBill.Taylor@Sun.COM 
22129517SBill.Taylor@Sun.COM 	/* If "grh" flag is set, then check for valid SGID index too */
22139517SBill.Taylor@Sun.COM 	gidtbl_sz = (1 << state->hs_queryport.log_max_gid);
22149517SBill.Taylor@Sun.COM 	if ((av->av_send_grh) && (av->av_sgid_ix > gidtbl_sz)) {
22159517SBill.Taylor@Sun.COM 		return (IBT_SGID_INVALID);
22169517SBill.Taylor@Sun.COM 	}
22179517SBill.Taylor@Sun.COM 
22189517SBill.Taylor@Sun.COM 	/*
22199517SBill.Taylor@Sun.COM 	 * Fill in all "global" values regardless of the value in the GRH
22209517SBill.Taylor@Sun.COM 	 * flag.  Because "grh" is not set unless "av_send_grh" is set, the
22219517SBill.Taylor@Sun.COM 	 * hardware will ignore the other "global" values as necessary.  Note:
22229517SBill.Taylor@Sun.COM 	 * SW does this here to enable later query operations to return
22239517SBill.Taylor@Sun.COM 	 * exactly the same params that were passed when the addr path was
22249517SBill.Taylor@Sun.COM 	 * last written.
22259517SBill.Taylor@Sun.COM 	 */
22269517SBill.Taylor@Sun.COM 	path->grh = av->av_send_grh;
22279517SBill.Taylor@Sun.COM 	if (type == HERMON_ADDRPATH_QP) {
22289517SBill.Taylor@Sun.COM 		path->mgid_index = av->av_sgid_ix;
22299517SBill.Taylor@Sun.COM 	} else {
22309517SBill.Taylor@Sun.COM 		/*
22319517SBill.Taylor@Sun.COM 		 * For Hermon UDAV, the "mgid_index" field is the index into
22329517SBill.Taylor@Sun.COM 		 * a combined table (not a per-port table), but having sections
22339517SBill.Taylor@Sun.COM 		 * for each port. So some extra calculations are necessary.
22349517SBill.Taylor@Sun.COM 		 */
22359517SBill.Taylor@Sun.COM 
22369517SBill.Taylor@Sun.COM 		path->mgid_index = ((av->av_port_num - 1) * gidtbl_sz) +
22379517SBill.Taylor@Sun.COM 		    av->av_sgid_ix;
22389517SBill.Taylor@Sun.COM 
22399517SBill.Taylor@Sun.COM 		udav->portnum = av->av_port_num;
22409517SBill.Taylor@Sun.COM 	}
22419517SBill.Taylor@Sun.COM 
22429517SBill.Taylor@Sun.COM 	/*
22439517SBill.Taylor@Sun.COM 	 * According to Hermon PRM, the (31:0) part of rgid_l must be set to
22449517SBill.Taylor@Sun.COM 	 * "0x2" if the 'grh' or 'g' bit is cleared.  It also says that we
22459517SBill.Taylor@Sun.COM 	 * only need to do it for UDAV's.  So we enforce that here.
22469517SBill.Taylor@Sun.COM 	 *
22479517SBill.Taylor@Sun.COM 	 * NOTE: The entire 64 bits worth of GUID info is actually being
22489517SBill.Taylor@Sun.COM 	 * preserved (for UDAVs) by the callers of this function
22499517SBill.Taylor@Sun.COM 	 * (hermon_ah_alloc() and hermon_ah_modify()) and as long as the
22509517SBill.Taylor@Sun.COM 	 * 'grh' bit is not set, the upper 32 bits (63:32) of rgid_l are
22519517SBill.Taylor@Sun.COM 	 * "don't care".
22529517SBill.Taylor@Sun.COM 	 */
22539517SBill.Taylor@Sun.COM 	if ((path->grh) || (type == HERMON_ADDRPATH_QP)) {
22549517SBill.Taylor@Sun.COM 		path->flow_label = av->av_flow;
22559517SBill.Taylor@Sun.COM 		path->tclass	 = av->av_tclass;
22569517SBill.Taylor@Sun.COM 		path->hop_limit	 = av->av_hop;
22579517SBill.Taylor@Sun.COM 		bcopy(&(av->av_dgid.gid_prefix), &(path->rgid_h),
22589517SBill.Taylor@Sun.COM 		    sizeof (uint64_t));
22599517SBill.Taylor@Sun.COM 		bcopy(&(av->av_dgid.gid_guid), &(path->rgid_l),
22609517SBill.Taylor@Sun.COM 		    sizeof (uint64_t));
22619517SBill.Taylor@Sun.COM 	} else {
22629517SBill.Taylor@Sun.COM 		path->rgid_l	 = 0x2;
22639517SBill.Taylor@Sun.COM 		path->flow_label = 0;
22649517SBill.Taylor@Sun.COM 		path->tclass	 = 0;
22659517SBill.Taylor@Sun.COM 		path->hop_limit	 = 0;
22669517SBill.Taylor@Sun.COM 		path->rgid_h	 = 0;
22679517SBill.Taylor@Sun.COM 	}
22689517SBill.Taylor@Sun.COM 	/* extract the default service level */
22699517SBill.Taylor@Sun.COM 	udav->sl = (HERMON_DEF_SCHED_SELECTION & 0x3C) >> 2;
22709517SBill.Taylor@Sun.COM 
22719517SBill.Taylor@Sun.COM 	return (DDI_SUCCESS);
22729517SBill.Taylor@Sun.COM }
22739517SBill.Taylor@Sun.COM 
22749517SBill.Taylor@Sun.COM 
22759517SBill.Taylor@Sun.COM /*
22769517SBill.Taylor@Sun.COM  * hermon_get_addr_path()
22779517SBill.Taylor@Sun.COM  *    Context: Can be called from interrupt or base context.
22789517SBill.Taylor@Sun.COM  *
22799517SBill.Taylor@Sun.COM  * Note: Just like hermon_set_addr_path() above, this routine is used for two
22809517SBill.Taylor@Sun.COM  * purposes.  It is used to read in the Hermon UDAV fields, and it is used to
22819517SBill.Taylor@Sun.COM  * read in the address path information for QPs.  Because the two Hermon
22829517SBill.Taylor@Sun.COM  * structures are similar, common fields can be read in here.  But because
22839517SBill.Taylor@Sun.COM  * they are slightly different, we pass an additional flag to indicate which
22849517SBill.Taylor@Sun.COM  * type is being read.
22859517SBill.Taylor@Sun.COM  */
22869517SBill.Taylor@Sun.COM void
hermon_get_addr_path(hermon_state_t * state,hermon_hw_addr_path_t * path,ibt_adds_vect_t * av,uint_t type)22879517SBill.Taylor@Sun.COM hermon_get_addr_path(hermon_state_t *state, hermon_hw_addr_path_t *path,
22889517SBill.Taylor@Sun.COM     ibt_adds_vect_t *av, uint_t type)
22899517SBill.Taylor@Sun.COM {
22909517SBill.Taylor@Sun.COM 	uint_t		gidtbl_sz;
22919517SBill.Taylor@Sun.COM 
22929517SBill.Taylor@Sun.COM 	_NOTE(NOW_INVISIBLE_TO_OTHER_THREADS(*path))
22939517SBill.Taylor@Sun.COM 	_NOTE(NOW_INVISIBLE_TO_OTHER_THREADS(*av))
22949517SBill.Taylor@Sun.COM 
22959517SBill.Taylor@Sun.COM 	av->av_src_path	= path->mlid;
22969517SBill.Taylor@Sun.COM 	av->av_dlid	= path->rlid;
22979517SBill.Taylor@Sun.COM 
22989517SBill.Taylor@Sun.COM 	/* Set "av_ipd" value from max_stat_rate */
22999580SBill.Taylor@Sun.COM 	switch (path->max_stat_rate) {
23009580SBill.Taylor@Sun.COM 	case 7:				/* 1xSDR-2.5Gb/s injection rate */
23019580SBill.Taylor@Sun.COM 		av->av_srate = IBT_SRATE_2; break;
23029580SBill.Taylor@Sun.COM 	case 8:				/* 4xSDR-10.0Gb/s injection rate */
23039580SBill.Taylor@Sun.COM 		av->av_srate = IBT_SRATE_10; break;
23049580SBill.Taylor@Sun.COM 	case 9:				/* 12xSDR-30Gb/s injection rate */
23059580SBill.Taylor@Sun.COM 		av->av_srate = IBT_SRATE_30; break;
23069580SBill.Taylor@Sun.COM 	case 10:			/* 1xDDR-5Gb/s injection rate */
23079580SBill.Taylor@Sun.COM 		av->av_srate = IBT_SRATE_5; break;
23089580SBill.Taylor@Sun.COM 	case 11:			/* 4xDDR-20Gb/s injection rate */
23099580SBill.Taylor@Sun.COM 		av->av_srate = IBT_SRATE_20; break;
23109580SBill.Taylor@Sun.COM 	case 12:			/* xQDR-40Gb/s injection rate */
23119580SBill.Taylor@Sun.COM 		av->av_srate = IBT_SRATE_40; break;
23129580SBill.Taylor@Sun.COM 	case 13:			/* 12xDDR-60Gb/s injection rate */
23139580SBill.Taylor@Sun.COM 		av->av_srate = IBT_SRATE_60; break;
23149580SBill.Taylor@Sun.COM 	case 14:			/* 8xQDR-80Gb/s injection rate */
23159580SBill.Taylor@Sun.COM 		av->av_srate = IBT_SRATE_80; break;
23169580SBill.Taylor@Sun.COM 	case 15:			/* 12xQDR-120Gb/s injection rate */
23179580SBill.Taylor@Sun.COM 		av->av_srate = IBT_SRATE_120; break;
23189580SBill.Taylor@Sun.COM 	case 0:				/* max */
23199969SGiri.Adari@Sun.COM 		av->av_srate = IBT_SRATE_NOT_SPECIFIED; break;
23209580SBill.Taylor@Sun.COM 	default:			/* 1x injection rate */
23219580SBill.Taylor@Sun.COM 		av->av_srate = IBT_SRATE_1X;
23229517SBill.Taylor@Sun.COM 	}
23239517SBill.Taylor@Sun.COM 
23249517SBill.Taylor@Sun.COM 	/*
23259517SBill.Taylor@Sun.COM 	 * Extract all "global" values regardless of the value in the GRH
23269517SBill.Taylor@Sun.COM 	 * flag.  Because "av_send_grh" is set only if "grh" is set, software
23279517SBill.Taylor@Sun.COM 	 * knows to ignore the other "global" values as necessary.  Note: SW
23289517SBill.Taylor@Sun.COM 	 * does it this way to enable these query operations to return exactly
23299517SBill.Taylor@Sun.COM 	 * the same params that were passed when the addr path was last written.
23309517SBill.Taylor@Sun.COM 	 */
23319517SBill.Taylor@Sun.COM 	av->av_send_grh		= path->grh;
23329517SBill.Taylor@Sun.COM 	if (type == HERMON_ADDRPATH_QP) {
23339517SBill.Taylor@Sun.COM 		av->av_sgid_ix  = path->mgid_index;
23349517SBill.Taylor@Sun.COM 	} else {
23359517SBill.Taylor@Sun.COM 		/*
23369517SBill.Taylor@Sun.COM 		 * For Hermon UDAV, the "mgid_index" field is the index into
23379517SBill.Taylor@Sun.COM 		 * a combined table (not a per-port table).
23389517SBill.Taylor@Sun.COM 		 */
23399517SBill.Taylor@Sun.COM 		gidtbl_sz = (1 << state->hs_queryport.log_max_gid);
23409517SBill.Taylor@Sun.COM 		av->av_sgid_ix = path->mgid_index - ((av->av_port_num - 1) *
23419517SBill.Taylor@Sun.COM 		    gidtbl_sz);
23429517SBill.Taylor@Sun.COM 
23439517SBill.Taylor@Sun.COM 		av->av_port_num = ((hermon_hw_udav_t *)(void *)path)->portnum;
23449517SBill.Taylor@Sun.COM 	}
23459517SBill.Taylor@Sun.COM 	av->av_flow		= path->flow_label;
23469517SBill.Taylor@Sun.COM 	av->av_tclass		= path->tclass;
23479517SBill.Taylor@Sun.COM 	av->av_hop		= path->hop_limit;
23489517SBill.Taylor@Sun.COM 	/* this is for alignment issue w/ the addr path struct in Hermon */
23499517SBill.Taylor@Sun.COM 	bcopy(&(path->rgid_h), &(av->av_dgid.gid_prefix), sizeof (uint64_t));
23509517SBill.Taylor@Sun.COM 	bcopy(&(path->rgid_l), &(av->av_dgid.gid_guid), sizeof (uint64_t));
23519517SBill.Taylor@Sun.COM }
23529517SBill.Taylor@Sun.COM 
23539517SBill.Taylor@Sun.COM 
23549517SBill.Taylor@Sun.COM /*
23559517SBill.Taylor@Sun.COM  * hermon_portnum_is_valid()
23569517SBill.Taylor@Sun.COM  *    Context: Can be called from interrupt or base context.
23579517SBill.Taylor@Sun.COM  */
23589517SBill.Taylor@Sun.COM int
hermon_portnum_is_valid(hermon_state_t * state,uint_t portnum)23599517SBill.Taylor@Sun.COM hermon_portnum_is_valid(hermon_state_t *state, uint_t portnum)
23609517SBill.Taylor@Sun.COM {
23619517SBill.Taylor@Sun.COM 	uint_t	max_port;
23629517SBill.Taylor@Sun.COM 
23639517SBill.Taylor@Sun.COM 	max_port = state->hs_cfg_profile->cp_num_ports;
23649517SBill.Taylor@Sun.COM 	if ((portnum <= max_port) && (portnum != 0)) {
23659517SBill.Taylor@Sun.COM 		return (1);
23669517SBill.Taylor@Sun.COM 	} else {
23679517SBill.Taylor@Sun.COM 		return (0);
23689517SBill.Taylor@Sun.COM 	}
23699517SBill.Taylor@Sun.COM }
23709517SBill.Taylor@Sun.COM 
23719517SBill.Taylor@Sun.COM 
23729517SBill.Taylor@Sun.COM /*
23739517SBill.Taylor@Sun.COM  * hermon_pkeyindex_is_valid()
23749517SBill.Taylor@Sun.COM  *    Context: Can be called from interrupt or base context.
23759517SBill.Taylor@Sun.COM  */
23769517SBill.Taylor@Sun.COM int
hermon_pkeyindex_is_valid(hermon_state_t * state,uint_t pkeyindx)23779517SBill.Taylor@Sun.COM hermon_pkeyindex_is_valid(hermon_state_t *state, uint_t pkeyindx)
23789517SBill.Taylor@Sun.COM {
23799517SBill.Taylor@Sun.COM 	uint_t	max_pkeyindx;
23809517SBill.Taylor@Sun.COM 
23819517SBill.Taylor@Sun.COM 	max_pkeyindx = 1 << state->hs_cfg_profile->cp_log_max_pkeytbl;
23829517SBill.Taylor@Sun.COM 	if (pkeyindx < max_pkeyindx) {
23839517SBill.Taylor@Sun.COM 		return (1);
23849517SBill.Taylor@Sun.COM 	} else {
23859517SBill.Taylor@Sun.COM 		return (0);
23869517SBill.Taylor@Sun.COM 	}
23879517SBill.Taylor@Sun.COM }
23889517SBill.Taylor@Sun.COM 
23899517SBill.Taylor@Sun.COM 
23909517SBill.Taylor@Sun.COM /*
23919517SBill.Taylor@Sun.COM  * hermon_queue_alloc()
23929517SBill.Taylor@Sun.COM  *    Context: Can be called from interrupt or base context.
23939517SBill.Taylor@Sun.COM  */
23949517SBill.Taylor@Sun.COM int
hermon_queue_alloc(hermon_state_t * state,hermon_qalloc_info_t * qa_info,uint_t sleepflag)23959517SBill.Taylor@Sun.COM hermon_queue_alloc(hermon_state_t *state, hermon_qalloc_info_t *qa_info,
23969517SBill.Taylor@Sun.COM     uint_t sleepflag)
23979517SBill.Taylor@Sun.COM {
23989517SBill.Taylor@Sun.COM 	ddi_dma_attr_t		dma_attr;
23999517SBill.Taylor@Sun.COM 	int			(*callback)(caddr_t);
24009517SBill.Taylor@Sun.COM 	uint64_t		realsize, alloc_mask;
24019517SBill.Taylor@Sun.COM 	int			flag, status;
24029517SBill.Taylor@Sun.COM 
24039517SBill.Taylor@Sun.COM 	_NOTE(NOW_INVISIBLE_TO_OTHER_THREADS(*qa_info))
24049517SBill.Taylor@Sun.COM 
24059517SBill.Taylor@Sun.COM 	/* Set the callback flag appropriately */
24069517SBill.Taylor@Sun.COM 	callback = (sleepflag == HERMON_SLEEP) ? DDI_DMA_SLEEP :
24079517SBill.Taylor@Sun.COM 	    DDI_DMA_DONTWAIT;
24089517SBill.Taylor@Sun.COM 
24099517SBill.Taylor@Sun.COM 	/*
24109517SBill.Taylor@Sun.COM 	 * Initialize many of the default DMA attributes.  Then set additional
24119517SBill.Taylor@Sun.COM 	 * alignment restrictions as necessary for the queue memory.  Also
24129517SBill.Taylor@Sun.COM 	 * respect the configured value for IOMMU bypass
24139517SBill.Taylor@Sun.COM 	 */
24149517SBill.Taylor@Sun.COM 	hermon_dma_attr_init(state, &dma_attr);
24159517SBill.Taylor@Sun.COM 	dma_attr.dma_attr_align = qa_info->qa_bind_align;
241611972SBill.Taylor@Sun.COM #ifdef	__sparc
241711972SBill.Taylor@Sun.COM 	if (state->hs_cfg_profile->cp_iommu_bypass == HERMON_BINDMEM_BYPASS) {
24189517SBill.Taylor@Sun.COM 		dma_attr.dma_attr_flags = DDI_DMA_FORCE_PHYSICAL;
24199517SBill.Taylor@Sun.COM 	}
242011972SBill.Taylor@Sun.COM #endif
24219517SBill.Taylor@Sun.COM 
24229517SBill.Taylor@Sun.COM 	/* Allocate a DMA handle */
24239517SBill.Taylor@Sun.COM 	status = ddi_dma_alloc_handle(state->hs_dip, &dma_attr, callback, NULL,
24249517SBill.Taylor@Sun.COM 	    &qa_info->qa_dmahdl);
24259517SBill.Taylor@Sun.COM 	if (status != DDI_SUCCESS) {
24269517SBill.Taylor@Sun.COM 		return (DDI_FAILURE);
24279517SBill.Taylor@Sun.COM 	}
24289517SBill.Taylor@Sun.COM 
24299517SBill.Taylor@Sun.COM 	/*
24309517SBill.Taylor@Sun.COM 	 * Determine the amount of memory to allocate, depending on the values
24319517SBill.Taylor@Sun.COM 	 * in "qa_bind_align" and "qa_alloc_align".  The problem we are trying
24329517SBill.Taylor@Sun.COM 	 * to solve here is that allocating a DMA handle with IOMMU bypass
24339517SBill.Taylor@Sun.COM 	 * (DDI_DMA_FORCE_PHYSICAL) constrains us to only requesting alignments
24349517SBill.Taylor@Sun.COM 	 * that are less restrictive than the page size.  Since we may need
24359517SBill.Taylor@Sun.COM 	 * stricter alignments on the memory allocated by ddi_dma_mem_alloc()
24369517SBill.Taylor@Sun.COM 	 * (e.g. in Hermon QP work queue memory allocation), we use the
24379517SBill.Taylor@Sun.COM 	 * following method to calculate how much additional memory to request,
24389517SBill.Taylor@Sun.COM 	 * and we enforce our own alignment on the allocated result.
24399517SBill.Taylor@Sun.COM 	 */
24409517SBill.Taylor@Sun.COM 	alloc_mask = qa_info->qa_alloc_align - 1;
24419517SBill.Taylor@Sun.COM 	if (qa_info->qa_bind_align == qa_info->qa_alloc_align) {
24429517SBill.Taylor@Sun.COM 		realsize = qa_info->qa_size;
24439517SBill.Taylor@Sun.COM 	} else {
24449517SBill.Taylor@Sun.COM 		realsize = qa_info->qa_size + alloc_mask;
24459517SBill.Taylor@Sun.COM 	}
24469517SBill.Taylor@Sun.COM 
24479517SBill.Taylor@Sun.COM 	/*
24489517SBill.Taylor@Sun.COM 	 * If we are to allocate the queue from system memory, then use
24499517SBill.Taylor@Sun.COM 	 * ddi_dma_mem_alloc() to find the space.  Otherwise, this is a
24509517SBill.Taylor@Sun.COM 	 * host memory allocation, use ddi_umem_alloc(). In either case,
24519517SBill.Taylor@Sun.COM 	 * return a pointer to the memory range allocated (including any
24529517SBill.Taylor@Sun.COM 	 * necessary alignment adjustments), the "real" memory pointer,
24539517SBill.Taylor@Sun.COM 	 * the "real" size, and a ddi_acc_handle_t to use when reading
24549517SBill.Taylor@Sun.COM 	 * from/writing to the memory.
24559517SBill.Taylor@Sun.COM 	 */
24569517SBill.Taylor@Sun.COM 	if (qa_info->qa_location == HERMON_QUEUE_LOCATION_NORMAL) {
24579517SBill.Taylor@Sun.COM 		/* Allocate system memory for the queue */
24589517SBill.Taylor@Sun.COM 		status = ddi_dma_mem_alloc(qa_info->qa_dmahdl, realsize,
24599517SBill.Taylor@Sun.COM 		    &state->hs_reg_accattr, DDI_DMA_CONSISTENT, callback, NULL,
24609517SBill.Taylor@Sun.COM 		    (caddr_t *)&qa_info->qa_buf_real,
24619517SBill.Taylor@Sun.COM 		    (size_t *)&qa_info->qa_buf_realsz, &qa_info->qa_acchdl);
24629517SBill.Taylor@Sun.COM 		if (status != DDI_SUCCESS) {
24639517SBill.Taylor@Sun.COM 			ddi_dma_free_handle(&qa_info->qa_dmahdl);
24649517SBill.Taylor@Sun.COM 			return (DDI_FAILURE);
24659517SBill.Taylor@Sun.COM 		}
24669517SBill.Taylor@Sun.COM 
24679517SBill.Taylor@Sun.COM 		/*
24689517SBill.Taylor@Sun.COM 		 * Save temporary copy of the real pointer.  (This may be
24699517SBill.Taylor@Sun.COM 		 * modified in the last step below).
24709517SBill.Taylor@Sun.COM 		 */
24719517SBill.Taylor@Sun.COM 		qa_info->qa_buf_aligned = qa_info->qa_buf_real;
24729517SBill.Taylor@Sun.COM 
24739517SBill.Taylor@Sun.COM 		bzero(qa_info->qa_buf_real, qa_info->qa_buf_realsz);
24749517SBill.Taylor@Sun.COM 
24759517SBill.Taylor@Sun.COM 	} else { /* HERMON_QUEUE_LOCATION_USERLAND */
24769517SBill.Taylor@Sun.COM 
24779517SBill.Taylor@Sun.COM 		/* Allocate userland mappable memory for the queue */
24789517SBill.Taylor@Sun.COM 		flag = (sleepflag == HERMON_SLEEP) ? DDI_UMEM_SLEEP :
24799517SBill.Taylor@Sun.COM 		    DDI_UMEM_NOSLEEP;
24809517SBill.Taylor@Sun.COM 		qa_info->qa_buf_real = ddi_umem_alloc(realsize, flag,
24819517SBill.Taylor@Sun.COM 		    &qa_info->qa_umemcookie);
24829517SBill.Taylor@Sun.COM 		if (qa_info->qa_buf_real == NULL) {
24839517SBill.Taylor@Sun.COM 			ddi_dma_free_handle(&qa_info->qa_dmahdl);
24849517SBill.Taylor@Sun.COM 			return (DDI_FAILURE);
24859517SBill.Taylor@Sun.COM 		}
24869517SBill.Taylor@Sun.COM 
24879517SBill.Taylor@Sun.COM 		/*
24889517SBill.Taylor@Sun.COM 		 * Save temporary copy of the real pointer.  (This may be
24899517SBill.Taylor@Sun.COM 		 * modified in the last step below).
24909517SBill.Taylor@Sun.COM 		 */
24919517SBill.Taylor@Sun.COM 		qa_info->qa_buf_aligned = qa_info->qa_buf_real;
24929517SBill.Taylor@Sun.COM 
24939517SBill.Taylor@Sun.COM 	}
24949517SBill.Taylor@Sun.COM 
24959517SBill.Taylor@Sun.COM 	/*
24969517SBill.Taylor@Sun.COM 	 * The next to last step is to ensure that the final address
24979517SBill.Taylor@Sun.COM 	 * ("qa_buf_aligned") has the appropriate "alloc" alignment
24989517SBill.Taylor@Sun.COM 	 * restriction applied to it (if necessary).
24999517SBill.Taylor@Sun.COM 	 */
25009517SBill.Taylor@Sun.COM 	if (qa_info->qa_bind_align != qa_info->qa_alloc_align) {
25019517SBill.Taylor@Sun.COM 		qa_info->qa_buf_aligned = (uint32_t *)(uintptr_t)(((uintptr_t)
25029517SBill.Taylor@Sun.COM 		    qa_info->qa_buf_aligned + alloc_mask) & ~alloc_mask);
25039517SBill.Taylor@Sun.COM 	}
25049517SBill.Taylor@Sun.COM 	/*
25059517SBill.Taylor@Sun.COM 	 * The last step is to figure out the offset of the start relative
25069517SBill.Taylor@Sun.COM 	 * to the first page of the region - will be used in the eqc/cqc
25079517SBill.Taylor@Sun.COM 	 * passed to the HW
25089517SBill.Taylor@Sun.COM 	 */
25099517SBill.Taylor@Sun.COM 	qa_info->qa_pgoffs = (uint_t)((uintptr_t)
251011972SBill.Taylor@Sun.COM 	    qa_info->qa_buf_aligned & HERMON_PAGEOFFSET);
25119517SBill.Taylor@Sun.COM 
25129517SBill.Taylor@Sun.COM 	return (DDI_SUCCESS);
25139517SBill.Taylor@Sun.COM }
25149517SBill.Taylor@Sun.COM 
25159517SBill.Taylor@Sun.COM 
25169517SBill.Taylor@Sun.COM /*
25179517SBill.Taylor@Sun.COM  * hermon_queue_free()
25189517SBill.Taylor@Sun.COM  *    Context: Can be called from interrupt or base context.
25199517SBill.Taylor@Sun.COM  */
25209517SBill.Taylor@Sun.COM void
hermon_queue_free(hermon_qalloc_info_t * qa_info)25219517SBill.Taylor@Sun.COM hermon_queue_free(hermon_qalloc_info_t *qa_info)
25229517SBill.Taylor@Sun.COM {
25239517SBill.Taylor@Sun.COM 	_NOTE(NOW_INVISIBLE_TO_OTHER_THREADS(*qa_info))
25249517SBill.Taylor@Sun.COM 
25259517SBill.Taylor@Sun.COM 	/*
25269517SBill.Taylor@Sun.COM 	 * Depending on how (i.e. from where) we allocated the memory for
25279517SBill.Taylor@Sun.COM 	 * this queue, we choose the appropriate method for releasing the
25289517SBill.Taylor@Sun.COM 	 * resources.
25299517SBill.Taylor@Sun.COM 	 */
25309517SBill.Taylor@Sun.COM 	if (qa_info->qa_location == HERMON_QUEUE_LOCATION_NORMAL) {
25319517SBill.Taylor@Sun.COM 
25329517SBill.Taylor@Sun.COM 		ddi_dma_mem_free(&qa_info->qa_acchdl);
25339517SBill.Taylor@Sun.COM 
25349517SBill.Taylor@Sun.COM 	} else if (qa_info->qa_location == HERMON_QUEUE_LOCATION_USERLAND) {
25359517SBill.Taylor@Sun.COM 
25369517SBill.Taylor@Sun.COM 		ddi_umem_free(qa_info->qa_umemcookie);
25379517SBill.Taylor@Sun.COM 
25389517SBill.Taylor@Sun.COM 	}
25399517SBill.Taylor@Sun.COM 
25409517SBill.Taylor@Sun.COM 	/* Always free the dma handle */
25419517SBill.Taylor@Sun.COM 	ddi_dma_free_handle(&qa_info->qa_dmahdl);
25429517SBill.Taylor@Sun.COM }
25439517SBill.Taylor@Sun.COM 
25449517SBill.Taylor@Sun.COM /*
254511972SBill.Taylor@Sun.COM  * hermon_create_fmr_pool()
25469517SBill.Taylor@Sun.COM  * Create a pool of FMRs.
25479517SBill.Taylor@Sun.COM  *     Context: Can be called from kernel context only.
25489517SBill.Taylor@Sun.COM  */
25499517SBill.Taylor@Sun.COM int
hermon_create_fmr_pool(hermon_state_t * state,hermon_pdhdl_t pd,ibt_fmr_pool_attr_t * fmr_attr,hermon_fmrhdl_t * fmrpoolp)25509517SBill.Taylor@Sun.COM hermon_create_fmr_pool(hermon_state_t *state, hermon_pdhdl_t pd,
25519517SBill.Taylor@Sun.COM     ibt_fmr_pool_attr_t *fmr_attr, hermon_fmrhdl_t *fmrpoolp)
25529517SBill.Taylor@Sun.COM {
25539517SBill.Taylor@Sun.COM 	hermon_fmrhdl_t	fmrpool;
25549517SBill.Taylor@Sun.COM 	hermon_fmr_list_t *fmr, *fmr_next;
25559517SBill.Taylor@Sun.COM 	hermon_mrhdl_t   mr;
25569517SBill.Taylor@Sun.COM 	int		status;
25579517SBill.Taylor@Sun.COM 	int		sleep;
25589517SBill.Taylor@Sun.COM 	int		i;
25599517SBill.Taylor@Sun.COM 
25609517SBill.Taylor@Sun.COM 	sleep = (fmr_attr->fmr_flags & IBT_MR_SLEEP) ? HERMON_SLEEP :
25619517SBill.Taylor@Sun.COM 	    HERMON_NOSLEEP;
25629517SBill.Taylor@Sun.COM 	if ((sleep == HERMON_SLEEP) &&
25639517SBill.Taylor@Sun.COM 	    (sleep != HERMON_SLEEPFLAG_FOR_CONTEXT())) {
25649517SBill.Taylor@Sun.COM 		return (IBT_INVALID_PARAM);
25659517SBill.Taylor@Sun.COM 	}
25669517SBill.Taylor@Sun.COM 
25679517SBill.Taylor@Sun.COM 	fmrpool = (hermon_fmrhdl_t)kmem_zalloc(sizeof (*fmrpool), sleep);
25689517SBill.Taylor@Sun.COM 	if (fmrpool == NULL) {
25699517SBill.Taylor@Sun.COM 		status = IBT_INSUFF_RESOURCE;
25709517SBill.Taylor@Sun.COM 		goto fail;
25719517SBill.Taylor@Sun.COM 	}
25729517SBill.Taylor@Sun.COM 	_NOTE(NOW_INVISIBLE_TO_OTHER_THREADS(*fmrpool))
25739517SBill.Taylor@Sun.COM 
25749517SBill.Taylor@Sun.COM 	mutex_init(&fmrpool->fmr_lock, NULL, MUTEX_DRIVER,
25759517SBill.Taylor@Sun.COM 	    DDI_INTR_PRI(state->hs_intrmsi_pri));
2576*12965SWilliam.Taylor@Oracle.COM 	mutex_init(&fmrpool->remap_lock, NULL, MUTEX_DRIVER,
2577*12965SWilliam.Taylor@Oracle.COM 	    DDI_INTR_PRI(state->hs_intrmsi_pri));
2578*12965SWilliam.Taylor@Oracle.COM 	mutex_init(&fmrpool->dirty_lock, NULL, MUTEX_DRIVER,
2579*12965SWilliam.Taylor@Oracle.COM 	    DDI_INTR_PRI(state->hs_intrmsi_pri));
25809517SBill.Taylor@Sun.COM 
25819517SBill.Taylor@Sun.COM 	fmrpool->fmr_state	    = state;
25829517SBill.Taylor@Sun.COM 	fmrpool->fmr_flush_function = fmr_attr->fmr_func_hdlr;
25839517SBill.Taylor@Sun.COM 	fmrpool->fmr_flush_arg	    = fmr_attr->fmr_func_arg;
25849517SBill.Taylor@Sun.COM 	fmrpool->fmr_pool_size	    = 0;
25859517SBill.Taylor@Sun.COM 	fmrpool->fmr_max_pages	    = fmr_attr->fmr_max_pages_per_fmr;
25869517SBill.Taylor@Sun.COM 	fmrpool->fmr_page_sz	    = fmr_attr->fmr_page_sz;
2587*12965SWilliam.Taylor@Oracle.COM 	fmrpool->fmr_dirty_watermark = fmr_attr->fmr_pool_size / 4;
25889517SBill.Taylor@Sun.COM 	fmrpool->fmr_dirty_len	    = 0;
2589*12965SWilliam.Taylor@Oracle.COM 	fmrpool->fmr_remap_watermark = fmr_attr->fmr_pool_size / 32;
2590*12965SWilliam.Taylor@Oracle.COM 	fmrpool->fmr_remap_len	    = 0;
25919517SBill.Taylor@Sun.COM 	fmrpool->fmr_flags	    = fmr_attr->fmr_flags;
2592*12965SWilliam.Taylor@Oracle.COM 	fmrpool->fmr_stat_register  = 0;
2593*12965SWilliam.Taylor@Oracle.COM 	fmrpool->fmr_max_remaps	    = state->hs_cfg_profile->cp_fmr_max_remaps;
2594*12965SWilliam.Taylor@Oracle.COM 	fmrpool->fmr_remap_gen	    = 1;
25959517SBill.Taylor@Sun.COM 
2596*12965SWilliam.Taylor@Oracle.COM 	fmrpool->fmr_free_list_tail = &fmrpool->fmr_free_list;
25979517SBill.Taylor@Sun.COM 	fmrpool->fmr_dirty_list = NULL;
2598*12965SWilliam.Taylor@Oracle.COM 	fmrpool->fmr_dirty_list_tail = &fmrpool->fmr_dirty_list;
2599*12965SWilliam.Taylor@Oracle.COM 	fmrpool->fmr_remap_list = NULL;
2600*12965SWilliam.Taylor@Oracle.COM 	fmrpool->fmr_remap_list_tail = &fmrpool->fmr_remap_list;
2601*12965SWilliam.Taylor@Oracle.COM 	fmrpool->fmr_pool_size = fmrpool->fmr_free_len =
2602*12965SWilliam.Taylor@Oracle.COM 	    fmr_attr->fmr_pool_size;
26039517SBill.Taylor@Sun.COM 
26049517SBill.Taylor@Sun.COM 	for (i = 0; i < fmr_attr->fmr_pool_size; i++) {
26059517SBill.Taylor@Sun.COM 		status = hermon_mr_alloc_fmr(state, pd, fmrpool, &mr);
26069517SBill.Taylor@Sun.COM 		if (status != DDI_SUCCESS) {
26079517SBill.Taylor@Sun.COM 			goto fail2;
26089517SBill.Taylor@Sun.COM 		}
26099517SBill.Taylor@Sun.COM 
26109517SBill.Taylor@Sun.COM 		fmr = (hermon_fmr_list_t *)kmem_zalloc(
26119517SBill.Taylor@Sun.COM 		    sizeof (hermon_fmr_list_t), sleep);
26129517SBill.Taylor@Sun.COM 		_NOTE(NOW_INVISIBLE_TO_OTHER_THREADS(*fmr))
26139517SBill.Taylor@Sun.COM 
26149517SBill.Taylor@Sun.COM 		fmr->fmr = mr;
26159517SBill.Taylor@Sun.COM 		fmr->fmr_remaps = 0;
2616*12965SWilliam.Taylor@Oracle.COM 		fmr->fmr_remap_gen = fmrpool->fmr_remap_gen;
26179517SBill.Taylor@Sun.COM 		fmr->fmr_pool = fmrpool;
26189517SBill.Taylor@Sun.COM 		_NOTE(NOW_INVISIBLE_TO_OTHER_THREADS(*mr))
26199517SBill.Taylor@Sun.COM 		mr->mr_fmr = fmr;
26209517SBill.Taylor@Sun.COM 
2621*12965SWilliam.Taylor@Oracle.COM 		if (!i)		/* address of last entry's link */
2622*12965SWilliam.Taylor@Oracle.COM 			fmrpool->fmr_free_list_tail = &fmr->fmr_next;
26239517SBill.Taylor@Sun.COM 		fmr->fmr_next = fmrpool->fmr_free_list;
26249517SBill.Taylor@Sun.COM 		fmrpool->fmr_free_list = fmr;
26259517SBill.Taylor@Sun.COM 	}
26269517SBill.Taylor@Sun.COM 
26279517SBill.Taylor@Sun.COM 	/* Set to return pool */
26289517SBill.Taylor@Sun.COM 	*fmrpoolp = fmrpool;
26299517SBill.Taylor@Sun.COM 
2630*12965SWilliam.Taylor@Oracle.COM 	IBTF_DPRINTF_L2("fmr", "create_fmr_pool SUCCESS");
26319517SBill.Taylor@Sun.COM 	return (IBT_SUCCESS);
26329517SBill.Taylor@Sun.COM fail2:
26339517SBill.Taylor@Sun.COM 	for (fmr = fmrpool->fmr_free_list; fmr != NULL; fmr = fmr_next) {
26349517SBill.Taylor@Sun.COM 		_NOTE(NOW_INVISIBLE_TO_OTHER_THREADS(*fmr))
26359517SBill.Taylor@Sun.COM 		fmr_next = fmr->fmr_next;
26369517SBill.Taylor@Sun.COM 		(void) hermon_mr_dealloc_fmr(state, &fmr->fmr);
26379517SBill.Taylor@Sun.COM 		kmem_free(fmr, sizeof (hermon_fmr_list_t));
26389517SBill.Taylor@Sun.COM 	}
26399517SBill.Taylor@Sun.COM 	kmem_free(fmrpool, sizeof (*fmrpool));
26409517SBill.Taylor@Sun.COM fail:
2641*12965SWilliam.Taylor@Oracle.COM 	*fmrpoolp = NULL;
2642*12965SWilliam.Taylor@Oracle.COM 	IBTF_DPRINTF_L2("fmr", "create_fmr_pool FAILED");
26439517SBill.Taylor@Sun.COM 	if (status == DDI_FAILURE) {
26449517SBill.Taylor@Sun.COM 		return (ibc_get_ci_failure(0));
26459517SBill.Taylor@Sun.COM 	} else {
26469517SBill.Taylor@Sun.COM 		return (status);
26479517SBill.Taylor@Sun.COM 	}
26489517SBill.Taylor@Sun.COM }
26499517SBill.Taylor@Sun.COM 
26509517SBill.Taylor@Sun.COM /*
26519517SBill.Taylor@Sun.COM  * hermon_destroy_fmr_pool()
26529517SBill.Taylor@Sun.COM  * Destroy an FMR pool and free all associated resources.
26539517SBill.Taylor@Sun.COM  *     Context: Can be called from kernel context only.
26549517SBill.Taylor@Sun.COM  */
26559517SBill.Taylor@Sun.COM int
hermon_destroy_fmr_pool(hermon_state_t * state,hermon_fmrhdl_t fmrpool)26569517SBill.Taylor@Sun.COM hermon_destroy_fmr_pool(hermon_state_t *state, hermon_fmrhdl_t fmrpool)
26579517SBill.Taylor@Sun.COM {
26589517SBill.Taylor@Sun.COM 	hermon_fmr_list_t	*fmr, *fmr_next;
26599517SBill.Taylor@Sun.COM 
26609517SBill.Taylor@Sun.COM 	mutex_enter(&fmrpool->fmr_lock);
2661*12965SWilliam.Taylor@Oracle.COM 	hermon_fmr_cleanup(fmrpool);
26629517SBill.Taylor@Sun.COM 
26639517SBill.Taylor@Sun.COM 	for (fmr = fmrpool->fmr_free_list; fmr != NULL; fmr = fmr_next) {
26649517SBill.Taylor@Sun.COM 		fmr_next = fmr->fmr_next;
26659517SBill.Taylor@Sun.COM 
26669517SBill.Taylor@Sun.COM 		(void) hermon_mr_dealloc_fmr(state, &fmr->fmr);
26679517SBill.Taylor@Sun.COM 		kmem_free(fmr, sizeof (hermon_fmr_list_t));
2668*12965SWilliam.Taylor@Oracle.COM 
2669*12965SWilliam.Taylor@Oracle.COM 		--fmrpool->fmr_pool_size;
26709517SBill.Taylor@Sun.COM 	}
2671*12965SWilliam.Taylor@Oracle.COM 	ASSERT(fmrpool->fmr_pool_size == 0);
26729517SBill.Taylor@Sun.COM 	mutex_exit(&fmrpool->fmr_lock);
26739517SBill.Taylor@Sun.COM 
26749517SBill.Taylor@Sun.COM 	mutex_destroy(&fmrpool->fmr_lock);
2675*12965SWilliam.Taylor@Oracle.COM 	mutex_destroy(&fmrpool->dirty_lock);
2676*12965SWilliam.Taylor@Oracle.COM 	mutex_destroy(&fmrpool->remap_lock);
26779517SBill.Taylor@Sun.COM 
26789517SBill.Taylor@Sun.COM 	kmem_free(fmrpool, sizeof (*fmrpool));
2679*12965SWilliam.Taylor@Oracle.COM 	IBTF_DPRINTF_L2("fmr", "destroy_fmr_pool SUCCESS");
26809517SBill.Taylor@Sun.COM 	return (DDI_SUCCESS);
26819517SBill.Taylor@Sun.COM }
26829517SBill.Taylor@Sun.COM 
26839517SBill.Taylor@Sun.COM /*
26849517SBill.Taylor@Sun.COM  * hermon_flush_fmr_pool()
26859517SBill.Taylor@Sun.COM  * Ensure that all unmapped FMRs are fully invalidated.
26869517SBill.Taylor@Sun.COM  *     Context: Can be called from kernel context only.
26879517SBill.Taylor@Sun.COM  */
2688*12965SWilliam.Taylor@Oracle.COM /* ARGSUSED */
26899517SBill.Taylor@Sun.COM int
hermon_flush_fmr_pool(hermon_state_t * state,hermon_fmrhdl_t fmrpool)26909517SBill.Taylor@Sun.COM hermon_flush_fmr_pool(hermon_state_t *state, hermon_fmrhdl_t fmrpool)
26919517SBill.Taylor@Sun.COM {
26929517SBill.Taylor@Sun.COM 	/*
26939517SBill.Taylor@Sun.COM 	 * Force the unmapping of all entries on the dirty list, regardless of
26949517SBill.Taylor@Sun.COM 	 * whether the watermark has been hit yet.
26959517SBill.Taylor@Sun.COM 	 */
26969517SBill.Taylor@Sun.COM 	/* grab the pool lock */
26979517SBill.Taylor@Sun.COM 	mutex_enter(&fmrpool->fmr_lock);
2698*12965SWilliam.Taylor@Oracle.COM 	hermon_fmr_cleanup(fmrpool);
26999517SBill.Taylor@Sun.COM 	mutex_exit(&fmrpool->fmr_lock);
2700*12965SWilliam.Taylor@Oracle.COM 	return (DDI_SUCCESS);
27019517SBill.Taylor@Sun.COM }
27029517SBill.Taylor@Sun.COM 
27039517SBill.Taylor@Sun.COM /*
2704*12965SWilliam.Taylor@Oracle.COM  * hermon_register_physical_fmr()
27059517SBill.Taylor@Sun.COM  * Map memory into FMR
27069517SBill.Taylor@Sun.COM  *    Context: Can be called from interrupt or base context.
27079517SBill.Taylor@Sun.COM  */
27089517SBill.Taylor@Sun.COM 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)27099517SBill.Taylor@Sun.COM hermon_register_physical_fmr(hermon_state_t *state, hermon_fmrhdl_t fmrpool,
27109517SBill.Taylor@Sun.COM     ibt_pmr_attr_t *mem_pattr, hermon_mrhdl_t *mr,
27119517SBill.Taylor@Sun.COM     ibt_pmr_desc_t *mem_desc_p)
27129517SBill.Taylor@Sun.COM {
27139517SBill.Taylor@Sun.COM 	hermon_fmr_list_t	*fmr;
27149517SBill.Taylor@Sun.COM 	int			status;
27159517SBill.Taylor@Sun.COM 
27169517SBill.Taylor@Sun.COM 	/* Check length */
27179517SBill.Taylor@Sun.COM 	if (mem_pattr->pmr_len < 1 || (mem_pattr->pmr_num_buf >
27189517SBill.Taylor@Sun.COM 	    fmrpool->fmr_max_pages)) {
27199517SBill.Taylor@Sun.COM 		return (IBT_MR_LEN_INVALID);
27209517SBill.Taylor@Sun.COM 	}
27219517SBill.Taylor@Sun.COM 
2722*12965SWilliam.Taylor@Oracle.COM 	mutex_enter(&fmrpool->fmr_lock);
2723*12965SWilliam.Taylor@Oracle.COM 	if (fmrpool->fmr_free_list == NULL) {
2724*12965SWilliam.Taylor@Oracle.COM 		if (hermon_fmr_verbose & 2)
2725*12965SWilliam.Taylor@Oracle.COM 			IBTF_DPRINTF_L2("fmr", "register needs remap");
2726*12965SWilliam.Taylor@Oracle.COM 		mutex_enter(&fmrpool->remap_lock);
2727*12965SWilliam.Taylor@Oracle.COM 		if (fmrpool->fmr_remap_list) {
2728*12965SWilliam.Taylor@Oracle.COM 			/* add to free list */
2729*12965SWilliam.Taylor@Oracle.COM 			*(fmrpool->fmr_free_list_tail) =
2730*12965SWilliam.Taylor@Oracle.COM 			    fmrpool->fmr_remap_list;
2731*12965SWilliam.Taylor@Oracle.COM 			fmrpool->fmr_remap_list = NULL;
2732*12965SWilliam.Taylor@Oracle.COM 			fmrpool->fmr_free_list_tail =
2733*12965SWilliam.Taylor@Oracle.COM 			    fmrpool->fmr_remap_list_tail;
27349517SBill.Taylor@Sun.COM 
2735*12965SWilliam.Taylor@Oracle.COM 			/* reset list */
2736*12965SWilliam.Taylor@Oracle.COM 			fmrpool->fmr_remap_list_tail = &fmrpool->fmr_remap_list;
2737*12965SWilliam.Taylor@Oracle.COM 			fmrpool->fmr_free_len += fmrpool->fmr_remap_len;
2738*12965SWilliam.Taylor@Oracle.COM 			fmrpool->fmr_remap_len = 0;
27399517SBill.Taylor@Sun.COM 		}
2740*12965SWilliam.Taylor@Oracle.COM 		mutex_exit(&fmrpool->remap_lock);
27419517SBill.Taylor@Sun.COM 	}
2742*12965SWilliam.Taylor@Oracle.COM 	if (fmrpool->fmr_free_list == NULL) {
2743*12965SWilliam.Taylor@Oracle.COM 		if (hermon_fmr_verbose & 2)
2744*12965SWilliam.Taylor@Oracle.COM 			IBTF_DPRINTF_L2("fmr", "register needs cleanup");
2745*12965SWilliam.Taylor@Oracle.COM 		hermon_fmr_cleanup(fmrpool);
2746*12965SWilliam.Taylor@Oracle.COM 	}
27479517SBill.Taylor@Sun.COM 
27489517SBill.Taylor@Sun.COM 	/* grab next free entry */
27499517SBill.Taylor@Sun.COM 	fmr = fmrpool->fmr_free_list;
27509517SBill.Taylor@Sun.COM 	if (fmr == NULL) {
275111972SBill.Taylor@Sun.COM 		IBTF_DPRINTF_L2("fmr", "WARNING: no free fmr resource");
2752*12965SWilliam.Taylor@Oracle.COM 		cmn_err(CE_CONT, "no free fmr resource\n");
27539517SBill.Taylor@Sun.COM 		mutex_exit(&fmrpool->fmr_lock);
27549517SBill.Taylor@Sun.COM 		return (IBT_INSUFF_RESOURCE);
27559517SBill.Taylor@Sun.COM 	}
27569517SBill.Taylor@Sun.COM 
2757*12965SWilliam.Taylor@Oracle.COM 	if ((fmrpool->fmr_free_list = fmr->fmr_next) == NULL)
2758*12965SWilliam.Taylor@Oracle.COM 		fmrpool->fmr_free_list_tail = &fmrpool->fmr_free_list;
27599517SBill.Taylor@Sun.COM 	fmr->fmr_next = NULL;
2760*12965SWilliam.Taylor@Oracle.COM 	fmrpool->fmr_stat_register++;
2761*12965SWilliam.Taylor@Oracle.COM 	mutex_exit(&fmrpool->fmr_lock);
27629517SBill.Taylor@Sun.COM 
2763*12965SWilliam.Taylor@Oracle.COM 	_NOTE(NOW_INVISIBLE_TO_OTHER_THREADS(*fmr))
27649517SBill.Taylor@Sun.COM 	status = hermon_mr_register_physical_fmr(state, mem_pattr, fmr->fmr,
27659517SBill.Taylor@Sun.COM 	    mem_desc_p);
27669517SBill.Taylor@Sun.COM 	if (status != DDI_SUCCESS) {
27679517SBill.Taylor@Sun.COM 		return (status);
27689517SBill.Taylor@Sun.COM 	}
276911972SBill.Taylor@Sun.COM 	_NOTE(NOW_INVISIBLE_TO_OTHER_THREADS(*fmr->fmr))
277011972SBill.Taylor@Sun.COM 	if (hermon_rdma_debug & 0x4)
277111972SBill.Taylor@Sun.COM 		IBTF_DPRINTF_L2("fmr", "  reg: mr %p  key %x",
277211972SBill.Taylor@Sun.COM 		    fmr->fmr, fmr->fmr->mr_rkey);
277311972SBill.Taylor@Sun.COM 	_NOTE(NOW_VISIBLE_TO_OTHER_THREADS(*fmr->fmr))
2774*12965SWilliam.Taylor@Oracle.COM 	if (fmr->fmr_remap_gen != fmrpool->fmr_remap_gen) {
2775*12965SWilliam.Taylor@Oracle.COM 		fmr->fmr_remap_gen = fmrpool->fmr_remap_gen;
2776*12965SWilliam.Taylor@Oracle.COM 		fmr->fmr_remaps = 0;
2777*12965SWilliam.Taylor@Oracle.COM 	}
27789517SBill.Taylor@Sun.COM 
27799517SBill.Taylor@Sun.COM 	fmr->fmr_remaps++;
27809517SBill.Taylor@Sun.COM 
27819517SBill.Taylor@Sun.COM 	*mr = (hermon_mrhdl_t)fmr->fmr;
27829517SBill.Taylor@Sun.COM 
27839517SBill.Taylor@Sun.COM 	return (DDI_SUCCESS);
27849517SBill.Taylor@Sun.COM }
27859517SBill.Taylor@Sun.COM 
27869517SBill.Taylor@Sun.COM /*
27879517SBill.Taylor@Sun.COM  * hermon_deregister_fmr()
27889517SBill.Taylor@Sun.COM  * Unmap FMR
27899517SBill.Taylor@Sun.COM  *    Context: Can be called from kernel context only.
27909517SBill.Taylor@Sun.COM  */
27919517SBill.Taylor@Sun.COM int
hermon_deregister_fmr(hermon_state_t * state,hermon_mrhdl_t mr)27929517SBill.Taylor@Sun.COM hermon_deregister_fmr(hermon_state_t *state, hermon_mrhdl_t mr)
27939517SBill.Taylor@Sun.COM {
27949517SBill.Taylor@Sun.COM 	hermon_fmrhdl_t		fmrpool;
2795*12965SWilliam.Taylor@Oracle.COM 	hermon_fmr_list_t	*fmr, **fmrlast;
2796*12965SWilliam.Taylor@Oracle.COM 	int			len;
27979517SBill.Taylor@Sun.COM 
27989517SBill.Taylor@Sun.COM 	fmr = mr->mr_fmr;
2799*12965SWilliam.Taylor@Oracle.COM 	_NOTE(NOW_INVISIBLE_TO_OTHER_THREADS(*fmr))
28009517SBill.Taylor@Sun.COM 	fmrpool = fmr->fmr_pool;
28019517SBill.Taylor@Sun.COM 
2802*12965SWilliam.Taylor@Oracle.COM 	/* mark as owned by software */
2803*12965SWilliam.Taylor@Oracle.COM 	_NOTE(NOW_INVISIBLE_TO_OTHER_THREADS(*(fmr->fmr)))
2804*12965SWilliam.Taylor@Oracle.COM 	*(uint8_t *)(fmr->fmr->mr_mptrsrcp->hr_addr) = 0xF0;
28059517SBill.Taylor@Sun.COM 
2806*12965SWilliam.Taylor@Oracle.COM 	if (fmr->fmr_remaps <
2807*12965SWilliam.Taylor@Oracle.COM 	    state->hs_cfg_profile->cp_fmr_max_remaps) {
2808*12965SWilliam.Taylor@Oracle.COM 		/* add to remap list */
2809*12965SWilliam.Taylor@Oracle.COM 		_NOTE(NOW_INVISIBLE_TO_OTHER_THREADS(*(fmr->fmr)))
2810*12965SWilliam.Taylor@Oracle.COM 		if (hermon_rdma_debug & 0x4)
2811*12965SWilliam.Taylor@Oracle.COM 			IBTF_DPRINTF_L2("fmr", "dereg: mr %p  key %x",
2812*12965SWilliam.Taylor@Oracle.COM 			    fmr->fmr, fmr->fmr->mr_rkey);
2813*12965SWilliam.Taylor@Oracle.COM 		_NOTE(NOW_VISIBLE_TO_OTHER_THREADS(*(fmr->fmr)))
2814*12965SWilliam.Taylor@Oracle.COM 		mutex_enter(&fmrpool->remap_lock);
2815*12965SWilliam.Taylor@Oracle.COM 		fmr->fmr_next = NULL;
2816*12965SWilliam.Taylor@Oracle.COM 		*(fmrpool->fmr_remap_list_tail) = fmr;
2817*12965SWilliam.Taylor@Oracle.COM 		fmrpool->fmr_remap_list_tail = &fmr->fmr_next;
2818*12965SWilliam.Taylor@Oracle.COM 		fmrpool->fmr_remap_len++;
28199517SBill.Taylor@Sun.COM 
2820*12965SWilliam.Taylor@Oracle.COM 		/* conditionally add remap list back to free list */
2821*12965SWilliam.Taylor@Oracle.COM 		fmrlast = NULL;
2822*12965SWilliam.Taylor@Oracle.COM 		if (fmrpool->fmr_remap_len >=
2823*12965SWilliam.Taylor@Oracle.COM 		    fmrpool->fmr_remap_watermark) {
2824*12965SWilliam.Taylor@Oracle.COM 			fmr = fmrpool->fmr_remap_list;
2825*12965SWilliam.Taylor@Oracle.COM 			fmrlast = fmrpool->fmr_remap_list_tail;
2826*12965SWilliam.Taylor@Oracle.COM 			len = fmrpool->fmr_remap_len;
2827*12965SWilliam.Taylor@Oracle.COM 			fmrpool->fmr_remap_len = 0;
2828*12965SWilliam.Taylor@Oracle.COM 			fmrpool->fmr_remap_list = NULL;
2829*12965SWilliam.Taylor@Oracle.COM 			fmrpool->fmr_remap_list_tail =
2830*12965SWilliam.Taylor@Oracle.COM 			    &fmrpool->fmr_remap_list;
28319517SBill.Taylor@Sun.COM 		}
2832*12965SWilliam.Taylor@Oracle.COM 		mutex_exit(&fmrpool->remap_lock);
2833*12965SWilliam.Taylor@Oracle.COM 		if (fmrlast) {
2834*12965SWilliam.Taylor@Oracle.COM 			mutex_enter(&fmrpool->fmr_lock);
2835*12965SWilliam.Taylor@Oracle.COM 			*(fmrpool->fmr_free_list_tail) = fmr;
2836*12965SWilliam.Taylor@Oracle.COM 			fmrpool->fmr_free_list_tail = fmrlast;
2837*12965SWilliam.Taylor@Oracle.COM 			fmrpool->fmr_free_len += len;
2838*12965SWilliam.Taylor@Oracle.COM 			mutex_exit(&fmrpool->fmr_lock);
2839*12965SWilliam.Taylor@Oracle.COM 		}
2840*12965SWilliam.Taylor@Oracle.COM 	} else {
2841*12965SWilliam.Taylor@Oracle.COM 		/* add to dirty list */
2842*12965SWilliam.Taylor@Oracle.COM 		_NOTE(NOW_INVISIBLE_TO_OTHER_THREADS(*(fmr->fmr)))
2843*12965SWilliam.Taylor@Oracle.COM 		if (hermon_rdma_debug & 0x4)
2844*12965SWilliam.Taylor@Oracle.COM 			IBTF_DPRINTF_L2("fmr", "dirty: mr %p  key %x",
2845*12965SWilliam.Taylor@Oracle.COM 			    fmr->fmr, fmr->fmr->mr_rkey);
2846*12965SWilliam.Taylor@Oracle.COM 		_NOTE(NOW_VISIBLE_TO_OTHER_THREADS(*(fmr->fmr)))
28479517SBill.Taylor@Sun.COM 
2848*12965SWilliam.Taylor@Oracle.COM 		mutex_enter(&fmrpool->dirty_lock);
2849*12965SWilliam.Taylor@Oracle.COM 		fmr->fmr_next = NULL;
2850*12965SWilliam.Taylor@Oracle.COM 		*(fmrpool->fmr_dirty_list_tail) = fmr;
2851*12965SWilliam.Taylor@Oracle.COM 		fmrpool->fmr_dirty_list_tail = &fmr->fmr_next;
2852*12965SWilliam.Taylor@Oracle.COM 		fmrpool->fmr_dirty_len++;
28539517SBill.Taylor@Sun.COM 
2854*12965SWilliam.Taylor@Oracle.COM 		if (fmrpool->fmr_dirty_len >=
2855*12965SWilliam.Taylor@Oracle.COM 		    fmrpool->fmr_dirty_watermark) {
2856*12965SWilliam.Taylor@Oracle.COM 			mutex_exit(&fmrpool->dirty_lock);
2857*12965SWilliam.Taylor@Oracle.COM 			mutex_enter(&fmrpool->fmr_lock);
2858*12965SWilliam.Taylor@Oracle.COM 			hermon_fmr_cleanup(fmrpool);
28599517SBill.Taylor@Sun.COM 			mutex_exit(&fmrpool->fmr_lock);
2860*12965SWilliam.Taylor@Oracle.COM 		} else
2861*12965SWilliam.Taylor@Oracle.COM 			mutex_exit(&fmrpool->dirty_lock);
28629517SBill.Taylor@Sun.COM 	}
2863*12965SWilliam.Taylor@Oracle.COM 	return (DDI_SUCCESS);
28649517SBill.Taylor@Sun.COM }
28659517SBill.Taylor@Sun.COM 
28669517SBill.Taylor@Sun.COM /*
28679517SBill.Taylor@Sun.COM  * hermon_fmr_cleanup()
2868*12965SWilliam.Taylor@Oracle.COM  *     Context: Called from any context.
28699517SBill.Taylor@Sun.COM  */
2870*12965SWilliam.Taylor@Oracle.COM static void
hermon_fmr_cleanup(hermon_fmrhdl_t fmrpool)2871*12965SWilliam.Taylor@Oracle.COM hermon_fmr_cleanup(hermon_fmrhdl_t fmrpool)
28729517SBill.Taylor@Sun.COM {
28739517SBill.Taylor@Sun.COM 	int			status;
28749517SBill.Taylor@Sun.COM 
28759517SBill.Taylor@Sun.COM 	ASSERT(MUTEX_HELD(&fmrpool->fmr_lock));
28769517SBill.Taylor@Sun.COM 
2877*12965SWilliam.Taylor@Oracle.COM 	if (fmrpool->fmr_stat_register == 0)
2878*12965SWilliam.Taylor@Oracle.COM 		return;
28799517SBill.Taylor@Sun.COM 
2880*12965SWilliam.Taylor@Oracle.COM 	fmrpool->fmr_stat_register = 0;
2881*12965SWilliam.Taylor@Oracle.COM 	membar_producer();
28829517SBill.Taylor@Sun.COM 
2883*12965SWilliam.Taylor@Oracle.COM 	if (hermon_fmr_verbose)
2884*12965SWilliam.Taylor@Oracle.COM 		IBTF_DPRINTF_L2("fmr", "TPT_SYNC");
2885*12965SWilliam.Taylor@Oracle.COM 	status = hermon_sync_tpt_cmd_post(fmrpool->fmr_state,
2886*12965SWilliam.Taylor@Oracle.COM 	    HERMON_CMD_NOSLEEP_SPIN);
2887*12965SWilliam.Taylor@Oracle.COM 	if (status != HERMON_CMD_SUCCESS) {
2888*12965SWilliam.Taylor@Oracle.COM 		cmn_err(CE_WARN, "fmr SYNC_TPT failed(%x)\n", status);
2889*12965SWilliam.Taylor@Oracle.COM 	}
2890*12965SWilliam.Taylor@Oracle.COM 	fmrpool->fmr_remap_gen++;
28919517SBill.Taylor@Sun.COM 
2892*12965SWilliam.Taylor@Oracle.COM 	/* add everything back to the free list */
2893*12965SWilliam.Taylor@Oracle.COM 	mutex_enter(&fmrpool->dirty_lock);
2894*12965SWilliam.Taylor@Oracle.COM 	if (fmrpool->fmr_dirty_list) {
2895*12965SWilliam.Taylor@Oracle.COM 		/* add to free list */
2896*12965SWilliam.Taylor@Oracle.COM 		*(fmrpool->fmr_free_list_tail) = fmrpool->fmr_dirty_list;
2897*12965SWilliam.Taylor@Oracle.COM 		fmrpool->fmr_dirty_list = NULL;
2898*12965SWilliam.Taylor@Oracle.COM 		fmrpool->fmr_free_list_tail = fmrpool->fmr_dirty_list_tail;
28999517SBill.Taylor@Sun.COM 
2900*12965SWilliam.Taylor@Oracle.COM 		/* reset list */
2901*12965SWilliam.Taylor@Oracle.COM 		fmrpool->fmr_dirty_list_tail = &fmrpool->fmr_dirty_list;
2902*12965SWilliam.Taylor@Oracle.COM 		fmrpool->fmr_free_len += fmrpool->fmr_dirty_len;
2903*12965SWilliam.Taylor@Oracle.COM 		fmrpool->fmr_dirty_len = 0;
29049517SBill.Taylor@Sun.COM 	}
2905*12965SWilliam.Taylor@Oracle.COM 	mutex_exit(&fmrpool->dirty_lock);
29069517SBill.Taylor@Sun.COM 
2907*12965SWilliam.Taylor@Oracle.COM 	mutex_enter(&fmrpool->remap_lock);
2908*12965SWilliam.Taylor@Oracle.COM 	if (fmrpool->fmr_remap_list) {
2909*12965SWilliam.Taylor@Oracle.COM 		/* add to free list */
2910*12965SWilliam.Taylor@Oracle.COM 		*(fmrpool->fmr_free_list_tail) = fmrpool->fmr_remap_list;
2911*12965SWilliam.Taylor@Oracle.COM 		fmrpool->fmr_remap_list = NULL;
2912*12965SWilliam.Taylor@Oracle.COM 		fmrpool->fmr_free_list_tail = fmrpool->fmr_remap_list_tail;
29139517SBill.Taylor@Sun.COM 
2914*12965SWilliam.Taylor@Oracle.COM 		/* reset list */
2915*12965SWilliam.Taylor@Oracle.COM 		fmrpool->fmr_remap_list_tail = &fmrpool->fmr_remap_list;
2916*12965SWilliam.Taylor@Oracle.COM 		fmrpool->fmr_free_len += fmrpool->fmr_remap_len;
2917*12965SWilliam.Taylor@Oracle.COM 		fmrpool->fmr_remap_len = 0;
2918*12965SWilliam.Taylor@Oracle.COM 	}
2919*12965SWilliam.Taylor@Oracle.COM 	mutex_exit(&fmrpool->remap_lock);
29209517SBill.Taylor@Sun.COM 
2921*12965SWilliam.Taylor@Oracle.COM 	if (fmrpool->fmr_flush_function != NULL) {
2922*12965SWilliam.Taylor@Oracle.COM 		(void) fmrpool->fmr_flush_function(
2923*12965SWilliam.Taylor@Oracle.COM 		    (ibc_fmr_pool_hdl_t)fmrpool,
2924*12965SWilliam.Taylor@Oracle.COM 		    fmrpool->fmr_flush_arg);
29259517SBill.Taylor@Sun.COM 	}
29269517SBill.Taylor@Sun.COM }
2927