/* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved. */ #include #include #include #include #include #include #include eib_chan_t * eib_chan_init(void) { eib_chan_t *chan; /* * Allocate a eib_chan_t to store stuff about admin qp and * initialize some basic stuff */ chan = kmem_zalloc(sizeof (eib_chan_t), KM_SLEEP); mutex_init(&chan->ch_pkey_lock, NULL, MUTEX_DRIVER, NULL); mutex_init(&chan->ch_cep_lock, NULL, MUTEX_DRIVER, NULL); mutex_init(&chan->ch_tx_lock, NULL, MUTEX_DRIVER, NULL); mutex_init(&chan->ch_rx_lock, NULL, MUTEX_DRIVER, NULL); mutex_init(&chan->ch_vhub_lock, NULL, MUTEX_DRIVER, NULL); cv_init(&chan->ch_cep_cv, NULL, CV_DEFAULT, NULL); cv_init(&chan->ch_tx_cv, NULL, CV_DEFAULT, NULL); cv_init(&chan->ch_rx_cv, NULL, CV_DEFAULT, NULL); return (chan); } void eib_chan_fini(eib_chan_t *chan) { if (chan) { cv_destroy(&chan->ch_rx_cv); cv_destroy(&chan->ch_tx_cv); cv_destroy(&chan->ch_cep_cv); mutex_destroy(&chan->ch_vhub_lock); mutex_destroy(&chan->ch_rx_lock); mutex_destroy(&chan->ch_tx_lock); mutex_destroy(&chan->ch_cep_lock); mutex_destroy(&chan->ch_pkey_lock); kmem_free(chan, sizeof (eib_chan_t)); } } int eib_chan_post_rx(eib_t *ss, eib_chan_t *chan, uint_t *n_posted) { eib_wqe_t *rwqes[EIB_RWR_CHUNK_SZ]; ibt_status_t ret; uint_t n_got = 0; uint_t n_good = 0; uint_t limit = 0; uint_t room = 0; uint_t chunk_sz; int wndx; int i; /* * We don't want to post beyond the maximum rwqe size for this channel */ room = chan->ch_max_rwqes - chan->ch_rx_posted; limit = (room > chan->ch_rwqe_bktsz) ? chan->ch_rwqe_bktsz : room; for (wndx = 0; wndx < limit; wndx += chunk_sz) { /* * Grab a chunk of rwqes */ chunk_sz = ((limit - wndx) < EIB_RWR_CHUNK_SZ) ? (limit - wndx) : EIB_RWR_CHUNK_SZ; /* * When eib_chan_post_rx() is called to post a bunch of rwqes, * it is either during the vnic setup or when we're refilling * the data channel. Neither situation is important enough for * us to grab the wqes reserved for sending keepalives of * previously established vnics. */ ret = eib_rsrc_grab_rwqes(ss, rwqes, chunk_sz, &n_got, EIB_WPRI_LO); if (ret != EIB_E_SUCCESS) break; /* * Post work requests from the rwqes we just grabbed */ for (i = 0; i < n_got; i++) { eib_wqe_t *rwqe = rwqes[i]; ret = eib_chan_post_recv(ss, chan, rwqe); if (ret == EIB_E_SUCCESS) { n_good++; } else if (rwqe->qe_mp) { freemsg(rwqe->qe_mp); } else { eib_rsrc_return_rwqe(ss, rwqe, NULL); } } /* * If we got less rwqes than we asked for during the grab * earlier, we'll stop asking for more and quit now. */ if (n_got < chunk_sz) break; } /* * If we posted absolutely nothing, we return failure; otherwise * return success. */ if (n_good == 0) return (EIB_E_FAILURE); if (n_posted) *n_posted = n_good; return (EIB_E_SUCCESS); } /*ARGSUSED*/ int eib_chan_post_recv(eib_t *ss, eib_chan_t *chan, eib_wqe_t *rwqe) { ibt_status_t ret; uint8_t *mp_base; size_t mp_len; rwqe->qe_sgl.ds_va = (ib_vaddr_t)(uintptr_t)rwqe->qe_cpbuf; rwqe->qe_sgl.ds_len = rwqe->qe_bufsz; /* * If this channel has receive buffer alignment restrictions, make * sure the requirements are met */ if (chan->ch_ip_hdr_align) { rwqe->qe_sgl.ds_va += chan->ch_ip_hdr_align; rwqe->qe_sgl.ds_len -= chan->ch_ip_hdr_align; } /* * If the receive buffer for this channel needs to have an mblk * allocated, do it */ if (chan->ch_alloc_mp) { mp_base = (uint8_t *)(uintptr_t)(rwqe->qe_sgl.ds_va); mp_len = rwqe->qe_sgl.ds_len; rwqe->qe_mp = desballoc(mp_base, mp_len, 0, &rwqe->qe_frp); if (rwqe->qe_mp == NULL) { EIB_DPRINTF_ERR(ss->ei_instance, "eib_chan_post_recv: " "desballoc(base=0x%llx, len=0x%llx) failed", mp_base, mp_len); return (EIB_E_FAILURE); } } /* * Check if the recv queue is already full or if we can post one more */ mutex_enter(&chan->ch_rx_lock); if (chan->ch_rx_posted > (chan->ch_max_rwqes - 1)) { EIB_DPRINTF_ERR(ss->ei_instance, "eib_chan_post_recv: " "too many rwqes posted already, posted=0x%lx, max=0x%lx", chan->ch_rx_posted, chan->ch_max_rwqes); mutex_exit(&chan->ch_rx_lock); return (EIB_E_FAILURE); } rwqe->qe_vnic_inst = chan->ch_vnic_inst; rwqe->qe_chan = chan; rwqe->qe_info |= EIB_WQE_FLG_POSTED_TO_HCA; ret = ibt_post_recv(chan->ch_chan, &(rwqe->qe_wr.recv), 1, NULL); if (ret != IBT_SUCCESS) { EIB_DPRINTF_ERR(ss->ei_instance, "eib_chan_post_recv: " "ibt_post_recv() failed, ret=%d", ret); mutex_exit(&chan->ch_rx_lock); return (EIB_E_FAILURE); } chan->ch_rx_posted++; mutex_exit(&chan->ch_rx_lock); return (EIB_E_SUCCESS); }