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
eib_adm_setup_qp(eib_t * ss,int * err)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
eib_adm_comp_handler(caddr_t arg1,caddr_t arg2)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
eib_rb_adm_setup_qp(eib_t * ss)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
eib_adm_setup_cq(eib_t * ss)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
eib_adm_setup_ud_channel(eib_t * ss)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
eib_adm_comp_intr(ibt_cq_hdl_t cq_hdl,void * arg)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
eib_adm_rx_comp(eib_t * ss,eib_wqe_t * wqe)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
eib_adm_tx_comp(eib_t * ss,eib_wqe_t * wqe)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
eib_adm_err_comp(eib_t * ss,eib_wqe_t * wqe,ibt_wc_t * wc)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
eib_rb_adm_setup_cq(eib_t * ss)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
eib_rb_adm_setup_ud_channel(eib_t * ss)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