xref: /illumos-gate/usr/src/uts/common/io/ib/clients/eoib/eib_adm.c (revision b494511a9cf72b1fc4eb13a0e593f55c624ab829)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 
22 /*
23  * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
24  */
25 
26 #include <sys/types.h>
27 #include <sys/kmem.h>
28 #include <sys/conf.h>
29 #include <sys/ddi.h>
30 #include <sys/sunddi.h>
31 #include <sys/ksynch.h>
32 
33 #include <sys/ib/clients/eoib/eib_impl.h>
34 
35 /*
36  * Declarations private to this file
37  */
38 static int eib_adm_setup_cq(eib_t *);
39 static int eib_adm_setup_ud_channel(eib_t *);
40 static void eib_adm_comp_intr(ibt_cq_hdl_t, void *);
41 static void eib_adm_rx_comp(eib_t *, eib_wqe_t *);
42 static void eib_adm_tx_comp(eib_t *, eib_wqe_t *);
43 static void eib_adm_err_comp(eib_t *, eib_wqe_t *, ibt_wc_t *);
44 static void eib_rb_adm_setup_cq(eib_t *);
45 static void eib_rb_adm_setup_ud_channel(eib_t *);
46 
47 int
48 eib_adm_setup_qp(eib_t *ss, int *err)
49 {
50 	eib_chan_t *chan;
51 	ibt_status_t ret;
52 	uint16_t pkey_ix;
53 
54 	/*
55 	 * Verify pkey
56 	 */
57 	ret = ibt_pkey2index(ss->ei_hca_hdl, ss->ei_props->ep_port_num,
58 	    EIB_ADMIN_PKEY, &pkey_ix);
59 	if (ret != IBT_SUCCESS) {
60 		EIB_DPRINTF_ERR(ss->ei_instance, "eib_adm_setup_qp: "
61 		    "ibt_pkey2index() failed, port_num=0x%x, "
62 		    "pkey=0x%x, ret=%d", ss->ei_props->ep_port_num,
63 		    EIB_ADMIN_PKEY, ret);
64 		*err = ENONET;
65 		goto adm_setup_qp_fail;
66 	}
67 
68 	/*
69 	 * Allocate a eib_chan_t to store stuff about admin qp and
70 	 * initialize some basic stuff
71 	 */
72 	ss->ei_admin_chan = eib_chan_init();
73 
74 	chan = ss->ei_admin_chan;
75 	chan->ch_pkey = EIB_ADMIN_PKEY;
76 	chan->ch_pkey_ix = pkey_ix;
77 	chan->ch_vnic_inst = -1;
78 
79 	/*
80 	 * Setup a combined CQ and completion handler
81 	 */
82 	if (eib_adm_setup_cq(ss) != EIB_E_SUCCESS) {
83 		EIB_DPRINTF_ERR(ss->ei_instance, "eib_adm_setup_qp: "
84 		    "eib_adm_setup_cq() failed");
85 		*err = ENOMEM;
86 		goto adm_setup_qp_fail;
87 	}
88 
89 	/*
90 	 * Setup UD channel
91 	 */
92 	if (eib_adm_setup_ud_channel(ss) != EIB_E_SUCCESS) {
93 		EIB_DPRINTF_ERR(ss->ei_instance, "eib_adm_setup_qp: "
94 		    "eib_adm_setup_ud_channel() failed");
95 		*err = ENOMEM;
96 		goto adm_setup_qp_fail;
97 	}
98 
99 	/*
100 	 * Post initial set of rx buffers to the HCA
101 	 */
102 	if (eib_chan_post_rx(ss, chan, NULL) != EIB_E_SUCCESS) {
103 		EIB_DPRINTF_ERR(ss->ei_instance, "eib_adm_setup_qp: "
104 		    "eib_chan_post_rx() failed");
105 		*err = ENOMEM;
106 		goto adm_setup_qp_fail;
107 	}
108 
109 	return (EIB_E_SUCCESS);
110 
111 adm_setup_qp_fail:
112 	eib_rb_adm_setup_qp(ss);
113 	return (EIB_E_FAILURE);
114 }
115 
116 /*ARGSUSED*/
117 uint_t
118 eib_adm_comp_handler(caddr_t arg1, caddr_t arg2)
119 {
120 	eib_t *ss = (eib_t *)(void *)arg1;
121 	eib_chan_t *chan = ss->ei_admin_chan;
122 	ibt_wc_t *wc;
123 	eib_wqe_t *wqe;
124 	ibt_status_t ret;
125 	uint_t polled;
126 	int i;
127 
128 	/*
129 	 * Re-arm the notification callback before we start polling
130 	 * the completion queue.  There's nothing much we can do if the
131 	 * enable_cq_notify fails - we issue a warning and move on.
132 	 */
133 	ret = ibt_enable_cq_notify(chan->ch_cq_hdl, IBT_NEXT_COMPLETION);
134 	if (ret != IBT_SUCCESS) {
135 		EIB_DPRINTF_WARN(ss->ei_instance, "eib_adm_comp_handler: "
136 		    "ibt_enable_cq_notify() failed, ret=%d", ret);
137 	}
138 
139 	/*
140 	 * Handle tx and rx completions
141 	 */
142 	while ((ret = ibt_poll_cq(chan->ch_cq_hdl, chan->ch_wc, chan->ch_cq_sz,
143 	    &polled)) == IBT_SUCCESS) {
144 		for (wc = chan->ch_wc, i = 0; i < polled; i++, wc++) {
145 			wqe = (eib_wqe_t *)(uintptr_t)wc->wc_id;
146 			if (wc->wc_status != IBT_WC_SUCCESS) {
147 				eib_adm_err_comp(ss, wqe, wc);
148 			} else if (EIB_WQE_TYPE(wqe->qe_info) == EIB_WQE_RX) {
149 				eib_adm_rx_comp(ss, wqe);
150 			} else {
151 				eib_adm_tx_comp(ss, wqe);
152 			}
153 		}
154 	}
155 
156 	return (DDI_INTR_CLAIMED);
157 }
158 
159 void
160 eib_rb_adm_setup_qp(eib_t *ss)
161 {
162 	eib_rb_adm_setup_ud_channel(ss);
163 
164 	eib_rb_adm_setup_cq(ss);
165 
166 	eib_chan_fini(ss->ei_admin_chan);
167 	ss->ei_admin_chan = NULL;
168 }
169 
170 static int
171 eib_adm_setup_cq(eib_t *ss)
172 {
173 	eib_chan_t *chan = ss->ei_admin_chan;
174 	ibt_cq_attr_t cq_attr;
175 	ibt_status_t ret;
176 	uint_t sz;
177 	int rv;
178 
179 	/*
180 	 * Allocate the admin completion queue for sending vnic logins and
181 	 * logouts and receiving vnic login acks.
182 	 */
183 	cq_attr.cq_sched = NULL;
184 	cq_attr.cq_flags = IBT_CQ_NO_FLAGS;
185 	if (ss->ei_hca_attrs->hca_max_cq_sz < EIB_ADMIN_CQ_SIZE)
186 		cq_attr.cq_size = ss->ei_hca_attrs->hca_max_cq_sz;
187 	else
188 		cq_attr.cq_size = EIB_ADMIN_CQ_SIZE;
189 
190 	ret = ibt_alloc_cq(ss->ei_hca_hdl, &cq_attr, &chan->ch_cq_hdl, &sz);
191 	if (ret != IBT_SUCCESS) {
192 		EIB_DPRINTF_ERR(ss->ei_instance, "eib_adm_setup_cq: "
193 		    "ibt_alloc_cq(cq_sz=0x%lx) failed, ret=%d",
194 		    cq_attr.cq_size, ret);
195 		goto adm_setup_cq_fail;
196 	}
197 
198 	/*
199 	 * Set up other parameters for collecting completion information
200 	 */
201 	chan->ch_cq_sz = sz;
202 	chan->ch_wc = kmem_zalloc(sizeof (ibt_wc_t) * sz, KM_SLEEP);
203 
204 	/*
205 	 * Allocate soft interrupt for the admin channel cq handler and
206 	 * set up the handler as well.
207 	 */
208 	if ((rv = ddi_intr_add_softint(ss->ei_dip, &ss->ei_admin_si_hdl,
209 	    EIB_SOFTPRI_ADM, eib_adm_comp_handler, ss)) != DDI_SUCCESS) {
210 		EIB_DPRINTF_ERR(ss->ei_instance, "eib_adm_setup_cq: "
211 		    "ddi_intr_add_softint() failed for adm qp, ret=%d", rv);
212 		goto adm_setup_cq_fail;
213 	}
214 
215 	/*
216 	 * Now, set up the admin completion queue handler.
217 	 */
218 	ibt_set_cq_handler(chan->ch_cq_hdl, eib_adm_comp_intr, ss);
219 
220 	ret = ibt_enable_cq_notify(chan->ch_cq_hdl, IBT_NEXT_COMPLETION);
221 	if (ret != IBT_SUCCESS) {
222 		EIB_DPRINTF_ERR(ss->ei_instance, "eib_adm_setup_cq: "
223 		    "ibt_enable_cq_notify() failed, ret=%d", ret);
224 		goto adm_setup_cq_fail;
225 	}
226 
227 	return (EIB_E_SUCCESS);
228 
229 adm_setup_cq_fail:
230 	eib_rb_adm_setup_cq(ss);
231 	return (EIB_E_FAILURE);
232 }
233 
234 static int
235 eib_adm_setup_ud_channel(eib_t *ss)
236 {
237 	eib_chan_t *chan = ss->ei_admin_chan;
238 	ibt_ud_chan_alloc_args_t alloc_attr;
239 	ibt_ud_chan_query_attr_t query_attr;
240 	ibt_status_t ret;
241 
242 	bzero(&alloc_attr, sizeof (ibt_ud_chan_alloc_args_t));
243 	bzero(&query_attr, sizeof (ibt_ud_chan_query_attr_t));
244 
245 	alloc_attr.ud_flags = IBT_ALL_SIGNALED;
246 	alloc_attr.ud_hca_port_num = ss->ei_props->ep_port_num;
247 	alloc_attr.ud_pkey_ix = chan->ch_pkey_ix;
248 	alloc_attr.ud_sizes.cs_sq = EIB_ADMIN_MAX_SWQE;
249 	alloc_attr.ud_sizes.cs_rq = EIB_ADMIN_MAX_RWQE;
250 	alloc_attr.ud_sizes.cs_sq_sgl = 1;
251 	alloc_attr.ud_sizes.cs_rq_sgl = 1;
252 	alloc_attr.ud_sizes.cs_inline = 0;
253 
254 	alloc_attr.ud_qkey = EIB_FIP_QKEY;
255 	alloc_attr.ud_scq = chan->ch_cq_hdl;
256 	alloc_attr.ud_rcq = chan->ch_cq_hdl;
257 	alloc_attr.ud_pd = ss->ei_pd_hdl;
258 
259 	ret = ibt_alloc_ud_channel(ss->ei_hca_hdl, IBT_ACHAN_NO_FLAGS,
260 	    &alloc_attr, &chan->ch_chan, NULL);
261 	if (ret != IBT_SUCCESS) {
262 		EIB_DPRINTF_ERR(ss->ei_instance, "eib_adm_setup_ud_channel: "
263 		    "ibt_alloc_ud_channel(port=0x%x, pkey_ix=0x%x) "
264 		    "failed, ret=%d", alloc_attr.ud_hca_port_num,
265 		    chan->ch_pkey_ix, ret);
266 		goto adm_setup_ud_channel_fail;
267 	}
268 
269 	ret = ibt_query_ud_channel(chan->ch_chan, &query_attr);
270 	if (ret != IBT_SUCCESS) {
271 		EIB_DPRINTF_ERR(ss->ei_instance, "eib_adm_setup_ud_channel: "
272 		    "ibt_query_ud_channel() failed, ret=%d", ret);
273 		goto adm_setup_ud_channel_fail;
274 	}
275 
276 	chan->ch_qpn = query_attr.ud_qpn;
277 	chan->ch_max_swqes = query_attr.ud_chan_sizes.cs_sq;
278 	chan->ch_max_rwqes = query_attr.ud_chan_sizes.cs_rq;
279 	chan->ch_lwm_rwqes = chan->ch_max_rwqes >> 2;
280 	chan->ch_rwqe_bktsz = chan->ch_max_rwqes;
281 	chan->ch_ip_hdr_align = 0;
282 	chan->ch_alloc_mp = B_FALSE;
283 	chan->ch_tear_down = B_FALSE;
284 
285 	return (EIB_E_SUCCESS);
286 
287 adm_setup_ud_channel_fail:
288 	eib_rb_adm_setup_ud_channel(ss);
289 	return (EIB_E_FAILURE);
290 }
291 
292 static void
293 eib_adm_comp_intr(ibt_cq_hdl_t cq_hdl, void *arg)
294 {
295 	eib_t *ss = arg;
296 	eib_chan_t *chan = ss->ei_admin_chan;
297 
298 	if (cq_hdl != chan->ch_cq_hdl) {
299 		EIB_DPRINTF_DEBUG(ss->ei_instance, "eib_adm_comp_intr: "
300 		    "cq_hdl(0x%llx) != chan->ch_cq_hdl(0x%llx), "
301 		    "ignoring completion", cq_hdl, chan->ch_cq_hdl);
302 		return;
303 	}
304 
305 	ASSERT(ss->ei_admin_si_hdl != NULL);
306 
307 	(void) ddi_intr_trigger_softint(ss->ei_admin_si_hdl, NULL);
308 }
309 
310 static void
311 eib_adm_rx_comp(eib_t *ss, eib_wqe_t *wqe)
312 {
313 	eib_chan_t *chan = ss->ei_admin_chan;
314 	eib_login_data_t ld;
315 	uint8_t *pkt = (uint8_t *)(uintptr_t)(wqe->qe_sgl.ds_va);
316 	ibt_status_t ret;
317 
318 	/*
319 	 * Skip the GRH and parse the login ack message in the packet
320 	 */
321 	if (eib_fip_parse_login_ack(ss, pkt + EIB_GRH_SZ, &ld) == EIB_E_SUCCESS)
322 		eib_vnic_login_ack(ss, &ld);
323 
324 	/*
325 	 * Try to repost the rwqe.  For admin channel, we can take the shortcut
326 	 * and not go through eib_chan_post_recv(), since we know that the
327 	 * qe_info flag, qe_chan and qe_vinst are all already set correctly; we
328 	 * just took this out of the rx queue, so the ch_rx_posted will be ok
329 	 * if we just posted it back. And there are no mblk allocation or
330 	 * buffer alignment restrictions for this channel as well.
331 	 */
332 	if (chan->ch_tear_down) {
333 		eib_rsrc_return_rwqe(ss, wqe, chan);
334 	} else {
335 		ret = ibt_post_recv(chan->ch_chan, &(wqe->qe_wr.recv), 1, NULL);
336 		if (ret != IBT_SUCCESS) {
337 			EIB_DPRINTF_ERR(ss->ei_instance, "eib_adm_rx_comp: "
338 			    "ibt_post_recv() failed, ret=%d", ret);
339 			eib_rsrc_return_rwqe(ss, wqe, chan);
340 		}
341 	}
342 }
343 
344 static void
345 eib_adm_tx_comp(eib_t *ss, eib_wqe_t *wqe)
346 {
347 	eib_rsrc_return_swqe(ss, wqe, ss->ei_admin_chan);
348 }
349 
350 /*ARGSUSED*/
351 static void
352 eib_adm_err_comp(eib_t *ss, eib_wqe_t *wqe, ibt_wc_t *wc)
353 {
354 	/*
355 	 * Currently, all we do is report
356 	 */
357 	switch (wc->wc_status) {
358 	case IBT_WC_WR_FLUSHED_ERR:
359 		break;
360 
361 	case IBT_WC_LOCAL_CHAN_OP_ERR:
362 		EIB_DPRINTF_ERR(ss->ei_instance, "eib_adm_err_comp: "
363 		    "IBT_WC_LOCAL_CHAN_OP_ERR seen, wqe_info=0x%lx ",
364 		    wqe->qe_info);
365 		break;
366 
367 	case IBT_WC_LOCAL_PROTECT_ERR:
368 		EIB_DPRINTF_ERR(ss->ei_instance, "eib_adm_err_comp: "
369 		    "IBT_WC_LOCAL_PROTECT_ERR seen, wqe_info=0x%lx ",
370 		    wqe->qe_info);
371 		break;
372 	}
373 
374 	/*
375 	 * When a wc indicates error, we do not attempt to repost but
376 	 * simply return it to the wqe pool.
377 	 */
378 	if (EIB_WQE_TYPE(wqe->qe_info) == EIB_WQE_RX)
379 		eib_rsrc_return_rwqe(ss, wqe, ss->ei_admin_chan);
380 	else
381 		eib_rsrc_return_swqe(ss, wqe, ss->ei_admin_chan);
382 }
383 
384 static void
385 eib_rb_adm_setup_cq(eib_t *ss)
386 {
387 	eib_chan_t *chan = ss->ei_admin_chan;
388 	ibt_status_t ret;
389 
390 	if (chan == NULL)
391 		return;
392 
393 	/*
394 	 * Reset any completion handler we may have set up
395 	 */
396 	if (chan->ch_cq_hdl)
397 		ibt_set_cq_handler(chan->ch_cq_hdl, NULL, NULL);
398 
399 	/*
400 	 * Remove any softint we may have allocated for the admin cq
401 	 */
402 	if (ss->ei_admin_si_hdl) {
403 		(void) ddi_intr_remove_softint(ss->ei_admin_si_hdl);
404 		ss->ei_admin_si_hdl = NULL;
405 	}
406 
407 	/*
408 	 * Release any work completion buffers we may have allocated
409 	 */
410 	if (chan->ch_wc && chan->ch_cq_sz)
411 		kmem_free(chan->ch_wc, sizeof (ibt_wc_t) * chan->ch_cq_sz);
412 
413 	chan->ch_cq_sz = 0;
414 	chan->ch_wc = NULL;
415 
416 	/*
417 	 * Free any completion queue we may have allocated
418 	 */
419 	if (chan->ch_cq_hdl) {
420 		ret = ibt_free_cq(chan->ch_cq_hdl);
421 		if (ret != IBT_SUCCESS) {
422 			EIB_DPRINTF_WARN(ss->ei_instance,
423 			    "eib_rb_adm_setup_cq: "
424 			    "ibt_free_cq() failed, ret=%d", ret);
425 		}
426 		chan->ch_cq_hdl = NULL;
427 	}
428 }
429 
430 static void
431 eib_rb_adm_setup_ud_channel(eib_t *ss)
432 {
433 	eib_chan_t *chan = ss->ei_admin_chan;
434 	ibt_status_t ret;
435 
436 	if (chan == NULL)
437 		return;
438 
439 	if (chan->ch_chan) {
440 		/*
441 		 * We're trying to tear down this UD channel. Make sure that
442 		 * we don't attempt to refill (repost) at any point from now on.
443 		 */
444 		chan->ch_tear_down = B_TRUE;
445 		if ((ret = ibt_flush_channel(chan->ch_chan)) != IBT_SUCCESS) {
446 			EIB_DPRINTF_WARN(ss->ei_instance,
447 			    "eib_rb_adm_setup_ud_channel: "
448 			    "ibt_flush_channel() failed, ret=%d", ret);
449 		}
450 
451 		/*
452 		 * Wait until all posted tx wqes on this channel are back with
453 		 * the wqe pool.
454 		 */
455 		mutex_enter(&chan->ch_tx_lock);
456 		while (chan->ch_tx_posted > 0)
457 			cv_wait(&chan->ch_tx_cv, &chan->ch_tx_lock);
458 		mutex_exit(&chan->ch_tx_lock);
459 
460 		/*
461 		 * Wait until all posted rx wqes on this channel are back with
462 		 * the wqe pool.
463 		 */
464 		mutex_enter(&chan->ch_rx_lock);
465 		while (chan->ch_rx_posted > 0)
466 			cv_wait(&chan->ch_rx_cv, &chan->ch_rx_lock);
467 		mutex_exit(&chan->ch_rx_lock);
468 
469 		/*
470 		 * Now we're ready to free this channel
471 		 */
472 		if ((ret = ibt_free_channel(chan->ch_chan)) != IBT_SUCCESS) {
473 			EIB_DPRINTF_WARN(ss->ei_instance,
474 			    "eib_rb_adm_setup_ud_channel: "
475 			    "ibt_free_channel() failed, ret=%d", ret);
476 		}
477 
478 		chan->ch_alloc_mp = B_FALSE;
479 		chan->ch_ip_hdr_align = 0;
480 		chan->ch_rwqe_bktsz = 0;
481 		chan->ch_lwm_rwqes = 0;
482 		chan->ch_max_rwqes = 0;
483 		chan->ch_max_swqes = 0;
484 		chan->ch_qpn = 0;
485 		chan->ch_chan = NULL;
486 	}
487 }
488