#include <sys/cdefs.h>
#include <linux/module.h>
#include <linux/err.h>
#include <linux/slab.h>
#include <rdma/ib_verbs.h>
#define IB_CQ_POLL_MAX 16
#define IB_CQ_POLL_BUDGET 65536
#define IB_CQ_POLL_FLAGS (IB_CQ_NEXT_COMP | IB_CQ_REPORT_MISSED_EVENTS)
static void
ib_cq_poll_work(struct work_struct *work)
{
struct ib_wc ib_wc[IB_CQ_POLL_MAX];
struct ib_cq *cq = container_of(work, struct ib_cq, work);
int total = 0;
int i;
int n;
while (1) {
n = ib_poll_cq(cq, IB_CQ_POLL_MAX, ib_wc);
for (i = 0; i < n; i++) {
struct ib_wc *wc = ib_wc + i;
if (wc->wr_cqe != NULL)
wc->wr_cqe->done(cq, wc);
}
if (n != IB_CQ_POLL_MAX) {
if (ib_req_notify_cq(cq, IB_CQ_POLL_FLAGS) > 0)
break;
else
return;
}
total += n;
if (total >= IB_CQ_POLL_BUDGET)
break;
}
queue_work(ib_comp_wq, &cq->work);
}
static void
ib_cq_completion_workqueue(struct ib_cq *cq, void *private)
{
queue_work(ib_comp_wq, &cq->work);
}
struct ib_cq *
__ib_alloc_cq_user(struct ib_device *dev, void *private,
int nr_cqe, int comp_vector,
enum ib_poll_context poll_ctx,
const char *caller, struct ib_udata *udata)
{
struct ib_cq_init_attr cq_attr = {
.cqe = nr_cqe,
.comp_vector = comp_vector,
};
struct ib_cq *cq;
int ret;
switch (poll_ctx) {
case IB_POLL_DIRECT:
case IB_POLL_SOFTIRQ:
case IB_POLL_WORKQUEUE:
break;
default:
return (ERR_PTR(-EINVAL));
}
cq = rdma_zalloc_drv_obj(dev, ib_cq);
if (!cq)
return ERR_PTR(-ENOMEM);
cq->device = dev;
cq->cq_context = private;
cq->poll_ctx = poll_ctx;
atomic_set(&cq->usecnt, 0);
ret = dev->create_cq(cq, &cq_attr, NULL);
if (ret)
goto out_free_cq;
switch (poll_ctx) {
case IB_POLL_DIRECT:
cq->comp_handler = NULL;
break;
case IB_POLL_SOFTIRQ:
case IB_POLL_WORKQUEUE:
cq->comp_handler = ib_cq_completion_workqueue;
INIT_WORK(&cq->work, ib_cq_poll_work);
ib_req_notify_cq(cq, IB_CQ_NEXT_COMP);
break;
default:
break;
}
return (cq);
out_free_cq:
kfree(cq);
return (ERR_PTR(ret));
}
EXPORT_SYMBOL(__ib_alloc_cq_user);
void
ib_free_cq_user(struct ib_cq *cq, struct ib_udata *udata)
{
if (WARN_ON_ONCE(atomic_read(&cq->usecnt) != 0))
return;
switch (cq->poll_ctx) {
case IB_POLL_DIRECT:
break;
case IB_POLL_SOFTIRQ:
case IB_POLL_WORKQUEUE:
flush_work(&cq->work);
break;
default:
break;
}
cq->device->destroy_cq(cq, udata);
kfree(cq);
}
EXPORT_SYMBOL(ib_free_cq_user);