1ebb7c6fdSAlex Wilson /*
2ebb7c6fdSAlex Wilson  * This file and its contents are supplied under the terms of the
3ebb7c6fdSAlex Wilson  * Common Development and Distribution License ("CDDL"), version 1.0.
4ebb7c6fdSAlex Wilson  * You may only use this file in accordance with the terms of version
5ebb7c6fdSAlex Wilson  * 1.0 of the CDDL.
6ebb7c6fdSAlex Wilson  *
7ebb7c6fdSAlex Wilson  * A full copy of the text of the CDDL should have accompanied this
8ebb7c6fdSAlex Wilson  * source.  A copy of the CDDL is also available via the Internet at
9ebb7c6fdSAlex Wilson  * http://www.illumos.org/license/CDDL.
10ebb7c6fdSAlex Wilson  */
11ebb7c6fdSAlex Wilson 
12ebb7c6fdSAlex Wilson /*
13*5014e1faSAlex Wilson  * Copyright 2023 The University of Queensland
14ebb7c6fdSAlex Wilson  * Copyright (c) 2018, Joyent, Inc.
1522d05228SPaul Winder  * Copyright 2020 RackTop Systems, Inc.
16ebb7c6fdSAlex Wilson  */
17ebb7c6fdSAlex Wilson 
18ebb7c6fdSAlex Wilson /*
19ebb7c6fdSAlex Wilson  * Mellanox Connect-X 4/5/6 driver.
20ebb7c6fdSAlex Wilson  */
21ebb7c6fdSAlex Wilson 
22ebb7c6fdSAlex Wilson #include <sys/modctl.h>
23ebb7c6fdSAlex Wilson #include <sys/conf.h>
24ebb7c6fdSAlex Wilson #include <sys/devops.h>
25ebb7c6fdSAlex Wilson #include <sys/sysmacros.h>
26ebb7c6fdSAlex Wilson #include <sys/atomic.h>
27ebb7c6fdSAlex Wilson #include <sys/cpuvar.h>
2882b4190eSPaul Winder #include <sys/sdt.h>
29ebb7c6fdSAlex Wilson 
30ebb7c6fdSAlex Wilson #include <sys/pattr.h>
31ebb7c6fdSAlex Wilson #include <sys/dlpi.h>
32ebb7c6fdSAlex Wilson 
33ebb7c6fdSAlex Wilson #include <sys/mac_provider.h>
34ebb7c6fdSAlex Wilson 
35ebb7c6fdSAlex Wilson #include <sys/random.h>
36ebb7c6fdSAlex Wilson 
37ebb7c6fdSAlex Wilson #include <mlxcx.h>
38ebb7c6fdSAlex Wilson 
39ebb7c6fdSAlex Wilson boolean_t
mlxcx_wq_alloc_dma(mlxcx_t * mlxp,mlxcx_work_queue_t * mlwq)40ebb7c6fdSAlex Wilson mlxcx_wq_alloc_dma(mlxcx_t *mlxp, mlxcx_work_queue_t *mlwq)
41ebb7c6fdSAlex Wilson {
42ebb7c6fdSAlex Wilson 	ddi_device_acc_attr_t acc;
43ebb7c6fdSAlex Wilson 	ddi_dma_attr_t attr;
44ebb7c6fdSAlex Wilson 	boolean_t ret;
45ebb7c6fdSAlex Wilson 	size_t sz;
46ebb7c6fdSAlex Wilson 
47ebb7c6fdSAlex Wilson 	VERIFY0(mlwq->mlwq_state & MLXCX_WQ_ALLOC);
48ebb7c6fdSAlex Wilson 
49ebb7c6fdSAlex Wilson 	/* Receive and send queue entries might be different sizes. */
50ebb7c6fdSAlex Wilson 	switch (mlwq->mlwq_type) {
51ebb7c6fdSAlex Wilson 	case MLXCX_WQ_TYPE_SENDQ:
52ebb7c6fdSAlex Wilson 		mlwq->mlwq_entshift = mlxp->mlx_props.mldp_sq_size_shift;
53ebb7c6fdSAlex Wilson 		mlwq->mlwq_nents = (1 << mlwq->mlwq_entshift);
54ebb7c6fdSAlex Wilson 		sz = mlwq->mlwq_nents * sizeof (mlxcx_sendq_ent_t);
55ebb7c6fdSAlex Wilson 		break;
56ebb7c6fdSAlex Wilson 	case MLXCX_WQ_TYPE_RECVQ:
57ebb7c6fdSAlex Wilson 		mlwq->mlwq_entshift = mlxp->mlx_props.mldp_rq_size_shift;
58ebb7c6fdSAlex Wilson 		mlwq->mlwq_nents = (1 << mlwq->mlwq_entshift);
59ebb7c6fdSAlex Wilson 		sz = mlwq->mlwq_nents * sizeof (mlxcx_recvq_ent_t);
60ebb7c6fdSAlex Wilson 		break;
61ebb7c6fdSAlex Wilson 	default:
62ebb7c6fdSAlex Wilson 		VERIFY(0);
63ebb7c6fdSAlex Wilson 		return (B_FALSE);
64ebb7c6fdSAlex Wilson 	}
65ebb7c6fdSAlex Wilson 	ASSERT3U(sz & (MLXCX_HW_PAGE_SIZE - 1), ==, 0);
66ebb7c6fdSAlex Wilson 
67ebb7c6fdSAlex Wilson 	mlxcx_dma_acc_attr(mlxp, &acc);
68ebb7c6fdSAlex Wilson 	mlxcx_dma_queue_attr(mlxp, &attr);
69ebb7c6fdSAlex Wilson 
70ebb7c6fdSAlex Wilson 	ret = mlxcx_dma_alloc(mlxp, &mlwq->mlwq_dma, &attr, &acc,
71ebb7c6fdSAlex Wilson 	    B_TRUE, sz, B_TRUE);
72ebb7c6fdSAlex Wilson 	if (!ret) {
73ebb7c6fdSAlex Wilson 		mlxcx_warn(mlxp, "failed to allocate WQ memory");
74ebb7c6fdSAlex Wilson 		return (B_FALSE);
75ebb7c6fdSAlex Wilson 	}
76ebb7c6fdSAlex Wilson 
77ebb7c6fdSAlex Wilson 	/*
78ebb7c6fdSAlex Wilson 	 * Just set the first pointer in the union. Yes, this is a strict
79ebb7c6fdSAlex Wilson 	 * aliasing violation. No, I don't care.
80ebb7c6fdSAlex Wilson 	 */
81ebb7c6fdSAlex Wilson 	mlwq->mlwq_send_ent = (mlxcx_sendq_ent_t *)mlwq->mlwq_dma.mxdb_va;
82ebb7c6fdSAlex Wilson 
83ebb7c6fdSAlex Wilson 	mlxcx_dma_acc_attr(mlxp, &acc);
84ebb7c6fdSAlex Wilson 	mlxcx_dma_qdbell_attr(mlxp, &attr);
85ebb7c6fdSAlex Wilson 	sz = sizeof (mlxcx_workq_doorbell_t);
86ebb7c6fdSAlex Wilson 	ret = mlxcx_dma_alloc(mlxp, &mlwq->mlwq_doorbell_dma, &attr, &acc,
87ebb7c6fdSAlex Wilson 	    B_TRUE, sz, B_TRUE);
88ebb7c6fdSAlex Wilson 	if (!ret) {
89ebb7c6fdSAlex Wilson 		mlxcx_warn(mlxp, "failed to allocate WQ doorbell memory");
90ebb7c6fdSAlex Wilson 		mlxcx_dma_free(&mlwq->mlwq_dma);
91ebb7c6fdSAlex Wilson 		mlwq->mlwq_send_ent = NULL;
92ebb7c6fdSAlex Wilson 		return (B_FALSE);
93ebb7c6fdSAlex Wilson 	}
94ebb7c6fdSAlex Wilson 
95ebb7c6fdSAlex Wilson 	mlwq->mlwq_doorbell =
96ebb7c6fdSAlex Wilson 	    (mlxcx_workq_doorbell_t *)mlwq->mlwq_doorbell_dma.mxdb_va;
97ebb7c6fdSAlex Wilson 
98ebb7c6fdSAlex Wilson 	mlwq->mlwq_state |= MLXCX_WQ_ALLOC;
99ebb7c6fdSAlex Wilson 
100ebb7c6fdSAlex Wilson 	return (B_TRUE);
101ebb7c6fdSAlex Wilson }
102ebb7c6fdSAlex Wilson 
103ebb7c6fdSAlex Wilson void
mlxcx_wq_rele_dma(mlxcx_t * mlxp,mlxcx_work_queue_t * mlwq)104ebb7c6fdSAlex Wilson mlxcx_wq_rele_dma(mlxcx_t *mlxp, mlxcx_work_queue_t *mlwq)
105ebb7c6fdSAlex Wilson {
106ebb7c6fdSAlex Wilson 	VERIFY(mlwq->mlwq_state & MLXCX_WQ_ALLOC);
107ebb7c6fdSAlex Wilson 	if (mlwq->mlwq_state & MLXCX_WQ_CREATED)
108ebb7c6fdSAlex Wilson 		VERIFY(mlwq->mlwq_state & MLXCX_WQ_DESTROYED);
109ebb7c6fdSAlex Wilson 
110ebb7c6fdSAlex Wilson 	mlxcx_dma_free(&mlwq->mlwq_dma);
111ebb7c6fdSAlex Wilson 	mlwq->mlwq_send_ent = NULL;
112ebb7c6fdSAlex Wilson 	mlxcx_dma_free(&mlwq->mlwq_doorbell_dma);
113ebb7c6fdSAlex Wilson 	mlwq->mlwq_doorbell = NULL;
114ebb7c6fdSAlex Wilson 
115ebb7c6fdSAlex Wilson 	mlwq->mlwq_state &= ~MLXCX_CQ_ALLOC;
116ebb7c6fdSAlex Wilson }
117ebb7c6fdSAlex Wilson 
11822d05228SPaul Winder static boolean_t
mlxcx_cq_alloc_dma(mlxcx_t * mlxp,mlxcx_completion_queue_t * mlcq,uint_t ent_shift)11922d05228SPaul Winder mlxcx_cq_alloc_dma(mlxcx_t *mlxp, mlxcx_completion_queue_t *mlcq,
12022d05228SPaul Winder     uint_t ent_shift)
121ebb7c6fdSAlex Wilson {
122ebb7c6fdSAlex Wilson 	ddi_device_acc_attr_t acc;
123ebb7c6fdSAlex Wilson 	ddi_dma_attr_t attr;
124ebb7c6fdSAlex Wilson 	boolean_t ret;
125ebb7c6fdSAlex Wilson 	size_t sz, i;
126ebb7c6fdSAlex Wilson 
127ebb7c6fdSAlex Wilson 	VERIFY0(mlcq->mlcq_state & MLXCX_EQ_ALLOC);
128ebb7c6fdSAlex Wilson 
12922d05228SPaul Winder 	mlcq->mlcq_entshift = ent_shift;
130ebb7c6fdSAlex Wilson 	mlcq->mlcq_nents = (1 << mlcq->mlcq_entshift);
131ebb7c6fdSAlex Wilson 	sz = mlcq->mlcq_nents * sizeof (mlxcx_completionq_ent_t);
132ebb7c6fdSAlex Wilson 	ASSERT3U(sz & (MLXCX_HW_PAGE_SIZE - 1), ==, 0);
133ebb7c6fdSAlex Wilson 
134ebb7c6fdSAlex Wilson 	mlxcx_dma_acc_attr(mlxp, &acc);
135ebb7c6fdSAlex Wilson 	mlxcx_dma_queue_attr(mlxp, &attr);
136ebb7c6fdSAlex Wilson 
137ebb7c6fdSAlex Wilson 	ret = mlxcx_dma_alloc(mlxp, &mlcq->mlcq_dma, &attr, &acc,
138ebb7c6fdSAlex Wilson 	    B_TRUE, sz, B_TRUE);
139ebb7c6fdSAlex Wilson 	if (!ret) {
140ebb7c6fdSAlex Wilson 		mlxcx_warn(mlxp, "failed to allocate CQ memory");
141ebb7c6fdSAlex Wilson 		return (B_FALSE);
142ebb7c6fdSAlex Wilson 	}
143ebb7c6fdSAlex Wilson 
144ebb7c6fdSAlex Wilson 	mlcq->mlcq_ent = (mlxcx_completionq_ent_t *)mlcq->mlcq_dma.mxdb_va;
145ebb7c6fdSAlex Wilson 
146ebb7c6fdSAlex Wilson 	for (i = 0; i < mlcq->mlcq_nents; ++i) {
147ebb7c6fdSAlex Wilson 		mlcq->mlcq_ent[i].mlcqe_opcode = MLXCX_CQE_OP_INVALID;
148ebb7c6fdSAlex Wilson 		mlcq->mlcq_ent[i].mlcqe_owner = MLXCX_CQE_OWNER_INIT;
149ebb7c6fdSAlex Wilson 	}
150ebb7c6fdSAlex Wilson 
151ebb7c6fdSAlex Wilson 	mlxcx_dma_acc_attr(mlxp, &acc);
152ebb7c6fdSAlex Wilson 	mlxcx_dma_qdbell_attr(mlxp, &attr);
153ebb7c6fdSAlex Wilson 	sz = sizeof (mlxcx_completionq_doorbell_t);
154ebb7c6fdSAlex Wilson 	ret = mlxcx_dma_alloc(mlxp, &mlcq->mlcq_doorbell_dma, &attr, &acc,
155ebb7c6fdSAlex Wilson 	    B_TRUE, sz, B_TRUE);
156ebb7c6fdSAlex Wilson 	if (!ret) {
157ebb7c6fdSAlex Wilson 		mlxcx_warn(mlxp, "failed to allocate CQ doorbell memory");
158ebb7c6fdSAlex Wilson 		mlxcx_dma_free(&mlcq->mlcq_dma);
159ebb7c6fdSAlex Wilson 		mlcq->mlcq_ent = NULL;
160ebb7c6fdSAlex Wilson 		return (B_FALSE);
161ebb7c6fdSAlex Wilson 	}
162ebb7c6fdSAlex Wilson 
163ebb7c6fdSAlex Wilson 	mlcq->mlcq_doorbell =
164ebb7c6fdSAlex Wilson 	    (mlxcx_completionq_doorbell_t *)mlcq->mlcq_doorbell_dma.mxdb_va;
165ebb7c6fdSAlex Wilson 
1660207f820SPaul Winder 	atomic_or_uint(&mlcq->mlcq_state, MLXCX_CQ_ALLOC);
167ebb7c6fdSAlex Wilson 
168ebb7c6fdSAlex Wilson 	return (B_TRUE);
169ebb7c6fdSAlex Wilson }
170ebb7c6fdSAlex Wilson 
17122d05228SPaul Winder static void
mlxcx_cq_rele_dma(mlxcx_t * mlxp,mlxcx_completion_queue_t * mlcq)172ebb7c6fdSAlex Wilson mlxcx_cq_rele_dma(mlxcx_t *mlxp, mlxcx_completion_queue_t *mlcq)
173ebb7c6fdSAlex Wilson {
174ebb7c6fdSAlex Wilson 	VERIFY(mlcq->mlcq_state & MLXCX_CQ_ALLOC);
175ebb7c6fdSAlex Wilson 	if (mlcq->mlcq_state & MLXCX_CQ_CREATED)
176ebb7c6fdSAlex Wilson 		VERIFY(mlcq->mlcq_state & MLXCX_CQ_DESTROYED);
177ebb7c6fdSAlex Wilson 
178ebb7c6fdSAlex Wilson 	mlxcx_dma_free(&mlcq->mlcq_dma);
179ebb7c6fdSAlex Wilson 	mlcq->mlcq_ent = NULL;
180ebb7c6fdSAlex Wilson 	mlxcx_dma_free(&mlcq->mlcq_doorbell_dma);
181ebb7c6fdSAlex Wilson 	mlcq->mlcq_doorbell = NULL;
182ebb7c6fdSAlex Wilson 
1830207f820SPaul Winder 	atomic_and_uint(&mlcq->mlcq_state, ~MLXCX_CQ_ALLOC);
184ebb7c6fdSAlex Wilson }
185ebb7c6fdSAlex Wilson 
186ebb7c6fdSAlex Wilson void
mlxcx_wq_teardown(mlxcx_t * mlxp,mlxcx_work_queue_t * mlwq)187ebb7c6fdSAlex Wilson mlxcx_wq_teardown(mlxcx_t *mlxp, mlxcx_work_queue_t *mlwq)
188ebb7c6fdSAlex Wilson {
189ebb7c6fdSAlex Wilson 	mlxcx_completion_queue_t *mlcq;
190ebb7c6fdSAlex Wilson 
191ebb7c6fdSAlex Wilson 	/*
192ebb7c6fdSAlex Wilson 	 * If something is holding the lock on a long operation like a
193ebb7c6fdSAlex Wilson 	 * refill, setting this flag asks them to exit early if possible.
194ebb7c6fdSAlex Wilson 	 */
195ebb7c6fdSAlex Wilson 	atomic_or_uint(&mlwq->mlwq_state, MLXCX_WQ_TEARDOWN);
196ebb7c6fdSAlex Wilson 
197ebb7c6fdSAlex Wilson 	mutex_enter(&mlwq->mlwq_mtx);
198ebb7c6fdSAlex Wilson 
199ebb7c6fdSAlex Wilson 	list_remove(&mlxp->mlx_wqs, mlwq);
200ebb7c6fdSAlex Wilson 
201ebb7c6fdSAlex Wilson 	if ((mlwq->mlwq_state & MLXCX_WQ_CREATED) &&
202ebb7c6fdSAlex Wilson 	    !(mlwq->mlwq_state & MLXCX_WQ_DESTROYED)) {
203ebb7c6fdSAlex Wilson 		if (mlwq->mlwq_type == MLXCX_WQ_TYPE_RECVQ &&
204ebb7c6fdSAlex Wilson 		    mlwq->mlwq_state & MLXCX_WQ_STARTED &&
205ebb7c6fdSAlex Wilson 		    !mlxcx_cmd_stop_rq(mlxp, mlwq)) {
206ebb7c6fdSAlex Wilson 			mlxcx_warn(mlxp, "failed to stop "
207ebb7c6fdSAlex Wilson 			    "recv queue num %x", mlwq->mlwq_num);
208ebb7c6fdSAlex Wilson 		}
209ebb7c6fdSAlex Wilson 		if (mlwq->mlwq_type == MLXCX_WQ_TYPE_SENDQ &&
210ebb7c6fdSAlex Wilson 		    mlwq->mlwq_state & MLXCX_WQ_STARTED &&
211ebb7c6fdSAlex Wilson 		    !mlxcx_cmd_stop_sq(mlxp, mlwq)) {
212ebb7c6fdSAlex Wilson 			mlxcx_warn(mlxp, "failed to stop "
213ebb7c6fdSAlex Wilson 			    "send queue num %x", mlwq->mlwq_num);
214ebb7c6fdSAlex Wilson 		}
215ebb7c6fdSAlex Wilson 		if (mlwq->mlwq_type == MLXCX_WQ_TYPE_RECVQ &&
216ebb7c6fdSAlex Wilson 		    !mlxcx_cmd_destroy_rq(mlxp, mlwq)) {
217ebb7c6fdSAlex Wilson 			mlxcx_warn(mlxp, "failed to destroy "
218ebb7c6fdSAlex Wilson 			    "recv queue num %x", mlwq->mlwq_num);
219ebb7c6fdSAlex Wilson 		}
220ebb7c6fdSAlex Wilson 		if (mlwq->mlwq_type == MLXCX_WQ_TYPE_SENDQ &&
221ebb7c6fdSAlex Wilson 		    !mlxcx_cmd_destroy_sq(mlxp, mlwq)) {
222ebb7c6fdSAlex Wilson 			mlxcx_warn(mlxp, "failed to destroy "
223ebb7c6fdSAlex Wilson 			    "send queue num %x", mlwq->mlwq_num);
224ebb7c6fdSAlex Wilson 		}
225ebb7c6fdSAlex Wilson 	}
226ebb7c6fdSAlex Wilson 	if (mlwq->mlwq_state & MLXCX_WQ_ALLOC) {
227ebb7c6fdSAlex Wilson 		mlxcx_wq_rele_dma(mlxp, mlwq);
228ebb7c6fdSAlex Wilson 	}
229ebb7c6fdSAlex Wilson 	mlcq = mlwq->mlwq_cq;
230ebb7c6fdSAlex Wilson 
231ebb7c6fdSAlex Wilson 	/* These will be released by mlxcx_teardown_bufs() */
232ebb7c6fdSAlex Wilson 	mlwq->mlwq_bufs = NULL;
233ebb7c6fdSAlex Wilson 	mlwq->mlwq_foreign_bufs = NULL;
234ebb7c6fdSAlex Wilson 
235ebb7c6fdSAlex Wilson 	mutex_exit(&mlwq->mlwq_mtx);
236ebb7c6fdSAlex Wilson 
237ebb7c6fdSAlex Wilson 	mutex_enter(&mlcq->mlcq_mtx);
238ebb7c6fdSAlex Wilson 	mutex_enter(&mlwq->mlwq_mtx);
239ebb7c6fdSAlex Wilson 	ASSERT3P(mlcq->mlcq_wq, ==, mlwq);
240ebb7c6fdSAlex Wilson 	mlcq->mlcq_wq = NULL;
241ebb7c6fdSAlex Wilson 	mutex_exit(&mlwq->mlwq_mtx);
242ebb7c6fdSAlex Wilson 	mutex_exit(&mlcq->mlcq_mtx);
243ebb7c6fdSAlex Wilson 
244ebb7c6fdSAlex Wilson 	mutex_destroy(&mlwq->mlwq_mtx);
245ebb7c6fdSAlex Wilson }
246ebb7c6fdSAlex Wilson 
247ebb7c6fdSAlex Wilson void
mlxcx_cq_teardown(mlxcx_t * mlxp,mlxcx_completion_queue_t * mlcq)248ebb7c6fdSAlex Wilson mlxcx_cq_teardown(mlxcx_t *mlxp, mlxcx_completion_queue_t *mlcq)
249ebb7c6fdSAlex Wilson {
250ebb7c6fdSAlex Wilson 	mlxcx_event_queue_t *mleq;
251ebb7c6fdSAlex Wilson 	mlxcx_buffer_t *b;
252ebb7c6fdSAlex Wilson 
253ebb7c6fdSAlex Wilson 	/*
254ebb7c6fdSAlex Wilson 	 * If something is holding the lock on a long operation like polling
255ebb7c6fdSAlex Wilson 	 * which we're going to abort anyway, this flag asks them to exit
256ebb7c6fdSAlex Wilson 	 * early if possible.
257ebb7c6fdSAlex Wilson 	 */
258ebb7c6fdSAlex Wilson 	atomic_or_uint(&mlcq->mlcq_state, MLXCX_CQ_TEARDOWN);
259ebb7c6fdSAlex Wilson 
260ebb7c6fdSAlex Wilson 	mutex_enter(&mlcq->mlcq_mtx);
261ebb7c6fdSAlex Wilson 
262ebb7c6fdSAlex Wilson 	list_remove(&mlxp->mlx_cqs, mlcq);
263ebb7c6fdSAlex Wilson 
264ebb7c6fdSAlex Wilson 	if ((mlcq->mlcq_state & MLXCX_CQ_CREATED) &&
265ebb7c6fdSAlex Wilson 	    !(mlcq->mlcq_state & MLXCX_CQ_DESTROYED)) {
266ebb7c6fdSAlex Wilson 		if (!mlxcx_cmd_destroy_cq(mlxp, mlcq)) {
267ebb7c6fdSAlex Wilson 			mlxcx_warn(mlxp, "failed to destroy "
268ebb7c6fdSAlex Wilson 			    "completion queue num %u",
269ebb7c6fdSAlex Wilson 			    mlcq->mlcq_num);
270ebb7c6fdSAlex Wilson 		}
271ebb7c6fdSAlex Wilson 	}
272ebb7c6fdSAlex Wilson 	if (mlcq->mlcq_state & MLXCX_CQ_ALLOC) {
273ebb7c6fdSAlex Wilson 		mlxcx_cq_rele_dma(mlxp, mlcq);
274ebb7c6fdSAlex Wilson 	}
275ebb7c6fdSAlex Wilson 	/*
276ebb7c6fdSAlex Wilson 	 * If we're on an EQ AVL tree, then we need to grab
277ebb7c6fdSAlex Wilson 	 * the EQ's mutex to take it off. The ISR always takes
278ebb7c6fdSAlex Wilson 	 * EQ mutex before CQ mutex, so we have to let go of
279ebb7c6fdSAlex Wilson 	 * the CQ mutex then come back again.
280ebb7c6fdSAlex Wilson 	 *
281ebb7c6fdSAlex Wilson 	 * The ISR will bail out if tries to touch this CQ now since
282ebb7c6fdSAlex Wilson 	 * we added the CQ_DESTROYED flag above.
283ebb7c6fdSAlex Wilson 	 */
284ebb7c6fdSAlex Wilson 	if (mlcq->mlcq_state & MLXCX_CQ_EQAVL) {
285ebb7c6fdSAlex Wilson 		mleq = mlcq->mlcq_eq;
286ebb7c6fdSAlex Wilson 	} else {
287ebb7c6fdSAlex Wilson 		mleq = NULL;
288ebb7c6fdSAlex Wilson 	}
289ebb7c6fdSAlex Wilson 
290ebb7c6fdSAlex Wilson 	/* Return any outstanding buffers to the free pool. */
291ebb7c6fdSAlex Wilson 	while ((b = list_remove_head(&mlcq->mlcq_buffers)) != NULL) {
292ebb7c6fdSAlex Wilson 		mlxcx_buf_return_chain(mlxp, b, B_FALSE);
293ebb7c6fdSAlex Wilson 	}
294ebb7c6fdSAlex Wilson 	mutex_enter(&mlcq->mlcq_bufbmtx);
295ebb7c6fdSAlex Wilson 	while ((b = list_remove_head(&mlcq->mlcq_buffers_b)) != NULL) {
296ebb7c6fdSAlex Wilson 		mlxcx_buf_return_chain(mlxp, b, B_FALSE);
297ebb7c6fdSAlex Wilson 	}
298ebb7c6fdSAlex Wilson 	mutex_exit(&mlcq->mlcq_bufbmtx);
299ebb7c6fdSAlex Wilson 
300ebb7c6fdSAlex Wilson 	/*
301ebb7c6fdSAlex Wilson 	 * Since the interrupt handlers take the EQ lock before the CQ one,
302ebb7c6fdSAlex Wilson 	 * we must do the same here. That means letting go of the lock
303ebb7c6fdSAlex Wilson 	 * for a brief window here (we'll double-check the state when we
304ebb7c6fdSAlex Wilson 	 * get back in).
305ebb7c6fdSAlex Wilson 	 */
306ebb7c6fdSAlex Wilson 	mutex_exit(&mlcq->mlcq_mtx);
307ebb7c6fdSAlex Wilson 
308ebb7c6fdSAlex Wilson 	if (mleq != NULL) {
309ebb7c6fdSAlex Wilson 		mutex_enter(&mleq->mleq_mtx);
310ebb7c6fdSAlex Wilson 		mutex_enter(&mlcq->mlcq_mtx);
311ebb7c6fdSAlex Wilson 		/*
312ebb7c6fdSAlex Wilson 		 * Double-check the state, we let go of the
313ebb7c6fdSAlex Wilson 		 * mutex briefly.
314ebb7c6fdSAlex Wilson 		 */
315ebb7c6fdSAlex Wilson 		if (mlcq->mlcq_state & MLXCX_CQ_EQAVL) {
316ebb7c6fdSAlex Wilson 			avl_remove(&mleq->mleq_cqs, mlcq);
3170207f820SPaul Winder 			atomic_and_uint(&mlcq->mlcq_state, ~MLXCX_CQ_EQAVL);
318ebb7c6fdSAlex Wilson 		}
319ebb7c6fdSAlex Wilson 		mutex_exit(&mlcq->mlcq_mtx);
320ebb7c6fdSAlex Wilson 		mutex_exit(&mleq->mleq_mtx);
321ebb7c6fdSAlex Wilson 	}
322ebb7c6fdSAlex Wilson 
323ebb7c6fdSAlex Wilson 	mutex_enter(&mlcq->mlcq_mtx);
324ebb7c6fdSAlex Wilson 	ASSERT0(mlcq->mlcq_state & ~(MLXCX_CQ_CREATED | MLXCX_CQ_DESTROYED |
325ebb7c6fdSAlex Wilson 	    MLXCX_CQ_TEARDOWN | MLXCX_CQ_ARMED));
326ebb7c6fdSAlex Wilson 	mutex_exit(&mlcq->mlcq_mtx);
327ebb7c6fdSAlex Wilson 
328ebb7c6fdSAlex Wilson 	mutex_destroy(&mlcq->mlcq_mtx);
3290207f820SPaul Winder 	mutex_destroy(&mlcq->mlcq_arm_mtx);
330ebb7c6fdSAlex Wilson 	mutex_destroy(&mlcq->mlcq_bufbmtx);
331ebb7c6fdSAlex Wilson 	list_destroy(&mlcq->mlcq_buffers);
332ebb7c6fdSAlex Wilson 	list_destroy(&mlcq->mlcq_buffers_b);
333ebb7c6fdSAlex Wilson 	kmem_free(mlcq, sizeof (mlxcx_completion_queue_t));
334ebb7c6fdSAlex Wilson }
335ebb7c6fdSAlex Wilson 
336ebb7c6fdSAlex Wilson static boolean_t
mlxcx_cq_setup(mlxcx_t * mlxp,mlxcx_event_queue_t * eq,mlxcx_completion_queue_t ** cqp,uint_t ent_shift)337ebb7c6fdSAlex Wilson mlxcx_cq_setup(mlxcx_t *mlxp, mlxcx_event_queue_t *eq,
33822d05228SPaul Winder     mlxcx_completion_queue_t **cqp, uint_t ent_shift)
339ebb7c6fdSAlex Wilson {
340ebb7c6fdSAlex Wilson 	mlxcx_completion_queue_t *cq;
341ebb7c6fdSAlex Wilson 
342ebb7c6fdSAlex Wilson 	cq = kmem_zalloc(sizeof (mlxcx_completion_queue_t), KM_SLEEP);
343ebb7c6fdSAlex Wilson 	mutex_init(&cq->mlcq_mtx, NULL, MUTEX_DRIVER,
344ebb7c6fdSAlex Wilson 	    DDI_INTR_PRI(mlxp->mlx_intr_pri));
3450207f820SPaul Winder 	mutex_init(&cq->mlcq_arm_mtx, NULL, MUTEX_DRIVER,
3460207f820SPaul Winder 	    DDI_INTR_PRI(mlxp->mlx_intr_pri));
347ebb7c6fdSAlex Wilson 	mutex_init(&cq->mlcq_bufbmtx, NULL, MUTEX_DRIVER,
348ebb7c6fdSAlex Wilson 	    DDI_INTR_PRI(mlxp->mlx_intr_pri));
349ebb7c6fdSAlex Wilson 	list_create(&cq->mlcq_buffers, sizeof (mlxcx_buffer_t),
350ebb7c6fdSAlex Wilson 	    offsetof(mlxcx_buffer_t, mlb_cq_entry));
351ebb7c6fdSAlex Wilson 	list_create(&cq->mlcq_buffers_b, sizeof (mlxcx_buffer_t),
352ebb7c6fdSAlex Wilson 	    offsetof(mlxcx_buffer_t, mlb_cq_entry));
353ebb7c6fdSAlex Wilson 
354ebb7c6fdSAlex Wilson 	cq->mlcq_mlx = mlxp;
355ebb7c6fdSAlex Wilson 	list_insert_tail(&mlxp->mlx_cqs, cq);
356ebb7c6fdSAlex Wilson 
357ebb7c6fdSAlex Wilson 	mutex_enter(&cq->mlcq_mtx);
358ebb7c6fdSAlex Wilson 
35922d05228SPaul Winder 	if (!mlxcx_cq_alloc_dma(mlxp, cq, ent_shift)) {
360ebb7c6fdSAlex Wilson 		mutex_exit(&cq->mlcq_mtx);
361ebb7c6fdSAlex Wilson 		return (B_FALSE);
362ebb7c6fdSAlex Wilson 	}
363ebb7c6fdSAlex Wilson 
364ebb7c6fdSAlex Wilson 	cq->mlcq_bufhwm = cq->mlcq_nents - MLXCX_CQ_HWM_GAP;
365ebb7c6fdSAlex Wilson 	cq->mlcq_buflwm = cq->mlcq_nents - MLXCX_CQ_LWM_GAP;
366ebb7c6fdSAlex Wilson 
367ebb7c6fdSAlex Wilson 	cq->mlcq_uar = &mlxp->mlx_uar;
368ebb7c6fdSAlex Wilson 	cq->mlcq_eq = eq;
369ebb7c6fdSAlex Wilson 
370ebb7c6fdSAlex Wilson 	cq->mlcq_cqemod_period_usec = mlxp->mlx_props.mldp_cqemod_period_usec;
371ebb7c6fdSAlex Wilson 	cq->mlcq_cqemod_count = mlxp->mlx_props.mldp_cqemod_count;
372ebb7c6fdSAlex Wilson 
373ebb7c6fdSAlex Wilson 	if (!mlxcx_cmd_create_cq(mlxp, cq)) {
374ebb7c6fdSAlex Wilson 		mutex_exit(&cq->mlcq_mtx);
375ebb7c6fdSAlex Wilson 		return (B_FALSE);
376ebb7c6fdSAlex Wilson 	}
377ebb7c6fdSAlex Wilson 
378ebb7c6fdSAlex Wilson 	mutex_exit(&cq->mlcq_mtx);
379ebb7c6fdSAlex Wilson 
380ebb7c6fdSAlex Wilson 	mutex_enter(&eq->mleq_mtx);
3817fdea60dSRobert Mustacchi 	mutex_enter(&cq->mlcq_arm_mtx);
382ebb7c6fdSAlex Wilson 	mutex_enter(&cq->mlcq_mtx);
383ebb7c6fdSAlex Wilson 	ASSERT0(cq->mlcq_state & MLXCX_CQ_EQAVL);
384ebb7c6fdSAlex Wilson 	avl_add(&eq->mleq_cqs, cq);
3850207f820SPaul Winder 	atomic_or_uint(&cq->mlcq_state, MLXCX_CQ_EQAVL);
386ebb7c6fdSAlex Wilson 	mlxcx_arm_cq(mlxp, cq);
387ebb7c6fdSAlex Wilson 	mutex_exit(&cq->mlcq_mtx);
3887fdea60dSRobert Mustacchi 	mutex_exit(&cq->mlcq_arm_mtx);
389ebb7c6fdSAlex Wilson 	mutex_exit(&eq->mleq_mtx);
390ebb7c6fdSAlex Wilson 
391ebb7c6fdSAlex Wilson 	*cqp = cq;
392ebb7c6fdSAlex Wilson 	return (B_TRUE);
393ebb7c6fdSAlex Wilson }
394ebb7c6fdSAlex Wilson 
395ebb7c6fdSAlex Wilson static boolean_t
mlxcx_rq_setup(mlxcx_t * mlxp,mlxcx_completion_queue_t * cq,mlxcx_work_queue_t * wq)396ebb7c6fdSAlex Wilson mlxcx_rq_setup(mlxcx_t *mlxp, mlxcx_completion_queue_t *cq,
397ebb7c6fdSAlex Wilson     mlxcx_work_queue_t *wq)
398ebb7c6fdSAlex Wilson {
399ebb7c6fdSAlex Wilson 	mutex_init(&wq->mlwq_mtx, NULL, MUTEX_DRIVER,
400ebb7c6fdSAlex Wilson 	    DDI_INTR_PRI(mlxp->mlx_intr_pri));
401ebb7c6fdSAlex Wilson 
402ebb7c6fdSAlex Wilson 	list_insert_tail(&mlxp->mlx_wqs, wq);
403ebb7c6fdSAlex Wilson 
404ebb7c6fdSAlex Wilson 	mutex_enter(&wq->mlwq_mtx);
405ebb7c6fdSAlex Wilson 
406ebb7c6fdSAlex Wilson 	wq->mlwq_mlx = mlxp;
407ebb7c6fdSAlex Wilson 	wq->mlwq_type = MLXCX_WQ_TYPE_RECVQ;
408ebb7c6fdSAlex Wilson 	wq->mlwq_cq = cq;
409ebb7c6fdSAlex Wilson 	wq->mlwq_pd = &mlxp->mlx_pd;
410ebb7c6fdSAlex Wilson 	wq->mlwq_uar = &mlxp->mlx_uar;
411ebb7c6fdSAlex Wilson 
412ebb7c6fdSAlex Wilson 	wq->mlwq_bufs = mlxcx_mlbs_create(mlxp);
413ebb7c6fdSAlex Wilson 
414ebb7c6fdSAlex Wilson 	if (!mlxcx_wq_alloc_dma(mlxp, wq)) {
415ebb7c6fdSAlex Wilson 		mutex_exit(&wq->mlwq_mtx);
416ebb7c6fdSAlex Wilson 		return (B_FALSE);
417ebb7c6fdSAlex Wilson 	}
418ebb7c6fdSAlex Wilson 
419ebb7c6fdSAlex Wilson 	if (!mlxcx_cmd_create_rq(mlxp, wq)) {
420ebb7c6fdSAlex Wilson 		mutex_exit(&wq->mlwq_mtx);
421ebb7c6fdSAlex Wilson 		return (B_FALSE);
422ebb7c6fdSAlex Wilson 	}
423ebb7c6fdSAlex Wilson 
42422d05228SPaul Winder 	wq->mlwq_bufhwm = wq->mlwq_nents - MLXCX_WQ_HWM_GAP;
42522d05228SPaul Winder 	wq->mlwq_buflwm = wq->mlwq_nents - MLXCX_WQ_LWM_GAP;
42622d05228SPaul Winder 
427ebb7c6fdSAlex Wilson 	mutex_exit(&wq->mlwq_mtx);
428ebb7c6fdSAlex Wilson 
429ebb7c6fdSAlex Wilson 	mutex_enter(&cq->mlcq_mtx);
430ebb7c6fdSAlex Wilson 	mutex_enter(&wq->mlwq_mtx);
431ebb7c6fdSAlex Wilson 	ASSERT3P(cq->mlcq_wq, ==, NULL);
432ebb7c6fdSAlex Wilson 	cq->mlcq_wq = wq;
433ebb7c6fdSAlex Wilson 	mutex_exit(&wq->mlwq_mtx);
434ebb7c6fdSAlex Wilson 	mutex_exit(&cq->mlcq_mtx);
435ebb7c6fdSAlex Wilson 
436ebb7c6fdSAlex Wilson 	return (B_TRUE);
437ebb7c6fdSAlex Wilson }
438ebb7c6fdSAlex Wilson 
439ebb7c6fdSAlex Wilson static boolean_t
mlxcx_sq_setup(mlxcx_t * mlxp,mlxcx_port_t * port,mlxcx_completion_queue_t * cq,mlxcx_tis_t * tis,mlxcx_work_queue_t * wq)440ebb7c6fdSAlex Wilson mlxcx_sq_setup(mlxcx_t *mlxp, mlxcx_port_t *port, mlxcx_completion_queue_t *cq,
441ebb7c6fdSAlex Wilson     mlxcx_tis_t *tis, mlxcx_work_queue_t *wq)
442ebb7c6fdSAlex Wilson {
443ebb7c6fdSAlex Wilson 	mutex_init(&wq->mlwq_mtx, NULL, MUTEX_DRIVER,
444ebb7c6fdSAlex Wilson 	    DDI_INTR_PRI(mlxp->mlx_intr_pri));
445ebb7c6fdSAlex Wilson 
446ebb7c6fdSAlex Wilson 	list_insert_tail(&mlxp->mlx_wqs, wq);
447ebb7c6fdSAlex Wilson 
448ebb7c6fdSAlex Wilson 	mutex_enter(&wq->mlwq_mtx);
449ebb7c6fdSAlex Wilson 
450ebb7c6fdSAlex Wilson 	wq->mlwq_mlx = mlxp;
451ebb7c6fdSAlex Wilson 	wq->mlwq_type = MLXCX_WQ_TYPE_SENDQ;
452ebb7c6fdSAlex Wilson 	wq->mlwq_cq = cq;
453ebb7c6fdSAlex Wilson 	wq->mlwq_pd = &mlxp->mlx_pd;
454ebb7c6fdSAlex Wilson 	wq->mlwq_uar = &mlxp->mlx_uar;
455ebb7c6fdSAlex Wilson 	wq->mlwq_tis = tis;
456ebb7c6fdSAlex Wilson 
457ebb7c6fdSAlex Wilson 	wq->mlwq_bufs = mlxcx_mlbs_create(mlxp);
458ebb7c6fdSAlex Wilson 	wq->mlwq_foreign_bufs = mlxcx_mlbs_create(mlxp);
459ebb7c6fdSAlex Wilson 
460ebb7c6fdSAlex Wilson 	VERIFY3U(port->mlp_wqe_min_inline, <=, MLXCX_ETH_INLINE_L2);
461ebb7c6fdSAlex Wilson 	wq->mlwq_inline_mode = MLXCX_ETH_INLINE_L2;
462ebb7c6fdSAlex Wilson 
463ebb7c6fdSAlex Wilson 	if (!mlxcx_wq_alloc_dma(mlxp, wq)) {
464ebb7c6fdSAlex Wilson 		mutex_exit(&wq->mlwq_mtx);
465ebb7c6fdSAlex Wilson 		return (B_FALSE);
466ebb7c6fdSAlex Wilson 	}
467ebb7c6fdSAlex Wilson 
468ebb7c6fdSAlex Wilson 	if (!mlxcx_cmd_create_sq(mlxp, wq)) {
469ebb7c6fdSAlex Wilson 		mutex_exit(&wq->mlwq_mtx);
470ebb7c6fdSAlex Wilson 		return (B_FALSE);
471ebb7c6fdSAlex Wilson 	}
472ebb7c6fdSAlex Wilson 
47322d05228SPaul Winder 	wq->mlwq_bufhwm = wq->mlwq_nents - MLXCX_WQ_HWM_GAP;
47422d05228SPaul Winder 	wq->mlwq_buflwm = wq->mlwq_nents - MLXCX_WQ_LWM_GAP;
47522d05228SPaul Winder 
476ebb7c6fdSAlex Wilson 	mutex_exit(&wq->mlwq_mtx);
477ebb7c6fdSAlex Wilson 
478ebb7c6fdSAlex Wilson 	mutex_enter(&cq->mlcq_mtx);
479ebb7c6fdSAlex Wilson 	mutex_enter(&wq->mlwq_mtx);
480ebb7c6fdSAlex Wilson 	ASSERT3P(cq->mlcq_wq, ==, NULL);
481ebb7c6fdSAlex Wilson 	cq->mlcq_wq = wq;
482ebb7c6fdSAlex Wilson 	mutex_exit(&wq->mlwq_mtx);
483ebb7c6fdSAlex Wilson 	mutex_exit(&cq->mlcq_mtx);
484ebb7c6fdSAlex Wilson 
485ebb7c6fdSAlex Wilson 	return (B_TRUE);
486ebb7c6fdSAlex Wilson }
487ebb7c6fdSAlex Wilson 
48822d05228SPaul Winder /*
48922d05228SPaul Winder  * Before we tear down the queues associated with the rx group,
49022d05228SPaul Winder  * flag each cq as being torn down and wake up any tasks.
49122d05228SPaul Winder  */
49222d05228SPaul Winder static void
mlxcx_quiesce_rx_cqs(mlxcx_t * mlxp,mlxcx_ring_group_t * g)49322d05228SPaul Winder mlxcx_quiesce_rx_cqs(mlxcx_t *mlxp, mlxcx_ring_group_t *g)
49422d05228SPaul Winder {
49522d05228SPaul Winder 	mlxcx_work_queue_t *wq;
49622d05228SPaul Winder 	mlxcx_completion_queue_t *cq;
49722d05228SPaul Winder 	mlxcx_buf_shard_t *s;
49822d05228SPaul Winder 	uint_t i;
49922d05228SPaul Winder 
50022d05228SPaul Winder 	mutex_enter(&g->mlg_mtx);
50122d05228SPaul Winder 
50222d05228SPaul Winder 	for (i = 0; i < g->mlg_nwqs; ++i) {
50322d05228SPaul Winder 		wq = &g->mlg_wqs[i];
50422d05228SPaul Winder 		cq = wq->mlwq_cq;
50522d05228SPaul Winder 		if (cq != NULL) {
50622d05228SPaul Winder 			s = wq->mlwq_bufs;
50722d05228SPaul Winder 			mutex_enter(&s->mlbs_mtx);
50822d05228SPaul Winder 			atomic_or_uint(&cq->mlcq_state, MLXCX_CQ_TEARDOWN);
50922d05228SPaul Winder 			cv_broadcast(&s->mlbs_free_nonempty);
51022d05228SPaul Winder 			mutex_exit(&s->mlbs_mtx);
51122d05228SPaul Winder 		}
51222d05228SPaul Winder 	}
51322d05228SPaul Winder 
51422d05228SPaul Winder 	mutex_exit(&g->mlg_mtx);
51522d05228SPaul Winder }
51622d05228SPaul Winder 
517ebb7c6fdSAlex Wilson void
mlxcx_teardown_rx_group(mlxcx_t * mlxp,mlxcx_ring_group_t * g)518ebb7c6fdSAlex Wilson mlxcx_teardown_rx_group(mlxcx_t *mlxp, mlxcx_ring_group_t *g)
519ebb7c6fdSAlex Wilson {
520ebb7c6fdSAlex Wilson 	mlxcx_work_queue_t *wq;
521ebb7c6fdSAlex Wilson 	mlxcx_completion_queue_t *cq;
522ebb7c6fdSAlex Wilson 	mlxcx_flow_entry_t *fe;
523ebb7c6fdSAlex Wilson 	mlxcx_flow_group_t *fg;
524ebb7c6fdSAlex Wilson 	mlxcx_flow_table_t *ft;
525ebb7c6fdSAlex Wilson 	uint_t i;
526ebb7c6fdSAlex Wilson 
527ebb7c6fdSAlex Wilson 	mutex_enter(&g->mlg_port->mlp_mtx);
528ebb7c6fdSAlex Wilson 	mutex_enter(&g->mlg_mtx);
529ebb7c6fdSAlex Wilson 
530ebb7c6fdSAlex Wilson 	if (g->mlg_state & MLXCX_GROUP_FLOWS) {
531ebb7c6fdSAlex Wilson 		mlxcx_remove_all_umcast_entries(mlxp, g->mlg_port, g);
532ebb7c6fdSAlex Wilson 
533ebb7c6fdSAlex Wilson 		if (g->mlg_rx_vlan_ft != NULL)
534ebb7c6fdSAlex Wilson 			mlxcx_remove_all_vlan_entries(mlxp, g);
535ebb7c6fdSAlex Wilson 
536ebb7c6fdSAlex Wilson 		if (g == &mlxp->mlx_rx_groups[0]) {
537ebb7c6fdSAlex Wilson 			ft = g->mlg_port->mlp_rx_flow;
538ebb7c6fdSAlex Wilson 			mutex_enter(&ft->mlft_mtx);
539ebb7c6fdSAlex Wilson 
540ebb7c6fdSAlex Wilson 			fg = g->mlg_port->mlp_bcast;
541ebb7c6fdSAlex Wilson 			fe = list_head(&fg->mlfg_entries);
542ebb7c6fdSAlex Wilson 			if (fe->mlfe_state & MLXCX_FLOW_ENTRY_CREATED) {
543ebb7c6fdSAlex Wilson 				(void) mlxcx_cmd_delete_flow_table_entry(
544ebb7c6fdSAlex Wilson 				    mlxp, fe);
545ebb7c6fdSAlex Wilson 			}
546ebb7c6fdSAlex Wilson 
547ebb7c6fdSAlex Wilson 			fg = g->mlg_port->mlp_promisc;
548ebb7c6fdSAlex Wilson 			fe = list_head(&fg->mlfg_entries);
549ebb7c6fdSAlex Wilson 			if (fe->mlfe_state & MLXCX_FLOW_ENTRY_CREATED) {
550ebb7c6fdSAlex Wilson 				(void) mlxcx_cmd_delete_flow_table_entry(
551ebb7c6fdSAlex Wilson 				    mlxp, fe);
552ebb7c6fdSAlex Wilson 			}
553ebb7c6fdSAlex Wilson 
554ebb7c6fdSAlex Wilson 			mutex_exit(&ft->mlft_mtx);
555ebb7c6fdSAlex Wilson 		}
556ebb7c6fdSAlex Wilson 
557ebb7c6fdSAlex Wilson 		if (g->mlg_rx_vlan_ft != NULL) {
558ebb7c6fdSAlex Wilson 			mutex_enter(&g->mlg_rx_vlan_ft->mlft_mtx);
559ebb7c6fdSAlex Wilson 			ASSERT(list_is_empty(&g->mlg_rx_vlans));
560ebb7c6fdSAlex Wilson 			fg = g->mlg_rx_vlan_def_fg;
5615f0e3176SPaul Winder 			if (fg != NULL) {
5625f0e3176SPaul Winder 				fe = list_head(&fg->mlfg_entries);
5635f0e3176SPaul Winder 				if (fe->mlfe_state & MLXCX_FLOW_ENTRY_CREATED) {
5645f0e3176SPaul Winder 					(void)
5655f0e3176SPaul Winder 					    mlxcx_cmd_delete_flow_table_entry(
5665f0e3176SPaul Winder 					    mlxp, fe);
5675f0e3176SPaul Winder 				}
568ebb7c6fdSAlex Wilson 			}
569ebb7c6fdSAlex Wilson 			fg = g->mlg_rx_vlan_promisc_fg;
5705f0e3176SPaul Winder 			if (fg != NULL) {
5715f0e3176SPaul Winder 				fe = list_head(&fg->mlfg_entries);
5725f0e3176SPaul Winder 				if (fe->mlfe_state & MLXCX_FLOW_ENTRY_CREATED) {
5735f0e3176SPaul Winder 					(void)
5745f0e3176SPaul Winder 					    mlxcx_cmd_delete_flow_table_entry(
5755f0e3176SPaul Winder 					    mlxp, fe);
5765f0e3176SPaul Winder 				}
577ebb7c6fdSAlex Wilson 			}
578ebb7c6fdSAlex Wilson 			mlxcx_teardown_flow_table(mlxp, g->mlg_rx_vlan_ft);
579ebb7c6fdSAlex Wilson 			list_destroy(&g->mlg_rx_vlans);
580ebb7c6fdSAlex Wilson 
581ebb7c6fdSAlex Wilson 			g->mlg_rx_vlan_ft = NULL;
582ebb7c6fdSAlex Wilson 		}
583ebb7c6fdSAlex Wilson 
584ebb7c6fdSAlex Wilson 		mutex_enter(&g->mlg_rx_hash_ft->mlft_mtx);
585ebb7c6fdSAlex Wilson 		mlxcx_teardown_flow_table(mlxp, g->mlg_rx_hash_ft);
586ebb7c6fdSAlex Wilson 		g->mlg_rx_hash_ft = NULL;
587ebb7c6fdSAlex Wilson 
588ebb7c6fdSAlex Wilson 		avl_destroy(&g->mlg_rx_macs);
589ebb7c6fdSAlex Wilson 		g->mlg_state &= ~MLXCX_GROUP_FLOWS;
590ebb7c6fdSAlex Wilson 	}
591ebb7c6fdSAlex Wilson 
592ebb7c6fdSAlex Wilson 	if (g->mlg_state & MLXCX_GROUP_RUNNING) {
593ebb7c6fdSAlex Wilson 		for (i = 0; i < g->mlg_nwqs; ++i) {
594ebb7c6fdSAlex Wilson 			wq = &g->mlg_wqs[i];
595ebb7c6fdSAlex Wilson 			mutex_enter(&wq->mlwq_mtx);
596ebb7c6fdSAlex Wilson 			if (wq->mlwq_state & MLXCX_WQ_STARTED &&
597ebb7c6fdSAlex Wilson 			    !mlxcx_cmd_stop_rq(mlxp, wq)) {
598ebb7c6fdSAlex Wilson 				mlxcx_warn(mlxp, "failed to stop rq %x",
599ebb7c6fdSAlex Wilson 				    wq->mlwq_num);
600ebb7c6fdSAlex Wilson 			}
601ebb7c6fdSAlex Wilson 			mutex_exit(&wq->mlwq_mtx);
602ebb7c6fdSAlex Wilson 		}
60322d05228SPaul Winder 		taskq_destroy(g->mlg_refill_tq);
604ebb7c6fdSAlex Wilson 		g->mlg_state &= ~MLXCX_GROUP_RUNNING;
605ebb7c6fdSAlex Wilson 	}
606ebb7c6fdSAlex Wilson 
607ebb7c6fdSAlex Wilson 	if (g->mlg_state & MLXCX_GROUP_TIRTIS) {
608ebb7c6fdSAlex Wilson 		for (i = 0; i < MLXCX_TIRS_PER_GROUP; ++i) {
609ebb7c6fdSAlex Wilson 			mlxcx_tir_t *tir = &g->mlg_tir[i];
610ebb7c6fdSAlex Wilson 			if (tir->mltir_state & MLXCX_TIR_CREATED &&
611ebb7c6fdSAlex Wilson 			    !(tir->mltir_state & MLXCX_TIR_DESTROYED)) {
612ebb7c6fdSAlex Wilson 				if (!mlxcx_cmd_destroy_tir(mlxp, tir)) {
613ebb7c6fdSAlex Wilson 					mlxcx_warn(mlxp,
614ebb7c6fdSAlex Wilson 					    "failed to destroy tir %u "
615ebb7c6fdSAlex Wilson 					    "for rx ring", tir->mltir_num);
616ebb7c6fdSAlex Wilson 				}
617ebb7c6fdSAlex Wilson 			}
618ebb7c6fdSAlex Wilson 		}
619ebb7c6fdSAlex Wilson 		g->mlg_state &= ~MLXCX_GROUP_TIRTIS;
620ebb7c6fdSAlex Wilson 	}
621ebb7c6fdSAlex Wilson 
622ebb7c6fdSAlex Wilson 	if (g->mlg_state & MLXCX_GROUP_RQT) {
623ebb7c6fdSAlex Wilson 		if (g->mlg_rqt->mlrqt_state & MLXCX_RQT_CREATED &&
624ebb7c6fdSAlex Wilson 		    !(g->mlg_rqt->mlrqt_state & MLXCX_RQT_DESTROYED)) {
625ebb7c6fdSAlex Wilson 			if (!mlxcx_cmd_destroy_rqt(mlxp, g->mlg_rqt)) {
626ebb7c6fdSAlex Wilson 				mlxcx_warn(mlxp, "failed to destroy rqt %u "
627ebb7c6fdSAlex Wilson 				    "for rx ring", g->mlg_rqt->mlrqt_num);
628ebb7c6fdSAlex Wilson 			}
629ebb7c6fdSAlex Wilson 			kmem_free(g->mlg_rqt->mlrqt_rq,
630ebb7c6fdSAlex Wilson 			    g->mlg_rqt->mlrqt_rq_size);
631ebb7c6fdSAlex Wilson 			g->mlg_rqt->mlrqt_rq = NULL;
632ebb7c6fdSAlex Wilson 			kmem_free(g->mlg_rqt, sizeof (mlxcx_rqtable_t));
633ebb7c6fdSAlex Wilson 			g->mlg_rqt = NULL;
634ebb7c6fdSAlex Wilson 		}
635ebb7c6fdSAlex Wilson 		g->mlg_state &= ~MLXCX_GROUP_RQT;
636ebb7c6fdSAlex Wilson 	}
637ebb7c6fdSAlex Wilson 
638ebb7c6fdSAlex Wilson 	for (i = 0; i < g->mlg_nwqs; ++i) {
639ebb7c6fdSAlex Wilson 		wq = &g->mlg_wqs[i];
640ebb7c6fdSAlex Wilson 		cq = wq->mlwq_cq;
641ebb7c6fdSAlex Wilson 		mlxcx_wq_teardown(mlxp, wq);
642ebb7c6fdSAlex Wilson 		if (cq != NULL)
643ebb7c6fdSAlex Wilson 			mlxcx_cq_teardown(mlxp, cq);
644ebb7c6fdSAlex Wilson 	}
645ebb7c6fdSAlex Wilson 	kmem_free(g->mlg_wqs, g->mlg_wqs_size);
646ebb7c6fdSAlex Wilson 	g->mlg_wqs = NULL;
647ebb7c6fdSAlex Wilson 	g->mlg_state &= ~MLXCX_GROUP_WQS;
648ebb7c6fdSAlex Wilson 
649ebb7c6fdSAlex Wilson 	mutex_exit(&g->mlg_mtx);
650ebb7c6fdSAlex Wilson 	mutex_exit(&g->mlg_port->mlp_mtx);
651ebb7c6fdSAlex Wilson 
652ebb7c6fdSAlex Wilson 	mutex_destroy(&g->mlg_mtx);
653ebb7c6fdSAlex Wilson 
654ebb7c6fdSAlex Wilson 	g->mlg_state &= ~MLXCX_GROUP_INIT;
655ebb7c6fdSAlex Wilson 	ASSERT3S(g->mlg_state, ==, 0);
656ebb7c6fdSAlex Wilson }
657ebb7c6fdSAlex Wilson 
658ebb7c6fdSAlex Wilson void
mlxcx_teardown_tx_group(mlxcx_t * mlxp,mlxcx_ring_group_t * g)659ebb7c6fdSAlex Wilson mlxcx_teardown_tx_group(mlxcx_t *mlxp, mlxcx_ring_group_t *g)
660ebb7c6fdSAlex Wilson {
661ebb7c6fdSAlex Wilson 	mlxcx_work_queue_t *wq;
662ebb7c6fdSAlex Wilson 	mlxcx_completion_queue_t *cq;
663ebb7c6fdSAlex Wilson 	uint_t i;
664ebb7c6fdSAlex Wilson 
665ebb7c6fdSAlex Wilson 	mutex_enter(&g->mlg_mtx);
666ebb7c6fdSAlex Wilson 
667ebb7c6fdSAlex Wilson 	if (g->mlg_state & MLXCX_GROUP_WQS) {
668ebb7c6fdSAlex Wilson 		for (i = 0; i < g->mlg_nwqs; ++i) {
669ebb7c6fdSAlex Wilson 			wq = &g->mlg_wqs[i];
670ebb7c6fdSAlex Wilson 			mutex_enter(&wq->mlwq_mtx);
671ebb7c6fdSAlex Wilson 			cq = wq->mlwq_cq;
672ebb7c6fdSAlex Wilson 			if (wq->mlwq_state & MLXCX_WQ_STARTED &&
673ebb7c6fdSAlex Wilson 			    !mlxcx_cmd_stop_sq(mlxp, wq)) {
674ebb7c6fdSAlex Wilson 				mlxcx_warn(mlxp, "failed to stop sq %x",
675ebb7c6fdSAlex Wilson 				    wq->mlwq_num);
676ebb7c6fdSAlex Wilson 			}
677ebb7c6fdSAlex Wilson 			mutex_exit(&wq->mlwq_mtx);
678ebb7c6fdSAlex Wilson 			mlxcx_wq_teardown(mlxp, wq);
679ebb7c6fdSAlex Wilson 			if (cq != NULL)
680ebb7c6fdSAlex Wilson 				mlxcx_cq_teardown(mlxp, cq);
681ebb7c6fdSAlex Wilson 		}
682ebb7c6fdSAlex Wilson 		g->mlg_state &= ~MLXCX_GROUP_RUNNING;
683ebb7c6fdSAlex Wilson 		kmem_free(g->mlg_wqs, g->mlg_wqs_size);
684ebb7c6fdSAlex Wilson 		g->mlg_wqs = NULL;
685ebb7c6fdSAlex Wilson 		g->mlg_state &= ~MLXCX_GROUP_WQS;
686ebb7c6fdSAlex Wilson 	}
687ebb7c6fdSAlex Wilson 
688ebb7c6fdSAlex Wilson 	if ((g->mlg_state & MLXCX_GROUP_TIRTIS) &&
689ebb7c6fdSAlex Wilson 	    g->mlg_tis.mltis_state & MLXCX_TIS_CREATED &&
690ebb7c6fdSAlex Wilson 	    !(g->mlg_tis.mltis_state & MLXCX_TIS_DESTROYED)) {
691ebb7c6fdSAlex Wilson 		if (!mlxcx_cmd_destroy_tis(mlxp, &g->mlg_tis)) {
692ebb7c6fdSAlex Wilson 			mlxcx_warn(mlxp, "failed to destroy tis %u for tx ring",
693ebb7c6fdSAlex Wilson 			    g->mlg_tis.mltis_num);
694ebb7c6fdSAlex Wilson 		}
695ebb7c6fdSAlex Wilson 	}
696ebb7c6fdSAlex Wilson 	g->mlg_state &= ~MLXCX_GROUP_TIRTIS;
697ebb7c6fdSAlex Wilson 
698ebb7c6fdSAlex Wilson 	mutex_exit(&g->mlg_mtx);
699ebb7c6fdSAlex Wilson 	mutex_destroy(&g->mlg_mtx);
700ebb7c6fdSAlex Wilson 	g->mlg_state &= ~MLXCX_GROUP_INIT;
701ebb7c6fdSAlex Wilson 	ASSERT3S(g->mlg_state, ==, 0);
702ebb7c6fdSAlex Wilson }
703ebb7c6fdSAlex Wilson 
704ebb7c6fdSAlex Wilson void
mlxcx_teardown_groups(mlxcx_t * mlxp)705ebb7c6fdSAlex Wilson mlxcx_teardown_groups(mlxcx_t *mlxp)
706ebb7c6fdSAlex Wilson {
707ebb7c6fdSAlex Wilson 	mlxcx_ring_group_t *g;
708ebb7c6fdSAlex Wilson 	uint_t i;
709ebb7c6fdSAlex Wilson 
710ebb7c6fdSAlex Wilson 	for (i = 0; i < mlxp->mlx_rx_ngroups; ++i) {
711ebb7c6fdSAlex Wilson 		g = &mlxp->mlx_rx_groups[i];
712ebb7c6fdSAlex Wilson 		if (!(g->mlg_state & MLXCX_GROUP_INIT))
713ebb7c6fdSAlex Wilson 			continue;
714ebb7c6fdSAlex Wilson 		ASSERT3S(g->mlg_type, ==, MLXCX_GROUP_RX);
71522d05228SPaul Winder 		mlxcx_quiesce_rx_cqs(mlxp, g);
71622d05228SPaul Winder 	}
71722d05228SPaul Winder 
71822d05228SPaul Winder 	for (i = 0; i < mlxp->mlx_rx_ngroups; ++i) {
71922d05228SPaul Winder 		g = &mlxp->mlx_rx_groups[i];
72022d05228SPaul Winder 		if (!(g->mlg_state & MLXCX_GROUP_INIT))
72122d05228SPaul Winder 			continue;
722ebb7c6fdSAlex Wilson 		mlxcx_teardown_rx_group(mlxp, g);
723ebb7c6fdSAlex Wilson 	}
72422d05228SPaul Winder 
725ebb7c6fdSAlex Wilson 	kmem_free(mlxp->mlx_rx_groups, mlxp->mlx_rx_groups_size);
726ebb7c6fdSAlex Wilson 	mlxp->mlx_rx_groups = NULL;
727ebb7c6fdSAlex Wilson 
728ebb7c6fdSAlex Wilson 	for (i = 0; i < mlxp->mlx_tx_ngroups; ++i) {
729ebb7c6fdSAlex Wilson 		g = &mlxp->mlx_tx_groups[i];
730ebb7c6fdSAlex Wilson 		if (!(g->mlg_state & MLXCX_GROUP_INIT))
731ebb7c6fdSAlex Wilson 			continue;
732ebb7c6fdSAlex Wilson 		ASSERT3S(g->mlg_type, ==, MLXCX_GROUP_TX);
733ebb7c6fdSAlex Wilson 		mlxcx_teardown_tx_group(mlxp, g);
734ebb7c6fdSAlex Wilson 	}
73522d05228SPaul Winder 
736ebb7c6fdSAlex Wilson 	kmem_free(mlxp->mlx_tx_groups, mlxp->mlx_tx_groups_size);
737ebb7c6fdSAlex Wilson 	mlxp->mlx_tx_groups = NULL;
738ebb7c6fdSAlex Wilson }
739ebb7c6fdSAlex Wilson 
740ebb7c6fdSAlex Wilson boolean_t
mlxcx_rx_group_setup(mlxcx_t * mlxp,mlxcx_ring_group_t * g)741ebb7c6fdSAlex Wilson mlxcx_rx_group_setup(mlxcx_t *mlxp, mlxcx_ring_group_t *g)
742ebb7c6fdSAlex Wilson {
743ebb7c6fdSAlex Wilson 	mlxcx_event_queue_t *eq;
744ebb7c6fdSAlex Wilson 	mlxcx_completion_queue_t *cq;
745ebb7c6fdSAlex Wilson 	mlxcx_work_queue_t *rq;
746ebb7c6fdSAlex Wilson 	mlxcx_flow_table_t *ft;
747ebb7c6fdSAlex Wilson 	mlxcx_flow_group_t *fg;
748ebb7c6fdSAlex Wilson 	mlxcx_flow_entry_t *fe;
74922d05228SPaul Winder 	uint_t ent_shift;
750ebb7c6fdSAlex Wilson 	uint_t i, j;
751ebb7c6fdSAlex Wilson 
752ebb7c6fdSAlex Wilson 	ASSERT3S(g->mlg_state, ==, 0);
753ebb7c6fdSAlex Wilson 
754ebb7c6fdSAlex Wilson 	mutex_init(&g->mlg_mtx, NULL, MUTEX_DRIVER,
755ebb7c6fdSAlex Wilson 	    DDI_INTR_PRI(mlxp->mlx_intr_pri));
756ebb7c6fdSAlex Wilson 	mutex_enter(&g->mlg_mtx);
757ebb7c6fdSAlex Wilson 	g->mlg_mlx = mlxp;
758ebb7c6fdSAlex Wilson 	g->mlg_type = MLXCX_GROUP_RX;
759ebb7c6fdSAlex Wilson 	g->mlg_port = &mlxp->mlx_ports[0];
760ebb7c6fdSAlex Wilson 	g->mlg_state |= MLXCX_GROUP_INIT;
761ebb7c6fdSAlex Wilson 
762ebb7c6fdSAlex Wilson 	g->mlg_nwqs = mlxp->mlx_props.mldp_rx_nrings_per_small_group;
763ebb7c6fdSAlex Wilson 	i = g - &mlxp->mlx_rx_groups[0];
764ebb7c6fdSAlex Wilson 	if (i < mlxp->mlx_props.mldp_rx_ngroups_large)
765ebb7c6fdSAlex Wilson 		g->mlg_nwqs = mlxp->mlx_props.mldp_rx_nrings_per_large_group;
766ebb7c6fdSAlex Wilson 
767ebb7c6fdSAlex Wilson 	g->mlg_wqs_size = g->mlg_nwqs * sizeof (mlxcx_work_queue_t);
768ebb7c6fdSAlex Wilson 	g->mlg_wqs = kmem_zalloc(g->mlg_wqs_size, KM_SLEEP);
769ebb7c6fdSAlex Wilson 	g->mlg_state |= MLXCX_GROUP_WQS;
770ebb7c6fdSAlex Wilson 
771ebb7c6fdSAlex Wilson 	g->mlg_rqt = kmem_zalloc(sizeof (mlxcx_rqtable_t), KM_SLEEP);
772ebb7c6fdSAlex Wilson 	g->mlg_rqt->mlrqt_max = 2;
773ebb7c6fdSAlex Wilson 	while (g->mlg_rqt->mlrqt_max < g->mlg_nwqs)
774ebb7c6fdSAlex Wilson 		g->mlg_rqt->mlrqt_max <<= 1;
775ebb7c6fdSAlex Wilson 	g->mlg_rqt->mlrqt_rq_size = g->mlg_rqt->mlrqt_max *
776ebb7c6fdSAlex Wilson 	    sizeof (mlxcx_work_queue_t *);
777ebb7c6fdSAlex Wilson 	g->mlg_rqt->mlrqt_rq = kmem_zalloc(g->mlg_rqt->mlrqt_rq_size, KM_SLEEP);
778ebb7c6fdSAlex Wilson 	g->mlg_state |= MLXCX_GROUP_RQT;
779ebb7c6fdSAlex Wilson 
780ebb7c6fdSAlex Wilson 	for (i = 0; i < g->mlg_nwqs; ++i) {
781ebb7c6fdSAlex Wilson 		eq = NULL;
782ebb7c6fdSAlex Wilson 		while (eq == NULL) {
783ebb7c6fdSAlex Wilson 			eq = &mlxp->mlx_eqs[mlxp->mlx_next_eq++];
784ebb7c6fdSAlex Wilson 			if (mlxp->mlx_next_eq >= mlxp->mlx_intr_count)
7855f0e3176SPaul Winder 				mlxp->mlx_next_eq = mlxp->mlx_intr_cq0;
786ebb7c6fdSAlex Wilson 			if (eq->mleq_type != MLXCX_EQ_TYPE_ANY &&
787ebb7c6fdSAlex Wilson 			    eq->mleq_type != MLXCX_EQ_TYPE_RX) {
788ebb7c6fdSAlex Wilson 				/* Try the next one */
789ebb7c6fdSAlex Wilson 				eq = NULL;
790ebb7c6fdSAlex Wilson 			}
791ebb7c6fdSAlex Wilson 		}
792ebb7c6fdSAlex Wilson 
79322d05228SPaul Winder 		/*
79422d05228SPaul Winder 		 * A single completion is indicated for each rq entry as
79522d05228SPaul Winder 		 * it is used. So, the number of cq entries never needs
79622d05228SPaul Winder 		 * to be larger than the rq.
79722d05228SPaul Winder 		 */
79822d05228SPaul Winder 		ent_shift = MIN(mlxp->mlx_props.mldp_cq_size_shift,
79922d05228SPaul Winder 		    mlxp->mlx_props.mldp_rq_size_shift);
80022d05228SPaul Winder 		if (!mlxcx_cq_setup(mlxp, eq, &cq, ent_shift)) {
801ebb7c6fdSAlex Wilson 			g->mlg_nwqs = i;
802ebb7c6fdSAlex Wilson 			break;
803ebb7c6fdSAlex Wilson 		}
80422d05228SPaul Winder 
805ebb7c6fdSAlex Wilson 		cq->mlcq_stats = &g->mlg_port->mlp_stats;
806ebb7c6fdSAlex Wilson 
807ebb7c6fdSAlex Wilson 		rq = &g->mlg_wqs[i];
808ebb7c6fdSAlex Wilson 		if (!mlxcx_rq_setup(mlxp, cq, rq)) {
809ebb7c6fdSAlex Wilson 			g->mlg_nwqs = i;
810ebb7c6fdSAlex Wilson 			break;
811ebb7c6fdSAlex Wilson 		}
812ebb7c6fdSAlex Wilson 		g->mlg_rqt->mlrqt_rq[g->mlg_rqt->mlrqt_used++] = rq;
813ebb7c6fdSAlex Wilson 		g->mlg_rqt->mlrqt_state |= MLXCX_RQT_DIRTY;
814ebb7c6fdSAlex Wilson 		rq->mlwq_group = g;
815ebb7c6fdSAlex Wilson 	}
816ebb7c6fdSAlex Wilson 	if (g->mlg_nwqs == 0) {
817ebb7c6fdSAlex Wilson 		mutex_exit(&g->mlg_mtx);
818ebb7c6fdSAlex Wilson 		return (B_FALSE);
819ebb7c6fdSAlex Wilson 	}
820ebb7c6fdSAlex Wilson 
821ebb7c6fdSAlex Wilson 	if (!mlxcx_cmd_create_rqt(mlxp, g->mlg_rqt)) {
822ebb7c6fdSAlex Wilson 		mutex_exit(&g->mlg_mtx);
823ebb7c6fdSAlex Wilson 		return (B_FALSE);
824ebb7c6fdSAlex Wilson 	}
825ebb7c6fdSAlex Wilson 
826ebb7c6fdSAlex Wilson 	for (i = 0; i < MLXCX_TIRS_PER_GROUP; ++i) {
827ebb7c6fdSAlex Wilson 		mlxcx_tir_t *tir = &g->mlg_tir[i];
828ebb7c6fdSAlex Wilson 		tir->mltir_tdom = &mlxp->mlx_tdom;
829ebb7c6fdSAlex Wilson 		switch (i) {
830ebb7c6fdSAlex Wilson 		case MLXCX_TIR_ROLE_OTHER:
831ebb7c6fdSAlex Wilson 			tir->mltir_type = MLXCX_TIR_DIRECT;
832ebb7c6fdSAlex Wilson 			tir->mltir_rq = &g->mlg_wqs[0];
833ebb7c6fdSAlex Wilson 			break;
834ebb7c6fdSAlex Wilson 		case MLXCX_TIR_ROLE_IPv4:
835ebb7c6fdSAlex Wilson 		case MLXCX_TIR_ROLE_IPv6:
836ebb7c6fdSAlex Wilson 		case MLXCX_TIR_ROLE_TCPv4:
837ebb7c6fdSAlex Wilson 		case MLXCX_TIR_ROLE_TCPv6:
838ebb7c6fdSAlex Wilson 		case MLXCX_TIR_ROLE_UDPv4:
839ebb7c6fdSAlex Wilson 		case MLXCX_TIR_ROLE_UDPv6:
840ebb7c6fdSAlex Wilson 			tir->mltir_type = MLXCX_TIR_INDIRECT;
841ebb7c6fdSAlex Wilson 			tir->mltir_rqtable = g->mlg_rqt;
842ebb7c6fdSAlex Wilson 			tir->mltir_hash_fn = MLXCX_TIR_HASH_TOEPLITZ;
843ebb7c6fdSAlex Wilson 			(void) random_get_pseudo_bytes(tir->mltir_toeplitz_key,
844ebb7c6fdSAlex Wilson 			    sizeof (tir->mltir_toeplitz_key));
845ebb7c6fdSAlex Wilson 			break;
846ebb7c6fdSAlex Wilson 		}
847ebb7c6fdSAlex Wilson 		switch (i) {
848ebb7c6fdSAlex Wilson 		case MLXCX_TIR_ROLE_OTHER:
849ebb7c6fdSAlex Wilson 			break;
850ebb7c6fdSAlex Wilson 		case MLXCX_TIR_ROLE_IPv4:
851ebb7c6fdSAlex Wilson 		case MLXCX_TIR_ROLE_TCPv4:
852ebb7c6fdSAlex Wilson 		case MLXCX_TIR_ROLE_UDPv4:
853ebb7c6fdSAlex Wilson 			tir->mltir_l3_type = MLXCX_RX_HASH_L3_IPv4;
854ebb7c6fdSAlex Wilson 			tir->mltir_hash_fields =
855ebb7c6fdSAlex Wilson 			    MLXCX_RX_HASH_SRC_IP | MLXCX_RX_HASH_DST_IP;
856ebb7c6fdSAlex Wilson 			break;
857ebb7c6fdSAlex Wilson 		case MLXCX_TIR_ROLE_IPv6:
858ebb7c6fdSAlex Wilson 		case MLXCX_TIR_ROLE_TCPv6:
859ebb7c6fdSAlex Wilson 		case MLXCX_TIR_ROLE_UDPv6:
860ebb7c6fdSAlex Wilson 			tir->mltir_l3_type = MLXCX_RX_HASH_L3_IPv6;
861ebb7c6fdSAlex Wilson 			tir->mltir_hash_fields =
862ebb7c6fdSAlex Wilson 			    MLXCX_RX_HASH_SRC_IP | MLXCX_RX_HASH_DST_IP;
863ebb7c6fdSAlex Wilson 			break;
864ebb7c6fdSAlex Wilson 		}
865ebb7c6fdSAlex Wilson 		switch (i) {
866ebb7c6fdSAlex Wilson 		case MLXCX_TIR_ROLE_OTHER:
867ebb7c6fdSAlex Wilson 		case MLXCX_TIR_ROLE_IPv4:
868ebb7c6fdSAlex Wilson 		case MLXCX_TIR_ROLE_IPv6:
869ebb7c6fdSAlex Wilson 			break;
870ebb7c6fdSAlex Wilson 		case MLXCX_TIR_ROLE_TCPv4:
871ebb7c6fdSAlex Wilson 		case MLXCX_TIR_ROLE_TCPv6:
872ebb7c6fdSAlex Wilson 			tir->mltir_l4_type = MLXCX_RX_HASH_L4_TCP;
873ebb7c6fdSAlex Wilson 			tir->mltir_hash_fields |=
874ebb7c6fdSAlex Wilson 			    MLXCX_RX_HASH_L4_SPORT | MLXCX_RX_HASH_L4_DPORT;
875ebb7c6fdSAlex Wilson 			break;
876ebb7c6fdSAlex Wilson 		case MLXCX_TIR_ROLE_UDPv4:
877ebb7c6fdSAlex Wilson 		case MLXCX_TIR_ROLE_UDPv6:
878ebb7c6fdSAlex Wilson 			tir->mltir_l4_type = MLXCX_RX_HASH_L4_UDP;
879ebb7c6fdSAlex Wilson 			tir->mltir_hash_fields |=
880ebb7c6fdSAlex Wilson 			    MLXCX_RX_HASH_L4_SPORT | MLXCX_RX_HASH_L4_DPORT;
881ebb7c6fdSAlex Wilson 			break;
882ebb7c6fdSAlex Wilson 		}
883ebb7c6fdSAlex Wilson 
884ebb7c6fdSAlex Wilson 		if (!mlxcx_cmd_create_tir(mlxp, tir)) {
885ebb7c6fdSAlex Wilson 			mutex_exit(&g->mlg_mtx);
886ebb7c6fdSAlex Wilson 			return (B_FALSE);
887ebb7c6fdSAlex Wilson 		}
888ebb7c6fdSAlex Wilson 
889ebb7c6fdSAlex Wilson 		g->mlg_state |= MLXCX_GROUP_TIRTIS;
890ebb7c6fdSAlex Wilson 	}
891ebb7c6fdSAlex Wilson 
892ebb7c6fdSAlex Wilson 	/*
893ebb7c6fdSAlex Wilson 	 * Flow table: our RX hashing breakout table for RSS
894ebb7c6fdSAlex Wilson 	 */
895ebb7c6fdSAlex Wilson 
896ebb7c6fdSAlex Wilson 	g->mlg_rx_hash_ft = (ft = kmem_zalloc(sizeof (mlxcx_flow_table_t),
897ebb7c6fdSAlex Wilson 	    KM_SLEEP));
898ebb7c6fdSAlex Wilson 	mutex_init(&ft->mlft_mtx, NULL, MUTEX_DRIVER,
899ebb7c6fdSAlex Wilson 	    DDI_INTR_PRI(mlxp->mlx_intr_pri));
900ebb7c6fdSAlex Wilson 	avl_create(&g->mlg_rx_macs, mlxcx_grmac_compare,
901ebb7c6fdSAlex Wilson 	    sizeof (mlxcx_group_mac_t),
902ebb7c6fdSAlex Wilson 	    offsetof(mlxcx_group_mac_t, mlgm_group_entry));
903ebb7c6fdSAlex Wilson 	g->mlg_state |= MLXCX_GROUP_FLOWS;
904ebb7c6fdSAlex Wilson 
905ebb7c6fdSAlex Wilson 	mutex_enter(&ft->mlft_mtx);
906ebb7c6fdSAlex Wilson 
907ebb7c6fdSAlex Wilson 	ft->mlft_type = MLXCX_FLOW_TABLE_NIC_RX;
908ebb7c6fdSAlex Wilson 	ft->mlft_level = 2;
909ebb7c6fdSAlex Wilson 	ft->mlft_port = g->mlg_port;
910ebb7c6fdSAlex Wilson 	ft->mlft_entshift = MLXCX_RX_HASH_FT_SIZE_SHIFT;
911ebb7c6fdSAlex Wilson 	ft->mlft_nents = (1 << ft->mlft_entshift);
912ebb7c6fdSAlex Wilson 	ASSERT3U(ft->mlft_nents, >=, MLXCX_TIRS_PER_GROUP);
913ebb7c6fdSAlex Wilson 	ft->mlft_entsize = ft->mlft_nents * sizeof (mlxcx_flow_entry_t);
914ebb7c6fdSAlex Wilson 	ft->mlft_ent = kmem_zalloc(ft->mlft_entsize, KM_SLEEP);
915ebb7c6fdSAlex Wilson 	list_create(&ft->mlft_groups, sizeof (mlxcx_flow_group_t),
916ebb7c6fdSAlex Wilson 	    offsetof(mlxcx_flow_group_t, mlfg_entry));
917ebb7c6fdSAlex Wilson 
918ebb7c6fdSAlex Wilson 	for (j = 0; j < ft->mlft_nents; ++j) {
919ebb7c6fdSAlex Wilson 		ft->mlft_ent[j].mlfe_table = ft;
920ebb7c6fdSAlex Wilson 		ft->mlft_ent[j].mlfe_index = j;
921ebb7c6fdSAlex Wilson 	}
922ebb7c6fdSAlex Wilson 
923ebb7c6fdSAlex Wilson 	if (!mlxcx_cmd_create_flow_table(mlxp, ft)) {
924ebb7c6fdSAlex Wilson 		mutex_exit(&ft->mlft_mtx);
925ebb7c6fdSAlex Wilson 		mutex_exit(&g->mlg_mtx);
926ebb7c6fdSAlex Wilson 		return (B_FALSE);
927ebb7c6fdSAlex Wilson 	}
928ebb7c6fdSAlex Wilson 
929ebb7c6fdSAlex Wilson 	fg = kmem_zalloc(sizeof (mlxcx_flow_group_t), KM_SLEEP);
930ebb7c6fdSAlex Wilson 	list_insert_tail(&ft->mlft_groups, fg);
931ebb7c6fdSAlex Wilson 	fg->mlfg_table = ft;
932ebb7c6fdSAlex Wilson 	fg->mlfg_size = 1;
933ebb7c6fdSAlex Wilson 	fg->mlfg_mask |= MLXCX_FLOW_MATCH_IP_VER | MLXCX_FLOW_MATCH_IP_PROTO;
934ebb7c6fdSAlex Wilson 	if (!mlxcx_setup_flow_group(mlxp, ft, fg)) {
935ebb7c6fdSAlex Wilson 		mutex_exit(&ft->mlft_mtx);
936ebb7c6fdSAlex Wilson 		mutex_exit(&g->mlg_mtx);
937ebb7c6fdSAlex Wilson 		return (B_FALSE);
938ebb7c6fdSAlex Wilson 	}
939ebb7c6fdSAlex Wilson 	fe = list_head(&fg->mlfg_entries);
940ebb7c6fdSAlex Wilson 	fe->mlfe_ip_version = 6;
941ebb7c6fdSAlex Wilson 	fe->mlfe_ip_proto = IPPROTO_UDP;
942ebb7c6fdSAlex Wilson 	fe->mlfe_action = MLXCX_FLOW_ACTION_FORWARD;
943ebb7c6fdSAlex Wilson 	fe->mlfe_dest[fe->mlfe_ndest++].mlfed_tir =
944ebb7c6fdSAlex Wilson 	    &g->mlg_tir[MLXCX_TIR_ROLE_UDPv6];
945ebb7c6fdSAlex Wilson 	if (!mlxcx_cmd_set_flow_table_entry(mlxp, fe)) {
946ebb7c6fdSAlex Wilson 		mutex_exit(&ft->mlft_mtx);
947ebb7c6fdSAlex Wilson 		mutex_exit(&g->mlg_mtx);
948ebb7c6fdSAlex Wilson 		return (B_FALSE);
949ebb7c6fdSAlex Wilson 	}
950ebb7c6fdSAlex Wilson 
951ebb7c6fdSAlex Wilson 	fg = kmem_zalloc(sizeof (mlxcx_flow_group_t), KM_SLEEP);
952ebb7c6fdSAlex Wilson 	list_insert_tail(&ft->mlft_groups, fg);
953ebb7c6fdSAlex Wilson 	fg->mlfg_table = ft;
954ebb7c6fdSAlex Wilson 	fg->mlfg_size = 1;
955ebb7c6fdSAlex Wilson 	fg->mlfg_mask |= MLXCX_FLOW_MATCH_IP_VER | MLXCX_FLOW_MATCH_IP_PROTO;
956ebb7c6fdSAlex Wilson 	if (!mlxcx_setup_flow_group(mlxp, ft, fg)) {
957ebb7c6fdSAlex Wilson 		mutex_exit(&ft->mlft_mtx);
958ebb7c6fdSAlex Wilson 		mutex_exit(&g->mlg_mtx);
959ebb7c6fdSAlex Wilson 		return (B_FALSE);
960ebb7c6fdSAlex Wilson 	}
961ebb7c6fdSAlex Wilson 	fe = list_head(&fg->mlfg_entries);
962ebb7c6fdSAlex Wilson 	fe->mlfe_ip_version = 4;
963ebb7c6fdSAlex Wilson 	fe->mlfe_ip_proto = IPPROTO_UDP;
964ebb7c6fdSAlex Wilson 	fe->mlfe_action = MLXCX_FLOW_ACTION_FORWARD;
965ebb7c6fdSAlex Wilson 	fe->mlfe_dest[fe->mlfe_ndest++].mlfed_tir =
966ebb7c6fdSAlex Wilson 	    &g->mlg_tir[MLXCX_TIR_ROLE_UDPv4];
967ebb7c6fdSAlex Wilson 	if (!mlxcx_cmd_set_flow_table_entry(mlxp, fe)) {
968ebb7c6fdSAlex Wilson 		mutex_exit(&ft->mlft_mtx);
969ebb7c6fdSAlex Wilson 		mutex_exit(&g->mlg_mtx);
970ebb7c6fdSAlex Wilson 		return (B_FALSE);
971ebb7c6fdSAlex Wilson 	}
972ebb7c6fdSAlex Wilson 
973ebb7c6fdSAlex Wilson 	fg = kmem_zalloc(sizeof (mlxcx_flow_group_t), KM_SLEEP);
974ebb7c6fdSAlex Wilson 	list_insert_tail(&ft->mlft_groups, fg);
975ebb7c6fdSAlex Wilson 	fg->mlfg_table = ft;
976ebb7c6fdSAlex Wilson 	fg->mlfg_size = 1;
977ebb7c6fdSAlex Wilson 	fg->mlfg_mask |= MLXCX_FLOW_MATCH_IP_VER | MLXCX_FLOW_MATCH_IP_PROTO;
978ebb7c6fdSAlex Wilson 	if (!mlxcx_setup_flow_group(mlxp, ft, fg)) {
979ebb7c6fdSAlex Wilson 		mutex_exit(&ft->mlft_mtx);
980ebb7c6fdSAlex Wilson 		mutex_exit(&g->mlg_mtx);
981ebb7c6fdSAlex Wilson 		return (B_FALSE);
982ebb7c6fdSAlex Wilson 	}
983ebb7c6fdSAlex Wilson 	fe = list_head(&fg->mlfg_entries);
984ebb7c6fdSAlex Wilson 	fe->mlfe_ip_version = 6;
985ebb7c6fdSAlex Wilson 	fe->mlfe_ip_proto = IPPROTO_TCP;
986ebb7c6fdSAlex Wilson 	fe->mlfe_action = MLXCX_FLOW_ACTION_FORWARD;
987ebb7c6fdSAlex Wilson 	fe->mlfe_dest[fe->mlfe_ndest++].mlfed_tir =
988ebb7c6fdSAlex Wilson 	    &g->mlg_tir[MLXCX_TIR_ROLE_TCPv6];
989ebb7c6fdSAlex Wilson 	if (!mlxcx_cmd_set_flow_table_entry(mlxp, fe)) {
990ebb7c6fdSAlex Wilson 		mutex_exit(&ft->mlft_mtx);
991ebb7c6fdSAlex Wilson 		mutex_exit(&g->mlg_mtx);
992ebb7c6fdSAlex Wilson 		return (B_FALSE);
993ebb7c6fdSAlex Wilson 	}
994ebb7c6fdSAlex Wilson 
995ebb7c6fdSAlex Wilson 	fg = kmem_zalloc(sizeof (mlxcx_flow_group_t), KM_SLEEP);
996ebb7c6fdSAlex Wilson 	list_insert_tail(&ft->mlft_groups, fg);
997ebb7c6fdSAlex Wilson 	fg->mlfg_table = ft;
998ebb7c6fdSAlex Wilson 	fg->mlfg_size = 1;
999ebb7c6fdSAlex Wilson 	fg->mlfg_mask |= MLXCX_FLOW_MATCH_IP_VER | MLXCX_FLOW_MATCH_IP_PROTO;
1000ebb7c6fdSAlex Wilson 	if (!mlxcx_setup_flow_group(mlxp, ft, fg)) {
1001ebb7c6fdSAlex Wilson 		mutex_exit(&ft->mlft_mtx);
1002ebb7c6fdSAlex Wilson 		mutex_exit(&g->mlg_mtx);
1003ebb7c6fdSAlex Wilson 		return (B_FALSE);
1004ebb7c6fdSAlex Wilson 	}
1005ebb7c6fdSAlex Wilson 	fe = list_head(&fg->mlfg_entries);
1006ebb7c6fdSAlex Wilson 	fe->mlfe_ip_version = 4;
1007ebb7c6fdSAlex Wilson 	fe->mlfe_ip_proto = IPPROTO_TCP;
1008ebb7c6fdSAlex Wilson 	fe->mlfe_action = MLXCX_FLOW_ACTION_FORWARD;
1009ebb7c6fdSAlex Wilson 	fe->mlfe_dest[fe->mlfe_ndest++].mlfed_tir =
1010ebb7c6fdSAlex Wilson 	    &g->mlg_tir[MLXCX_TIR_ROLE_TCPv4];
1011ebb7c6fdSAlex Wilson 	if (!mlxcx_cmd_set_flow_table_entry(mlxp, fe)) {
1012ebb7c6fdSAlex Wilson 		mutex_exit(&ft->mlft_mtx);
1013ebb7c6fdSAlex Wilson 		mutex_exit(&g->mlg_mtx);
1014ebb7c6fdSAlex Wilson 		return (B_FALSE);
1015ebb7c6fdSAlex Wilson 	}
1016ebb7c6fdSAlex Wilson 
1017ebb7c6fdSAlex Wilson 	fg = kmem_zalloc(sizeof (mlxcx_flow_group_t), KM_SLEEP);
1018ebb7c6fdSAlex Wilson 	list_insert_tail(&ft->mlft_groups, fg);
1019ebb7c6fdSAlex Wilson 	fg->mlfg_table = ft;
1020ebb7c6fdSAlex Wilson 	fg->mlfg_size = 1;
1021ebb7c6fdSAlex Wilson 	fg->mlfg_mask |= MLXCX_FLOW_MATCH_IP_VER;
1022ebb7c6fdSAlex Wilson 	if (!mlxcx_setup_flow_group(mlxp, ft, fg)) {
1023ebb7c6fdSAlex Wilson 		mutex_exit(&ft->mlft_mtx);
1024ebb7c6fdSAlex Wilson 		mutex_exit(&g->mlg_mtx);
1025ebb7c6fdSAlex Wilson 		return (B_FALSE);
1026ebb7c6fdSAlex Wilson 	}
1027ebb7c6fdSAlex Wilson 	fe = list_head(&fg->mlfg_entries);
1028ebb7c6fdSAlex Wilson 	fe->mlfe_ip_version = 6;
1029ebb7c6fdSAlex Wilson 	fe->mlfe_action = MLXCX_FLOW_ACTION_FORWARD;
1030ebb7c6fdSAlex Wilson 	fe->mlfe_dest[fe->mlfe_ndest++].mlfed_tir =
1031ebb7c6fdSAlex Wilson 	    &g->mlg_tir[MLXCX_TIR_ROLE_IPv6];
1032ebb7c6fdSAlex Wilson 	if (!mlxcx_cmd_set_flow_table_entry(mlxp, fe)) {
1033ebb7c6fdSAlex Wilson 		mutex_exit(&ft->mlft_mtx);
1034ebb7c6fdSAlex Wilson 		mutex_exit(&g->mlg_mtx);
1035ebb7c6fdSAlex Wilson 		return (B_FALSE);
1036ebb7c6fdSAlex Wilson 	}
1037ebb7c6fdSAlex Wilson 
1038ebb7c6fdSAlex Wilson 	fg = kmem_zalloc(sizeof (mlxcx_flow_group_t), KM_SLEEP);
1039ebb7c6fdSAlex Wilson 	list_insert_tail(&ft->mlft_groups, fg);
1040ebb7c6fdSAlex Wilson 	fg->mlfg_table = ft;
1041ebb7c6fdSAlex Wilson 	fg->mlfg_size = 1;
1042ebb7c6fdSAlex Wilson 	fg->mlfg_mask |= MLXCX_FLOW_MATCH_IP_VER;
1043ebb7c6fdSAlex Wilson 	if (!mlxcx_setup_flow_group(mlxp, ft, fg)) {
1044ebb7c6fdSAlex Wilson 		mutex_exit(&ft->mlft_mtx);
1045ebb7c6fdSAlex Wilson 		mutex_exit(&g->mlg_mtx);
1046ebb7c6fdSAlex Wilson 		return (B_FALSE);
1047ebb7c6fdSAlex Wilson 	}
1048ebb7c6fdSAlex Wilson 	fe = list_head(&fg->mlfg_entries);
1049ebb7c6fdSAlex Wilson 	fe->mlfe_ip_version = 4;
1050ebb7c6fdSAlex Wilson 	fe->mlfe_action = MLXCX_FLOW_ACTION_FORWARD;
1051ebb7c6fdSAlex Wilson 	fe->mlfe_dest[fe->mlfe_ndest++].mlfed_tir =
1052ebb7c6fdSAlex Wilson 	    &g->mlg_tir[MLXCX_TIR_ROLE_IPv4];
1053ebb7c6fdSAlex Wilson 	if (!mlxcx_cmd_set_flow_table_entry(mlxp, fe)) {
1054ebb7c6fdSAlex Wilson 		mutex_exit(&ft->mlft_mtx);
1055ebb7c6fdSAlex Wilson 		mutex_exit(&g->mlg_mtx);
1056ebb7c6fdSAlex Wilson 		return (B_FALSE);
1057ebb7c6fdSAlex Wilson 	}
1058ebb7c6fdSAlex Wilson 
1059ebb7c6fdSAlex Wilson 	fg = kmem_zalloc(sizeof (mlxcx_flow_group_t), KM_SLEEP);
1060ebb7c6fdSAlex Wilson 	list_insert_tail(&ft->mlft_groups, fg);
1061ebb7c6fdSAlex Wilson 	fg->mlfg_table = ft;
1062ebb7c6fdSAlex Wilson 	fg->mlfg_size = 1;
1063ebb7c6fdSAlex Wilson 	if (!mlxcx_setup_flow_group(mlxp, ft, fg)) {
1064ebb7c6fdSAlex Wilson 		mutex_exit(&ft->mlft_mtx);
1065ebb7c6fdSAlex Wilson 		mutex_exit(&g->mlg_mtx);
1066ebb7c6fdSAlex Wilson 		return (B_FALSE);
1067ebb7c6fdSAlex Wilson 	}
1068ebb7c6fdSAlex Wilson 	fe = list_head(&fg->mlfg_entries);
1069ebb7c6fdSAlex Wilson 	fe->mlfe_action = MLXCX_FLOW_ACTION_FORWARD;
1070ebb7c6fdSAlex Wilson 	fe->mlfe_dest[fe->mlfe_ndest++].mlfed_tir =
1071ebb7c6fdSAlex Wilson 	    &g->mlg_tir[MLXCX_TIR_ROLE_OTHER];
1072ebb7c6fdSAlex Wilson 	if (!mlxcx_cmd_set_flow_table_entry(mlxp, fe)) {
1073ebb7c6fdSAlex Wilson 		mutex_exit(&ft->mlft_mtx);
1074ebb7c6fdSAlex Wilson 		mutex_exit(&g->mlg_mtx);
1075ebb7c6fdSAlex Wilson 		return (B_FALSE);
1076ebb7c6fdSAlex Wilson 	}
1077ebb7c6fdSAlex Wilson 
1078ebb7c6fdSAlex Wilson 	mutex_exit(&ft->mlft_mtx);
1079ebb7c6fdSAlex Wilson 
1080ebb7c6fdSAlex Wilson 	/*
1081ebb7c6fdSAlex Wilson 	 * Flow table: the VLAN breakout table for doing VLAN filtering after
1082ebb7c6fdSAlex Wilson 	 * we've matched a MAC address.
1083ebb7c6fdSAlex Wilson 	 */
1084ebb7c6fdSAlex Wilson 
1085ebb7c6fdSAlex Wilson 	g->mlg_rx_vlan_ft = (ft = kmem_zalloc(sizeof (mlxcx_flow_table_t),
1086ebb7c6fdSAlex Wilson 	    KM_SLEEP));
1087ebb7c6fdSAlex Wilson 	mutex_init(&ft->mlft_mtx, NULL, MUTEX_DRIVER,
1088ebb7c6fdSAlex Wilson 	    DDI_INTR_PRI(mlxp->mlx_intr_pri));
1089ebb7c6fdSAlex Wilson 	list_create(&g->mlg_rx_vlans, sizeof (mlxcx_group_vlan_t),
1090ebb7c6fdSAlex Wilson 	    offsetof(mlxcx_group_vlan_t, mlgv_entry));
1091ebb7c6fdSAlex Wilson 
1092ebb7c6fdSAlex Wilson 	mutex_enter(&ft->mlft_mtx);
1093ebb7c6fdSAlex Wilson 
1094ebb7c6fdSAlex Wilson 	ft->mlft_type = MLXCX_FLOW_TABLE_NIC_RX;
1095ebb7c6fdSAlex Wilson 	ft->mlft_level = 1;
1096ebb7c6fdSAlex Wilson 	ft->mlft_port = g->mlg_port;
1097ebb7c6fdSAlex Wilson 	ft->mlft_entshift = mlxp->mlx_props.mldp_ftbl_vlan_size_shift;
1098ebb7c6fdSAlex Wilson 	ft->mlft_nents = (1 << ft->mlft_entshift);
1099ebb7c6fdSAlex Wilson 	ft->mlft_entsize = ft->mlft_nents * sizeof (mlxcx_flow_entry_t);
1100ebb7c6fdSAlex Wilson 	ft->mlft_ent = kmem_zalloc(ft->mlft_entsize, KM_SLEEP);
1101ebb7c6fdSAlex Wilson 	list_create(&ft->mlft_groups, sizeof (mlxcx_flow_group_t),
1102ebb7c6fdSAlex Wilson 	    offsetof(mlxcx_flow_group_t, mlfg_entry));
1103ebb7c6fdSAlex Wilson 
1104ebb7c6fdSAlex Wilson 	for (j = 0; j < ft->mlft_nents; ++j) {
1105ebb7c6fdSAlex Wilson 		fe = &ft->mlft_ent[j];
1106ebb7c6fdSAlex Wilson 		fe->mlfe_table = ft;
1107ebb7c6fdSAlex Wilson 		fe->mlfe_index = j;
1108ebb7c6fdSAlex Wilson 		fe->mlfe_action = MLXCX_FLOW_ACTION_FORWARD;
1109ebb7c6fdSAlex Wilson 		fe->mlfe_dest[fe->mlfe_ndest++].mlfed_flow = g->mlg_rx_hash_ft;
1110ebb7c6fdSAlex Wilson 	}
1111ebb7c6fdSAlex Wilson 
1112ebb7c6fdSAlex Wilson 	if (!mlxcx_cmd_create_flow_table(mlxp, ft)) {
1113ebb7c6fdSAlex Wilson 		mutex_exit(&ft->mlft_mtx);
1114ebb7c6fdSAlex Wilson 		mutex_exit(&g->mlg_mtx);
1115ebb7c6fdSAlex Wilson 		return (B_FALSE);
1116ebb7c6fdSAlex Wilson 	}
1117ebb7c6fdSAlex Wilson 
1118ebb7c6fdSAlex Wilson 	/* First group is all actual matched VLANs */
1119ebb7c6fdSAlex Wilson 	fg = kmem_zalloc(sizeof (mlxcx_flow_group_t), KM_SLEEP);
1120ebb7c6fdSAlex Wilson 	g->mlg_rx_vlan_fg = fg;
1121ebb7c6fdSAlex Wilson 	list_insert_tail(&ft->mlft_groups, fg);
1122ebb7c6fdSAlex Wilson 	fg->mlfg_table = ft;
1123ebb7c6fdSAlex Wilson 	fg->mlfg_size = ft->mlft_nents - 2;
1124ebb7c6fdSAlex Wilson 	fg->mlfg_mask |= MLXCX_FLOW_MATCH_VLAN;
1125ebb7c6fdSAlex Wilson 	fg->mlfg_mask |= MLXCX_FLOW_MATCH_VID;
1126ebb7c6fdSAlex Wilson 	if (!mlxcx_setup_flow_group(mlxp, ft, fg)) {
1127ebb7c6fdSAlex Wilson 		mutex_exit(&ft->mlft_mtx);
1128ebb7c6fdSAlex Wilson 		mutex_exit(&g->mlg_mtx);
1129ebb7c6fdSAlex Wilson 		return (B_FALSE);
1130ebb7c6fdSAlex Wilson 	}
1131ebb7c6fdSAlex Wilson 
1132ebb7c6fdSAlex Wilson 	/*
1133ebb7c6fdSAlex Wilson 	 * Then the "default" entry which we enable when we have no VLAN IDs
1134ebb7c6fdSAlex Wilson 	 * added to the group (we start with this enabled).
1135ebb7c6fdSAlex Wilson 	 */
1136ebb7c6fdSAlex Wilson 	fg = kmem_zalloc(sizeof (mlxcx_flow_group_t), KM_SLEEP);
1137ebb7c6fdSAlex Wilson 	g->mlg_rx_vlan_def_fg = fg;
1138ebb7c6fdSAlex Wilson 	list_insert_tail(&ft->mlft_groups, fg);
1139ebb7c6fdSAlex Wilson 	fg->mlfg_table = ft;
1140ebb7c6fdSAlex Wilson 	fg->mlfg_size = 1;
1141ebb7c6fdSAlex Wilson 	if (!mlxcx_setup_flow_group(mlxp, ft, fg)) {
1142ebb7c6fdSAlex Wilson 		mutex_exit(&ft->mlft_mtx);
1143ebb7c6fdSAlex Wilson 		mutex_exit(&g->mlg_mtx);
1144ebb7c6fdSAlex Wilson 		return (B_FALSE);
1145ebb7c6fdSAlex Wilson 	}
1146ebb7c6fdSAlex Wilson 	fe = list_head(&fg->mlfg_entries);
1147ebb7c6fdSAlex Wilson 	if (!mlxcx_cmd_set_flow_table_entry(mlxp, fe)) {
1148ebb7c6fdSAlex Wilson 		mutex_exit(&ft->mlft_mtx);
1149ebb7c6fdSAlex Wilson 		mutex_exit(&g->mlg_mtx);
1150ebb7c6fdSAlex Wilson 		return (B_FALSE);
1151ebb7c6fdSAlex Wilson 	}
1152ebb7c6fdSAlex Wilson 
1153ebb7c6fdSAlex Wilson 	/*
1154ebb7c6fdSAlex Wilson 	 * Finally, the promisc entry which points at the *hash ft* from the
1155ebb7c6fdSAlex Wilson 	 * default group. We only enable this when we have promisc on.
1156ebb7c6fdSAlex Wilson 	 */
1157ebb7c6fdSAlex Wilson 	fg = kmem_zalloc(sizeof (mlxcx_flow_group_t), KM_SLEEP);
1158ebb7c6fdSAlex Wilson 	g->mlg_rx_vlan_promisc_fg = fg;
1159ebb7c6fdSAlex Wilson 	list_insert_tail(&ft->mlft_groups, fg);
1160ebb7c6fdSAlex Wilson 	fg->mlfg_table = ft;
1161ebb7c6fdSAlex Wilson 	fg->mlfg_size = 1;
1162ebb7c6fdSAlex Wilson 	if (!mlxcx_setup_flow_group(mlxp, ft, fg)) {
1163ebb7c6fdSAlex Wilson 		mutex_exit(&ft->mlft_mtx);
1164ebb7c6fdSAlex Wilson 		mutex_exit(&g->mlg_mtx);
1165ebb7c6fdSAlex Wilson 		return (B_FALSE);
1166ebb7c6fdSAlex Wilson 	}
1167ebb7c6fdSAlex Wilson 	fe = list_head(&fg->mlfg_entries);
1168ebb7c6fdSAlex Wilson 	fe->mlfe_ndest = 1;
1169ebb7c6fdSAlex Wilson 	fe->mlfe_dest[0].mlfed_flow = mlxp->mlx_rx_groups[0].mlg_rx_hash_ft;
1170ebb7c6fdSAlex Wilson 
1171ebb7c6fdSAlex Wilson 	mutex_exit(&ft->mlft_mtx);
1172ebb7c6fdSAlex Wilson 
1173ebb7c6fdSAlex Wilson 	mutex_exit(&g->mlg_mtx);
1174ebb7c6fdSAlex Wilson 
1175ebb7c6fdSAlex Wilson 	return (B_TRUE);
1176ebb7c6fdSAlex Wilson }
1177ebb7c6fdSAlex Wilson 
1178ebb7c6fdSAlex Wilson boolean_t
mlxcx_rx_ring_start(mlxcx_t * mlxp,mlxcx_ring_group_t * g,mlxcx_work_queue_t * rq)1179ebb7c6fdSAlex Wilson mlxcx_rx_ring_start(mlxcx_t *mlxp, mlxcx_ring_group_t *g,
1180ebb7c6fdSAlex Wilson     mlxcx_work_queue_t *rq)
1181ebb7c6fdSAlex Wilson {
1182ebb7c6fdSAlex Wilson 	uint_t j;
1183ebb7c6fdSAlex Wilson 	mlxcx_buffer_t *b;
1184ebb7c6fdSAlex Wilson 	mlxcx_completion_queue_t *cq;
1185ebb7c6fdSAlex Wilson 
1186ebb7c6fdSAlex Wilson 	mutex_enter(&g->mlg_mtx);
1187ebb7c6fdSAlex Wilson 	/*
1188ebb7c6fdSAlex Wilson 	 * Sadly, even though MAC has the mgi_start callback, it is not always
1189ebb7c6fdSAlex Wilson 	 * called -- in particular when we are being managed under an aggr, the
1190ebb7c6fdSAlex Wilson 	 * mgi_start callback will only ever be called on the default group.
1191ebb7c6fdSAlex Wilson 	 *
1192ebb7c6fdSAlex Wilson 	 * So instead of asserting about the group state here, we have to
1193ebb7c6fdSAlex Wilson 	 * check it and call group start if needed.
1194ebb7c6fdSAlex Wilson 	 */
1195ebb7c6fdSAlex Wilson 	if (!(g->mlg_state & MLXCX_GROUP_RUNNING)) {
1196ebb7c6fdSAlex Wilson 		mutex_exit(&g->mlg_mtx);
1197ebb7c6fdSAlex Wilson 		if (!mlxcx_rx_group_start(mlxp, g))
1198ebb7c6fdSAlex Wilson 			return (B_FALSE);
1199ebb7c6fdSAlex Wilson 		mutex_enter(&g->mlg_mtx);
1200ebb7c6fdSAlex Wilson 	}
1201ebb7c6fdSAlex Wilson 	ASSERT(g->mlg_state & MLXCX_GROUP_RUNNING);
1202ebb7c6fdSAlex Wilson 
1203ebb7c6fdSAlex Wilson 	cq = rq->mlwq_cq;
1204ebb7c6fdSAlex Wilson 	ASSERT(cq != NULL);
1205ebb7c6fdSAlex Wilson 
1206ebb7c6fdSAlex Wilson 	mutex_enter(&cq->mlcq_mtx);
1207ebb7c6fdSAlex Wilson 	mutex_enter(&rq->mlwq_mtx);
1208ebb7c6fdSAlex Wilson 
1209ebb7c6fdSAlex Wilson 	if (rq->mlwq_state & MLXCX_WQ_STARTED) {
1210ebb7c6fdSAlex Wilson 		mutex_exit(&rq->mlwq_mtx);
1211ebb7c6fdSAlex Wilson 		mutex_exit(&cq->mlcq_mtx);
1212ebb7c6fdSAlex Wilson 		mutex_exit(&g->mlg_mtx);
1213ebb7c6fdSAlex Wilson 		return (B_TRUE);
1214ebb7c6fdSAlex Wilson 	}
1215ebb7c6fdSAlex Wilson 
1216ebb7c6fdSAlex Wilson 	if (!mlxcx_cmd_start_rq(mlxp, rq)) {
1217ebb7c6fdSAlex Wilson 		mutex_exit(&rq->mlwq_mtx);
1218ebb7c6fdSAlex Wilson 		mutex_exit(&cq->mlcq_mtx);
1219ebb7c6fdSAlex Wilson 		mutex_exit(&g->mlg_mtx);
1220ebb7c6fdSAlex Wilson 		return (B_FALSE);
1221ebb7c6fdSAlex Wilson 	}
1222ebb7c6fdSAlex Wilson 	ASSERT(rq->mlwq_state & MLXCX_WQ_STARTED);
1223ebb7c6fdSAlex Wilson 
1224ebb7c6fdSAlex Wilson 	ASSERT0(rq->mlwq_state & MLXCX_WQ_BUFFERS);
1225ebb7c6fdSAlex Wilson 	rq->mlwq_state |= MLXCX_WQ_BUFFERS;
1226ebb7c6fdSAlex Wilson 
122719325e87SPaul Winder 	mlxcx_shard_ready(rq->mlwq_bufs);
122819325e87SPaul Winder 
1229ebb7c6fdSAlex Wilson 	for (j = 0; j < rq->mlwq_nents; ++j) {
1230ebb7c6fdSAlex Wilson 		if (!mlxcx_buf_create(mlxp, rq->mlwq_bufs, &b))
1231ebb7c6fdSAlex Wilson 			break;
1232ebb7c6fdSAlex Wilson 		mlxcx_buf_return(mlxp, b);
1233ebb7c6fdSAlex Wilson 	}
1234ebb7c6fdSAlex Wilson 	for (j = 0; j < rq->mlwq_nents / 2; ++j) {
1235ebb7c6fdSAlex Wilson 		if (!mlxcx_buf_create(mlxp, rq->mlwq_bufs, &b))
1236ebb7c6fdSAlex Wilson 			break;
1237ebb7c6fdSAlex Wilson 		mlxcx_buf_return(mlxp, b);
1238ebb7c6fdSAlex Wilson 	}
1239ebb7c6fdSAlex Wilson 
1240ebb7c6fdSAlex Wilson 	mlxcx_rq_refill(mlxp, rq);
1241ebb7c6fdSAlex Wilson 
1242ebb7c6fdSAlex Wilson 	mutex_exit(&rq->mlwq_mtx);
1243ebb7c6fdSAlex Wilson 	mutex_exit(&cq->mlcq_mtx);
1244ebb7c6fdSAlex Wilson 	mutex_exit(&g->mlg_mtx);
1245ebb7c6fdSAlex Wilson 
1246ebb7c6fdSAlex Wilson 	return (B_TRUE);
1247ebb7c6fdSAlex Wilson }
1248ebb7c6fdSAlex Wilson 
1249ebb7c6fdSAlex Wilson boolean_t
mlxcx_rx_group_start(mlxcx_t * mlxp,mlxcx_ring_group_t * g)1250ebb7c6fdSAlex Wilson mlxcx_rx_group_start(mlxcx_t *mlxp, mlxcx_ring_group_t *g)
1251ebb7c6fdSAlex Wilson {
1252ebb7c6fdSAlex Wilson 	mlxcx_flow_table_t *ft;
1253ebb7c6fdSAlex Wilson 	mlxcx_flow_group_t *fg;
1254ebb7c6fdSAlex Wilson 	mlxcx_flow_entry_t *fe;
125522d05228SPaul Winder 	char tq_name[TASKQ_NAMELEN];
1256ebb7c6fdSAlex Wilson 
1257ebb7c6fdSAlex Wilson 	mutex_enter(&g->mlg_mtx);
1258ebb7c6fdSAlex Wilson 
1259ebb7c6fdSAlex Wilson 	if (g->mlg_state & MLXCX_GROUP_RUNNING) {
1260ebb7c6fdSAlex Wilson 		mutex_exit(&g->mlg_mtx);
1261ebb7c6fdSAlex Wilson 		return (B_TRUE);
1262ebb7c6fdSAlex Wilson 	}
1263ebb7c6fdSAlex Wilson 
1264ebb7c6fdSAlex Wilson 	ASSERT0(g->mlg_state & MLXCX_GROUP_RUNNING);
1265ebb7c6fdSAlex Wilson 
1266ebb7c6fdSAlex Wilson 	g->mlg_state |= MLXCX_GROUP_RUNNING;
1267ebb7c6fdSAlex Wilson 
1268fd7fa860SDan McDonald 	(void) snprintf(tq_name, sizeof (tq_name), "%s_refill_%d_%ld",
126922d05228SPaul Winder 	    ddi_driver_name(mlxp->mlx_dip), mlxp->mlx_inst,
127022d05228SPaul Winder 	    g - &mlxp->mlx_rx_groups[0]);
127122d05228SPaul Winder 
127222d05228SPaul Winder 	/*
127322d05228SPaul Winder 	 * Create one refill taskq per group with one thread per work queue.
127422d05228SPaul Winder 	 * The refill task may block waiting for resources, so by effectively
127522d05228SPaul Winder 	 * having one thread per work queue we avoid work queues blocking each
127622d05228SPaul Winder 	 * other.
127722d05228SPaul Winder 	 */
127822d05228SPaul Winder 	if ((g->mlg_refill_tq = taskq_create(tq_name, g->mlg_nwqs, minclsyspri,
127922d05228SPaul Winder 	    g->mlg_nwqs, INT_MAX, TASKQ_PREPOPULATE)) == NULL) {
128022d05228SPaul Winder 		mlxcx_warn(mlxp, "failed to create rq refill task queue");
128122d05228SPaul Winder 		mutex_exit(&g->mlg_mtx);
128222d05228SPaul Winder 		return (B_FALSE);
128322d05228SPaul Winder 	}
128422d05228SPaul Winder 
1285ebb7c6fdSAlex Wilson 	if (g == &mlxp->mlx_rx_groups[0]) {
1286ebb7c6fdSAlex Wilson 		ft = g->mlg_port->mlp_rx_flow;
1287ebb7c6fdSAlex Wilson 		mutex_enter(&ft->mlft_mtx);
1288ebb7c6fdSAlex Wilson 
1289ebb7c6fdSAlex Wilson 		/*
1290ebb7c6fdSAlex Wilson 		 * Broadcast and promisc entries go directly to group 0's
1291ebb7c6fdSAlex Wilson 		 * RSS hash fanout flow table. They bypass VLAN filtering.
1292ebb7c6fdSAlex Wilson 		 */
1293ebb7c6fdSAlex Wilson 		fg = g->mlg_port->mlp_bcast;
1294ebb7c6fdSAlex Wilson 		fe = list_head(&fg->mlfg_entries);
1295ebb7c6fdSAlex Wilson 		fe->mlfe_dest[fe->mlfe_ndest++].mlfed_flow = g->mlg_rx_hash_ft;
1296ebb7c6fdSAlex Wilson 		if (!mlxcx_cmd_set_flow_table_entry(mlxp, fe)) {
1297ebb7c6fdSAlex Wilson 			mutex_exit(&ft->mlft_mtx);
129822d05228SPaul Winder 			g->mlg_state &= ~MLXCX_GROUP_RUNNING;
129922d05228SPaul Winder 			taskq_destroy(g->mlg_refill_tq);
1300ebb7c6fdSAlex Wilson 			mutex_exit(&g->mlg_mtx);
1301ebb7c6fdSAlex Wilson 			return (B_FALSE);
1302ebb7c6fdSAlex Wilson 		}
1303ebb7c6fdSAlex Wilson 
1304ebb7c6fdSAlex Wilson 		fg = g->mlg_port->mlp_promisc;
1305ebb7c6fdSAlex Wilson 		fe = list_head(&fg->mlfg_entries);
1306ebb7c6fdSAlex Wilson 		fe->mlfe_dest[fe->mlfe_ndest++].mlfed_flow = g->mlg_rx_hash_ft;
1307ebb7c6fdSAlex Wilson 		/*
1308ebb7c6fdSAlex Wilson 		 * Don't actually set the promisc entry until promisc is
1309ebb7c6fdSAlex Wilson 		 * enabled.
1310ebb7c6fdSAlex Wilson 		 */
1311ebb7c6fdSAlex Wilson 
1312ebb7c6fdSAlex Wilson 		mutex_exit(&ft->mlft_mtx);
1313ebb7c6fdSAlex Wilson 	}
1314ebb7c6fdSAlex Wilson 
1315ebb7c6fdSAlex Wilson 	mutex_exit(&g->mlg_mtx);
1316ebb7c6fdSAlex Wilson 
1317ebb7c6fdSAlex Wilson 	return (B_TRUE);
1318ebb7c6fdSAlex Wilson }
1319ebb7c6fdSAlex Wilson 
1320ebb7c6fdSAlex Wilson boolean_t
mlxcx_tx_group_setup(mlxcx_t * mlxp,mlxcx_ring_group_t * g)1321ebb7c6fdSAlex Wilson mlxcx_tx_group_setup(mlxcx_t *mlxp, mlxcx_ring_group_t *g)
1322ebb7c6fdSAlex Wilson {
1323ebb7c6fdSAlex Wilson 	mlxcx_event_queue_t *eq;
1324ebb7c6fdSAlex Wilson 	mlxcx_completion_queue_t *cq;
1325ebb7c6fdSAlex Wilson 	mlxcx_work_queue_t *sq;
1326ebb7c6fdSAlex Wilson 	uint_t i;
1327ebb7c6fdSAlex Wilson 
1328ebb7c6fdSAlex Wilson 	ASSERT3S(g->mlg_state, ==, 0);
1329ebb7c6fdSAlex Wilson 
1330ebb7c6fdSAlex Wilson 	mutex_init(&g->mlg_mtx, NULL, MUTEX_DRIVER,
1331ebb7c6fdSAlex Wilson 	    DDI_INTR_PRI(mlxp->mlx_intr_pri));
1332ebb7c6fdSAlex Wilson 	g->mlg_state |= MLXCX_GROUP_INIT;
1333ebb7c6fdSAlex Wilson 	mutex_enter(&g->mlg_mtx);
1334ebb7c6fdSAlex Wilson 
1335ebb7c6fdSAlex Wilson 	g->mlg_mlx = mlxp;
1336ebb7c6fdSAlex Wilson 	g->mlg_type = MLXCX_GROUP_TX;
1337ebb7c6fdSAlex Wilson 	g->mlg_port = &mlxp->mlx_ports[0];
1338ebb7c6fdSAlex Wilson 
1339ebb7c6fdSAlex Wilson 	g->mlg_nwqs = mlxp->mlx_props.mldp_tx_nrings_per_group;
1340ebb7c6fdSAlex Wilson 	g->mlg_wqs_size = g->mlg_nwqs * sizeof (mlxcx_work_queue_t);
1341ebb7c6fdSAlex Wilson 	g->mlg_wqs = kmem_zalloc(g->mlg_wqs_size, KM_SLEEP);
1342ebb7c6fdSAlex Wilson 	g->mlg_state |= MLXCX_GROUP_WQS;
1343ebb7c6fdSAlex Wilson 
1344ebb7c6fdSAlex Wilson 	g->mlg_tis.mltis_tdom = &mlxp->mlx_tdom;
1345ebb7c6fdSAlex Wilson 
1346ebb7c6fdSAlex Wilson 	if (!mlxcx_cmd_create_tis(mlxp, &g->mlg_tis)) {
1347ebb7c6fdSAlex Wilson 		mutex_exit(&g->mlg_mtx);
1348ebb7c6fdSAlex Wilson 		return (B_FALSE);
1349ebb7c6fdSAlex Wilson 	}
1350ebb7c6fdSAlex Wilson 
1351ebb7c6fdSAlex Wilson 	g->mlg_state |= MLXCX_GROUP_TIRTIS;
1352ebb7c6fdSAlex Wilson 
1353ebb7c6fdSAlex Wilson 	for (i = 0; i < g->mlg_nwqs; ++i) {
1354ebb7c6fdSAlex Wilson 		eq = NULL;
1355ebb7c6fdSAlex Wilson 		while (eq == NULL) {
1356ebb7c6fdSAlex Wilson 			eq = &mlxp->mlx_eqs[mlxp->mlx_next_eq++];
1357ebb7c6fdSAlex Wilson 			if (mlxp->mlx_next_eq >= mlxp->mlx_intr_count)
13585f0e3176SPaul Winder 				mlxp->mlx_next_eq = mlxp->mlx_intr_cq0;
1359ebb7c6fdSAlex Wilson 			if (eq->mleq_type != MLXCX_EQ_TYPE_ANY &&
1360ebb7c6fdSAlex Wilson 			    eq->mleq_type != MLXCX_EQ_TYPE_TX) {
1361ebb7c6fdSAlex Wilson 				/* Try the next one */
1362ebb7c6fdSAlex Wilson 				eq = NULL;
1363ebb7c6fdSAlex Wilson 			}
1364ebb7c6fdSAlex Wilson 		}
1365ebb7c6fdSAlex Wilson 
136622d05228SPaul Winder 		if (!mlxcx_cq_setup(mlxp, eq, &cq,
136722d05228SPaul Winder 		    mlxp->mlx_props.mldp_cq_size_shift))
1368ebb7c6fdSAlex Wilson 			return (B_FALSE);
136922d05228SPaul Winder 
1370ebb7c6fdSAlex Wilson 		cq->mlcq_stats = &g->mlg_port->mlp_stats;
1371ebb7c6fdSAlex Wilson 
1372ebb7c6fdSAlex Wilson 		sq = &g->mlg_wqs[i];
1373ebb7c6fdSAlex Wilson 		if (!mlxcx_sq_setup(mlxp, g->mlg_port, cq, &g->mlg_tis, sq)) {
1374ebb7c6fdSAlex Wilson 			mutex_exit(&g->mlg_mtx);
1375ebb7c6fdSAlex Wilson 			return (B_FALSE);
1376ebb7c6fdSAlex Wilson 		}
1377ebb7c6fdSAlex Wilson 		sq->mlwq_group = g;
1378ebb7c6fdSAlex Wilson 	}
1379ebb7c6fdSAlex Wilson 
1380ebb7c6fdSAlex Wilson 	mutex_exit(&g->mlg_mtx);
1381ebb7c6fdSAlex Wilson 
1382ebb7c6fdSAlex Wilson 	return (B_TRUE);
1383ebb7c6fdSAlex Wilson }
1384ebb7c6fdSAlex Wilson 
1385ebb7c6fdSAlex Wilson boolean_t
mlxcx_tx_ring_start(mlxcx_t * mlxp,mlxcx_ring_group_t * g,mlxcx_work_queue_t * sq)1386ebb7c6fdSAlex Wilson mlxcx_tx_ring_start(mlxcx_t *mlxp, mlxcx_ring_group_t *g,
1387ebb7c6fdSAlex Wilson     mlxcx_work_queue_t *sq)
1388ebb7c6fdSAlex Wilson {
1389ebb7c6fdSAlex Wilson 	uint_t i;
1390ebb7c6fdSAlex Wilson 	mlxcx_buffer_t *b;
1391ebb7c6fdSAlex Wilson 	mlxcx_completion_queue_t *cq;
1392ebb7c6fdSAlex Wilson 
1393ebb7c6fdSAlex Wilson 	mutex_enter(&g->mlg_mtx);
1394ebb7c6fdSAlex Wilson 
1395ebb7c6fdSAlex Wilson 	cq = sq->mlwq_cq;
1396ebb7c6fdSAlex Wilson 	ASSERT(cq != NULL);
1397ebb7c6fdSAlex Wilson 
1398ebb7c6fdSAlex Wilson 	mutex_enter(&cq->mlcq_mtx);
1399ebb7c6fdSAlex Wilson 	mutex_enter(&sq->mlwq_mtx);
1400ebb7c6fdSAlex Wilson 	if (sq->mlwq_state & MLXCX_WQ_STARTED) {
1401ebb7c6fdSAlex Wilson 		mutex_exit(&sq->mlwq_mtx);
1402ebb7c6fdSAlex Wilson 		mutex_exit(&cq->mlcq_mtx);
1403ebb7c6fdSAlex Wilson 		mutex_exit(&g->mlg_mtx);
1404ebb7c6fdSAlex Wilson 		return (B_TRUE);
1405ebb7c6fdSAlex Wilson 	}
1406ebb7c6fdSAlex Wilson 
1407ebb7c6fdSAlex Wilson 	ASSERT0(sq->mlwq_state & MLXCX_WQ_BUFFERS);
1408ebb7c6fdSAlex Wilson 	for (i = 0; i < sq->mlwq_nents; ++i) {
1409ebb7c6fdSAlex Wilson 		if (!mlxcx_buf_create_foreign(mlxp, sq->mlwq_foreign_bufs, &b))
1410ebb7c6fdSAlex Wilson 			break;
1411ebb7c6fdSAlex Wilson 		mlxcx_buf_return(mlxp, b);
1412ebb7c6fdSAlex Wilson 	}
1413ebb7c6fdSAlex Wilson 	for (i = 0; i < sq->mlwq_nents / 2; ++i) {
1414ebb7c6fdSAlex Wilson 		if (!mlxcx_buf_create_foreign(mlxp, sq->mlwq_foreign_bufs, &b))
1415ebb7c6fdSAlex Wilson 			break;
1416ebb7c6fdSAlex Wilson 		mlxcx_buf_return(mlxp, b);
1417ebb7c6fdSAlex Wilson 	}
1418ebb7c6fdSAlex Wilson 	for (i = 0; i < sq->mlwq_nents; ++i) {
1419ebb7c6fdSAlex Wilson 		if (!mlxcx_buf_create(mlxp, sq->mlwq_bufs, &b))
1420ebb7c6fdSAlex Wilson 			break;
1421ebb7c6fdSAlex Wilson 		mlxcx_buf_return(mlxp, b);
1422ebb7c6fdSAlex Wilson 	}
1423ebb7c6fdSAlex Wilson 	sq->mlwq_state |= MLXCX_WQ_BUFFERS;
1424ebb7c6fdSAlex Wilson 
142519325e87SPaul Winder 	mlxcx_shard_ready(sq->mlwq_bufs);
142619325e87SPaul Winder 	mlxcx_shard_ready(sq->mlwq_foreign_bufs);
142719325e87SPaul Winder 
1428ebb7c6fdSAlex Wilson 	if (!mlxcx_cmd_start_sq(mlxp, sq)) {
1429ebb7c6fdSAlex Wilson 		mutex_exit(&sq->mlwq_mtx);
1430ebb7c6fdSAlex Wilson 		mutex_exit(&cq->mlcq_mtx);
1431ebb7c6fdSAlex Wilson 		mutex_exit(&g->mlg_mtx);
1432ebb7c6fdSAlex Wilson 		return (B_FALSE);
1433ebb7c6fdSAlex Wilson 	}
1434ebb7c6fdSAlex Wilson 	g->mlg_state |= MLXCX_GROUP_RUNNING;
1435ebb7c6fdSAlex Wilson 
1436ebb7c6fdSAlex Wilson 	(void) mlxcx_sq_add_nop(mlxp, sq);
1437ebb7c6fdSAlex Wilson 
1438ebb7c6fdSAlex Wilson 	mutex_exit(&sq->mlwq_mtx);
1439ebb7c6fdSAlex Wilson 	mutex_exit(&cq->mlcq_mtx);
1440ebb7c6fdSAlex Wilson 	mutex_exit(&g->mlg_mtx);
1441ebb7c6fdSAlex Wilson 
1442ebb7c6fdSAlex Wilson 	return (B_TRUE);
1443ebb7c6fdSAlex Wilson }
1444ebb7c6fdSAlex Wilson 
1445ebb7c6fdSAlex Wilson static boolean_t
mlxcx_sq_ring_dbell(mlxcx_t * mlxp,mlxcx_work_queue_t * mlwq,uint_t first)1446ebb7c6fdSAlex Wilson mlxcx_sq_ring_dbell(mlxcx_t *mlxp, mlxcx_work_queue_t *mlwq, uint_t first)
1447ebb7c6fdSAlex Wilson {
1448ebb7c6fdSAlex Wilson 	uint_t idx;
1449ebb7c6fdSAlex Wilson 	mlxcx_bf_t *bf;
1450ebb7c6fdSAlex Wilson 	ddi_fm_error_t err;
1451ebb7c6fdSAlex Wilson 	uint_t try = 0;
1452ebb7c6fdSAlex Wilson 
1453ebb7c6fdSAlex Wilson 	ASSERT3U(mlwq->mlwq_type, ==, MLXCX_WQ_TYPE_SENDQ);
1454ebb7c6fdSAlex Wilson 	ASSERT(mutex_owned(&mlwq->mlwq_mtx));
1455ebb7c6fdSAlex Wilson 
1456ebb7c6fdSAlex Wilson 	mlwq->mlwq_doorbell->mlwqd_send_counter = to_be16(mlwq->mlwq_pc);
1457ebb7c6fdSAlex Wilson 
1458ebb7c6fdSAlex Wilson 	ASSERT(mlwq->mlwq_cq != NULL);
1459ebb7c6fdSAlex Wilson 	ASSERT(mlwq->mlwq_cq->mlcq_eq != NULL);
1460ebb7c6fdSAlex Wilson 	idx = mlwq->mlwq_cq->mlcq_eq->mleq_intr_index & MLXCX_BF_PER_UAR_MASK;
1461ebb7c6fdSAlex Wilson 	bf = &mlwq->mlwq_uar->mlu_bf[idx];
1462ebb7c6fdSAlex Wilson 
1463ebb7c6fdSAlex Wilson retry:
1464ebb7c6fdSAlex Wilson 	MLXCX_DMA_SYNC(mlwq->mlwq_doorbell_dma, DDI_DMA_SYNC_FORDEV);
1465ebb7c6fdSAlex Wilson 	ddi_fm_dma_err_get(mlwq->mlwq_doorbell_dma.mxdb_dma_handle, &err,
1466ebb7c6fdSAlex Wilson 	    DDI_FME_VERSION);
1467ebb7c6fdSAlex Wilson 	if (err.fme_status != DDI_FM_OK) {
1468ebb7c6fdSAlex Wilson 		if (try++ < mlxcx_doorbell_tries) {
1469ebb7c6fdSAlex Wilson 			ddi_fm_dma_err_clear(
1470ebb7c6fdSAlex Wilson 			    mlwq->mlwq_doorbell_dma.mxdb_dma_handle,
1471ebb7c6fdSAlex Wilson 			    DDI_FME_VERSION);
1472ebb7c6fdSAlex Wilson 			goto retry;
1473ebb7c6fdSAlex Wilson 		} else {
1474ebb7c6fdSAlex Wilson 			goto err;
1475ebb7c6fdSAlex Wilson 		}
1476ebb7c6fdSAlex Wilson 	}
1477ebb7c6fdSAlex Wilson 
1478ebb7c6fdSAlex Wilson 	mlxcx_put64(mlxp, bf->mbf_even, from_be64(
1479ebb7c6fdSAlex Wilson 	    mlwq->mlwq_bf_ent[first].mlsqbf_qwords[0]));
1480ebb7c6fdSAlex Wilson 	ddi_fm_acc_err_get(mlxp->mlx_regs_handle, &err,
1481ebb7c6fdSAlex Wilson 	    DDI_FME_VERSION);
1482ebb7c6fdSAlex Wilson 	if (err.fme_status == DDI_FM_OK)
1483ebb7c6fdSAlex Wilson 		return (B_TRUE);
1484ebb7c6fdSAlex Wilson 	if (try++ < mlxcx_doorbell_tries) {
1485ebb7c6fdSAlex Wilson 		ddi_fm_acc_err_clear(mlxp->mlx_regs_handle, DDI_FME_VERSION);
1486ebb7c6fdSAlex Wilson 		goto retry;
1487ebb7c6fdSAlex Wilson 	}
1488ebb7c6fdSAlex Wilson 
1489ebb7c6fdSAlex Wilson err:
1490ebb7c6fdSAlex Wilson 	ddi_fm_service_impact(mlxp->mlx_dip, DDI_SERVICE_LOST);
1491ebb7c6fdSAlex Wilson 	return (B_FALSE);
1492ebb7c6fdSAlex Wilson }
1493ebb7c6fdSAlex Wilson 
1494ebb7c6fdSAlex Wilson boolean_t
mlxcx_sq_add_nop(mlxcx_t * mlxp,mlxcx_work_queue_t * mlwq)1495ebb7c6fdSAlex Wilson mlxcx_sq_add_nop(mlxcx_t *mlxp, mlxcx_work_queue_t *mlwq)
1496ebb7c6fdSAlex Wilson {
1497ebb7c6fdSAlex Wilson 	uint_t index, start_pc;
1498ebb7c6fdSAlex Wilson 	mlxcx_sendq_ent_t *ent0;
1499ebb7c6fdSAlex Wilson 	ddi_fm_error_t err;
1500ebb7c6fdSAlex Wilson 
1501ebb7c6fdSAlex Wilson 	ASSERT(mutex_owned(&mlwq->mlwq_mtx));
1502ebb7c6fdSAlex Wilson 
1503ebb7c6fdSAlex Wilson 	index = mlwq->mlwq_pc & (mlwq->mlwq_nents - 1);
1504ebb7c6fdSAlex Wilson 	ent0 = &mlwq->mlwq_send_ent[index];
1505ebb7c6fdSAlex Wilson 	start_pc = mlwq->mlwq_pc;
1506ebb7c6fdSAlex Wilson 	++mlwq->mlwq_pc;
150722d05228SPaul Winder 	/*
150822d05228SPaul Winder 	 * This counter is manipulated in the interrupt handler, which
150922d05228SPaul Winder 	 * does not hold the mlwq_mtx, hence the atomic.
151022d05228SPaul Winder 	 */
151122d05228SPaul Winder 	atomic_inc_64(&mlwq->mlwq_wqebb_used);
1512ebb7c6fdSAlex Wilson 
1513ebb7c6fdSAlex Wilson 	bzero(ent0, sizeof (mlxcx_sendq_ent_t));
1514ebb7c6fdSAlex Wilson 	ent0->mlsqe_control.mlcs_opcode = MLXCX_WQE_OP_NOP;
1515ebb7c6fdSAlex Wilson 	ent0->mlsqe_control.mlcs_qp_or_sq = to_be24(mlwq->mlwq_num);
1516ebb7c6fdSAlex Wilson 	ent0->mlsqe_control.mlcs_wqe_index = to_be16(start_pc);
1517ebb7c6fdSAlex Wilson 
1518ebb7c6fdSAlex Wilson 	set_bits8(&ent0->mlsqe_control.mlcs_flags,
1519ebb7c6fdSAlex Wilson 	    MLXCX_SQE_FENCE_MODE, MLXCX_SQE_FENCE_NONE);
1520ebb7c6fdSAlex Wilson 	set_bits8(&ent0->mlsqe_control.mlcs_flags,
1521ebb7c6fdSAlex Wilson 	    MLXCX_SQE_COMPLETION_MODE, MLXCX_SQE_CQE_ALWAYS);
1522ebb7c6fdSAlex Wilson 
1523ebb7c6fdSAlex Wilson 	ent0->mlsqe_control.mlcs_ds = 1;
1524ebb7c6fdSAlex Wilson 
1525ebb7c6fdSAlex Wilson 	VERIFY0(ddi_dma_sync(mlwq->mlwq_dma.mxdb_dma_handle,
1526ebb7c6fdSAlex Wilson 	    (uintptr_t)ent0 - (uintptr_t)mlwq->mlwq_send_ent,
1527ebb7c6fdSAlex Wilson 	    sizeof (mlxcx_sendq_ent_t), DDI_DMA_SYNC_FORDEV));
1528ebb7c6fdSAlex Wilson 	ddi_fm_dma_err_get(mlwq->mlwq_dma.mxdb_dma_handle, &err,
1529ebb7c6fdSAlex Wilson 	    DDI_FME_VERSION);
1530ebb7c6fdSAlex Wilson 	if (err.fme_status != DDI_FM_OK) {
1531ebb7c6fdSAlex Wilson 		return (B_FALSE);
1532ebb7c6fdSAlex Wilson 	}
1533ebb7c6fdSAlex Wilson 	if (!mlxcx_sq_ring_dbell(mlxp, mlwq, index)) {
1534ebb7c6fdSAlex Wilson 		return (B_FALSE);
1535ebb7c6fdSAlex Wilson 	}
1536ebb7c6fdSAlex Wilson 	return (B_TRUE);
1537ebb7c6fdSAlex Wilson }
1538ebb7c6fdSAlex Wilson 
1539ebb7c6fdSAlex Wilson boolean_t
mlxcx_sq_add_buffer(mlxcx_t * mlxp,mlxcx_work_queue_t * mlwq,uint8_t * inlinehdrs,size_t inlinelen,uint32_t chkflags,mlxcx_buffer_t * b0)1540ebb7c6fdSAlex Wilson mlxcx_sq_add_buffer(mlxcx_t *mlxp, mlxcx_work_queue_t *mlwq,
1541ebb7c6fdSAlex Wilson     uint8_t *inlinehdrs, size_t inlinelen, uint32_t chkflags,
1542ebb7c6fdSAlex Wilson     mlxcx_buffer_t *b0)
1543ebb7c6fdSAlex Wilson {
154422d05228SPaul Winder 	uint_t index, first, ents;
1545ebb7c6fdSAlex Wilson 	mlxcx_completion_queue_t *cq;
1546ebb7c6fdSAlex Wilson 	mlxcx_sendq_ent_t *ent0;
1547ebb7c6fdSAlex Wilson 	mlxcx_sendq_extra_ent_t *ent;
1548ebb7c6fdSAlex Wilson 	mlxcx_wqe_data_seg_t *seg;
1549ebb7c6fdSAlex Wilson 	uint_t ptri, nptr;
1550ebb7c6fdSAlex Wilson 	const ddi_dma_cookie_t *c;
1551ebb7c6fdSAlex Wilson 	size_t rem;
155222d05228SPaul Winder 	uint64_t wqebb_used;
1553ebb7c6fdSAlex Wilson 	mlxcx_buffer_t *b;
1554ebb7c6fdSAlex Wilson 	ddi_fm_error_t err;
155522d05228SPaul Winder 	boolean_t rv;
1556ebb7c6fdSAlex Wilson 
1557ebb7c6fdSAlex Wilson 	ASSERT(mutex_owned(&mlwq->mlwq_mtx));
1558ebb7c6fdSAlex Wilson 	ASSERT3P(b0->mlb_tx_head, ==, b0);
1559ebb7c6fdSAlex Wilson 	ASSERT3U(b0->mlb_state, ==, MLXCX_BUFFER_ON_WQ);
1560ebb7c6fdSAlex Wilson 	cq = mlwq->mlwq_cq;
1561ebb7c6fdSAlex Wilson 
1562ebb7c6fdSAlex Wilson 	index = mlwq->mlwq_pc & (mlwq->mlwq_nents - 1);
1563ebb7c6fdSAlex Wilson 	ent0 = &mlwq->mlwq_send_ent[index];
1564ebb7c6fdSAlex Wilson 	b0->mlb_wqe_index = mlwq->mlwq_pc;
156522d05228SPaul Winder 	ents = 1;
1566ebb7c6fdSAlex Wilson 
1567ebb7c6fdSAlex Wilson 	first = index;
1568ebb7c6fdSAlex Wilson 
1569ebb7c6fdSAlex Wilson 	bzero(ent0, sizeof (mlxcx_sendq_ent_t));
1570ebb7c6fdSAlex Wilson 	ent0->mlsqe_control.mlcs_opcode = MLXCX_WQE_OP_SEND;
1571ebb7c6fdSAlex Wilson 	ent0->mlsqe_control.mlcs_qp_or_sq = to_be24(mlwq->mlwq_num);
1572ebb7c6fdSAlex Wilson 	ent0->mlsqe_control.mlcs_wqe_index = to_be16(b0->mlb_wqe_index);
1573ebb7c6fdSAlex Wilson 
1574ebb7c6fdSAlex Wilson 	set_bits8(&ent0->mlsqe_control.mlcs_flags,
1575ebb7c6fdSAlex Wilson 	    MLXCX_SQE_FENCE_MODE, MLXCX_SQE_FENCE_WAIT_OTHERS);
1576ebb7c6fdSAlex Wilson 	set_bits8(&ent0->mlsqe_control.mlcs_flags,
1577ebb7c6fdSAlex Wilson 	    MLXCX_SQE_COMPLETION_MODE, MLXCX_SQE_CQE_ALWAYS);
1578ebb7c6fdSAlex Wilson 
1579ebb7c6fdSAlex Wilson 	VERIFY3U(inlinelen, <=, sizeof (ent0->mlsqe_eth.mles_inline_headers));
1580ebb7c6fdSAlex Wilson 	set_bits16(&ent0->mlsqe_eth.mles_szflags,
1581ebb7c6fdSAlex Wilson 	    MLXCX_SQE_ETH_INLINE_HDR_SZ, inlinelen);
1582ebb7c6fdSAlex Wilson 	if (inlinelen > 0) {
1583ebb7c6fdSAlex Wilson 		bcopy(inlinehdrs, ent0->mlsqe_eth.mles_inline_headers,
1584ebb7c6fdSAlex Wilson 		    inlinelen);
1585ebb7c6fdSAlex Wilson 	}
1586ebb7c6fdSAlex Wilson 
158782b4190eSPaul Winder 	ent0->mlsqe_control.mlcs_ds = offsetof(mlxcx_sendq_ent_t, mlsqe_data) /
158882b4190eSPaul Winder 	    MLXCX_WQE_OCTOWORD;
1589ebb7c6fdSAlex Wilson 
1590ebb7c6fdSAlex Wilson 	if (chkflags & HCK_IPV4_HDRCKSUM) {
1591ebb7c6fdSAlex Wilson 		ASSERT(mlxp->mlx_caps->mlc_checksum);
1592ebb7c6fdSAlex Wilson 		set_bit8(&ent0->mlsqe_eth.mles_csflags,
1593ebb7c6fdSAlex Wilson 		    MLXCX_SQE_ETH_CSFLAG_L3_CHECKSUM);
1594ebb7c6fdSAlex Wilson 	}
1595ebb7c6fdSAlex Wilson 	if (chkflags & HCK_FULLCKSUM) {
1596ebb7c6fdSAlex Wilson 		ASSERT(mlxp->mlx_caps->mlc_checksum);
1597ebb7c6fdSAlex Wilson 		set_bit8(&ent0->mlsqe_eth.mles_csflags,
1598ebb7c6fdSAlex Wilson 		    MLXCX_SQE_ETH_CSFLAG_L4_CHECKSUM);
1599ebb7c6fdSAlex Wilson 	}
1600ebb7c6fdSAlex Wilson 
160122d05228SPaul Winder 	/*
160222d05228SPaul Winder 	 * mlwq_wqebb_used is only incremented whilst holding
160322d05228SPaul Winder 	 * the mlwq_mtx mutex, but it is decremented (atomically) in
160422d05228SPaul Winder 	 * the interrupt context *not* under mlwq_mtx mutex.
160522d05228SPaul Winder 	 * So, now take a snapshot of the number of used wqes which will
160622d05228SPaul Winder 	 * be a conistent maximum we can use whilst iterating through
160722d05228SPaul Winder 	 * the buffers and DMA cookies.
160822d05228SPaul Winder 	 */
160922d05228SPaul Winder 	wqebb_used = mlwq->mlwq_wqebb_used;
161022d05228SPaul Winder 
1611ebb7c6fdSAlex Wilson 	b = b0;
1612ebb7c6fdSAlex Wilson 	ptri = 0;
1613ebb7c6fdSAlex Wilson 	nptr = sizeof (ent0->mlsqe_data) / sizeof (mlxcx_wqe_data_seg_t);
1614ebb7c6fdSAlex Wilson 	seg = ent0->mlsqe_data;
1615ebb7c6fdSAlex Wilson 	while (b != NULL) {
1616ebb7c6fdSAlex Wilson 		rem = b->mlb_used;
1617ebb7c6fdSAlex Wilson 
1618ebb7c6fdSAlex Wilson 		c = NULL;
1619ebb7c6fdSAlex Wilson 		while (rem > 0 &&
1620ebb7c6fdSAlex Wilson 		    (c = mlxcx_dma_cookie_iter(&b->mlb_dma, c)) != NULL) {
1621ebb7c6fdSAlex Wilson 			if (ptri >= nptr) {
162222d05228SPaul Winder 				if ((ents + wqebb_used) >= mlwq->mlwq_nents)
162322d05228SPaul Winder 					return (B_FALSE);
162422d05228SPaul Winder 
162522d05228SPaul Winder 				index = (mlwq->mlwq_pc + ents) &
162622d05228SPaul Winder 				    (mlwq->mlwq_nents - 1);
1627ebb7c6fdSAlex Wilson 				ent = &mlwq->mlwq_send_extra_ent[index];
1628ebb7c6fdSAlex Wilson 				++ents;
1629ebb7c6fdSAlex Wilson 
1630ebb7c6fdSAlex Wilson 				seg = ent->mlsqe_data;
1631ebb7c6fdSAlex Wilson 				ptri = 0;
1632ebb7c6fdSAlex Wilson 				nptr = sizeof (ent->mlsqe_data) /
1633ebb7c6fdSAlex Wilson 				    sizeof (mlxcx_wqe_data_seg_t);
1634ebb7c6fdSAlex Wilson 			}
1635ebb7c6fdSAlex Wilson 
1636ebb7c6fdSAlex Wilson 			seg->mlds_lkey = to_be32(mlxp->mlx_rsvd_lkey);
1637ebb7c6fdSAlex Wilson 			if (c->dmac_size > rem) {
1638ebb7c6fdSAlex Wilson 				seg->mlds_byte_count = to_be32(rem);
1639ebb7c6fdSAlex Wilson 				rem = 0;
1640ebb7c6fdSAlex Wilson 			} else {
1641ebb7c6fdSAlex Wilson 				seg->mlds_byte_count = to_be32(c->dmac_size);
1642ebb7c6fdSAlex Wilson 				rem -= c->dmac_size;
1643ebb7c6fdSAlex Wilson 			}
1644ebb7c6fdSAlex Wilson 			seg->mlds_address = to_be64(c->dmac_laddress);
1645ebb7c6fdSAlex Wilson 			++seg;
1646ebb7c6fdSAlex Wilson 			++ptri;
1647ebb7c6fdSAlex Wilson 			++ent0->mlsqe_control.mlcs_ds;
1648ebb7c6fdSAlex Wilson 
1649ebb7c6fdSAlex Wilson 			ASSERT3U(ent0->mlsqe_control.mlcs_ds, <=,
1650ebb7c6fdSAlex Wilson 			    MLXCX_SQE_MAX_DS);
1651ebb7c6fdSAlex Wilson 		}
1652ebb7c6fdSAlex Wilson 
1653ebb7c6fdSAlex Wilson 		if (b == b0) {
1654ebb7c6fdSAlex Wilson 			b = list_head(&b0->mlb_tx_chain);
1655ebb7c6fdSAlex Wilson 		} else {
1656ebb7c6fdSAlex Wilson 			b = list_next(&b0->mlb_tx_chain, b);
1657ebb7c6fdSAlex Wilson 		}
1658ebb7c6fdSAlex Wilson 	}
1659ebb7c6fdSAlex Wilson 
166022d05228SPaul Winder 	b0->mlb_wqebbs = ents;
166122d05228SPaul Winder 	mlwq->mlwq_pc += ents;
166222d05228SPaul Winder 	atomic_add_64(&mlwq->mlwq_wqebb_used, ents);
166322d05228SPaul Winder 
1664ebb7c6fdSAlex Wilson 	for (; ptri < nptr; ++ptri, ++seg) {
1665ebb7c6fdSAlex Wilson 		seg->mlds_lkey = to_be32(MLXCX_NULL_LKEY);
1666ebb7c6fdSAlex Wilson 		seg->mlds_byte_count = to_be32(0);
1667ebb7c6fdSAlex Wilson 		seg->mlds_address = to_be64(0);
1668ebb7c6fdSAlex Wilson 	}
1669ebb7c6fdSAlex Wilson 
1670ebb7c6fdSAlex Wilson 	/*
1671ebb7c6fdSAlex Wilson 	 * Make sure the workqueue entry is flushed out before updating
1672ebb7c6fdSAlex Wilson 	 * the doorbell.
167382b4190eSPaul Winder 	 * If the ring has wrapped, we need to flush the front and back.
1674ebb7c6fdSAlex Wilson 	 */
167582b4190eSPaul Winder 	if ((first + ents) > mlwq->mlwq_nents) {
167682b4190eSPaul Winder 		uint_t sync_cnt = mlwq->mlwq_nents - first;
167782b4190eSPaul Winder 
167882b4190eSPaul Winder 		VERIFY0(ddi_dma_sync(mlwq->mlwq_dma.mxdb_dma_handle,
167982b4190eSPaul Winder 		    (uintptr_t)ent0 - (uintptr_t)mlwq->mlwq_send_ent,
168082b4190eSPaul Winder 		    sync_cnt * sizeof (mlxcx_sendq_ent_t),
168182b4190eSPaul Winder 		    DDI_DMA_SYNC_FORDEV));
168282b4190eSPaul Winder 
168382b4190eSPaul Winder 		ent0 = &mlwq->mlwq_send_ent[0];
168482b4190eSPaul Winder 		ents -= sync_cnt;
168582b4190eSPaul Winder 	}
168682b4190eSPaul Winder 
1687ebb7c6fdSAlex Wilson 	VERIFY0(ddi_dma_sync(mlwq->mlwq_dma.mxdb_dma_handle,
1688ebb7c6fdSAlex Wilson 	    (uintptr_t)ent0 - (uintptr_t)mlwq->mlwq_send_ent,
1689ebb7c6fdSAlex Wilson 	    ents * sizeof (mlxcx_sendq_ent_t), DDI_DMA_SYNC_FORDEV));
1690ebb7c6fdSAlex Wilson 	ddi_fm_dma_err_get(mlwq->mlwq_dma.mxdb_dma_handle, &err,
1691ebb7c6fdSAlex Wilson 	    DDI_FME_VERSION);
1692ebb7c6fdSAlex Wilson 	if (err.fme_status != DDI_FM_OK) {
1693ebb7c6fdSAlex Wilson 		return (B_FALSE);
1694ebb7c6fdSAlex Wilson 	}
169522d05228SPaul Winder 
169622d05228SPaul Winder 	/*
169722d05228SPaul Winder 	 * Hold the bufmtx whilst ringing the doorbell, to prevent
169822d05228SPaul Winder 	 * the buffer from being moved to another list, so we can
169922d05228SPaul Winder 	 * safely remove it should the ring fail.
170022d05228SPaul Winder 	 */
170122d05228SPaul Winder 	mutex_enter(&cq->mlcq_bufbmtx);
170222d05228SPaul Winder 
170322d05228SPaul Winder 	list_insert_tail(&cq->mlcq_buffers_b, b0);
170422d05228SPaul Winder 	if ((rv = mlxcx_sq_ring_dbell(mlxp, mlwq, first))) {
170522d05228SPaul Winder 		atomic_inc_64(&cq->mlcq_bufcnt);
170622d05228SPaul Winder 	} else {
170722d05228SPaul Winder 		list_remove(&cq->mlcq_buffers_b, b0);
1708ebb7c6fdSAlex Wilson 	}
170922d05228SPaul Winder 
171022d05228SPaul Winder 	mutex_exit(&cq->mlcq_bufbmtx);
171122d05228SPaul Winder 
171222d05228SPaul Winder 	return (rv);
1713ebb7c6fdSAlex Wilson }
1714ebb7c6fdSAlex Wilson 
1715ebb7c6fdSAlex Wilson boolean_t
mlxcx_rq_add_buffer(mlxcx_t * mlxp,mlxcx_work_queue_t * mlwq,mlxcx_buffer_t * buf)1716ebb7c6fdSAlex Wilson mlxcx_rq_add_buffer(mlxcx_t *mlxp, mlxcx_work_queue_t *mlwq,
1717ebb7c6fdSAlex Wilson     mlxcx_buffer_t *buf)
1718ebb7c6fdSAlex Wilson {
1719ebb7c6fdSAlex Wilson 	return (mlxcx_rq_add_buffers(mlxp, mlwq, &buf, 1));
1720ebb7c6fdSAlex Wilson }
1721ebb7c6fdSAlex Wilson 
1722ebb7c6fdSAlex Wilson boolean_t
mlxcx_rq_add_buffers(mlxcx_t * mlxp,mlxcx_work_queue_t * mlwq,mlxcx_buffer_t ** bufs,size_t nbufs)1723ebb7c6fdSAlex Wilson mlxcx_rq_add_buffers(mlxcx_t *mlxp, mlxcx_work_queue_t *mlwq,
1724ebb7c6fdSAlex Wilson     mlxcx_buffer_t **bufs, size_t nbufs)
1725ebb7c6fdSAlex Wilson {
1726ebb7c6fdSAlex Wilson 	uint_t index;
1727ebb7c6fdSAlex Wilson 	mlxcx_recvq_ent_t *ent;
1728ebb7c6fdSAlex Wilson 	mlxcx_completion_queue_t *cq;
1729ebb7c6fdSAlex Wilson 	mlxcx_wqe_data_seg_t *seg;
1730ebb7c6fdSAlex Wilson 	uint_t bi, ptri;
1731ebb7c6fdSAlex Wilson 	const ddi_dma_cookie_t *c;
1732ebb7c6fdSAlex Wilson 	mlxcx_buffer_t *buf;
1733ebb7c6fdSAlex Wilson 	ddi_fm_error_t err;
1734ebb7c6fdSAlex Wilson 
1735ebb7c6fdSAlex Wilson 	ASSERT(mutex_owned(&mlwq->mlwq_mtx));
1736ebb7c6fdSAlex Wilson 	cq = mlwq->mlwq_cq;
1737ebb7c6fdSAlex Wilson 	ASSERT(mutex_owned(&cq->mlcq_mtx));
1738ebb7c6fdSAlex Wilson 
1739ebb7c6fdSAlex Wilson 	for (bi = 0; bi < nbufs; ++bi) {
1740ebb7c6fdSAlex Wilson 		buf = bufs[bi];
1741ebb7c6fdSAlex Wilson 		bufs[bi] = NULL;
1742ebb7c6fdSAlex Wilson 		ASSERT3U(buf->mlb_state, ==, MLXCX_BUFFER_ON_WQ);
1743ebb7c6fdSAlex Wilson 
1744ebb7c6fdSAlex Wilson 		index = mlwq->mlwq_pc & (mlwq->mlwq_nents - 1);
1745ebb7c6fdSAlex Wilson 		ent = &mlwq->mlwq_recv_ent[index];
1746ebb7c6fdSAlex Wilson 		buf->mlb_wqe_index = mlwq->mlwq_pc;
174722d05228SPaul Winder 		buf->mlb_wqebbs = 1;
1748ebb7c6fdSAlex Wilson 
1749ebb7c6fdSAlex Wilson 		++mlwq->mlwq_pc;
175022d05228SPaul Winder 		atomic_inc_64(&mlwq->mlwq_wqebb_used);
1751ebb7c6fdSAlex Wilson 
1752ebb7c6fdSAlex Wilson 		mutex_enter(&cq->mlcq_bufbmtx);
1753ebb7c6fdSAlex Wilson 		list_insert_tail(&cq->mlcq_buffers, buf);
1754ebb7c6fdSAlex Wilson 		atomic_inc_64(&cq->mlcq_bufcnt);
1755ebb7c6fdSAlex Wilson 		mutex_exit(&cq->mlcq_bufbmtx);
1756ebb7c6fdSAlex Wilson 
1757ebb7c6fdSAlex Wilson 		ASSERT3U(buf->mlb_dma.mxdb_ncookies, <=, MLXCX_RECVQ_MAX_PTRS);
1758ebb7c6fdSAlex Wilson 		ptri = 0;
1759ebb7c6fdSAlex Wilson 		c = NULL;
1760ebb7c6fdSAlex Wilson 		while ((c = mlxcx_dma_cookie_iter(&buf->mlb_dma, c)) != NULL) {
1761ebb7c6fdSAlex Wilson 			seg = &ent->mlrqe_data[ptri++];
1762ebb7c6fdSAlex Wilson 			seg->mlds_lkey = to_be32(mlxp->mlx_rsvd_lkey);
1763ebb7c6fdSAlex Wilson 			seg->mlds_byte_count = to_be32(c->dmac_size);
1764ebb7c6fdSAlex Wilson 			seg->mlds_address = to_be64(c->dmac_laddress);
1765ebb7c6fdSAlex Wilson 		}
1766ebb7c6fdSAlex Wilson 		/*
1767ebb7c6fdSAlex Wilson 		 * Fill any unused scatter pointers with the special null
1768ebb7c6fdSAlex Wilson 		 * value.
1769ebb7c6fdSAlex Wilson 		 */
1770ebb7c6fdSAlex Wilson 		for (; ptri < MLXCX_RECVQ_MAX_PTRS; ++ptri) {
1771ebb7c6fdSAlex Wilson 			seg = &ent->mlrqe_data[ptri];
1772ebb7c6fdSAlex Wilson 			seg->mlds_lkey = to_be32(MLXCX_NULL_LKEY);
1773ebb7c6fdSAlex Wilson 			seg->mlds_byte_count = to_be32(0);
1774ebb7c6fdSAlex Wilson 			seg->mlds_address = to_be64(0);
1775ebb7c6fdSAlex Wilson 		}
1776ebb7c6fdSAlex Wilson 
1777ebb7c6fdSAlex Wilson 		/*
1778ebb7c6fdSAlex Wilson 		 * Make sure the workqueue entry is flushed out before updating
1779ebb7c6fdSAlex Wilson 		 * the doorbell.
1780ebb7c6fdSAlex Wilson 		 */
1781ebb7c6fdSAlex Wilson 		VERIFY0(ddi_dma_sync(mlwq->mlwq_dma.mxdb_dma_handle,
1782ebb7c6fdSAlex Wilson 		    (uintptr_t)ent - (uintptr_t)mlwq->mlwq_recv_ent,
1783ebb7c6fdSAlex Wilson 		    sizeof (mlxcx_recvq_ent_t), DDI_DMA_SYNC_FORDEV));
1784ebb7c6fdSAlex Wilson 		ddi_fm_dma_err_get(mlwq->mlwq_dma.mxdb_dma_handle, &err,
1785ebb7c6fdSAlex Wilson 		    DDI_FME_VERSION);
1786ebb7c6fdSAlex Wilson 		if (err.fme_status != DDI_FM_OK) {
1787ebb7c6fdSAlex Wilson 			return (B_FALSE);
1788ebb7c6fdSAlex Wilson 		}
1789ebb7c6fdSAlex Wilson 	}
1790ebb7c6fdSAlex Wilson 
1791ebb7c6fdSAlex Wilson 	mlwq->mlwq_doorbell->mlwqd_recv_counter = to_be16(mlwq->mlwq_pc);
1792ebb7c6fdSAlex Wilson 	/*
1793ebb7c6fdSAlex Wilson 	 * Flush the CQ doorbell as well so that HW knows how many
1794ebb7c6fdSAlex Wilson 	 * completions we've consumed.
1795ebb7c6fdSAlex Wilson 	 */
1796ebb7c6fdSAlex Wilson 	MLXCX_DMA_SYNC(cq->mlcq_doorbell_dma, DDI_DMA_SYNC_FORDEV);
1797ebb7c6fdSAlex Wilson 	ddi_fm_dma_err_get(cq->mlcq_doorbell_dma.mxdb_dma_handle, &err,
1798ebb7c6fdSAlex Wilson 	    DDI_FME_VERSION);
1799ebb7c6fdSAlex Wilson 	if (err.fme_status != DDI_FM_OK) {
1800ebb7c6fdSAlex Wilson 		return (B_FALSE);
1801ebb7c6fdSAlex Wilson 	}
1802ebb7c6fdSAlex Wilson 	MLXCX_DMA_SYNC(mlwq->mlwq_doorbell_dma, DDI_DMA_SYNC_FORDEV);
1803ebb7c6fdSAlex Wilson 	ddi_fm_dma_err_get(mlwq->mlwq_doorbell_dma.mxdb_dma_handle, &err,
1804ebb7c6fdSAlex Wilson 	    DDI_FME_VERSION);
1805ebb7c6fdSAlex Wilson 	if (err.fme_status != DDI_FM_OK) {
1806ebb7c6fdSAlex Wilson 		return (B_FALSE);
1807ebb7c6fdSAlex Wilson 	}
1808ebb7c6fdSAlex Wilson 	return (B_TRUE);
1809ebb7c6fdSAlex Wilson }
1810ebb7c6fdSAlex Wilson 
181122d05228SPaul Winder static void
mlxcx_rq_refill_task(void * arg)181222d05228SPaul Winder mlxcx_rq_refill_task(void *arg)
181322d05228SPaul Winder {
181422d05228SPaul Winder 	mlxcx_work_queue_t *wq = arg;
181522d05228SPaul Winder 	mlxcx_completion_queue_t *cq = wq->mlwq_cq;
181622d05228SPaul Winder 	mlxcx_t *mlxp = wq->mlwq_mlx;
181722d05228SPaul Winder 	mlxcx_buf_shard_t *s = wq->mlwq_bufs;
181819325e87SPaul Winder 	boolean_t refill, draining;
181922d05228SPaul Winder 
182022d05228SPaul Winder 	do {
182122d05228SPaul Winder 		/*
182219325e87SPaul Winder 		 * Wait here until one of 3 conditions:
182319325e87SPaul Winder 		 * 1. The shard is draining, or
182419325e87SPaul Winder 		 * 2. There are buffers on the free list, or
182519325e87SPaul Winder 		 * 3. The WQ is being shut down.
182622d05228SPaul Winder 		 */
182722d05228SPaul Winder 		mutex_enter(&s->mlbs_mtx);
182819325e87SPaul Winder 		while (s->mlbs_state != MLXCX_SHARD_DRAINING &&
182919325e87SPaul Winder 		    list_is_empty(&s->mlbs_free) &&
183019325e87SPaul Winder 		    (cq->mlcq_state & MLXCX_CQ_TEARDOWN) == 0) {
183122d05228SPaul Winder 			cv_wait(&s->mlbs_free_nonempty, &s->mlbs_mtx);
183219325e87SPaul Winder 		}
183319325e87SPaul Winder 
183419325e87SPaul Winder 		draining = (s->mlbs_state == MLXCX_SHARD_DRAINING);
183522d05228SPaul Winder 		mutex_exit(&s->mlbs_mtx);
183622d05228SPaul Winder 
183722d05228SPaul Winder 		mutex_enter(&cq->mlcq_mtx);
183822d05228SPaul Winder 		mutex_enter(&wq->mlwq_mtx);
183922d05228SPaul Winder 
184019325e87SPaul Winder 		if (draining || (cq->mlcq_state & MLXCX_CQ_TEARDOWN) != 0) {
184122d05228SPaul Winder 			refill = B_FALSE;
184222d05228SPaul Winder 			wq->mlwq_state &= ~MLXCX_WQ_REFILLING;
184322d05228SPaul Winder 		} else {
184422d05228SPaul Winder 			mlxcx_rq_refill(mlxp, wq);
184522d05228SPaul Winder 
184622d05228SPaul Winder 			if (cq->mlcq_bufcnt < MLXCX_RQ_REFILL_STEP) {
184722d05228SPaul Winder 				refill = B_TRUE;
184822d05228SPaul Winder 			} else {
184922d05228SPaul Winder 				refill = B_FALSE;
185022d05228SPaul Winder 				wq->mlwq_state &= ~MLXCX_WQ_REFILLING;
185122d05228SPaul Winder 			}
185222d05228SPaul Winder 		}
185322d05228SPaul Winder 
185422d05228SPaul Winder 		mutex_exit(&wq->mlwq_mtx);
185522d05228SPaul Winder 		mutex_exit(&cq->mlcq_mtx);
185622d05228SPaul Winder 	} while (refill);
185722d05228SPaul Winder }
185822d05228SPaul Winder 
1859ebb7c6fdSAlex Wilson void
mlxcx_rq_refill(mlxcx_t * mlxp,mlxcx_work_queue_t * mlwq)1860ebb7c6fdSAlex Wilson mlxcx_rq_refill(mlxcx_t *mlxp, mlxcx_work_queue_t *mlwq)
1861ebb7c6fdSAlex Wilson {
1862ebb7c6fdSAlex Wilson 	size_t target, current, want, done, n;
1863ebb7c6fdSAlex Wilson 	mlxcx_completion_queue_t *cq;
186422d05228SPaul Winder 	mlxcx_ring_group_t *g;
1865ebb7c6fdSAlex Wilson 	mlxcx_buffer_t *b[MLXCX_RQ_REFILL_STEP];
1866ebb7c6fdSAlex Wilson 	uint_t i;
1867ebb7c6fdSAlex Wilson 
1868ebb7c6fdSAlex Wilson 	ASSERT(mutex_owned(&mlwq->mlwq_mtx));
1869ebb7c6fdSAlex Wilson 	cq = mlwq->mlwq_cq;
1870ebb7c6fdSAlex Wilson 	ASSERT(mutex_owned(&cq->mlcq_mtx));
1871ebb7c6fdSAlex Wilson 
1872ebb7c6fdSAlex Wilson 	ASSERT(mlwq->mlwq_state & MLXCX_WQ_BUFFERS);
1873ebb7c6fdSAlex Wilson 
1874ebb7c6fdSAlex Wilson 	target = mlwq->mlwq_nents - MLXCX_RQ_REFILL_STEP;
1875ebb7c6fdSAlex Wilson 	cq = mlwq->mlwq_cq;
1876ebb7c6fdSAlex Wilson 
187719325e87SPaul Winder 	if ((mlwq->mlwq_state & MLXCX_WQ_STARTED) == 0)
187819325e87SPaul Winder 		return;
187919325e87SPaul Winder 
188019325e87SPaul Winder 	if ((cq->mlcq_state & MLXCX_CQ_TEARDOWN) != 0)
1881ebb7c6fdSAlex Wilson 		return;
1882ebb7c6fdSAlex Wilson 
1883ebb7c6fdSAlex Wilson 	current = cq->mlcq_bufcnt;
1884ebb7c6fdSAlex Wilson 
1885ebb7c6fdSAlex Wilson 	if (current >= target - MLXCX_RQ_REFILL_STEP)
1886ebb7c6fdSAlex Wilson 		return;
1887ebb7c6fdSAlex Wilson 
1888ebb7c6fdSAlex Wilson 	want = target - current;
1889ebb7c6fdSAlex Wilson 	done = 0;
1890ebb7c6fdSAlex Wilson 
1891ebb7c6fdSAlex Wilson 	while (!(mlwq->mlwq_state & MLXCX_WQ_TEARDOWN) && done < want) {
1892ebb7c6fdSAlex Wilson 		n = mlxcx_buf_take_n(mlxp, mlwq, b, MLXCX_RQ_REFILL_STEP);
1893ebb7c6fdSAlex Wilson 		if (n == 0) {
189422d05228SPaul Winder 			/*
189522d05228SPaul Winder 			 * We didn't get any buffers from the free queue.
189622d05228SPaul Winder 			 * It might not be an issue, schedule a taskq
189722d05228SPaul Winder 			 * to wait for free buffers if the completion
189822d05228SPaul Winder 			 * queue is low.
189922d05228SPaul Winder 			 */
190022d05228SPaul Winder 			if (current < MLXCX_RQ_REFILL_STEP &&
190122d05228SPaul Winder 			    (mlwq->mlwq_state & MLXCX_WQ_REFILLING) == 0) {
190222d05228SPaul Winder 				mlwq->mlwq_state |= MLXCX_WQ_REFILLING;
190322d05228SPaul Winder 				g = mlwq->mlwq_group;
190422d05228SPaul Winder 				taskq_dispatch_ent(g->mlg_refill_tq,
190522d05228SPaul Winder 				    mlxcx_rq_refill_task, mlwq, TQ_NOSLEEP,
190622d05228SPaul Winder 				    &mlwq->mlwq_tqe);
190722d05228SPaul Winder 			}
190822d05228SPaul Winder 
1909ebb7c6fdSAlex Wilson 			return;
1910ebb7c6fdSAlex Wilson 		}
191122d05228SPaul Winder 
191219325e87SPaul Winder 		if ((mlwq->mlwq_state & MLXCX_WQ_TEARDOWN) != 0) {
1913ebb7c6fdSAlex Wilson 			for (i = 0; i < n; ++i)
1914ebb7c6fdSAlex Wilson 				mlxcx_buf_return(mlxp, b[i]);
1915ebb7c6fdSAlex Wilson 			return;
1916ebb7c6fdSAlex Wilson 		}
1917ebb7c6fdSAlex Wilson 		if (!mlxcx_rq_add_buffers(mlxp, mlwq, b, n)) {
1918ebb7c6fdSAlex Wilson 			/*
1919ebb7c6fdSAlex Wilson 			 * mlxcx_rq_add_buffers NULLs out the buffers as it
1920ebb7c6fdSAlex Wilson 			 * enqueues them, so any that are non-NULL we have to
1921ebb7c6fdSAlex Wilson 			 * free now. The others now belong to the WQ, even if
1922ebb7c6fdSAlex Wilson 			 * we failed.
1923ebb7c6fdSAlex Wilson 			 */
1924ebb7c6fdSAlex Wilson 			for (i = 0; i < n; ++i) {
1925ebb7c6fdSAlex Wilson 				if (b[i] != NULL) {
1926ebb7c6fdSAlex Wilson 					mlxcx_buf_return(mlxp, b[i]);
1927ebb7c6fdSAlex Wilson 				}
1928ebb7c6fdSAlex Wilson 			}
1929ebb7c6fdSAlex Wilson 			return;
1930ebb7c6fdSAlex Wilson 		}
1931ebb7c6fdSAlex Wilson 		done += n;
1932ebb7c6fdSAlex Wilson 	}
1933ebb7c6fdSAlex Wilson }
1934ebb7c6fdSAlex Wilson 
1935ebb7c6fdSAlex Wilson static const char *
mlxcx_cq_err_syndrome_string(mlxcx_cq_error_syndrome_t sy)1936ebb7c6fdSAlex Wilson mlxcx_cq_err_syndrome_string(mlxcx_cq_error_syndrome_t sy)
1937ebb7c6fdSAlex Wilson {
1938ebb7c6fdSAlex Wilson 	switch (sy) {
1939ebb7c6fdSAlex Wilson 	case MLXCX_CQ_ERR_LOCAL_LENGTH:
1940ebb7c6fdSAlex Wilson 		return ("LOCAL_LENGTH");
1941ebb7c6fdSAlex Wilson 	case MLXCX_CQ_ERR_LOCAL_QP_OP:
1942ebb7c6fdSAlex Wilson 		return ("LOCAL_QP_OP");
1943ebb7c6fdSAlex Wilson 	case MLXCX_CQ_ERR_LOCAL_PROTECTION:
1944ebb7c6fdSAlex Wilson 		return ("LOCAL_PROTECTION");
1945ebb7c6fdSAlex Wilson 	case MLXCX_CQ_ERR_WR_FLUSHED:
1946ebb7c6fdSAlex Wilson 		return ("WR_FLUSHED");
1947ebb7c6fdSAlex Wilson 	case MLXCX_CQ_ERR_MEM_WINDOW_BIND:
1948ebb7c6fdSAlex Wilson 		return ("MEM_WINDOW_BIND");
1949ebb7c6fdSAlex Wilson 	case MLXCX_CQ_ERR_BAD_RESPONSE:
1950ebb7c6fdSAlex Wilson 		return ("BAD_RESPONSE");
1951ebb7c6fdSAlex Wilson 	case MLXCX_CQ_ERR_LOCAL_ACCESS:
1952ebb7c6fdSAlex Wilson 		return ("LOCAL_ACCESS");
1953ebb7c6fdSAlex Wilson 	case MLXCX_CQ_ERR_XPORT_RETRY_CTR:
1954ebb7c6fdSAlex Wilson 		return ("XPORT_RETRY_CTR");
1955ebb7c6fdSAlex Wilson 	case MLXCX_CQ_ERR_RNR_RETRY_CTR:
1956ebb7c6fdSAlex Wilson 		return ("RNR_RETRY_CTR");
1957ebb7c6fdSAlex Wilson 	case MLXCX_CQ_ERR_ABORTED:
1958ebb7c6fdSAlex Wilson 		return ("ABORTED");
1959ebb7c6fdSAlex Wilson 	default:
1960ebb7c6fdSAlex Wilson 		return ("UNKNOWN");
1961ebb7c6fdSAlex Wilson 	}
1962ebb7c6fdSAlex Wilson }
1963ebb7c6fdSAlex Wilson 
1964ebb7c6fdSAlex Wilson static void
mlxcx_fm_cqe_ereport(mlxcx_t * mlxp,mlxcx_completion_queue_t * mlcq,mlxcx_completionq_error_ent_t * ent)1965ebb7c6fdSAlex Wilson mlxcx_fm_cqe_ereport(mlxcx_t *mlxp, mlxcx_completion_queue_t *mlcq,
1966ebb7c6fdSAlex Wilson     mlxcx_completionq_error_ent_t *ent)
1967ebb7c6fdSAlex Wilson {
1968ebb7c6fdSAlex Wilson 	uint64_t ena;
1969ebb7c6fdSAlex Wilson 	char buf[FM_MAX_CLASS];
1970ebb7c6fdSAlex Wilson 	const char *name = mlxcx_cq_err_syndrome_string(ent->mlcqee_syndrome);
1971ebb7c6fdSAlex Wilson 
1972ebb7c6fdSAlex Wilson 	if (!DDI_FM_EREPORT_CAP(mlxp->mlx_fm_caps))
1973ebb7c6fdSAlex Wilson 		return;
1974ebb7c6fdSAlex Wilson 
1975ebb7c6fdSAlex Wilson 	(void) snprintf(buf, FM_MAX_CLASS, "%s.%s",
1976ebb7c6fdSAlex Wilson 	    MLXCX_FM_SERVICE_MLXCX, "cqe.err");
1977ebb7c6fdSAlex Wilson 	ena = fm_ena_generate(0, FM_ENA_FMT1);
1978ebb7c6fdSAlex Wilson 
1979ebb7c6fdSAlex Wilson 	ddi_fm_ereport_post(mlxp->mlx_dip, buf, ena, DDI_NOSLEEP,
1980ebb7c6fdSAlex Wilson 	    FM_VERSION, DATA_TYPE_UINT8, FM_EREPORT_VERS0,
1981ebb7c6fdSAlex Wilson 	    "syndrome", DATA_TYPE_STRING, name,
1982ebb7c6fdSAlex Wilson 	    "syndrome_num", DATA_TYPE_UINT8, ent->mlcqee_syndrome,
1983ebb7c6fdSAlex Wilson 	    "vendor_syndrome", DATA_TYPE_UINT8,
1984ebb7c6fdSAlex Wilson 	    ent->mlcqee_vendor_error_syndrome,
1985ebb7c6fdSAlex Wilson 	    "wqe_counter", DATA_TYPE_UINT16, from_be16(ent->mlcqee_wqe_counter),
1986ebb7c6fdSAlex Wilson 	    "wq_type", DATA_TYPE_STRING,
1987ebb7c6fdSAlex Wilson 	    (mlcq->mlcq_wq->mlwq_type == MLXCX_WQ_TYPE_SENDQ) ? "send": "recv",
1988ebb7c6fdSAlex Wilson 	    "cq_num", DATA_TYPE_UINT32, mlcq->mlcq_num,
1989ebb7c6fdSAlex Wilson 	    "wq_num", DATA_TYPE_UINT32, mlcq->mlcq_wq->mlwq_num,
1990ebb7c6fdSAlex Wilson 	    NULL);
1991ebb7c6fdSAlex Wilson 	ddi_fm_service_impact(mlxp->mlx_dip, DDI_SERVICE_DEGRADED);
1992ebb7c6fdSAlex Wilson }
1993ebb7c6fdSAlex Wilson 
1994ebb7c6fdSAlex Wilson void
mlxcx_tx_completion(mlxcx_t * mlxp,mlxcx_completion_queue_t * mlcq,mlxcx_completionq_ent_t * ent,mlxcx_buffer_t * buf)1995ebb7c6fdSAlex Wilson mlxcx_tx_completion(mlxcx_t *mlxp, mlxcx_completion_queue_t *mlcq,
1996ebb7c6fdSAlex Wilson     mlxcx_completionq_ent_t *ent, mlxcx_buffer_t *buf)
1997ebb7c6fdSAlex Wilson {
1998ebb7c6fdSAlex Wilson 	ASSERT(mutex_owned(&mlcq->mlcq_mtx));
1999ebb7c6fdSAlex Wilson 	if (ent->mlcqe_opcode == MLXCX_CQE_OP_REQ_ERR) {
2000ebb7c6fdSAlex Wilson 		mlxcx_completionq_error_ent_t *eent =
2001ebb7c6fdSAlex Wilson 		    (mlxcx_completionq_error_ent_t *)ent;
2002ebb7c6fdSAlex Wilson 		mlxcx_fm_cqe_ereport(mlxp, mlcq, eent);
2003ebb7c6fdSAlex Wilson 		mlxcx_buf_return_chain(mlxp, buf, B_FALSE);
2004ebb7c6fdSAlex Wilson 		mutex_enter(&mlcq->mlcq_wq->mlwq_mtx);
2005ebb7c6fdSAlex Wilson 		mlxcx_check_sq(mlxp, mlcq->mlcq_wq);
2006ebb7c6fdSAlex Wilson 		mutex_exit(&mlcq->mlcq_wq->mlwq_mtx);
2007ebb7c6fdSAlex Wilson 		return;
2008ebb7c6fdSAlex Wilson 	}
2009ebb7c6fdSAlex Wilson 
2010ebb7c6fdSAlex Wilson 	if (ent->mlcqe_opcode != MLXCX_CQE_OP_REQ) {
2011ebb7c6fdSAlex Wilson 		mlxcx_warn(mlxp, "!got weird cq opcode: %x", ent->mlcqe_opcode);
2012ebb7c6fdSAlex Wilson 		mlxcx_buf_return_chain(mlxp, buf, B_FALSE);
2013ebb7c6fdSAlex Wilson 		return;
2014ebb7c6fdSAlex Wilson 	}
2015ebb7c6fdSAlex Wilson 
2016ebb7c6fdSAlex Wilson 	if (ent->mlcqe_send_wqe_opcode != MLXCX_WQE_OP_SEND) {
2017ebb7c6fdSAlex Wilson 		mlxcx_warn(mlxp, "!got weird cq wqe opcode: %x",
2018ebb7c6fdSAlex Wilson 		    ent->mlcqe_send_wqe_opcode);
2019ebb7c6fdSAlex Wilson 		mlxcx_buf_return_chain(mlxp, buf, B_FALSE);
2020ebb7c6fdSAlex Wilson 		return;
2021ebb7c6fdSAlex Wilson 	}
2022ebb7c6fdSAlex Wilson 
2023ebb7c6fdSAlex Wilson 	if (ent->mlcqe_format != MLXCX_CQE_FORMAT_BASIC) {
2024ebb7c6fdSAlex Wilson 		mlxcx_warn(mlxp, "!got weird cq format: %x", ent->mlcqe_format);
2025ebb7c6fdSAlex Wilson 		mlxcx_buf_return_chain(mlxp, buf, B_FALSE);
2026ebb7c6fdSAlex Wilson 		return;
2027ebb7c6fdSAlex Wilson 	}
2028ebb7c6fdSAlex Wilson 
2029ebb7c6fdSAlex Wilson 	mlxcx_buf_return_chain(mlxp, buf, B_FALSE);
2030ebb7c6fdSAlex Wilson }
2031ebb7c6fdSAlex Wilson 
2032ebb7c6fdSAlex Wilson mblk_t *
mlxcx_rx_completion(mlxcx_t * mlxp,mlxcx_completion_queue_t * mlcq,mlxcx_completionq_ent_t * ent,mlxcx_buffer_t * buf)2033ebb7c6fdSAlex Wilson mlxcx_rx_completion(mlxcx_t *mlxp, mlxcx_completion_queue_t *mlcq,
2034ebb7c6fdSAlex Wilson     mlxcx_completionq_ent_t *ent, mlxcx_buffer_t *buf)
2035ebb7c6fdSAlex Wilson {
2036ebb7c6fdSAlex Wilson 	uint32_t chkflags = 0;
2037*5014e1faSAlex Wilson 	uint_t wqe_index, used;
2038ebb7c6fdSAlex Wilson 	ddi_fm_error_t err;
2039*5014e1faSAlex Wilson 	mblk_t *mp;
2040ebb7c6fdSAlex Wilson 
2041ebb7c6fdSAlex Wilson 	ASSERT(mutex_owned(&mlcq->mlcq_mtx));
2042ebb7c6fdSAlex Wilson 
2043ebb7c6fdSAlex Wilson 	if (ent->mlcqe_opcode == MLXCX_CQE_OP_RESP_ERR) {
2044ebb7c6fdSAlex Wilson 		mlxcx_completionq_error_ent_t *eent =
2045ebb7c6fdSAlex Wilson 		    (mlxcx_completionq_error_ent_t *)ent;
2046ebb7c6fdSAlex Wilson 		mlxcx_fm_cqe_ereport(mlxp, mlcq, eent);
2047ebb7c6fdSAlex Wilson 		mlxcx_buf_return(mlxp, buf);
2048ebb7c6fdSAlex Wilson 		mutex_enter(&mlcq->mlcq_wq->mlwq_mtx);
2049ebb7c6fdSAlex Wilson 		mlxcx_check_rq(mlxp, mlcq->mlcq_wq);
2050ebb7c6fdSAlex Wilson 		mutex_exit(&mlcq->mlcq_wq->mlwq_mtx);
2051ebb7c6fdSAlex Wilson 		return (NULL);
2052ebb7c6fdSAlex Wilson 	}
2053ebb7c6fdSAlex Wilson 
2054ebb7c6fdSAlex Wilson 	if (ent->mlcqe_opcode != MLXCX_CQE_OP_RESP) {
2055ebb7c6fdSAlex Wilson 		mlxcx_warn(mlxp, "!got weird cq opcode: %x", ent->mlcqe_opcode);
2056ebb7c6fdSAlex Wilson 		mlxcx_buf_return(mlxp, buf);
2057ebb7c6fdSAlex Wilson 		return (NULL);
2058ebb7c6fdSAlex Wilson 	}
2059ebb7c6fdSAlex Wilson 
2060ebb7c6fdSAlex Wilson 	if (ent->mlcqe_format != MLXCX_CQE_FORMAT_BASIC) {
2061ebb7c6fdSAlex Wilson 		mlxcx_warn(mlxp, "!got weird cq format: %x", ent->mlcqe_format);
2062ebb7c6fdSAlex Wilson 		mlxcx_buf_return(mlxp, buf);
2063ebb7c6fdSAlex Wilson 		return (NULL);
2064ebb7c6fdSAlex Wilson 	}
2065ebb7c6fdSAlex Wilson 
2066ebb7c6fdSAlex Wilson 	if (ent->mlcqe_rx_drop_counter > 0) {
2067ebb7c6fdSAlex Wilson 		atomic_add_64(&mlcq->mlcq_stats->mlps_rx_drops,
2068ebb7c6fdSAlex Wilson 		    ent->mlcqe_rx_drop_counter);
2069ebb7c6fdSAlex Wilson 	}
2070ebb7c6fdSAlex Wilson 
2071ebb7c6fdSAlex Wilson 	MLXCX_DMA_SYNC(buf->mlb_dma, DDI_DMA_SYNC_FORCPU);
2072ebb7c6fdSAlex Wilson 	ddi_fm_dma_err_get(buf->mlb_dma.mxdb_dma_handle, &err,
2073ebb7c6fdSAlex Wilson 	    DDI_FME_VERSION);
2074ebb7c6fdSAlex Wilson 	if (err.fme_status != DDI_FM_OK) {
2075ebb7c6fdSAlex Wilson 		ddi_fm_dma_err_clear(buf->mlb_dma.mxdb_dma_handle,
2076ebb7c6fdSAlex Wilson 		    DDI_FME_VERSION);
2077ebb7c6fdSAlex Wilson 		mlxcx_buf_return(mlxp, buf);
2078ebb7c6fdSAlex Wilson 		return (NULL);
2079ebb7c6fdSAlex Wilson 	}
2080ebb7c6fdSAlex Wilson 
208122d05228SPaul Winder 	/*
208222d05228SPaul Winder 	 * mlxcx_buf_loan() will set mlb_wqe_index to zero.
208322d05228SPaul Winder 	 * Remember it for later.
208422d05228SPaul Winder 	 */
208522d05228SPaul Winder 	wqe_index = buf->mlb_wqe_index;
208622d05228SPaul Winder 
2087*5014e1faSAlex Wilson 	/* Set the used field with the actual length of the packet. */
2088*5014e1faSAlex Wilson 	buf->mlb_used = (used = from_be32(ent->mlcqe_byte_cnt));
2089*5014e1faSAlex Wilson 
2090*5014e1faSAlex Wilson 	/* Try to loan this buffer to MAC directly. */
2091*5014e1faSAlex Wilson 	if (mlxcx_buf_loan(mlxp, buf)) {
2092*5014e1faSAlex Wilson 		mp = buf->mlb_mp;
2093*5014e1faSAlex Wilson 
2094*5014e1faSAlex Wilson 	} else {
2095*5014e1faSAlex Wilson 		/*
2096*5014e1faSAlex Wilson 		 * Loan rejected: we will try to allocate a new mblk and copy
2097*5014e1faSAlex Wilson 		 * this packet for MAC instead.
2098*5014e1faSAlex Wilson 		 */
2099*5014e1faSAlex Wilson 		mp = allocb(buf->mlb_used, 0);
2100*5014e1faSAlex Wilson 		if (mp == NULL) {
2101*5014e1faSAlex Wilson 			/* No memory :( */
2102*5014e1faSAlex Wilson 			atomic_add_64(&mlcq->mlcq_stats->mlps_rx_drops, 1);
2103*5014e1faSAlex Wilson 			mlxcx_buf_return(mlxp, buf);
2104*5014e1faSAlex Wilson 			return (NULL);
2105*5014e1faSAlex Wilson 		}
2106*5014e1faSAlex Wilson 		bcopy((unsigned char *)buf->mlb_dma.mxdb_va, mp->b_rptr,
2107*5014e1faSAlex Wilson 		    buf->mlb_used);
2108*5014e1faSAlex Wilson 
2109*5014e1faSAlex Wilson 		/* We're done with this buf now, return it to the free list. */
2110ebb7c6fdSAlex Wilson 		mlxcx_buf_return(mlxp, buf);
2111*5014e1faSAlex Wilson 		buf = NULL;
2112ebb7c6fdSAlex Wilson 	}
2113ebb7c6fdSAlex Wilson 
2114*5014e1faSAlex Wilson 	mp->b_next = NULL;
2115*5014e1faSAlex Wilson 	mp->b_cont = NULL;
2116*5014e1faSAlex Wilson 	mp->b_wptr = mp->b_rptr + used;
2117ebb7c6fdSAlex Wilson 
2118ebb7c6fdSAlex Wilson 	if (get_bit8(ent->mlcqe_csflags, MLXCX_CQE_CSFLAGS_L4_OK)) {
2119ebb7c6fdSAlex Wilson 		chkflags |= HCK_FULLCKSUM_OK;
2120ebb7c6fdSAlex Wilson 	}
2121ebb7c6fdSAlex Wilson 	if (get_bit8(ent->mlcqe_csflags, MLXCX_CQE_CSFLAGS_L3_OK)) {
2122ebb7c6fdSAlex Wilson 		chkflags |= HCK_IPV4_HDRCKSUM_OK;
2123ebb7c6fdSAlex Wilson 	}
2124ebb7c6fdSAlex Wilson 	if (chkflags != 0) {
2125*5014e1faSAlex Wilson 		mac_hcksum_set(mp, 0, 0, 0, from_be16(ent->mlcqe_checksum),
2126*5014e1faSAlex Wilson 		    chkflags);
2127ebb7c6fdSAlex Wilson 	}
2128ebb7c6fdSAlex Wilson 
2129ebb7c6fdSAlex Wilson 	/*
2130ebb7c6fdSAlex Wilson 	 * Don't check if a refill is needed on every single completion,
2131ebb7c6fdSAlex Wilson 	 * since checking involves taking the RQ lock.
2132ebb7c6fdSAlex Wilson 	 */
213322d05228SPaul Winder 	if ((wqe_index & 0x7) == 0) {
2134ebb7c6fdSAlex Wilson 		mlxcx_work_queue_t *wq = mlcq->mlcq_wq;
2135ebb7c6fdSAlex Wilson 		ASSERT(wq != NULL);
2136ebb7c6fdSAlex Wilson 		mutex_enter(&wq->mlwq_mtx);
2137ebb7c6fdSAlex Wilson 		if (!(wq->mlwq_state & MLXCX_WQ_TEARDOWN))
2138ebb7c6fdSAlex Wilson 			mlxcx_rq_refill(mlxp, wq);
2139ebb7c6fdSAlex Wilson 		mutex_exit(&wq->mlwq_mtx);
2140ebb7c6fdSAlex Wilson 	}
2141ebb7c6fdSAlex Wilson 
2142*5014e1faSAlex Wilson 	return (mp);
2143ebb7c6fdSAlex Wilson }
2144ebb7c6fdSAlex Wilson 
2145ebb7c6fdSAlex Wilson static void
mlxcx_buf_mp_return(caddr_t arg)2146ebb7c6fdSAlex Wilson mlxcx_buf_mp_return(caddr_t arg)
2147ebb7c6fdSAlex Wilson {
2148ebb7c6fdSAlex Wilson 	mlxcx_buffer_t *b = (mlxcx_buffer_t *)arg;
2149ebb7c6fdSAlex Wilson 	mlxcx_t *mlxp = b->mlb_mlx;
2150ebb7c6fdSAlex Wilson 
215119325e87SPaul Winder 	/* The mblk has been used now, so NULL it out. */
2152ebb7c6fdSAlex Wilson 	b->mlb_mp = NULL;
215319325e87SPaul Winder 
215419325e87SPaul Winder 	if (b->mlb_state == MLXCX_BUFFER_ON_LOAN)
215519325e87SPaul Winder 		mlxcx_buf_return(mlxp, b);
2156ebb7c6fdSAlex Wilson }
2157ebb7c6fdSAlex Wilson 
2158ebb7c6fdSAlex Wilson boolean_t
mlxcx_buf_create(mlxcx_t * mlxp,mlxcx_buf_shard_t * shard,mlxcx_buffer_t ** bp)2159ebb7c6fdSAlex Wilson mlxcx_buf_create(mlxcx_t *mlxp, mlxcx_buf_shard_t *shard, mlxcx_buffer_t **bp)
2160ebb7c6fdSAlex Wilson {
2161ebb7c6fdSAlex Wilson 	mlxcx_buffer_t *b;
2162ebb7c6fdSAlex Wilson 	ddi_device_acc_attr_t acc;
2163ebb7c6fdSAlex Wilson 	ddi_dma_attr_t attr;
2164ebb7c6fdSAlex Wilson 	boolean_t ret;
2165ebb7c6fdSAlex Wilson 
2166ebb7c6fdSAlex Wilson 	b = kmem_cache_alloc(mlxp->mlx_bufs_cache, KM_SLEEP);
2167ebb7c6fdSAlex Wilson 	b->mlb_shard = shard;
2168ebb7c6fdSAlex Wilson 	b->mlb_foreign = B_FALSE;
2169ebb7c6fdSAlex Wilson 
2170ebb7c6fdSAlex Wilson 	mlxcx_dma_acc_attr(mlxp, &acc);
2171ebb7c6fdSAlex Wilson 	mlxcx_dma_buf_attr(mlxp, &attr);
2172ebb7c6fdSAlex Wilson 
2173ebb7c6fdSAlex Wilson 	ret = mlxcx_dma_alloc_offset(mlxp, &b->mlb_dma, &attr, &acc,
2174ebb7c6fdSAlex Wilson 	    B_FALSE, mlxp->mlx_ports[0].mlp_mtu, 2, B_TRUE);
2175ebb7c6fdSAlex Wilson 	if (!ret) {
2176ebb7c6fdSAlex Wilson 		kmem_cache_free(mlxp->mlx_bufs_cache, b);
2177ebb7c6fdSAlex Wilson 		return (B_FALSE);
2178ebb7c6fdSAlex Wilson 	}
2179ebb7c6fdSAlex Wilson 
2180ebb7c6fdSAlex Wilson 	b->mlb_frtn.free_func = mlxcx_buf_mp_return;
2181ebb7c6fdSAlex Wilson 	b->mlb_frtn.free_arg = (caddr_t)b;
2182ebb7c6fdSAlex Wilson 	b->mlb_mp = desballoc((unsigned char *)b->mlb_dma.mxdb_va,
2183ebb7c6fdSAlex Wilson 	    b->mlb_dma.mxdb_len, 0, &b->mlb_frtn);
2184ebb7c6fdSAlex Wilson 
2185ebb7c6fdSAlex Wilson 	*bp = b;
2186ebb7c6fdSAlex Wilson 
2187ebb7c6fdSAlex Wilson 	return (B_TRUE);
2188ebb7c6fdSAlex Wilson }
2189ebb7c6fdSAlex Wilson 
2190ebb7c6fdSAlex Wilson boolean_t
mlxcx_buf_create_foreign(mlxcx_t * mlxp,mlxcx_buf_shard_t * shard,mlxcx_buffer_t ** bp)2191ebb7c6fdSAlex Wilson mlxcx_buf_create_foreign(mlxcx_t *mlxp, mlxcx_buf_shard_t *shard,
2192ebb7c6fdSAlex Wilson     mlxcx_buffer_t **bp)
2193ebb7c6fdSAlex Wilson {
2194ebb7c6fdSAlex Wilson 	mlxcx_buffer_t *b;
2195ebb7c6fdSAlex Wilson 	ddi_dma_attr_t attr;
2196ebb7c6fdSAlex Wilson 	boolean_t ret;
2197ebb7c6fdSAlex Wilson 
2198ebb7c6fdSAlex Wilson 	b = kmem_cache_alloc(mlxp->mlx_bufs_cache, KM_SLEEP);
2199ebb7c6fdSAlex Wilson 	b->mlb_shard = shard;
2200ebb7c6fdSAlex Wilson 	b->mlb_foreign = B_TRUE;
2201ebb7c6fdSAlex Wilson 
2202ebb7c6fdSAlex Wilson 	mlxcx_dma_buf_attr(mlxp, &attr);
2203ebb7c6fdSAlex Wilson 
2204ebb7c6fdSAlex Wilson 	ret = mlxcx_dma_init(mlxp, &b->mlb_dma, &attr, B_TRUE);
2205ebb7c6fdSAlex Wilson 	if (!ret) {
2206ebb7c6fdSAlex Wilson 		kmem_cache_free(mlxp->mlx_bufs_cache, b);
2207ebb7c6fdSAlex Wilson 		return (B_FALSE);
2208ebb7c6fdSAlex Wilson 	}
2209ebb7c6fdSAlex Wilson 
2210ebb7c6fdSAlex Wilson 	*bp = b;
2211ebb7c6fdSAlex Wilson 
2212ebb7c6fdSAlex Wilson 	return (B_TRUE);
2213ebb7c6fdSAlex Wilson }
2214ebb7c6fdSAlex Wilson 
221522d05228SPaul Winder static mlxcx_buffer_t *
mlxcx_buf_take_foreign(mlxcx_t * mlxp,mlxcx_work_queue_t * wq)221622d05228SPaul Winder mlxcx_buf_take_foreign(mlxcx_t *mlxp, mlxcx_work_queue_t *wq)
2217ebb7c6fdSAlex Wilson {
2218ebb7c6fdSAlex Wilson 	mlxcx_buffer_t *b;
2219ebb7c6fdSAlex Wilson 	mlxcx_buf_shard_t *s = wq->mlwq_foreign_bufs;
2220ebb7c6fdSAlex Wilson 
2221ebb7c6fdSAlex Wilson 	mutex_enter(&s->mlbs_mtx);
222219325e87SPaul Winder 	if (s->mlbs_state != MLXCX_SHARD_READY) {
222319325e87SPaul Winder 		mutex_exit(&s->mlbs_mtx);
222419325e87SPaul Winder 		return (NULL);
222519325e87SPaul Winder 	}
222619325e87SPaul Winder 
222722d05228SPaul Winder 	if ((b = list_remove_head(&s->mlbs_free)) != NULL) {
222822d05228SPaul Winder 		ASSERT3U(b->mlb_state, ==, MLXCX_BUFFER_FREE);
222922d05228SPaul Winder 		ASSERT(b->mlb_foreign);
223022d05228SPaul Winder 		b->mlb_state = MLXCX_BUFFER_ON_WQ;
223122d05228SPaul Winder 		list_insert_tail(&s->mlbs_busy, b);
223222d05228SPaul Winder 	}
2233ebb7c6fdSAlex Wilson 	mutex_exit(&s->mlbs_mtx);
2234ebb7c6fdSAlex Wilson 
223522d05228SPaul Winder 	return (b);
2236ebb7c6fdSAlex Wilson }
2237ebb7c6fdSAlex Wilson 
223822d05228SPaul Winder static mlxcx_buffer_t *
mlxcx_copy_data(mlxcx_t * mlxp,mlxcx_work_queue_t * wq,uint8_t * rptr,size_t sz)223922d05228SPaul Winder mlxcx_copy_data(mlxcx_t *mlxp, mlxcx_work_queue_t *wq, uint8_t *rptr, size_t sz)
224022d05228SPaul Winder {
224122d05228SPaul Winder 	ddi_fm_error_t err;
224222d05228SPaul Winder 	mlxcx_buffer_t *b;
224322d05228SPaul Winder 	uint_t attempts = 0;
224422d05228SPaul Winder 
224522d05228SPaul Winder copyb:
224622d05228SPaul Winder 	if ((b = mlxcx_buf_take(mlxp, wq)) == NULL)
224722d05228SPaul Winder 		return (NULL);
224822d05228SPaul Winder 
224922d05228SPaul Winder 	ASSERT3U(b->mlb_dma.mxdb_len, >=, sz);
225022d05228SPaul Winder 	bcopy(rptr, b->mlb_dma.mxdb_va, sz);
225122d05228SPaul Winder 
225222d05228SPaul Winder 	MLXCX_DMA_SYNC(b->mlb_dma, DDI_DMA_SYNC_FORDEV);
225322d05228SPaul Winder 
225422d05228SPaul Winder 	ddi_fm_dma_err_get(b->mlb_dma.mxdb_dma_handle, &err,
225522d05228SPaul Winder 	    DDI_FME_VERSION);
225622d05228SPaul Winder 	if (err.fme_status != DDI_FM_OK) {
225722d05228SPaul Winder 		ddi_fm_dma_err_clear(b->mlb_dma.mxdb_dma_handle,
225822d05228SPaul Winder 		    DDI_FME_VERSION);
225922d05228SPaul Winder 		mlxcx_buf_return(mlxp, b);
226022d05228SPaul Winder 		if (++attempts > MLXCX_BUF_BIND_MAX_ATTEMTPS) {
226122d05228SPaul Winder 			return (NULL);
226222d05228SPaul Winder 		}
226322d05228SPaul Winder 		goto copyb;
226422d05228SPaul Winder 	}
226522d05228SPaul Winder 
226622d05228SPaul Winder 	return (b);
226722d05228SPaul Winder }
226822d05228SPaul Winder 
226982b4190eSPaul Winder static mlxcx_buffer_t *
mlxcx_bind_or_copy_mblk(mlxcx_t * mlxp,mlxcx_work_queue_t * wq,mblk_t * mp,size_t off)227082b4190eSPaul Winder mlxcx_bind_or_copy_mblk(mlxcx_t *mlxp, mlxcx_work_queue_t *wq,
227182b4190eSPaul Winder     mblk_t *mp, size_t off)
2272ebb7c6fdSAlex Wilson {
227382b4190eSPaul Winder 	mlxcx_buffer_t *b;
2274ebb7c6fdSAlex Wilson 	uint8_t *rptr;
2275ebb7c6fdSAlex Wilson 	size_t sz;
2276ebb7c6fdSAlex Wilson 	boolean_t ret;
2277ebb7c6fdSAlex Wilson 
227882b4190eSPaul Winder 	rptr = mp->b_rptr;
227982b4190eSPaul Winder 	sz = MBLKL(mp);
2280ebb7c6fdSAlex Wilson 
228182b4190eSPaul Winder #ifdef DEBUG
228282b4190eSPaul Winder 	if (off > 0) {
228382b4190eSPaul Winder 		ASSERT3U(off, <, sz);
228482b4190eSPaul Winder 	}
228582b4190eSPaul Winder #endif
2286ebb7c6fdSAlex Wilson 
228782b4190eSPaul Winder 	rptr += off;
228882b4190eSPaul Winder 	sz -= off;
2289ebb7c6fdSAlex Wilson 
229082b4190eSPaul Winder 	if (sz < mlxp->mlx_props.mldp_tx_bind_threshold) {
229182b4190eSPaul Winder 		b = mlxcx_copy_data(mlxp, wq, rptr, sz);
229282b4190eSPaul Winder 	} else {
229382b4190eSPaul Winder 		b = mlxcx_buf_take_foreign(mlxp, wq);
229482b4190eSPaul Winder 		if (b == NULL)
229582b4190eSPaul Winder 			return (NULL);
2296ebb7c6fdSAlex Wilson 
229782b4190eSPaul Winder 		ret = mlxcx_dma_bind_mblk(mlxp, &b->mlb_dma, mp, off,
229882b4190eSPaul Winder 		    B_FALSE);
229922d05228SPaul Winder 
230082b4190eSPaul Winder 		if (!ret) {
230182b4190eSPaul Winder 			mlxcx_buf_return(mlxp, b);
230282b4190eSPaul Winder 
230382b4190eSPaul Winder 			b = mlxcx_copy_data(mlxp, wq, rptr, sz);
2304ebb7c6fdSAlex Wilson 		}
230582b4190eSPaul Winder 	}
230682b4190eSPaul Winder 
230782b4190eSPaul Winder 	return (b);
230882b4190eSPaul Winder }
230982b4190eSPaul Winder 
231082b4190eSPaul Winder uint_t
mlxcx_buf_bind_or_copy(mlxcx_t * mlxp,mlxcx_work_queue_t * wq,mblk_t * mpb,size_t off,mlxcx_buffer_t ** bp)231182b4190eSPaul Winder mlxcx_buf_bind_or_copy(mlxcx_t *mlxp, mlxcx_work_queue_t *wq,
231282b4190eSPaul Winder     mblk_t *mpb, size_t off, mlxcx_buffer_t **bp)
231382b4190eSPaul Winder {
231482b4190eSPaul Winder 	mlxcx_buffer_t *b, *b0 = NULL;
231582b4190eSPaul Winder 	boolean_t first = B_TRUE;
231682b4190eSPaul Winder 	mblk_t *mp;
231782b4190eSPaul Winder 	size_t offset = off;
231882b4190eSPaul Winder 	size_t ncookies = 0;
231982b4190eSPaul Winder 	uint_t count = 0;
232082b4190eSPaul Winder 
232182b4190eSPaul Winder 	for (mp = mpb; mp != NULL && ncookies <= MLXCX_SQE_MAX_PTRS;
232282b4190eSPaul Winder 	    mp = mp->b_cont) {
232382b4190eSPaul Winder 		b = mlxcx_bind_or_copy_mblk(mlxp, wq, mp, offset);
232482b4190eSPaul Winder 		if (b == NULL)
232582b4190eSPaul Winder 			goto failed;
2326ebb7c6fdSAlex Wilson 
2327ebb7c6fdSAlex Wilson 		ncookies += b->mlb_dma.mxdb_ncookies;
2328ebb7c6fdSAlex Wilson 
2329ebb7c6fdSAlex Wilson 		if (first)
2330ebb7c6fdSAlex Wilson 			b0 = b;
2331ebb7c6fdSAlex Wilson 
2332ebb7c6fdSAlex Wilson 		if (!first)
2333ebb7c6fdSAlex Wilson 			b->mlb_state = MLXCX_BUFFER_ON_CHAIN;
2334ebb7c6fdSAlex Wilson 
2335ebb7c6fdSAlex Wilson 		b->mlb_tx_mp = mp;
2336ebb7c6fdSAlex Wilson 		b->mlb_tx_head = b0;
233782b4190eSPaul Winder 		b->mlb_used = MBLKL(mp) - offset;
2338ebb7c6fdSAlex Wilson 
2339ebb7c6fdSAlex Wilson 		if (!first)
2340ebb7c6fdSAlex Wilson 			list_insert_tail(&b0->mlb_tx_chain, b);
2341ebb7c6fdSAlex Wilson 		first = B_FALSE;
234282b4190eSPaul Winder 		offset = 0;
234382b4190eSPaul Winder 
234482b4190eSPaul Winder 		count++;
234582b4190eSPaul Winder 	}
234682b4190eSPaul Winder 
234782b4190eSPaul Winder 	/*
234882b4190eSPaul Winder 	 * The chain of mblks has resulted in too many cookies for
234982b4190eSPaul Winder 	 * a single message. This is unusual, so take the hit to tidy
235082b4190eSPaul Winder 	 * up, do a pullup to a single mblk and allocate the requisite
235182b4190eSPaul Winder 	 * buf.
235282b4190eSPaul Winder 	 */
235382b4190eSPaul Winder 	if (ncookies > MLXCX_SQE_MAX_PTRS) {
235482b4190eSPaul Winder 		DTRACE_PROBE4(pullup, mlxcx_t *, mlxp, mlxcx_work_queue_t *, wq,
235582b4190eSPaul Winder 		    mblk_t *, mpb, size_t, ncookies);
235682b4190eSPaul Winder 
235782b4190eSPaul Winder 		if (b0 != NULL)
235882b4190eSPaul Winder 			mlxcx_buf_return_chain(mlxp, b0, B_TRUE);
235982b4190eSPaul Winder 
236082b4190eSPaul Winder 		if ((mp = msgpullup(mpb, -1)) == NULL)
236182b4190eSPaul Winder 			return (0);
236282b4190eSPaul Winder 
236382b4190eSPaul Winder 		b0 = mlxcx_bind_or_copy_mblk(mlxp, wq, mp, off);
236482b4190eSPaul Winder 		if (b0 == NULL) {
236582b4190eSPaul Winder 			freemsg(mp);
236682b4190eSPaul Winder 			return (0);
236782b4190eSPaul Winder 		}
236882b4190eSPaul Winder 		freemsg(mpb);
236982b4190eSPaul Winder 
237082b4190eSPaul Winder 		b0->mlb_tx_mp = mp;
237182b4190eSPaul Winder 		b0->mlb_tx_head = b0;
237282b4190eSPaul Winder 		b0->mlb_used = MBLKL(mp) - off;
237382b4190eSPaul Winder 
237482b4190eSPaul Winder 		count = 1;
2375ebb7c6fdSAlex Wilson 	}
2376ebb7c6fdSAlex Wilson 
237782b4190eSPaul Winder 	*bp = b0;
2378ebb7c6fdSAlex Wilson 
237982b4190eSPaul Winder 	return (count);
238022d05228SPaul Winder 
238122d05228SPaul Winder failed:
238222d05228SPaul Winder 	if (b0 != NULL)
238322d05228SPaul Winder 		mlxcx_buf_return_chain(mlxp, b0, B_TRUE);
238422d05228SPaul Winder 
238582b4190eSPaul Winder 	return (0);
2386ebb7c6fdSAlex Wilson }
2387ebb7c6fdSAlex Wilson 
238822d05228SPaul Winder mlxcx_buffer_t *
mlxcx_buf_take(mlxcx_t * mlxp,mlxcx_work_queue_t * wq)238922d05228SPaul Winder mlxcx_buf_take(mlxcx_t *mlxp, mlxcx_work_queue_t *wq)
2390ebb7c6fdSAlex Wilson {
2391ebb7c6fdSAlex Wilson 	mlxcx_buffer_t *b;
2392ebb7c6fdSAlex Wilson 	mlxcx_buf_shard_t *s = wq->mlwq_bufs;
2393ebb7c6fdSAlex Wilson 
2394ebb7c6fdSAlex Wilson 	mutex_enter(&s->mlbs_mtx);
239519325e87SPaul Winder 	if (s->mlbs_state != MLXCX_SHARD_READY) {
239619325e87SPaul Winder 		mutex_exit(&s->mlbs_mtx);
239719325e87SPaul Winder 		return (NULL);
239819325e87SPaul Winder 	}
239919325e87SPaul Winder 
240022d05228SPaul Winder 	if ((b = list_remove_head(&s->mlbs_free)) != NULL) {
240122d05228SPaul Winder 		ASSERT3U(b->mlb_state, ==, MLXCX_BUFFER_FREE);
240222d05228SPaul Winder 		b->mlb_state = MLXCX_BUFFER_ON_WQ;
240322d05228SPaul Winder 		list_insert_tail(&s->mlbs_busy, b);
240422d05228SPaul Winder 	}
2405ebb7c6fdSAlex Wilson 	mutex_exit(&s->mlbs_mtx);
2406ebb7c6fdSAlex Wilson 
240722d05228SPaul Winder 	return (b);
2408ebb7c6fdSAlex Wilson }
2409ebb7c6fdSAlex Wilson 
2410ebb7c6fdSAlex Wilson size_t
mlxcx_buf_take_n(mlxcx_t * mlxp,mlxcx_work_queue_t * wq,mlxcx_buffer_t ** bp,size_t nbufs)24115f0e3176SPaul Winder mlxcx_buf_take_n(mlxcx_t *mlxp, mlxcx_work_queue_t *wq, mlxcx_buffer_t **bp,
24125f0e3176SPaul Winder     size_t nbufs)
2413ebb7c6fdSAlex Wilson {
2414ebb7c6fdSAlex Wilson 	mlxcx_buffer_t *b;
241522d05228SPaul Winder 	size_t done = 0;
2416ebb7c6fdSAlex Wilson 	mlxcx_buf_shard_t *s;
2417ebb7c6fdSAlex Wilson 
2418ebb7c6fdSAlex Wilson 	s = wq->mlwq_bufs;
2419ebb7c6fdSAlex Wilson 
2420ebb7c6fdSAlex Wilson 	mutex_enter(&s->mlbs_mtx);
242119325e87SPaul Winder 	if (s->mlbs_state != MLXCX_SHARD_READY) {
242219325e87SPaul Winder 		mutex_exit(&s->mlbs_mtx);
242319325e87SPaul Winder 		return (0);
242419325e87SPaul Winder 	}
242519325e87SPaul Winder 
242622d05228SPaul Winder 	while (done < nbufs && (b = list_remove_head(&s->mlbs_free)) != NULL) {
2427ebb7c6fdSAlex Wilson 		ASSERT3U(b->mlb_state, ==, MLXCX_BUFFER_FREE);
2428ebb7c6fdSAlex Wilson 		b->mlb_state = MLXCX_BUFFER_ON_WQ;
2429ebb7c6fdSAlex Wilson 		list_insert_tail(&s->mlbs_busy, b);
2430ebb7c6fdSAlex Wilson 		bp[done++] = b;
2431ebb7c6fdSAlex Wilson 	}
2432ebb7c6fdSAlex Wilson 	mutex_exit(&s->mlbs_mtx);
2433ebb7c6fdSAlex Wilson 	return (done);
2434ebb7c6fdSAlex Wilson }
2435ebb7c6fdSAlex Wilson 
2436ebb7c6fdSAlex Wilson boolean_t
mlxcx_buf_loan(mlxcx_t * mlxp,mlxcx_buffer_t * b)2437ebb7c6fdSAlex Wilson mlxcx_buf_loan(mlxcx_t *mlxp, mlxcx_buffer_t *b)
2438ebb7c6fdSAlex Wilson {
243919325e87SPaul Winder 	mlxcx_buf_shard_t *s = b->mlb_shard;
244019325e87SPaul Winder 
2441ebb7c6fdSAlex Wilson 	VERIFY3U(b->mlb_state, ==, MLXCX_BUFFER_ON_WQ);
2442ebb7c6fdSAlex Wilson 	ASSERT3P(b->mlb_mlx, ==, mlxp);
2443ebb7c6fdSAlex Wilson 
2444ebb7c6fdSAlex Wilson 	if (b->mlb_mp == NULL) {
2445ebb7c6fdSAlex Wilson 		b->mlb_mp = desballoc((unsigned char *)b->mlb_dma.mxdb_va,
2446ebb7c6fdSAlex Wilson 		    b->mlb_dma.mxdb_len, 0, &b->mlb_frtn);
2447ebb7c6fdSAlex Wilson 		if (b->mlb_mp == NULL)
2448ebb7c6fdSAlex Wilson 			return (B_FALSE);
2449ebb7c6fdSAlex Wilson 	}
2450ebb7c6fdSAlex Wilson 
2451*5014e1faSAlex Wilson 	mutex_enter(&s->mlbs_mtx);
2452*5014e1faSAlex Wilson 
2453*5014e1faSAlex Wilson 	/* Check if we have too many buffers on loan. */
2454*5014e1faSAlex Wilson 	if (s->mlbs_nloaned >= s->mlbs_hiwat1 &&
2455*5014e1faSAlex Wilson 	    b->mlb_used < mlxp->mlx_props.mldp_rx_p50_loan_min_size) {
2456*5014e1faSAlex Wilson 		mutex_exit(&s->mlbs_mtx);
2457*5014e1faSAlex Wilson 		return (B_FALSE);
2458*5014e1faSAlex Wilson 	} else if (s->mlbs_nloaned >= s->mlbs_hiwat2) {
2459*5014e1faSAlex Wilson 		mutex_exit(&s->mlbs_mtx);
2460*5014e1faSAlex Wilson 		return (B_FALSE);
2461*5014e1faSAlex Wilson 	}
2462*5014e1faSAlex Wilson 
2463ebb7c6fdSAlex Wilson 	b->mlb_state = MLXCX_BUFFER_ON_LOAN;
2464ebb7c6fdSAlex Wilson 	b->mlb_wqe_index = 0;
246519325e87SPaul Winder 	list_remove(&s->mlbs_busy, b);
246619325e87SPaul Winder 	list_insert_tail(&s->mlbs_loaned, b);
2467*5014e1faSAlex Wilson 	s->mlbs_nloaned++;
246819325e87SPaul Winder 	mutex_exit(&s->mlbs_mtx);
246919325e87SPaul Winder 
2470ebb7c6fdSAlex Wilson 	return (B_TRUE);
2471ebb7c6fdSAlex Wilson }
2472ebb7c6fdSAlex Wilson 
2473ebb7c6fdSAlex Wilson void
mlxcx_buf_return_chain(mlxcx_t * mlxp,mlxcx_buffer_t * b0,boolean_t keepmp)2474ebb7c6fdSAlex Wilson mlxcx_buf_return_chain(mlxcx_t *mlxp, mlxcx_buffer_t *b0, boolean_t keepmp)
2475ebb7c6fdSAlex Wilson {
2476ebb7c6fdSAlex Wilson 	mlxcx_buffer_t *b;
2477ebb7c6fdSAlex Wilson 
2478ebb7c6fdSAlex Wilson 	if (b0->mlb_tx_head != b0) {
2479ebb7c6fdSAlex Wilson 		mlxcx_buf_return(mlxp, b0);
2480ebb7c6fdSAlex Wilson 		return;
2481ebb7c6fdSAlex Wilson 	}
2482ebb7c6fdSAlex Wilson 
2483ebb7c6fdSAlex Wilson 	while ((b = list_head(&b0->mlb_tx_chain)) != NULL) {
2484ebb7c6fdSAlex Wilson 		mlxcx_buf_return(mlxp, b);
2485ebb7c6fdSAlex Wilson 	}
2486ebb7c6fdSAlex Wilson 	if (keepmp) {
2487ebb7c6fdSAlex Wilson 		b0->mlb_tx_mp = NULL;
2488ebb7c6fdSAlex Wilson 		b0->mlb_tx_head = NULL;
2489ebb7c6fdSAlex Wilson 	}
2490ebb7c6fdSAlex Wilson 	mlxcx_buf_return(mlxp, b0);
2491ebb7c6fdSAlex Wilson }
2492ebb7c6fdSAlex Wilson 
2493*5014e1faSAlex Wilson inline void
mlxcx_bufshard_adjust_total(mlxcx_buf_shard_t * s,int64_t incr)2494*5014e1faSAlex Wilson mlxcx_bufshard_adjust_total(mlxcx_buf_shard_t *s, int64_t incr)
2495*5014e1faSAlex Wilson {
2496*5014e1faSAlex Wilson 	s->mlbs_ntotal += incr;
2497*5014e1faSAlex Wilson 	s->mlbs_hiwat1 = s->mlbs_ntotal / 2;
2498*5014e1faSAlex Wilson 	s->mlbs_hiwat2 = 3 * (s->mlbs_ntotal / 4);
2499*5014e1faSAlex Wilson }
2500*5014e1faSAlex Wilson 
2501ebb7c6fdSAlex Wilson void
mlxcx_buf_return(mlxcx_t * mlxp,mlxcx_buffer_t * b)2502ebb7c6fdSAlex Wilson mlxcx_buf_return(mlxcx_t *mlxp, mlxcx_buffer_t *b)
2503ebb7c6fdSAlex Wilson {
2504ebb7c6fdSAlex Wilson 	mlxcx_buffer_state_t oldstate = b->mlb_state;
2505ebb7c6fdSAlex Wilson 	mlxcx_buffer_t *txhead = b->mlb_tx_head;
2506ebb7c6fdSAlex Wilson 	mlxcx_buf_shard_t *s = b->mlb_shard;
2507ebb7c6fdSAlex Wilson 	mblk_t *mp = b->mlb_tx_mp;
2508ebb7c6fdSAlex Wilson 
2509ebb7c6fdSAlex Wilson 	VERIFY3U(oldstate, !=, MLXCX_BUFFER_FREE);
2510ebb7c6fdSAlex Wilson 	ASSERT3P(b->mlb_mlx, ==, mlxp);
251122d05228SPaul Winder 
251222d05228SPaul Winder 	/*
251322d05228SPaul Winder 	 * The mlbs_mtx held below is a heavily contended lock, so it is
251422d05228SPaul Winder 	 * imperative we do as much of the buffer clean up outside the lock
251522d05228SPaul Winder 	 * as is possible.
251622d05228SPaul Winder 	 */
2517ebb7c6fdSAlex Wilson 	b->mlb_state = MLXCX_BUFFER_FREE;
2518ebb7c6fdSAlex Wilson 	b->mlb_wqe_index = 0;
2519ebb7c6fdSAlex Wilson 	b->mlb_tx_head = NULL;
2520ebb7c6fdSAlex Wilson 	b->mlb_tx_mp = NULL;
2521ebb7c6fdSAlex Wilson 	b->mlb_used = 0;
252222d05228SPaul Winder 	b->mlb_wqebbs = 0;
2523ebb7c6fdSAlex Wilson 	ASSERT(list_is_empty(&b->mlb_tx_chain));
2524ebb7c6fdSAlex Wilson 
252522d05228SPaul Winder 	if (b->mlb_foreign) {
252622d05228SPaul Winder 		if (b->mlb_dma.mxdb_flags & MLXCX_DMABUF_BOUND) {
252722d05228SPaul Winder 			mlxcx_dma_unbind(mlxp, &b->mlb_dma);
252822d05228SPaul Winder 		}
252922d05228SPaul Winder 	}
253022d05228SPaul Winder 
2531ebb7c6fdSAlex Wilson 	mutex_enter(&s->mlbs_mtx);
2532ebb7c6fdSAlex Wilson 	switch (oldstate) {
2533ebb7c6fdSAlex Wilson 	case MLXCX_BUFFER_INIT:
2534*5014e1faSAlex Wilson 		mlxcx_bufshard_adjust_total(s, 1);
2535ebb7c6fdSAlex Wilson 		break;
2536ebb7c6fdSAlex Wilson 	case MLXCX_BUFFER_ON_WQ:
2537ebb7c6fdSAlex Wilson 		list_remove(&s->mlbs_busy, b);
2538ebb7c6fdSAlex Wilson 		break;
2539ebb7c6fdSAlex Wilson 	case MLXCX_BUFFER_ON_LOAN:
2540ebb7c6fdSAlex Wilson 		ASSERT(!b->mlb_foreign);
2541*5014e1faSAlex Wilson 		--s->mlbs_nloaned;
254219325e87SPaul Winder 		list_remove(&s->mlbs_loaned, b);
254319325e87SPaul Winder 		if (s->mlbs_state == MLXCX_SHARD_DRAINING) {
254419325e87SPaul Winder 			/*
254519325e87SPaul Winder 			 * When we're draining, Eg during mac_stop(),
254619325e87SPaul Winder 			 * we destroy the buffer immediately rather than
254719325e87SPaul Winder 			 * recycling it. Otherwise we risk leaving it
254819325e87SPaul Winder 			 * on the free list and leaking it.
254919325e87SPaul Winder 			 */
255019325e87SPaul Winder 			list_insert_tail(&s->mlbs_free, b);
255119325e87SPaul Winder 			mlxcx_buf_destroy(mlxp, b);
255219325e87SPaul Winder 			/*
255319325e87SPaul Winder 			 * Teardown might be waiting for loaned list to empty.
255419325e87SPaul Winder 			 */
255519325e87SPaul Winder 			cv_broadcast(&s->mlbs_free_nonempty);
255619325e87SPaul Winder 			mutex_exit(&s->mlbs_mtx);
255719325e87SPaul Winder 			return;
255819325e87SPaul Winder 		}
2559ebb7c6fdSAlex Wilson 		break;
2560ebb7c6fdSAlex Wilson 	case MLXCX_BUFFER_FREE:
2561ebb7c6fdSAlex Wilson 		VERIFY(0);
2562ebb7c6fdSAlex Wilson 		break;
2563ebb7c6fdSAlex Wilson 	case MLXCX_BUFFER_ON_CHAIN:
2564ebb7c6fdSAlex Wilson 		ASSERT(txhead != NULL);
2565ebb7c6fdSAlex Wilson 		list_remove(&txhead->mlb_tx_chain, b);
2566ebb7c6fdSAlex Wilson 		list_remove(&s->mlbs_busy, b);
2567ebb7c6fdSAlex Wilson 		break;
2568ebb7c6fdSAlex Wilson 	}
2569ebb7c6fdSAlex Wilson 
2570ebb7c6fdSAlex Wilson 	list_insert_tail(&s->mlbs_free, b);
257119325e87SPaul Winder 	cv_broadcast(&s->mlbs_free_nonempty);
2572ebb7c6fdSAlex Wilson 
2573ebb7c6fdSAlex Wilson 	mutex_exit(&s->mlbs_mtx);
2574ebb7c6fdSAlex Wilson 
2575ebb7c6fdSAlex Wilson 	/*
2576ebb7c6fdSAlex Wilson 	 * For TX chain heads, free the mblk_t after we let go of the lock.
2577ebb7c6fdSAlex Wilson 	 * This might be a borrowed buf that we in turn loaned to MAC, in which
2578ebb7c6fdSAlex Wilson 	 * case calling freemsg() on it will re-enter this very function -- so
2579ebb7c6fdSAlex Wilson 	 * we better not be holding the lock!
2580ebb7c6fdSAlex Wilson 	 */
2581ebb7c6fdSAlex Wilson 	if (txhead == b)
2582ebb7c6fdSAlex Wilson 		freemsg(mp);
2583ebb7c6fdSAlex Wilson }
2584ebb7c6fdSAlex Wilson 
2585ebb7c6fdSAlex Wilson void
mlxcx_buf_destroy(mlxcx_t * mlxp,mlxcx_buffer_t * b)2586ebb7c6fdSAlex Wilson mlxcx_buf_destroy(mlxcx_t *mlxp, mlxcx_buffer_t *b)
2587ebb7c6fdSAlex Wilson {
2588ebb7c6fdSAlex Wilson 	mlxcx_buf_shard_t *s = b->mlb_shard;
258919325e87SPaul Winder 
2590ebb7c6fdSAlex Wilson 	VERIFY(b->mlb_state == MLXCX_BUFFER_FREE ||
2591ebb7c6fdSAlex Wilson 	    b->mlb_state == MLXCX_BUFFER_INIT);
2592ebb7c6fdSAlex Wilson 	ASSERT(mutex_owned(&s->mlbs_mtx));
259319325e87SPaul Winder 
2594*5014e1faSAlex Wilson 	if (b->mlb_state == MLXCX_BUFFER_FREE) {
2595ebb7c6fdSAlex Wilson 		list_remove(&s->mlbs_free, b);
2596*5014e1faSAlex Wilson 		mlxcx_bufshard_adjust_total(s, -1);
2597*5014e1faSAlex Wilson 	}
2598ebb7c6fdSAlex Wilson 
2599ebb7c6fdSAlex Wilson 	/*
2600ebb7c6fdSAlex Wilson 	 * This is going back to the kmem cache, so it needs to be set up in
2601ebb7c6fdSAlex Wilson 	 * the same way we expect a new buffer to come out (state INIT, other
2602ebb7c6fdSAlex Wilson 	 * fields NULL'd)
2603ebb7c6fdSAlex Wilson 	 */
2604ebb7c6fdSAlex Wilson 	b->mlb_state = MLXCX_BUFFER_INIT;
2605ebb7c6fdSAlex Wilson 	b->mlb_shard = NULL;
2606ebb7c6fdSAlex Wilson 	if (b->mlb_mp != NULL) {
2607ebb7c6fdSAlex Wilson 		freeb(b->mlb_mp);
2608ebb7c6fdSAlex Wilson 		ASSERT(b->mlb_mp == NULL);
2609ebb7c6fdSAlex Wilson 	}
2610ebb7c6fdSAlex Wilson 	mlxcx_dma_free(&b->mlb_dma);
2611ebb7c6fdSAlex Wilson 	ASSERT(list_is_empty(&b->mlb_tx_chain));
2612ebb7c6fdSAlex Wilson 
2613ebb7c6fdSAlex Wilson 	kmem_cache_free(mlxp->mlx_bufs_cache, b);
2614ebb7c6fdSAlex Wilson }
261519325e87SPaul Winder 
261619325e87SPaul Winder void
mlxcx_shard_ready(mlxcx_buf_shard_t * s)261719325e87SPaul Winder mlxcx_shard_ready(mlxcx_buf_shard_t *s)
261819325e87SPaul Winder {
261919325e87SPaul Winder 	mutex_enter(&s->mlbs_mtx);
262019325e87SPaul Winder 	s->mlbs_state = MLXCX_SHARD_READY;
262119325e87SPaul Winder 	mutex_exit(&s->mlbs_mtx);
262219325e87SPaul Winder }
262319325e87SPaul Winder 
262419325e87SPaul Winder void
mlxcx_shard_draining(mlxcx_buf_shard_t * s)262519325e87SPaul Winder mlxcx_shard_draining(mlxcx_buf_shard_t *s)
262619325e87SPaul Winder {
262719325e87SPaul Winder 	mutex_enter(&s->mlbs_mtx);
262819325e87SPaul Winder 	s->mlbs_state = MLXCX_SHARD_DRAINING;
262919325e87SPaul Winder 	cv_broadcast(&s->mlbs_free_nonempty);
263019325e87SPaul Winder 	mutex_exit(&s->mlbs_mtx);
263119325e87SPaul Winder }
2632