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 */
38static int eib_ctl_setup_cq(eib_t *, eib_vnic_t *);
39static int eib_ctl_setup_ud_channel(eib_t *, eib_vnic_t *);
40static void eib_ctl_comp_intr(ibt_cq_hdl_t, void *);
41static void eib_ctl_rx_comp(eib_vnic_t *, eib_wqe_t *);
42static void eib_ctl_tx_comp(eib_vnic_t *, eib_wqe_t *);
43static void eib_ctl_err_comp(eib_vnic_t *, eib_wqe_t *, ibt_wc_t *);
44static void eib_rb_ctl_setup_cq(eib_t *, eib_vnic_t *);
45static void eib_rb_ctl_setup_ud_channel(eib_t *, eib_vnic_t *);
46
47int
48eib_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
87ctl_create_qp_fail:
88	eib_rb_ctl_create_qp(ss, vnic);
89	return (EIB_E_FAILURE);
90}
91
92/*ARGSUSED*/
93uint_t
94eib_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
136void
137eib_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
147static int
148eib_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
208ctl_setup_cq_fail:
209	eib_rb_ctl_setup_cq(ss, vnic);
210	return (EIB_E_FAILURE);
211}
212
213static int
214eib_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
266ctl_setup_ud_channel_fail:
267	eib_rb_ctl_setup_ud_channel(ss, vnic);
268	return (EIB_E_FAILURE);
269}
270
271static void
272eib_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
290static void
291eib_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
323static void
324eib_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
329static void
330eib_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*/
365static void
366eib_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*/
412static void
413eib_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