blob: 2c8739bb628edd57ed7236fee7391ddc8d2cefd9 [file] [log] [blame] [edit]
/*
* Copyright (c) 2012-2016 VMware, Inc. All rights reserved.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of EITHER the GNU General Public License
* version 2 as published by the Free Software Foundation or the BSD
* 2-Clause License. This program is distributed in the hope that it
* will be useful, but WITHOUT ANY WARRANTY; WITHOUT EVEN THE IMPLIED
* WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License version 2 for more details at
* http://www.gnu.org/licenses/old-licenses/gpl-2.0.en.html.
*
* You should have received a copy of the GNU General Public License
* along with this program available in the file COPYING in the main
* directory of this source tree.
*
* The BSD 2-Clause License
*
* Redistribution and use in source and binary forms, with or
* without modification, are permitted provided that the following
* conditions are met:
*
* - Redistributions of source code must retain the above
* copyright notice, this list of conditions and the following
* disclaimer.
*
* - Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following
* disclaimer in the documentation and/or other materials
* provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
* OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <util/udma_barrier.h>
#include "pvrdma.h"
enum {
CQ_OK = 0,
CQ_EMPTY = -1,
CQ_POLL_ERR = -2,
};
enum {
PVRDMA_CQE_IS_SEND_MASK = 0x40,
PVRDMA_CQE_OPCODE_MASK = 0x1f,
};
int pvrdma_alloc_cq_buf(struct pvrdma_device *dev, struct pvrdma_cq *cq,
struct pvrdma_buf *buf, int entries)
{
if (pvrdma_alloc_buf(buf, cq->offset +
entries * (sizeof(struct pvrdma_cqe)),
dev->page_size))
return -1;
memset(buf->buf, 0, buf->length);
return 0;
}
static struct pvrdma_cqe *get_cqe(struct pvrdma_cq *cq, int entry)
{
return cq->buf.buf + cq->offset +
entry * (sizeof(struct pvrdma_cqe));
}
static int pvrdma_poll_one(struct pvrdma_cq *cq,
struct pvrdma_qp **cur_qp,
struct ibv_wc *wc)
{
struct pvrdma_context *ctx = to_vctx(cq->ibv_cq.context);
int has_data;
unsigned int head;
int tried = 0;
struct pvrdma_cqe *cqe;
retry:
has_data = pvrdma_idx_ring_has_data(&cq->ring_state->rx,
cq->cqe_cnt, &head);
if (has_data == 0) {
unsigned int val;
if (tried)
return CQ_EMPTY;
/* Pass down POLL to give physical HCA a chance to poll. */
val = cq->cqn | PVRDMA_UAR_CQ_POLL;
pvrdma_write_uar_cq(ctx->uar, val);
tried = 1;
goto retry;
} else if (has_data == -1) {
return CQ_POLL_ERR;
}
cqe = get_cqe(cq, head);
if (!cqe)
return CQ_EMPTY;
udma_from_device_barrier();
if (ctx->qp_tbl[cqe->qp & 0xFFFF])
*cur_qp = (struct pvrdma_qp *)ctx->qp_tbl[cqe->qp & 0xFFFF];
else
return CQ_POLL_ERR;
wc->opcode = pvrdma_wc_opcode_to_ibv(cqe->opcode);
wc->status = pvrdma_wc_status_to_ibv(cqe->status);
wc->wr_id = cqe->wr_id;
wc->qp_num = (*cur_qp)->ibv_qp.qp_num;
wc->byte_len = cqe->byte_len;
wc->imm_data = cqe->imm_data;
wc->src_qp = cqe->src_qp;
wc->wc_flags = cqe->wc_flags;
wc->pkey_index = cqe->pkey_index;
wc->slid = cqe->slid;
wc->sl = cqe->sl;
wc->dlid_path_bits = cqe->dlid_path_bits;
wc->vendor_err = 0;
/* Update shared ring state. */
pvrdma_idx_ring_inc(&(cq->ring_state->rx.cons_head), cq->cqe_cnt);
return CQ_OK;
}
int pvrdma_poll_cq(struct ibv_cq *ibcq, int num_entries, struct ibv_wc *wc)
{
struct pvrdma_cq *cq = to_vcq(ibcq);
struct pvrdma_qp *qp;
int npolled = 0;
if (num_entries < 1 || wc == NULL)
return 0;
pthread_spin_lock(&cq->lock);
for (npolled = 0; npolled < num_entries; ++npolled) {
if (pvrdma_poll_one(cq, &qp, wc + npolled) != CQ_OK)
break;
}
pthread_spin_unlock(&cq->lock);
return npolled;
}
void pvrdma_cq_clean_int(struct pvrdma_cq *cq, uint32_t qp_handle)
{
/* Flush CQEs from specified QP */
int has_data;
unsigned int head;
/* Lock held */
has_data = pvrdma_idx_ring_has_data(&cq->ring_state->rx,
cq->cqe_cnt, &head);
if (unlikely(has_data > 0)) {
int items;
int curr;
int tail = pvrdma_idx(&cq->ring_state->rx.prod_tail,
cq->cqe_cnt);
struct pvrdma_cqe *cqe;
struct pvrdma_cqe *curr_cqe;
items = (tail > head) ? (tail - head) :
(cq->cqe_cnt - head + tail);
curr = --tail;
while (items-- > 0) {
if (curr < 0)
curr = cq->cqe_cnt - 1;
if (tail < 0)
tail = cq->cqe_cnt - 1;
curr_cqe = get_cqe(cq, curr);
udma_from_device_barrier();
if ((curr_cqe->qp & 0xFFFF) != qp_handle) {
if (curr != tail) {
cqe = get_cqe(cq, tail);
udma_from_device_barrier();
*cqe = *curr_cqe;
}
tail--;
} else {
pvrdma_idx_ring_inc(
&cq->ring_state->rx.cons_head,
cq->cqe_cnt);
}
curr--;
}
}
}
void pvrdma_cq_clean(struct pvrdma_cq *cq, uint32_t qp_handle)
{
pthread_spin_lock(&cq->lock);
pvrdma_cq_clean_int(cq, qp_handle);
pthread_spin_unlock(&cq->lock);
}
struct ibv_cq *pvrdma_create_cq(struct ibv_context *context, int cqe,
struct ibv_comp_channel *channel,
int comp_vector)
{
struct pvrdma_device *dev = to_vdev(context->device);
struct user_pvrdma_create_cq cmd;
struct user_pvrdma_create_cq_resp resp;
struct pvrdma_cq *cq;
int ret;
if (cqe < 1)
return NULL;
cq = malloc(sizeof(*cq));
if (!cq)
return NULL;
/* Extra page for shared ring state */
cq->offset = dev->page_size;
if (pthread_spin_init(&cq->lock, PTHREAD_PROCESS_PRIVATE))
goto err;
cqe = align_next_power2(cqe);
if (pvrdma_alloc_cq_buf(dev, cq, &cq->buf, cqe))
goto err;
cq->ring_state = cq->buf.buf;
cmd.buf_addr = (uintptr_t) cq->buf.buf;
cmd.buf_size = cq->buf.length;
ret = ibv_cmd_create_cq(context, cqe, channel, comp_vector,
&cq->ibv_cq, &cmd.ibv_cmd, sizeof(cmd),
&resp.ibv_resp, sizeof(resp));
if (ret)
goto err_buf;
cq->cqn = resp.cqn;
cq->cqe_cnt = cq->ibv_cq.cqe;
return &cq->ibv_cq;
err_buf:
pvrdma_free_buf(&cq->buf);
err:
free(cq);
return NULL;
}
int pvrdma_destroy_cq(struct ibv_cq *cq)
{
int ret;
ret = ibv_cmd_destroy_cq(cq);
if (ret)
return ret;
pvrdma_free_buf(&to_vcq(cq)->buf);
free(to_vcq(cq));
return 0;
}
int pvrdma_req_notify_cq(struct ibv_cq *ibcq, int solicited)
{
struct pvrdma_context *ctx = to_vctx(ibcq->context);
struct pvrdma_cq *cq = to_vcq(ibcq);
unsigned int val = cq->cqn;
val |= solicited ? PVRDMA_UAR_CQ_ARM_SOL : PVRDMA_UAR_CQ_ARM;
pvrdma_write_uar_cq(ctx->uar, val);
return 0;
}