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 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 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 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 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 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 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 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 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 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 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 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