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.
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.
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]
23 * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
26 #include <sys/types.h>
30 #include <sys/sunddi.h>
31 #include <sys/ksynch.h>
33 #include <sys/ib/clients/eoib/eib_impl.h>
36 * Declarations private to this file
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
*);
48 eib_adm_setup_qp(eib_t
*ss
, int *err
)
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
,
65 goto adm_setup_qp_fail
;
69 * Allocate a eib_chan_t to store stuff about admin qp and
70 * initialize some basic stuff
72 ss
->ei_admin_chan
= eib_chan_init();
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;
80 * Setup a combined CQ and completion handler
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");
86 goto adm_setup_qp_fail
;
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");
96 goto adm_setup_qp_fail
;
100 * Post initial set of rx buffers to the HCA
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");
106 goto adm_setup_qp_fail
;
109 return (EIB_E_SUCCESS
);
112 eib_rb_adm_setup_qp(ss
);
113 return (EIB_E_FAILURE
);
118 eib_adm_comp_handler(caddr_t arg1
, caddr_t arg2
)
120 eib_t
*ss
= (eib_t
*)(void *)arg1
;
121 eib_chan_t
*chan
= ss
->ei_admin_chan
;
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.
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
);
140 * Handle tx and rx completions
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
);
151 eib_adm_tx_comp(ss
, wqe
);
156 return (DDI_INTR_CLAIMED
);
160 eib_rb_adm_setup_qp(eib_t
*ss
)
162 eib_rb_adm_setup_ud_channel(ss
);
164 eib_rb_adm_setup_cq(ss
);
166 eib_chan_fini(ss
->ei_admin_chan
);
167 ss
->ei_admin_chan
= NULL
;
171 eib_adm_setup_cq(eib_t
*ss
)
173 eib_chan_t
*chan
= ss
->ei_admin_chan
;
174 ibt_cq_attr_t cq_attr
;
180 * Allocate the admin completion queue for sending vnic logins and
181 * logouts and receiving vnic login acks.
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
;
188 cq_attr
.cq_size
= EIB_ADMIN_CQ_SIZE
;
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
;
199 * Set up other parameters for collecting completion information
202 chan
->ch_wc
= kmem_zalloc(sizeof (ibt_wc_t
) * sz
, KM_SLEEP
);
205 * Allocate soft interrupt for the admin channel cq handler and
206 * set up the handler as well.
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
;
216 * Now, set up the admin completion queue handler.
218 ibt_set_cq_handler(chan
->ch_cq_hdl
, eib_adm_comp_intr
, ss
);
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
;
227 return (EIB_E_SUCCESS
);
230 eib_rb_adm_setup_cq(ss
);
231 return (EIB_E_FAILURE
);
235 eib_adm_setup_ud_channel(eib_t
*ss
)
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
;
242 bzero(&alloc_attr
, sizeof (ibt_ud_chan_alloc_args_t
));
243 bzero(&query_attr
, sizeof (ibt_ud_chan_query_attr_t
));
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;
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
;
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
;
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
;
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
;
285 return (EIB_E_SUCCESS
);
287 adm_setup_ud_channel_fail
:
288 eib_rb_adm_setup_ud_channel(ss
);
289 return (EIB_E_FAILURE
);
293 eib_adm_comp_intr(ibt_cq_hdl_t cq_hdl
, void *arg
)
296 eib_chan_t
*chan
= ss
->ei_admin_chan
;
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
);
305 ASSERT(ss
->ei_admin_si_hdl
!= NULL
);
307 (void) ddi_intr_trigger_softint(ss
->ei_admin_si_hdl
, NULL
);
311 eib_adm_rx_comp(eib_t
*ss
, eib_wqe_t
*wqe
)
313 eib_chan_t
*chan
= ss
->ei_admin_chan
;
315 uint8_t *pkt
= (uint8_t *)(uintptr_t)(wqe
->qe_sgl
.ds_va
);
319 * Skip the GRH and parse the login ack message in the packet
321 if (eib_fip_parse_login_ack(ss
, pkt
+ EIB_GRH_SZ
, &ld
) == EIB_E_SUCCESS
)
322 eib_vnic_login_ack(ss
, &ld
);
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.
332 if (chan
->ch_tear_down
) {
333 eib_rsrc_return_rwqe(ss
, wqe
, chan
);
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
);
345 eib_adm_tx_comp(eib_t
*ss
, eib_wqe_t
*wqe
)
347 eib_rsrc_return_swqe(ss
, wqe
, ss
->ei_admin_chan
);
352 eib_adm_err_comp(eib_t
*ss
, eib_wqe_t
*wqe
, ibt_wc_t
*wc
)
355 * Currently, all we do is report
357 switch (wc
->wc_status
) {
358 case IBT_WC_WR_FLUSHED_ERR
:
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 ",
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 ",
375 * When a wc indicates error, we do not attempt to repost but
376 * simply return it to the wqe pool.
378 if (EIB_WQE_TYPE(wqe
->qe_info
) == EIB_WQE_RX
)
379 eib_rsrc_return_rwqe(ss
, wqe
, ss
->ei_admin_chan
);
381 eib_rsrc_return_swqe(ss
, wqe
, ss
->ei_admin_chan
);
385 eib_rb_adm_setup_cq(eib_t
*ss
)
387 eib_chan_t
*chan
= ss
->ei_admin_chan
;
394 * Reset any completion handler we may have set up
397 ibt_set_cq_handler(chan
->ch_cq_hdl
, NULL
, NULL
);
400 * Remove any softint we may have allocated for the admin cq
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
;
408 * Release any work completion buffers we may have allocated
410 if (chan
->ch_wc
&& chan
->ch_cq_sz
)
411 kmem_free(chan
->ch_wc
, sizeof (ibt_wc_t
) * chan
->ch_cq_sz
);
417 * Free any completion queue we may have allocated
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
);
426 chan
->ch_cq_hdl
= NULL
;
431 eib_rb_adm_setup_ud_channel(eib_t
*ss
)
433 eib_chan_t
*chan
= ss
->ei_admin_chan
;
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.
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
);
452 * Wait until all posted tx wqes on this channel are back with
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
);
461 * Wait until all posted rx wqes on this channel are back with
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
);
470 * Now we're ready to free this channel
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
);
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;
485 chan
->ch_chan
= NULL
;