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_ctl_setup_cq(eib_t *, eib_vnic_t *);
39 static int eib_ctl_setup_ud_channel(eib_t *, eib_vnic_t *);
40 static void eib_ctl_comp_intr(ibt_cq_hdl_t, void *);
41 static void eib_ctl_rx_comp(eib_vnic_t *, eib_wqe_t *);
42 static void eib_ctl_tx_comp(eib_vnic_t *, eib_wqe_t *);
43 static void eib_ctl_err_comp(eib_vnic_t *, eib_wqe_t *, ibt_wc_t *);
44 static void eib_rb_ctl_setup_cq(eib_t *, eib_vnic_t *);
45 static void eib_rb_ctl_setup_ud_channel(eib_t *, eib_vnic_t *);
46 
47 int
eib_ctl_create_qp(eib_t * ss,eib_vnic_t * vnic,int * err)48 eib_ctl_create_qp(eib_t *ss, eib_vnic_t *vnic, int *err)
49 {
50 	eib_chan_t *chan = NULL;
51 
52 	/*
53 	 * Allocate a eib_chan_t to store stuff about this vnic's ctl qp
54 	 * and initialize it with default admin qp pkey parameters. We'll
55 	 * re-associate this with the pkey we receive from the gw once we
56 	 * receive the login ack.
57 	 */
58 	vnic->vn_ctl_chan = eib_chan_init();
59 
60 	chan = vnic->vn_ctl_chan;
61 	chan->ch_pkey = ss->ei_admin_chan->ch_pkey;
62 	chan->ch_pkey_ix = ss->ei_admin_chan->ch_pkey_ix;
63 	chan->ch_vnic_inst = vnic->vn_instance;
64 
65 	/*
66 	 * Setup a combined CQ and completion handler
67 	 */
68 	if (eib_ctl_setup_cq(ss, vnic) != EIB_E_SUCCESS) {
69 		EIB_DPRINTF_ERR(ss->ei_instance, "eib_ctl_create_qp: "
70 		    "eib_ctl_setup_cq() failed");
71 		*err = ENOMEM;
72 		goto ctl_create_qp_fail;
73 	}
74 
75 	/*
76 	 * Setup UD channel
77 	 */
78 	if (eib_ctl_setup_ud_channel(ss, vnic) != EIB_E_SUCCESS) {
79 		EIB_DPRINTF_ERR(ss->ei_instance, "eib_ctl_create_qp: "
80 		    "eib_ctl_setup_ud_channel() failed");
81 		*err = ENOMEM;
82 		goto ctl_create_qp_fail;
83 	}
84 
85 	return (EIB_E_SUCCESS);
86 
87 ctl_create_qp_fail:
88 	eib_rb_ctl_create_qp(ss, vnic);
89 	return (EIB_E_FAILURE);
90 }
91 
92 /*ARGSUSED*/
93 uint_t
eib_ctl_comp_handler(caddr_t arg1,caddr_t arg2)94 eib_ctl_comp_handler(caddr_t arg1, caddr_t arg2)
95 {
96 	eib_vnic_t *vnic = (eib_vnic_t *)(void *)arg1;
97 	eib_chan_t *chan = vnic->vn_ctl_chan;
98 	eib_t *ss = vnic->vn_ss;
99 	ibt_wc_t *wc;
100 	eib_wqe_t *wqe;
101 	ibt_status_t ret;
102 	uint_t polled;
103 	int i;
104 
105 	/*
106 	 * Re-arm the notification callback before we start polling
107 	 * the completion queue.  There's nothing much we can do if the
108 	 * enable_cq_notify fails - we issue a warning and move on.
109 	 */
110 	ret = ibt_enable_cq_notify(chan->ch_cq_hdl, IBT_NEXT_COMPLETION);
111 	if (ret != IBT_SUCCESS) {
112 		EIB_DPRINTF_WARN(ss->ei_instance, "eib_ctl_comp_handler: "
113 		    "ibt_enable_cq_notify() failed, ret=%d", ret);
114 	}
115 
116 	/*
117 	 * Handle tx and rx completions
118 	 */
119 	while ((ret = ibt_poll_cq(chan->ch_cq_hdl, chan->ch_wc, chan->ch_cq_sz,
120 	    &polled)) == IBT_SUCCESS) {
121 		for (wc = chan->ch_wc, i = 0; i < polled; i++, wc++) {
122 			wqe = (eib_wqe_t *)(uintptr_t)wc->wc_id;
123 			if (wc->wc_status != IBT_WC_SUCCESS) {
124 				eib_ctl_err_comp(vnic, wqe, wc);
125 			} else if (EIB_WQE_TYPE(wqe->qe_info) == EIB_WQE_RX) {
126 				eib_ctl_rx_comp(vnic, wqe);
127 			} else {
128 				eib_ctl_tx_comp(vnic, wqe);
129 			}
130 		}
131 	}
132 
133 	return (DDI_INTR_CLAIMED);
134 }
135 
136 void
eib_rb_ctl_create_qp(eib_t * ss,eib_vnic_t * vnic)137 eib_rb_ctl_create_qp(eib_t *ss, eib_vnic_t *vnic)
138 {
139 	eib_rb_ctl_setup_ud_channel(ss, vnic);
140 
141 	eib_rb_ctl_setup_cq(ss, vnic);
142 
143 	eib_chan_fini(vnic->vn_ctl_chan);
144 	vnic->vn_ctl_chan = NULL;
145 }
146 
147 static int
eib_ctl_setup_cq(eib_t * ss,eib_vnic_t * vnic)148 eib_ctl_setup_cq(eib_t *ss, eib_vnic_t *vnic)
149 {
150 	eib_chan_t *chan = vnic->vn_ctl_chan;
151 	ibt_cq_attr_t cq_attr;
152 	ibt_status_t ret;
153 	uint_t sz;
154 	int rv;
155 
156 	/*
157 	 * Allocate a completion queue for sending vhub table request
158 	 * and vhub-update/vnic-alive messages and responses from the
159 	 * gateway
160 	 */
161 	cq_attr.cq_sched = NULL;
162 	cq_attr.cq_flags = IBT_CQ_NO_FLAGS;
163 	if (ss->ei_hca_attrs->hca_max_cq_sz < EIB_CTL_CQ_SIZE)
164 		cq_attr.cq_size = ss->ei_hca_attrs->hca_max_cq_sz;
165 	else
166 		cq_attr.cq_size = EIB_CTL_CQ_SIZE;
167 
168 	ret = ibt_alloc_cq(ss->ei_hca_hdl, &cq_attr, &chan->ch_cq_hdl, &sz);
169 	if (ret != IBT_SUCCESS) {
170 		EIB_DPRINTF_ERR(ss->ei_instance, "eib_ctl_setup_cq: "
171 		    "ibt_alloc_cq(cq_sz=0x%lx) failed, ret=%d",
172 		    cq_attr.cq_size, ret);
173 		goto ctl_setup_cq_fail;
174 	}
175 
176 	/*
177 	 * Set up other parameters for collecting completion information
178 	 */
179 	chan->ch_cq_sz = sz;
180 	chan->ch_wc = kmem_zalloc(sizeof (ibt_wc_t) * sz, KM_SLEEP);
181 
182 	/*
183 	 * Allocate soft interrupt for this vnic's control channel cq
184 	 * handler and set up the IBTL cq handler.
185 	 */
186 	if ((rv = ddi_intr_add_softint(ss->ei_dip, &vnic->vn_ctl_si_hdl,
187 	    EIB_SOFTPRI_CTL, eib_ctl_comp_handler, vnic)) != DDI_SUCCESS) {
188 		EIB_DPRINTF_ERR(ss->ei_instance, "eib_ctl_setup_cq: "
189 		    "ddi_intr_add_softint() failed for vnic %d ctl qp, ret=%d",
190 		    vnic->vn_instance, rv);
191 		goto ctl_setup_cq_fail;
192 	}
193 
194 	/*
195 	 * Now, set up this vnic's control channel completion queue handler
196 	 */
197 	ibt_set_cq_handler(chan->ch_cq_hdl, eib_ctl_comp_intr, vnic);
198 
199 	ret = ibt_enable_cq_notify(chan->ch_cq_hdl, IBT_NEXT_COMPLETION);
200 	if (ret != IBT_SUCCESS) {
201 		EIB_DPRINTF_ERR(ss->ei_instance, "eib_ctl_setup_cq: "
202 		    "ibt_enable_cq_notify() failed, ret=%d", ret);
203 		goto ctl_setup_cq_fail;
204 	}
205 
206 	return (EIB_E_SUCCESS);
207 
208 ctl_setup_cq_fail:
209 	eib_rb_ctl_setup_cq(ss, vnic);
210 	return (EIB_E_FAILURE);
211 }
212 
213 static int
eib_ctl_setup_ud_channel(eib_t * ss,eib_vnic_t * vnic)214 eib_ctl_setup_ud_channel(eib_t *ss, eib_vnic_t *vnic)
215 {
216 	eib_chan_t *chan = vnic->vn_ctl_chan;
217 	ibt_ud_chan_alloc_args_t alloc_attr;
218 	ibt_ud_chan_query_attr_t query_attr;
219 	ibt_status_t ret;
220 
221 	bzero(&alloc_attr, sizeof (ibt_ud_chan_alloc_args_t));
222 	bzero(&query_attr, sizeof (ibt_ud_chan_query_attr_t));
223 
224 	alloc_attr.ud_flags = IBT_ALL_SIGNALED;
225 	alloc_attr.ud_hca_port_num = ss->ei_props->ep_port_num;
226 	alloc_attr.ud_pkey_ix = chan->ch_pkey_ix;
227 	alloc_attr.ud_sizes.cs_sq = EIB_CTL_MAX_SWQE;
228 	alloc_attr.ud_sizes.cs_rq = EIB_CTL_MAX_RWQE;
229 	alloc_attr.ud_sizes.cs_sq_sgl = 1;
230 	alloc_attr.ud_sizes.cs_rq_sgl = 1;
231 	alloc_attr.ud_sizes.cs_inline = 0;
232 
233 	alloc_attr.ud_qkey = EIB_FIP_QKEY;
234 	alloc_attr.ud_scq = chan->ch_cq_hdl;
235 	alloc_attr.ud_rcq = chan->ch_cq_hdl;
236 	alloc_attr.ud_pd = ss->ei_pd_hdl;
237 
238 	ret = ibt_alloc_ud_channel(ss->ei_hca_hdl, IBT_ACHAN_NO_FLAGS,
239 	    &alloc_attr, &chan->ch_chan, NULL);
240 	if (ret != IBT_SUCCESS) {
241 		EIB_DPRINTF_ERR(ss->ei_instance, "eib_ctl_setup_ud_channel: "
242 		    "ibt_alloc_ud_channel(port=0x%x, pkey_ix=0x%x) "
243 		    "failed, ret=%d", alloc_attr.ud_hca_port_num,
244 		    chan->ch_pkey_ix, ret);
245 		goto ctl_setup_ud_channel_fail;
246 	}
247 
248 	ret = ibt_query_ud_channel(chan->ch_chan, &query_attr);
249 	if (ret != IBT_SUCCESS) {
250 		EIB_DPRINTF_ERR(ss->ei_instance, "eib_ctl_setup_ud_channel: "
251 		    "ibt_query_ud_channel() failed, ret=%d", ret);
252 		goto ctl_setup_ud_channel_fail;
253 	}
254 
255 	chan->ch_qpn = query_attr.ud_qpn;
256 	chan->ch_max_swqes = query_attr.ud_chan_sizes.cs_sq;
257 	chan->ch_max_rwqes = query_attr.ud_chan_sizes.cs_rq;
258 	chan->ch_lwm_rwqes = chan->ch_max_rwqes >> 2;
259 	chan->ch_rwqe_bktsz = chan->ch_max_rwqes;
260 	chan->ch_ip_hdr_align = 0;
261 	chan->ch_alloc_mp = B_FALSE;
262 	chan->ch_tear_down = B_FALSE;
263 
264 	return (EIB_E_SUCCESS);
265 
266 ctl_setup_ud_channel_fail:
267 	eib_rb_ctl_setup_ud_channel(ss, vnic);
268 	return (EIB_E_FAILURE);
269 }
270 
271 static void
eib_ctl_comp_intr(ibt_cq_hdl_t cq_hdl,void * arg)272 eib_ctl_comp_intr(ibt_cq_hdl_t cq_hdl, void *arg)
273 {
274 	eib_vnic_t *vnic = arg;
275 	eib_t *ss = vnic->vn_ss;
276 	eib_chan_t *chan = vnic->vn_ctl_chan;
277 
278 	if (cq_hdl != chan->ch_cq_hdl) {
279 		EIB_DPRINTF_DEBUG(ss->ei_instance, "eib_ctl_comp_intr: "
280 		    "cq_hdl(0x%llx) != chan->ch_cq_hdl(0x%llx), "
281 		    "ignoring completion", cq_hdl, chan->ch_cq_hdl);
282 		return;
283 	}
284 
285 	ASSERT(vnic->vn_ctl_si_hdl != NULL);
286 
287 	(void) ddi_intr_trigger_softint(vnic->vn_ctl_si_hdl, NULL);
288 }
289 
290 static void
eib_ctl_rx_comp(eib_vnic_t * vnic,eib_wqe_t * wqe)291 eib_ctl_rx_comp(eib_vnic_t *vnic, eib_wqe_t *wqe)
292 {
293 	eib_t *ss = vnic->vn_ss;
294 	eib_chan_t *chan = vnic->vn_ctl_chan;
295 	uint8_t *pkt = (uint8_t *)(uintptr_t)(wqe->qe_sgl.ds_va);
296 	ibt_status_t ret;
297 
298 	/*
299 	 * Skip the GRH and parse the message in the packet
300 	 */
301 	(void) eib_fip_parse_ctl_pkt(pkt + EIB_GRH_SZ, vnic);
302 
303 	/*
304 	 * Try to repost the rwqe.  For control channels, we take the shortcut
305 	 * and not go through eib_chan_post_recv(), since we know that the
306 	 * qe_info flag, qe_chan and qe_vinst are all already set correctly; we
307 	 * just took this out of the rx queue, so the ch_rx_posted will be ok
308 	 * if we just posted it back. And there are no mblk allocation or
309 	 * buffer alignment restrictions for this channel as well.
310 	 */
311 	if (chan->ch_tear_down) {
312 		eib_rsrc_return_rwqe(ss, wqe, chan);
313 	} else {
314 		ret = ibt_post_recv(chan->ch_chan, &(wqe->qe_wr.recv), 1, NULL);
315 		if (ret != IBT_SUCCESS) {
316 			EIB_DPRINTF_ERR(ss->ei_instance, "eib_ctl_rx_comp: "
317 			    "ibt_post_recv() failed, ret=%d", ret);
318 			eib_rsrc_return_rwqe(ss, wqe, chan);
319 		}
320 	}
321 }
322 
323 static void
eib_ctl_tx_comp(eib_vnic_t * vnic,eib_wqe_t * wqe)324 eib_ctl_tx_comp(eib_vnic_t *vnic, eib_wqe_t *wqe)
325 {
326 	eib_rsrc_return_swqe(vnic->vn_ss, wqe, vnic->vn_ctl_chan);
327 }
328 
329 static void
eib_ctl_err_comp(eib_vnic_t * vnic,eib_wqe_t * wqe,ibt_wc_t * wc)330 eib_ctl_err_comp(eib_vnic_t *vnic, eib_wqe_t *wqe, ibt_wc_t *wc)
331 {
332 	eib_t *ss = vnic->vn_ss;
333 
334 	/*
335 	 * Currently, all we do is report
336 	 */
337 	switch (wc->wc_status) {
338 	case IBT_WC_WR_FLUSHED_ERR:
339 		break;
340 
341 	case IBT_WC_LOCAL_CHAN_OP_ERR:
342 		EIB_DPRINTF_ERR(ss->ei_instance, "eib_ctl_err_comp: "
343 		    "IBT_WC_LOCAL_CHAN_OP_ERR seen, wqe_info=0x%lx ",
344 		    wqe->qe_info);
345 		break;
346 
347 	case IBT_WC_LOCAL_PROTECT_ERR:
348 		EIB_DPRINTF_ERR(ss->ei_instance, "eib_ctl_err_comp: "
349 		    "IBT_WC_LOCAL_PROTECT_ERR seen, wqe_info=0x%lx ",
350 		    wqe->qe_info);
351 		break;
352 	}
353 
354 	/*
355 	 * When a wc indicates error, we do not attempt to repost but
356 	 * simply return it to the wqe pool.
357 	 */
358 	if (EIB_WQE_TYPE(wqe->qe_info) == EIB_WQE_RX)
359 		eib_rsrc_return_rwqe(ss, wqe, vnic->vn_ctl_chan);
360 	else
361 		eib_rsrc_return_swqe(ss, wqe, vnic->vn_ctl_chan);
362 }
363 
364 /*ARGSUSED*/
365 static void
eib_rb_ctl_setup_cq(eib_t * ss,eib_vnic_t * vnic)366 eib_rb_ctl_setup_cq(eib_t *ss, eib_vnic_t *vnic)
367 {
368 	eib_chan_t *chan = vnic->vn_ctl_chan;
369 	ibt_status_t ret;
370 
371 	if (chan == NULL)
372 		return;
373 
374 	/*
375 	 * Reset any completion handler we may have set up
376 	 */
377 	if (chan->ch_cq_hdl)
378 		ibt_set_cq_handler(chan->ch_cq_hdl, NULL, NULL);
379 
380 	/*
381 	 * Remove any softint we may have allocated for this cq
382 	 */
383 	if (vnic->vn_ctl_si_hdl) {
384 		(void) ddi_intr_remove_softint(vnic->vn_ctl_si_hdl);
385 		vnic->vn_ctl_si_hdl = NULL;
386 	}
387 
388 	/*
389 	 * Release any work completion buffers we may have allocated
390 	 */
391 	if (chan->ch_wc && chan->ch_cq_sz)
392 		kmem_free(chan->ch_wc, sizeof (ibt_wc_t) * chan->ch_cq_sz);
393 
394 	chan->ch_cq_sz = 0;
395 	chan->ch_wc = NULL;
396 
397 	/*
398 	 * Free any completion queue we may have allocated
399 	 */
400 	if (chan->ch_cq_hdl) {
401 		ret = ibt_free_cq(chan->ch_cq_hdl);
402 		if (ret != IBT_SUCCESS) {
403 			EIB_DPRINTF_WARN(ss->ei_instance,
404 			    "eib_rb_ctl_setup_cq: "
405 			    "ibt_free_cq() failed, ret=%d", ret);
406 		}
407 		chan->ch_cq_hdl = NULL;
408 	}
409 }
410 
411 /*ARGSUSED*/
412 static void
eib_rb_ctl_setup_ud_channel(eib_t * ss,eib_vnic_t * vnic)413 eib_rb_ctl_setup_ud_channel(eib_t *ss, eib_vnic_t *vnic)
414 {
415 	eib_chan_t *chan = vnic->vn_ctl_chan;
416 	ibt_status_t ret;
417 
418 	if (chan == NULL)
419 		return;
420 
421 	if (chan->ch_chan) {
422 		/*
423 		 * We're trying to tear down this UD channel. Make sure that
424 		 * we don't attempt to refill (repost) at any point from now on.
425 		 */
426 		chan->ch_tear_down = B_TRUE;
427 		if ((ret = ibt_flush_channel(chan->ch_chan)) != IBT_SUCCESS) {
428 			EIB_DPRINTF_WARN(ss->ei_instance,
429 			    "eib_rb_ctl_setup_ud_channel: "
430 			    "ibt_flush_channel() failed, ret=%d", ret);
431 		}
432 
433 		/*
434 		 * Wait until all posted tx wqes on this channel are back with
435 		 * the wqe pool.
436 		 */
437 		mutex_enter(&chan->ch_tx_lock);
438 		while (chan->ch_tx_posted > 0)
439 			cv_wait(&chan->ch_tx_cv, &chan->ch_tx_lock);
440 		mutex_exit(&chan->ch_tx_lock);
441 
442 		/*
443 		 * Wait until all posted rx wqes on this channel are back with
444 		 * the wqe pool.
445 		 */
446 		mutex_enter(&chan->ch_rx_lock);
447 		while (chan->ch_rx_posted > 0)
448 			cv_wait(&chan->ch_rx_cv, &chan->ch_rx_lock);
449 		mutex_exit(&chan->ch_rx_lock);
450 
451 		/*
452 		 * Now we're ready to free this channel
453 		 */
454 		if ((ret = ibt_free_channel(chan->ch_chan)) != IBT_SUCCESS) {
455 			EIB_DPRINTF_WARN(ss->ei_instance,
456 			    "eib_rb_ctl_setup_ud_channel: "
457 			    "ibt_free_channel() failed, ret=%d", ret);
458 		}
459 
460 		chan->ch_alloc_mp = B_FALSE;
461 		chan->ch_ip_hdr_align = 0;
462 		chan->ch_rwqe_bktsz = 0;
463 		chan->ch_lwm_rwqes = 0;
464 		chan->ch_max_rwqes = 0;
465 		chan->ch_max_swqes = 0;
466 		chan->ch_qpn = 0;
467 		chan->ch_chan = NULL;
468 	}
469 }
470