| /* | |
| * Copyright (c) 2005 Topspin Communications. All rights reserved. | |
| * Copyright (c) 2005 PathScale, Inc. All rights reserved. | |
| * Copyright (c) 2006 Cisco Systems, Inc. All rights reserved. | |
| * | |
| * This software is available to you under a choice of one of two | |
| * licenses. You may choose to be licensed under the terms of the GNU | |
| * General Public License (GPL) Version 2, available from the file | |
| * COPYING in the main directory of this source tree, or the | |
| * OpenIB.org BSD license below: | |
| * | |
| * 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. | |
| * | |
| * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, | |
| * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF | |
| * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND | |
| * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS | |
| * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN | |
| * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN | |
| * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | |
| * SOFTWARE. | |
| */ | |
| #include <config.h> | |
| #include <stdio.h> | |
| #include <unistd.h> | |
| #include <stdlib.h> | |
| #include <errno.h> | |
| #include <alloca.h> | |
| #include <string.h> | |
| #include <infiniband/cmd_write.h> | |
| #include "ibverbs.h" | |
| #include <ccan/minmax.h> | |
| bool verbs_allow_disassociate_destroy; | |
| int ibv_cmd_alloc_pd(struct ibv_context *context, struct ibv_pd *pd, | |
| struct ibv_alloc_pd *cmd, size_t cmd_size, | |
| struct ib_uverbs_alloc_pd_resp *resp, size_t resp_size) | |
| { | |
| int ret; | |
| ret = execute_cmd_write(context, IB_USER_VERBS_CMD_ALLOC_PD, cmd, | |
| cmd_size, resp, resp_size); | |
| if (ret) | |
| return ret; | |
| pd->handle = resp->pd_handle; | |
| pd->context = context; | |
| return 0; | |
| } | |
| int ibv_cmd_open_xrcd(struct ibv_context *context, struct verbs_xrcd *xrcd, | |
| int vxrcd_size, | |
| struct ibv_xrcd_init_attr *attr, | |
| struct ibv_open_xrcd *cmd, size_t cmd_size, | |
| struct ib_uverbs_open_xrcd_resp *resp, size_t resp_size) | |
| { | |
| int ret; | |
| if (attr->comp_mask >= IBV_XRCD_INIT_ATTR_RESERVED) | |
| return EOPNOTSUPP; | |
| if (!(attr->comp_mask & IBV_XRCD_INIT_ATTR_FD) || | |
| !(attr->comp_mask & IBV_XRCD_INIT_ATTR_OFLAGS)) | |
| return EINVAL; | |
| cmd->fd = attr->fd; | |
| cmd->oflags = attr->oflags; | |
| ret = execute_cmd_write(context, IB_USER_VERBS_CMD_OPEN_XRCD, cmd, | |
| cmd_size, resp, resp_size); | |
| if (ret) | |
| return ret; | |
| xrcd->xrcd.context = context; | |
| xrcd->comp_mask = 0; | |
| if (vext_field_avail(struct verbs_xrcd, handle, vxrcd_size)) { | |
| xrcd->comp_mask = VERBS_XRCD_HANDLE; | |
| xrcd->handle = resp->xrcd_handle; | |
| } | |
| return 0; | |
| } | |
| int ibv_cmd_reg_mr(struct ibv_pd *pd, void *addr, size_t length, | |
| uint64_t hca_va, int access, | |
| struct verbs_mr *vmr, struct ibv_reg_mr *cmd, | |
| size_t cmd_size, | |
| struct ib_uverbs_reg_mr_resp *resp, size_t resp_size) | |
| { | |
| int ret; | |
| cmd->start = (uintptr_t) addr; | |
| cmd->length = length; | |
| /* On demand access and entire address space means implicit. | |
| * In that case set the value in the command to what kernel expects. | |
| */ | |
| if (access & IBV_ACCESS_ON_DEMAND) { | |
| if (length == SIZE_MAX && addr) { | |
| errno = EINVAL; | |
| return EINVAL; | |
| } | |
| if (length == SIZE_MAX) | |
| cmd->length = UINT64_MAX; | |
| } | |
| cmd->hca_va = hca_va; | |
| cmd->pd_handle = pd->handle; | |
| cmd->access_flags = access; | |
| ret = execute_cmd_write(pd->context, IB_USER_VERBS_CMD_REG_MR, cmd, | |
| cmd_size, resp, resp_size); | |
| if (ret) | |
| return ret; | |
| vmr->ibv_mr.handle = resp->mr_handle; | |
| vmr->ibv_mr.lkey = resp->lkey; | |
| vmr->ibv_mr.rkey = resp->rkey; | |
| vmr->ibv_mr.context = pd->context; | |
| vmr->mr_type = IBV_MR_TYPE_MR; | |
| vmr->access = access; | |
| return 0; | |
| } | |
| int ibv_cmd_rereg_mr(struct verbs_mr *vmr, uint32_t flags, void *addr, | |
| size_t length, uint64_t hca_va, int access, | |
| struct ibv_pd *pd, struct ibv_rereg_mr *cmd, | |
| size_t cmd_sz, struct ib_uverbs_rereg_mr_resp *resp, | |
| size_t resp_sz) | |
| { | |
| int ret; | |
| cmd->mr_handle = vmr->ibv_mr.handle; | |
| cmd->flags = flags; | |
| cmd->start = (uintptr_t)addr; | |
| cmd->length = length; | |
| cmd->hca_va = hca_va; | |
| cmd->pd_handle = (flags & IBV_REREG_MR_CHANGE_PD) ? pd->handle : 0; | |
| cmd->access_flags = access; | |
| ret = execute_cmd_write(vmr->ibv_mr.context, IB_USER_VERBS_CMD_REREG_MR, | |
| cmd, cmd_sz, resp, resp_sz); | |
| if (ret) | |
| return ret; | |
| vmr->ibv_mr.lkey = resp->lkey; | |
| vmr->ibv_mr.rkey = resp->rkey; | |
| if (flags & IBV_REREG_MR_CHANGE_PD) | |
| vmr->ibv_mr.context = pd->context; | |
| return 0; | |
| } | |
| int ibv_cmd_alloc_mw(struct ibv_pd *pd, enum ibv_mw_type type, | |
| struct ibv_mw *mw, struct ibv_alloc_mw *cmd, | |
| size_t cmd_size, | |
| struct ib_uverbs_alloc_mw_resp *resp, size_t resp_size) | |
| { | |
| int ret; | |
| cmd->pd_handle = pd->handle; | |
| cmd->mw_type = type; | |
| memset(cmd->reserved, 0, sizeof(cmd->reserved)); | |
| ret = execute_cmd_write(pd->context, IB_USER_VERBS_CMD_ALLOC_MW, cmd, | |
| cmd_size, resp, resp_size); | |
| if (ret) | |
| return ret; | |
| mw->context = pd->context; | |
| mw->pd = pd; | |
| mw->rkey = resp->rkey; | |
| mw->handle = resp->mw_handle; | |
| mw->type = type; | |
| return 0; | |
| } | |
| int ibv_cmd_poll_cq(struct ibv_cq *ibcq, int ne, struct ibv_wc *wc) | |
| { | |
| struct ibv_poll_cq cmd; | |
| struct ib_uverbs_poll_cq_resp *resp; | |
| int i; | |
| int rsize; | |
| int ret; | |
| rsize = sizeof *resp + ne * sizeof(struct ib_uverbs_wc); | |
| resp = malloc(rsize); | |
| if (!resp) | |
| return -1; | |
| cmd.cq_handle = ibcq->handle; | |
| cmd.ne = ne; | |
| ret = execute_cmd_write_no_uhw(ibcq->context, IB_USER_VERBS_CMD_POLL_CQ, | |
| &cmd, sizeof(cmd), resp, rsize); | |
| if (ret) { | |
| ret = -1; | |
| goto out; | |
| } | |
| for (i = 0; i < resp->count; i++) { | |
| wc[i].wr_id = resp->wc[i].wr_id; | |
| wc[i].status = resp->wc[i].status; | |
| wc[i].opcode = resp->wc[i].opcode; | |
| wc[i].vendor_err = resp->wc[i].vendor_err; | |
| wc[i].byte_len = resp->wc[i].byte_len; | |
| wc[i].imm_data = resp->wc[i].ex.imm_data; | |
| wc[i].qp_num = resp->wc[i].qp_num; | |
| wc[i].src_qp = resp->wc[i].src_qp; | |
| wc[i].wc_flags = resp->wc[i].wc_flags; | |
| wc[i].pkey_index = resp->wc[i].pkey_index; | |
| wc[i].slid = resp->wc[i].slid; | |
| wc[i].sl = resp->wc[i].sl; | |
| wc[i].dlid_path_bits = resp->wc[i].dlid_path_bits; | |
| } | |
| ret = resp->count; | |
| out: | |
| free(resp); | |
| return ret; | |
| } | |
| int ibv_cmd_req_notify_cq(struct ibv_cq *ibcq, int solicited_only) | |
| { | |
| struct ibv_req_notify_cq req; | |
| req.core_payload = (struct ib_uverbs_req_notify_cq){ | |
| .cq_handle = ibcq->handle, | |
| .solicited_only = !!solicited_only, | |
| }; | |
| return execute_cmd_write_req(ibcq->context, | |
| IB_USER_VERBS_CMD_REQ_NOTIFY_CQ, &req, | |
| sizeof(req)); | |
| } | |
| int ibv_cmd_resize_cq(struct ibv_cq *cq, int cqe, | |
| struct ibv_resize_cq *cmd, size_t cmd_size, | |
| struct ib_uverbs_resize_cq_resp *resp, size_t resp_size) | |
| { | |
| int ret; | |
| cmd->cq_handle = cq->handle; | |
| cmd->cqe = cqe; | |
| ret = execute_cmd_write(cq->context, IB_USER_VERBS_CMD_RESIZE_CQ, cmd, | |
| cmd_size, resp, resp_size); | |
| if (ret) | |
| return ret; | |
| cq->cqe = resp->cqe; | |
| return 0; | |
| } | |
| static int ibv_cmd_modify_srq_v3(struct ibv_srq *srq, | |
| struct ibv_srq_attr *srq_attr, | |
| int srq_attr_mask, | |
| struct ibv_modify_srq *new_cmd, | |
| size_t new_cmd_size) | |
| { | |
| struct ibv_modify_srq_v3 *cmd; | |
| size_t cmd_size; | |
| cmd_size = sizeof *cmd + new_cmd_size - sizeof *new_cmd; | |
| cmd = alloca(cmd_size); | |
| memcpy(cmd + 1, new_cmd + 1, new_cmd_size - sizeof *new_cmd); | |
| cmd->core_payload = (struct ib_uverbs_modify_srq_v3){ | |
| .srq_handle = srq->handle, | |
| .attr_mask = srq_attr_mask, | |
| .max_wr = srq_attr->max_wr, | |
| .srq_limit = srq_attr->srq_limit, | |
| }; | |
| return execute_cmd_write_req( | |
| srq->context, IB_USER_VERBS_CMD_MODIFY_SRQ_V3, cmd, cmd_size); | |
| } | |
| int ibv_cmd_modify_srq(struct ibv_srq *srq, | |
| struct ibv_srq_attr *srq_attr, | |
| int srq_attr_mask, | |
| struct ibv_modify_srq *cmd, size_t cmd_size) | |
| { | |
| if (abi_ver == 3) | |
| return ibv_cmd_modify_srq_v3(srq, srq_attr, srq_attr_mask, | |
| cmd, cmd_size); | |
| cmd->srq_handle = srq->handle; | |
| cmd->attr_mask = srq_attr_mask; | |
| cmd->max_wr = srq_attr->max_wr; | |
| cmd->srq_limit = srq_attr->srq_limit; | |
| return execute_cmd_write_req(srq->context, IB_USER_VERBS_CMD_MODIFY_SRQ, | |
| cmd, cmd_size); | |
| } | |
| int ibv_cmd_query_srq(struct ibv_srq *srq, struct ibv_srq_attr *srq_attr, | |
| struct ibv_query_srq *cmd, size_t cmd_size) | |
| { | |
| struct ib_uverbs_query_srq_resp resp; | |
| int ret; | |
| cmd->srq_handle = srq->handle; | |
| cmd->reserved = 0; | |
| ret = execute_cmd_write(srq->context, IB_USER_VERBS_CMD_QUERY_SRQ, cmd, | |
| cmd_size, &resp, sizeof(resp)); | |
| if (ret) | |
| return ret; | |
| srq_attr->max_wr = resp.max_wr; | |
| srq_attr->max_sge = resp.max_sge; | |
| srq_attr->srq_limit = resp.srq_limit; | |
| return 0; | |
| } | |
| enum { | |
| CREATE_QP_EX2_SUP_CREATE_FLAGS = IBV_QP_CREATE_BLOCK_SELF_MCAST_LB | | |
| IBV_QP_CREATE_SCATTER_FCS | | |
| IBV_QP_CREATE_CVLAN_STRIPPING | | |
| IBV_QP_CREATE_SOURCE_QPN | | |
| IBV_QP_CREATE_PCI_WRITE_END_PADDING, | |
| }; | |
| int ibv_cmd_open_qp(struct ibv_context *context, struct verbs_qp *qp, | |
| int vqp_sz, | |
| struct ibv_qp_open_attr *attr, | |
| struct ibv_open_qp *cmd, size_t cmd_size, | |
| struct ib_uverbs_create_qp_resp *resp, size_t resp_size) | |
| { | |
| struct verbs_xrcd *xrcd; | |
| int ret; | |
| if (attr->comp_mask >= IBV_QP_OPEN_ATTR_RESERVED) | |
| return EOPNOTSUPP; | |
| if (!(attr->comp_mask & IBV_QP_OPEN_ATTR_XRCD) || | |
| !(attr->comp_mask & IBV_QP_OPEN_ATTR_NUM) || | |
| !(attr->comp_mask & IBV_QP_OPEN_ATTR_TYPE)) | |
| return EINVAL; | |
| xrcd = container_of(attr->xrcd, struct verbs_xrcd, xrcd); | |
| cmd->user_handle = (uintptr_t) qp; | |
| cmd->pd_handle = xrcd->handle; | |
| cmd->qpn = attr->qp_num; | |
| cmd->qp_type = attr->qp_type; | |
| ret = execute_cmd_write(context, IB_USER_VERBS_CMD_OPEN_QP, cmd, | |
| cmd_size, resp, resp_size); | |
| if (ret) | |
| return ret; | |
| qp->qp.handle = resp->qp_handle; | |
| qp->qp.context = context; | |
| qp->qp.qp_context = attr->qp_context; | |
| qp->qp.pd = NULL; | |
| qp->qp.send_cq = NULL; | |
| qp->qp.recv_cq = NULL; | |
| qp->qp.srq = NULL; | |
| qp->qp.qp_num = attr->qp_num; | |
| qp->qp.qp_type = attr->qp_type; | |
| qp->qp.state = IBV_QPS_UNKNOWN; | |
| qp->qp.events_completed = 0; | |
| pthread_mutex_init(&qp->qp.mutex, NULL); | |
| pthread_cond_init(&qp->qp.cond, NULL); | |
| qp->comp_mask = 0; | |
| if (vext_field_avail(struct verbs_qp, xrcd, vqp_sz)) { | |
| qp->comp_mask = VERBS_QP_XRCD; | |
| qp->xrcd = xrcd; | |
| } | |
| return 0; | |
| } | |
| int ibv_cmd_query_qp(struct ibv_qp *qp, struct ibv_qp_attr *attr, | |
| int attr_mask, | |
| struct ibv_qp_init_attr *init_attr, | |
| struct ibv_query_qp *cmd, size_t cmd_size) | |
| { | |
| struct ib_uverbs_query_qp_resp resp; | |
| int ret; | |
| /* | |
| * Starting with IBV_QP_RATE_LIMIT the attribute must go through the | |
| * _ex path. | |
| */ | |
| if (attr_mask & ~(IBV_QP_RATE_LIMIT - 1)) | |
| return EOPNOTSUPP; | |
| cmd->qp_handle = qp->handle; | |
| cmd->attr_mask = attr_mask; | |
| ret = execute_cmd_write(qp->context, IB_USER_VERBS_CMD_QUERY_QP, cmd, | |
| cmd_size, &resp, sizeof(resp)); | |
| if (ret) | |
| return ret; | |
| attr->qkey = resp.qkey; | |
| attr->rq_psn = resp.rq_psn; | |
| attr->sq_psn = resp.sq_psn; | |
| attr->dest_qp_num = resp.dest_qp_num; | |
| attr->qp_access_flags = resp.qp_access_flags; | |
| attr->pkey_index = resp.pkey_index; | |
| attr->alt_pkey_index = resp.alt_pkey_index; | |
| attr->qp_state = resp.qp_state; | |
| attr->cur_qp_state = resp.cur_qp_state; | |
| attr->path_mtu = resp.path_mtu; | |
| attr->path_mig_state = resp.path_mig_state; | |
| attr->sq_draining = resp.sq_draining; | |
| attr->max_rd_atomic = resp.max_rd_atomic; | |
| attr->max_dest_rd_atomic = resp.max_dest_rd_atomic; | |
| attr->min_rnr_timer = resp.min_rnr_timer; | |
| attr->port_num = resp.port_num; | |
| attr->timeout = resp.timeout; | |
| attr->retry_cnt = resp.retry_cnt; | |
| attr->rnr_retry = resp.rnr_retry; | |
| attr->alt_port_num = resp.alt_port_num; | |
| attr->alt_timeout = resp.alt_timeout; | |
| attr->cap.max_send_wr = resp.max_send_wr; | |
| attr->cap.max_recv_wr = resp.max_recv_wr; | |
| attr->cap.max_send_sge = resp.max_send_sge; | |
| attr->cap.max_recv_sge = resp.max_recv_sge; | |
| attr->cap.max_inline_data = resp.max_inline_data; | |
| memcpy(attr->ah_attr.grh.dgid.raw, resp.dest.dgid, 16); | |
| attr->ah_attr.grh.flow_label = resp.dest.flow_label; | |
| attr->ah_attr.dlid = resp.dest.dlid; | |
| attr->ah_attr.grh.sgid_index = resp.dest.sgid_index; | |
| attr->ah_attr.grh.hop_limit = resp.dest.hop_limit; | |
| attr->ah_attr.grh.traffic_class = resp.dest.traffic_class; | |
| attr->ah_attr.sl = resp.dest.sl; | |
| attr->ah_attr.src_path_bits = resp.dest.src_path_bits; | |
| attr->ah_attr.static_rate = resp.dest.static_rate; | |
| attr->ah_attr.is_global = resp.dest.is_global; | |
| attr->ah_attr.port_num = resp.dest.port_num; | |
| memcpy(attr->alt_ah_attr.grh.dgid.raw, resp.alt_dest.dgid, 16); | |
| attr->alt_ah_attr.grh.flow_label = resp.alt_dest.flow_label; | |
| attr->alt_ah_attr.dlid = resp.alt_dest.dlid; | |
| attr->alt_ah_attr.grh.sgid_index = resp.alt_dest.sgid_index; | |
| attr->alt_ah_attr.grh.hop_limit = resp.alt_dest.hop_limit; | |
| attr->alt_ah_attr.grh.traffic_class = resp.alt_dest.traffic_class; | |
| attr->alt_ah_attr.sl = resp.alt_dest.sl; | |
| attr->alt_ah_attr.src_path_bits = resp.alt_dest.src_path_bits; | |
| attr->alt_ah_attr.static_rate = resp.alt_dest.static_rate; | |
| attr->alt_ah_attr.is_global = resp.alt_dest.is_global; | |
| attr->alt_ah_attr.port_num = resp.alt_dest.port_num; | |
| init_attr->qp_context = qp->qp_context; | |
| init_attr->send_cq = qp->send_cq; | |
| init_attr->recv_cq = qp->recv_cq; | |
| init_attr->srq = qp->srq; | |
| init_attr->qp_type = qp->qp_type; | |
| init_attr->cap.max_send_wr = resp.max_send_wr; | |
| init_attr->cap.max_recv_wr = resp.max_recv_wr; | |
| init_attr->cap.max_send_sge = resp.max_send_sge; | |
| init_attr->cap.max_recv_sge = resp.max_recv_sge; | |
| init_attr->cap.max_inline_data = resp.max_inline_data; | |
| init_attr->sq_sig_all = resp.sq_sig_all; | |
| return 0; | |
| } | |
| static void copy_modify_qp_fields(struct ibv_qp *qp, struct ibv_qp_attr *attr, | |
| int attr_mask, | |
| struct ib_uverbs_modify_qp *cmd) | |
| { | |
| cmd->qp_handle = qp->handle; | |
| cmd->attr_mask = attr_mask; | |
| if (attr_mask & IBV_QP_STATE) | |
| cmd->qp_state = attr->qp_state; | |
| if (attr_mask & IBV_QP_CUR_STATE) | |
| cmd->cur_qp_state = attr->cur_qp_state; | |
| if (attr_mask & IBV_QP_EN_SQD_ASYNC_NOTIFY) | |
| cmd->en_sqd_async_notify = attr->en_sqd_async_notify; | |
| if (attr_mask & IBV_QP_ACCESS_FLAGS) | |
| cmd->qp_access_flags = attr->qp_access_flags; | |
| if (attr_mask & IBV_QP_PKEY_INDEX) | |
| cmd->pkey_index = attr->pkey_index; | |
| if (attr_mask & IBV_QP_PORT) | |
| cmd->port_num = attr->port_num; | |
| if (attr_mask & IBV_QP_QKEY) | |
| cmd->qkey = attr->qkey; | |
| if (attr_mask & IBV_QP_AV) { | |
| memcpy(cmd->dest.dgid, attr->ah_attr.grh.dgid.raw, 16); | |
| cmd->dest.flow_label = attr->ah_attr.grh.flow_label; | |
| cmd->dest.dlid = attr->ah_attr.dlid; | |
| cmd->dest.reserved = 0; | |
| cmd->dest.sgid_index = attr->ah_attr.grh.sgid_index; | |
| cmd->dest.hop_limit = attr->ah_attr.grh.hop_limit; | |
| cmd->dest.traffic_class = attr->ah_attr.grh.traffic_class; | |
| cmd->dest.sl = attr->ah_attr.sl; | |
| cmd->dest.src_path_bits = attr->ah_attr.src_path_bits; | |
| cmd->dest.static_rate = attr->ah_attr.static_rate; | |
| cmd->dest.is_global = attr->ah_attr.is_global; | |
| cmd->dest.port_num = attr->ah_attr.port_num; | |
| } | |
| if (attr_mask & IBV_QP_PATH_MTU) | |
| cmd->path_mtu = attr->path_mtu; | |
| if (attr_mask & IBV_QP_TIMEOUT) | |
| cmd->timeout = attr->timeout; | |
| if (attr_mask & IBV_QP_RETRY_CNT) | |
| cmd->retry_cnt = attr->retry_cnt; | |
| if (attr_mask & IBV_QP_RNR_RETRY) | |
| cmd->rnr_retry = attr->rnr_retry; | |
| if (attr_mask & IBV_QP_RQ_PSN) | |
| cmd->rq_psn = attr->rq_psn; | |
| if (attr_mask & IBV_QP_MAX_QP_RD_ATOMIC) | |
| cmd->max_rd_atomic = attr->max_rd_atomic; | |
| if (attr_mask & IBV_QP_ALT_PATH) { | |
| cmd->alt_pkey_index = attr->alt_pkey_index; | |
| cmd->alt_port_num = attr->alt_port_num; | |
| cmd->alt_timeout = attr->alt_timeout; | |
| memcpy(cmd->alt_dest.dgid, attr->alt_ah_attr.grh.dgid.raw, 16); | |
| cmd->alt_dest.flow_label = attr->alt_ah_attr.grh.flow_label; | |
| cmd->alt_dest.dlid = attr->alt_ah_attr.dlid; | |
| cmd->alt_dest.reserved = 0; | |
| cmd->alt_dest.sgid_index = attr->alt_ah_attr.grh.sgid_index; | |
| cmd->alt_dest.hop_limit = attr->alt_ah_attr.grh.hop_limit; | |
| cmd->alt_dest.traffic_class = | |
| attr->alt_ah_attr.grh.traffic_class; | |
| cmd->alt_dest.sl = attr->alt_ah_attr.sl; | |
| cmd->alt_dest.src_path_bits = attr->alt_ah_attr.src_path_bits; | |
| cmd->alt_dest.static_rate = attr->alt_ah_attr.static_rate; | |
| cmd->alt_dest.is_global = attr->alt_ah_attr.is_global; | |
| cmd->alt_dest.port_num = attr->alt_ah_attr.port_num; | |
| } | |
| if (attr_mask & IBV_QP_MIN_RNR_TIMER) | |
| cmd->min_rnr_timer = attr->min_rnr_timer; | |
| if (attr_mask & IBV_QP_SQ_PSN) | |
| cmd->sq_psn = attr->sq_psn; | |
| if (attr_mask & IBV_QP_MAX_DEST_RD_ATOMIC) | |
| cmd->max_dest_rd_atomic = attr->max_dest_rd_atomic; | |
| if (attr_mask & IBV_QP_PATH_MIG_STATE) | |
| cmd->path_mig_state = attr->path_mig_state; | |
| if (attr_mask & IBV_QP_DEST_QPN) | |
| cmd->dest_qp_num = attr->dest_qp_num; | |
| cmd->reserved[0] = cmd->reserved[1] = 0; | |
| } | |
| int ibv_cmd_modify_qp(struct ibv_qp *qp, struct ibv_qp_attr *attr, | |
| int attr_mask, | |
| struct ibv_modify_qp *cmd, size_t cmd_size) | |
| { | |
| /* | |
| * Starting with IBV_QP_RATE_LIMIT the attribute must go through the | |
| * _ex path. | |
| */ | |
| if (attr_mask & ~(IBV_QP_RATE_LIMIT - 1)) | |
| return EOPNOTSUPP; | |
| copy_modify_qp_fields(qp, attr, attr_mask, &cmd->core_payload); | |
| return execute_cmd_write_req(qp->context, IB_USER_VERBS_CMD_MODIFY_QP, | |
| cmd, cmd_size); | |
| } | |
| int ibv_cmd_modify_qp_ex(struct ibv_qp *qp, struct ibv_qp_attr *attr, | |
| int attr_mask, struct ibv_modify_qp_ex *cmd, | |
| size_t cmd_size, | |
| struct ib_uverbs_ex_modify_qp_resp *resp, | |
| size_t resp_size) | |
| { | |
| copy_modify_qp_fields(qp, attr, attr_mask, &cmd->base); | |
| if (attr_mask & IBV_QP_RATE_LIMIT) { | |
| if (cmd_size >= offsetof(struct ibv_modify_qp_ex, rate_limit) + | |
| sizeof(cmd->rate_limit)) | |
| cmd->rate_limit = attr->rate_limit; | |
| else | |
| return EINVAL; | |
| } | |
| return execute_cmd_write_ex(qp->context, IB_USER_VERBS_EX_CMD_MODIFY_QP, | |
| cmd, cmd_size, resp, resp_size); | |
| } | |
| int ibv_cmd_post_send(struct ibv_qp *ibqp, struct ibv_send_wr *wr, | |
| struct ibv_send_wr **bad_wr) | |
| { | |
| struct ibv_post_send *cmd; | |
| struct ib_uverbs_post_send_resp resp; | |
| struct ibv_send_wr *i; | |
| struct ib_uverbs_send_wr *n, *tmp; | |
| struct ibv_sge *s; | |
| unsigned wr_count = 0; | |
| unsigned sge_count = 0; | |
| int cmd_size; | |
| int ret; | |
| for (i = wr; i; i = i->next) { | |
| wr_count++; | |
| sge_count += i->num_sge; | |
| } | |
| cmd_size = sizeof *cmd + wr_count * sizeof *n + sge_count * sizeof *s; | |
| cmd = alloca(cmd_size); | |
| cmd->qp_handle = ibqp->handle; | |
| cmd->wr_count = wr_count; | |
| cmd->sge_count = sge_count; | |
| cmd->wqe_size = sizeof *n; | |
| n = (struct ib_uverbs_send_wr *) ((void *) cmd + sizeof *cmd); | |
| s = (struct ibv_sge *) (n + wr_count); | |
| tmp = n; | |
| for (i = wr; i; i = i->next) { | |
| tmp->wr_id = i->wr_id; | |
| tmp->num_sge = i->num_sge; | |
| tmp->opcode = i->opcode; | |
| tmp->send_flags = i->send_flags; | |
| tmp->ex.imm_data = i->imm_data; | |
| if (ibqp->qp_type == IBV_QPT_UD) { | |
| tmp->wr.ud.ah = i->wr.ud.ah->handle; | |
| tmp->wr.ud.remote_qpn = i->wr.ud.remote_qpn; | |
| tmp->wr.ud.remote_qkey = i->wr.ud.remote_qkey; | |
| } else { | |
| switch (i->opcode) { | |
| case IBV_WR_RDMA_WRITE: | |
| case IBV_WR_RDMA_WRITE_WITH_IMM: | |
| case IBV_WR_RDMA_READ: | |
| tmp->wr.rdma.remote_addr = | |
| i->wr.rdma.remote_addr; | |
| tmp->wr.rdma.rkey = i->wr.rdma.rkey; | |
| break; | |
| case IBV_WR_ATOMIC_CMP_AND_SWP: | |
| case IBV_WR_ATOMIC_FETCH_AND_ADD: | |
| tmp->wr.atomic.remote_addr = | |
| i->wr.atomic.remote_addr; | |
| tmp->wr.atomic.compare_add = | |
| i->wr.atomic.compare_add; | |
| tmp->wr.atomic.swap = i->wr.atomic.swap; | |
| tmp->wr.atomic.rkey = i->wr.atomic.rkey; | |
| break; | |
| default: | |
| break; | |
| } | |
| } | |
| if (tmp->num_sge) { | |
| memcpy(s, i->sg_list, tmp->num_sge * sizeof *s); | |
| s += tmp->num_sge; | |
| } | |
| tmp++; | |
| } | |
| resp.bad_wr = 0; | |
| ret = execute_cmd_write_no_uhw(ibqp->context, | |
| IB_USER_VERBS_CMD_POST_SEND, cmd, | |
| cmd_size, &resp, sizeof(resp)); | |
| wr_count = resp.bad_wr; | |
| if (wr_count) { | |
| i = wr; | |
| while (--wr_count) | |
| i = i->next; | |
| *bad_wr = i; | |
| } else if (ret) | |
| *bad_wr = wr; | |
| return ret; | |
| } | |
| int ibv_cmd_post_recv(struct ibv_qp *ibqp, struct ibv_recv_wr *wr, | |
| struct ibv_recv_wr **bad_wr) | |
| { | |
| struct ibv_post_recv *cmd; | |
| struct ib_uverbs_post_recv_resp resp; | |
| struct ibv_recv_wr *i; | |
| struct ib_uverbs_recv_wr *n, *tmp; | |
| struct ibv_sge *s; | |
| unsigned wr_count = 0; | |
| unsigned sge_count = 0; | |
| int cmd_size; | |
| int ret; | |
| for (i = wr; i; i = i->next) { | |
| wr_count++; | |
| sge_count += i->num_sge; | |
| } | |
| cmd_size = sizeof *cmd + wr_count * sizeof *n + sge_count * sizeof *s; | |
| cmd = alloca(cmd_size); | |
| cmd->qp_handle = ibqp->handle; | |
| cmd->wr_count = wr_count; | |
| cmd->sge_count = sge_count; | |
| cmd->wqe_size = sizeof *n; | |
| n = (struct ib_uverbs_recv_wr *) ((void *) cmd + sizeof *cmd); | |
| s = (struct ibv_sge *) (n + wr_count); | |
| tmp = n; | |
| for (i = wr; i; i = i->next) { | |
| tmp->wr_id = i->wr_id; | |
| tmp->num_sge = i->num_sge; | |
| if (tmp->num_sge) { | |
| memcpy(s, i->sg_list, tmp->num_sge * sizeof *s); | |
| s += tmp->num_sge; | |
| } | |
| tmp++; | |
| } | |
| resp.bad_wr = 0; | |
| ret = execute_cmd_write_no_uhw(ibqp->context, | |
| IB_USER_VERBS_CMD_POST_RECV, cmd, | |
| cmd_size, &resp, sizeof(resp)); | |
| wr_count = resp.bad_wr; | |
| if (wr_count) { | |
| i = wr; | |
| while (--wr_count) | |
| i = i->next; | |
| *bad_wr = i; | |
| } else if (ret) | |
| *bad_wr = wr; | |
| return ret; | |
| } | |
| int ibv_cmd_post_srq_recv(struct ibv_srq *srq, struct ibv_recv_wr *wr, | |
| struct ibv_recv_wr **bad_wr) | |
| { | |
| struct ibv_post_srq_recv *cmd; | |
| struct ib_uverbs_post_srq_recv_resp resp; | |
| struct ibv_recv_wr *i; | |
| struct ib_uverbs_recv_wr *n, *tmp; | |
| struct ibv_sge *s; | |
| unsigned wr_count = 0; | |
| unsigned sge_count = 0; | |
| int cmd_size; | |
| int ret; | |
| for (i = wr; i; i = i->next) { | |
| wr_count++; | |
| sge_count += i->num_sge; | |
| } | |
| cmd_size = sizeof *cmd + wr_count * sizeof *n + sge_count * sizeof *s; | |
| cmd = alloca(cmd_size); | |
| cmd->srq_handle = srq->handle; | |
| cmd->wr_count = wr_count; | |
| cmd->sge_count = sge_count; | |
| cmd->wqe_size = sizeof *n; | |
| n = (struct ib_uverbs_recv_wr *) ((void *) cmd + sizeof *cmd); | |
| s = (struct ibv_sge *) (n + wr_count); | |
| tmp = n; | |
| for (i = wr; i; i = i->next) { | |
| tmp->wr_id = i->wr_id; | |
| tmp->num_sge = i->num_sge; | |
| if (tmp->num_sge) { | |
| memcpy(s, i->sg_list, tmp->num_sge * sizeof *s); | |
| s += tmp->num_sge; | |
| } | |
| tmp++; | |
| } | |
| resp.bad_wr = 0; | |
| ret = execute_cmd_write_no_uhw(srq->context, | |
| IB_USER_VERBS_CMD_POST_SRQ_RECV, cmd, | |
| cmd_size, &resp, sizeof(resp)); | |
| wr_count = resp.bad_wr; | |
| if (wr_count) { | |
| i = wr; | |
| while (--wr_count) | |
| i = i->next; | |
| *bad_wr = i; | |
| } else if (ret) | |
| *bad_wr = wr; | |
| return ret; | |
| } | |
| int ibv_cmd_create_ah(struct ibv_pd *pd, struct ibv_ah *ah, | |
| struct ibv_ah_attr *attr, | |
| struct ib_uverbs_create_ah_resp *resp, | |
| size_t resp_size) | |
| { | |
| struct ibv_create_ah cmd; | |
| int ret; | |
| cmd.user_handle = (uintptr_t) ah; | |
| cmd.pd_handle = pd->handle; | |
| cmd.reserved = 0; | |
| cmd.attr.dlid = attr->dlid; | |
| cmd.attr.sl = attr->sl; | |
| cmd.attr.src_path_bits = attr->src_path_bits; | |
| cmd.attr.static_rate = attr->static_rate; | |
| cmd.attr.is_global = attr->is_global; | |
| cmd.attr.port_num = attr->port_num; | |
| cmd.attr.reserved = 0; | |
| cmd.attr.grh.flow_label = attr->grh.flow_label; | |
| cmd.attr.grh.sgid_index = attr->grh.sgid_index; | |
| cmd.attr.grh.hop_limit = attr->grh.hop_limit; | |
| cmd.attr.grh.traffic_class = attr->grh.traffic_class; | |
| cmd.attr.grh.reserved = 0; | |
| memcpy(cmd.attr.grh.dgid, attr->grh.dgid.raw, 16); | |
| ret = execute_cmd_write(pd->context, IB_USER_VERBS_CMD_CREATE_AH, &cmd, | |
| sizeof(cmd), resp, resp_size); | |
| if (ret) | |
| return ret; | |
| ah->handle = resp->ah_handle; | |
| ah->context = pd->context; | |
| return 0; | |
| } | |
| int ibv_cmd_attach_mcast(struct ibv_qp *qp, const union ibv_gid *gid, uint16_t lid) | |
| { | |
| struct ibv_attach_mcast req; | |
| req.core_payload = (struct ib_uverbs_attach_mcast){ | |
| .qp_handle = qp->handle, | |
| .mlid = lid, | |
| }; | |
| memcpy(req.gid, gid->raw, sizeof(req.gid)); | |
| return execute_cmd_write_req( | |
| qp->context, IB_USER_VERBS_CMD_ATTACH_MCAST, &req, sizeof(req)); | |
| } | |
| int ibv_cmd_detach_mcast(struct ibv_qp *qp, const union ibv_gid *gid, uint16_t lid) | |
| { | |
| struct ibv_detach_mcast req; | |
| int ret; | |
| req.core_payload = (struct ib_uverbs_detach_mcast){ | |
| .qp_handle = qp->handle, | |
| .mlid = lid, | |
| }; | |
| memcpy(req.gid, gid->raw, sizeof(req.gid)); | |
| ret = execute_cmd_write_req(qp->context, IB_USER_VERBS_CMD_DETACH_MCAST, | |
| &req, sizeof(req)); | |
| if (verbs_is_destroy_err(&ret)) | |
| return ret; | |
| return 0; | |
| } | |
| static int buffer_is_zero(char *addr, ssize_t size) | |
| { | |
| return addr[0] == 0 && !memcmp(addr, addr + 1, size - 1); | |
| } | |
| static int get_filters_size(struct ibv_flow_spec *ib_spec, | |
| struct ibv_kern_spec *kern_spec, | |
| int *ib_filter_size, int *kern_filter_size, | |
| enum ibv_flow_spec_type type) | |
| { | |
| void *ib_spec_filter_mask; | |
| int curr_kern_filter_size; | |
| int min_filter_size; | |
| *ib_filter_size = (ib_spec->hdr.size - sizeof(ib_spec->hdr)) / 2; | |
| switch (type) { | |
| case IBV_FLOW_SPEC_IPV4_EXT: | |
| min_filter_size = | |
| offsetof(struct ib_uverbs_flow_ipv4_filter, flags) + | |
| sizeof(kern_spec->ipv4_ext.mask.flags); | |
| curr_kern_filter_size = min_filter_size; | |
| ib_spec_filter_mask = (void *)&ib_spec->ipv4_ext.val + | |
| *ib_filter_size; | |
| break; | |
| case IBV_FLOW_SPEC_IPV6: | |
| min_filter_size = | |
| offsetof(struct ib_uverbs_flow_ipv6_filter, hop_limit) + | |
| sizeof(kern_spec->ipv6.mask.hop_limit); | |
| curr_kern_filter_size = min_filter_size; | |
| ib_spec_filter_mask = (void *)&ib_spec->ipv6.val + | |
| *ib_filter_size; | |
| break; | |
| case IBV_FLOW_SPEC_VXLAN_TUNNEL: | |
| min_filter_size = | |
| offsetof(struct ib_uverbs_flow_tunnel_filter, | |
| tunnel_id) + | |
| sizeof(kern_spec->tunnel.mask.tunnel_id); | |
| curr_kern_filter_size = min_filter_size; | |
| ib_spec_filter_mask = (void *)&ib_spec->tunnel.val + | |
| *ib_filter_size; | |
| break; | |
| default: | |
| return EINVAL; | |
| } | |
| if (*ib_filter_size < min_filter_size) | |
| return EINVAL; | |
| if (*ib_filter_size > curr_kern_filter_size && | |
| !buffer_is_zero(ib_spec_filter_mask + curr_kern_filter_size, | |
| *ib_filter_size - curr_kern_filter_size)) | |
| return EOPNOTSUPP; | |
| *kern_filter_size = min_t(int, curr_kern_filter_size, *ib_filter_size); | |
| return 0; | |
| } | |
| static int ib_spec_to_kern_spec(struct ibv_flow_spec *ib_spec, | |
| struct ibv_kern_spec *kern_spec) | |
| { | |
| int kern_filter_size; | |
| int ib_filter_size; | |
| int ret; | |
| kern_spec->hdr.type = ib_spec->hdr.type; | |
| switch (kern_spec->hdr.type) { | |
| case IBV_FLOW_SPEC_ETH: | |
| case IBV_FLOW_SPEC_ETH | IBV_FLOW_SPEC_INNER: | |
| kern_spec->eth.size = sizeof(struct ib_uverbs_flow_spec_eth); | |
| memcpy(&kern_spec->eth.val, &ib_spec->eth.val, | |
| sizeof(struct ibv_flow_eth_filter)); | |
| memcpy(&kern_spec->eth.mask, &ib_spec->eth.mask, | |
| sizeof(struct ibv_flow_eth_filter)); | |
| break; | |
| case IBV_FLOW_SPEC_IPV4: | |
| case IBV_FLOW_SPEC_IPV4 | IBV_FLOW_SPEC_INNER: | |
| kern_spec->ipv4.size = sizeof(struct ibv_kern_spec_ipv4); | |
| memcpy(&kern_spec->ipv4.val, &ib_spec->ipv4.val, | |
| sizeof(struct ibv_flow_ipv4_filter)); | |
| memcpy(&kern_spec->ipv4.mask, &ib_spec->ipv4.mask, | |
| sizeof(struct ibv_flow_ipv4_filter)); | |
| break; | |
| case IBV_FLOW_SPEC_IPV4_EXT: | |
| case IBV_FLOW_SPEC_IPV4_EXT | IBV_FLOW_SPEC_INNER: | |
| ret = get_filters_size(ib_spec, kern_spec, | |
| &ib_filter_size, &kern_filter_size, | |
| IBV_FLOW_SPEC_IPV4_EXT); | |
| if (ret) | |
| return ret; | |
| kern_spec->hdr.type = IBV_FLOW_SPEC_IPV4 | | |
| (IBV_FLOW_SPEC_INNER & ib_spec->hdr.type); | |
| kern_spec->ipv4_ext.size = sizeof(struct | |
| ib_uverbs_flow_spec_ipv4); | |
| memcpy(&kern_spec->ipv4_ext.val, &ib_spec->ipv4_ext.val, | |
| kern_filter_size); | |
| memcpy(&kern_spec->ipv4_ext.mask, (void *)&ib_spec->ipv4_ext.val | |
| + ib_filter_size, kern_filter_size); | |
| break; | |
| case IBV_FLOW_SPEC_IPV6: | |
| case IBV_FLOW_SPEC_IPV6 | IBV_FLOW_SPEC_INNER: | |
| ret = get_filters_size(ib_spec, kern_spec, | |
| &ib_filter_size, &kern_filter_size, | |
| IBV_FLOW_SPEC_IPV6); | |
| if (ret) | |
| return ret; | |
| kern_spec->ipv6.size = sizeof(struct ib_uverbs_flow_spec_ipv6); | |
| memcpy(&kern_spec->ipv6.val, &ib_spec->ipv6.val, | |
| kern_filter_size); | |
| memcpy(&kern_spec->ipv6.mask, (void *)&ib_spec->ipv6.val | |
| + ib_filter_size, kern_filter_size); | |
| break; | |
| case IBV_FLOW_SPEC_ESP: | |
| case IBV_FLOW_SPEC_ESP | IBV_FLOW_SPEC_INNER: | |
| kern_spec->esp.size = sizeof(struct ib_uverbs_flow_spec_esp); | |
| memcpy(&kern_spec->esp.val, &ib_spec->esp.val, | |
| sizeof(struct ib_uverbs_flow_spec_esp_filter)); | |
| memcpy(&kern_spec->esp.mask, (void *)&ib_spec->esp.mask, | |
| sizeof(struct ib_uverbs_flow_spec_esp_filter)); | |
| break; | |
| case IBV_FLOW_SPEC_TCP: | |
| case IBV_FLOW_SPEC_UDP: | |
| case IBV_FLOW_SPEC_TCP | IBV_FLOW_SPEC_INNER: | |
| case IBV_FLOW_SPEC_UDP | IBV_FLOW_SPEC_INNER: | |
| kern_spec->tcp_udp.size = sizeof(struct ib_uverbs_flow_spec_tcp_udp); | |
| memcpy(&kern_spec->tcp_udp.val, &ib_spec->tcp_udp.val, | |
| sizeof(struct ibv_flow_tcp_udp_filter)); | |
| memcpy(&kern_spec->tcp_udp.mask, &ib_spec->tcp_udp.mask, | |
| sizeof(struct ibv_flow_tcp_udp_filter)); | |
| break; | |
| case IBV_FLOW_SPEC_GRE: | |
| kern_spec->gre.size = sizeof(struct ib_uverbs_flow_spec_gre); | |
| memcpy(&kern_spec->gre.val, &ib_spec->gre.val, | |
| sizeof(struct ibv_flow_gre_filter)); | |
| memcpy(&kern_spec->gre.mask, &ib_spec->gre.mask, | |
| sizeof(struct ibv_flow_gre_filter)); | |
| break; | |
| case IBV_FLOW_SPEC_MPLS: | |
| case IBV_FLOW_SPEC_MPLS | IBV_FLOW_SPEC_INNER: | |
| kern_spec->mpls.size = sizeof(struct ib_uverbs_flow_spec_mpls); | |
| memcpy(&kern_spec->mpls.val, &ib_spec->mpls.val, | |
| sizeof(struct ibv_flow_mpls_filter)); | |
| memcpy(&kern_spec->mpls.mask, &ib_spec->mpls.mask, | |
| sizeof(struct ibv_flow_mpls_filter)); | |
| break; | |
| case IBV_FLOW_SPEC_VXLAN_TUNNEL: | |
| ret = get_filters_size(ib_spec, kern_spec, | |
| &ib_filter_size, &kern_filter_size, | |
| IBV_FLOW_SPEC_VXLAN_TUNNEL); | |
| if (ret) | |
| return ret; | |
| kern_spec->tunnel.size = sizeof(struct ib_uverbs_flow_spec_tunnel); | |
| memcpy(&kern_spec->tunnel.val, &ib_spec->tunnel.val, | |
| kern_filter_size); | |
| memcpy(&kern_spec->tunnel.mask, (void *)&ib_spec->tunnel.val | |
| + ib_filter_size, kern_filter_size); | |
| break; | |
| case IBV_FLOW_SPEC_ACTION_TAG: | |
| kern_spec->flow_tag.size = | |
| sizeof(struct ib_uverbs_flow_spec_action_tag); | |
| kern_spec->flow_tag.tag_id = ib_spec->flow_tag.tag_id; | |
| break; | |
| case IBV_FLOW_SPEC_ACTION_DROP: | |
| kern_spec->drop.size = sizeof(struct ib_uverbs_flow_spec_action_drop); | |
| break; | |
| case IBV_FLOW_SPEC_ACTION_HANDLE: { | |
| const struct verbs_flow_action *vaction = | |
| container_of((const struct ibv_flow_action *)ib_spec->handle.action, | |
| const struct verbs_flow_action, action); | |
| kern_spec->handle.size = sizeof(struct ib_uverbs_flow_spec_action_handle); | |
| kern_spec->handle.handle = vaction->handle; | |
| break; | |
| } | |
| case IBV_FLOW_SPEC_ACTION_COUNT: { | |
| const struct verbs_counters *vcounters = | |
| container_of(ib_spec->flow_count.counters, | |
| const struct verbs_counters, counters); | |
| kern_spec->flow_count.size = | |
| sizeof(struct ib_uverbs_flow_spec_action_count); | |
| kern_spec->flow_count.handle = vcounters->handle; | |
| break; | |
| } | |
| default: | |
| return EINVAL; | |
| } | |
| return 0; | |
| } | |
| int ibv_cmd_create_flow(struct ibv_qp *qp, | |
| struct ibv_flow *flow_id, | |
| struct ibv_flow_attr *flow_attr, | |
| void *ucmd, | |
| size_t ucmd_size) | |
| { | |
| struct ibv_create_flow *cmd; | |
| struct ib_uverbs_create_flow_resp resp; | |
| size_t cmd_size; | |
| size_t written_size; | |
| int i, err; | |
| void *kern_spec; | |
| void *ib_spec; | |
| cmd_size = sizeof(*cmd) + (flow_attr->num_of_specs * | |
| sizeof(struct ibv_kern_spec)); | |
| cmd = alloca(cmd_size + ucmd_size); | |
| memset(cmd, 0, cmd_size + ucmd_size); | |
| cmd->qp_handle = qp->handle; | |
| cmd->flow_attr.type = flow_attr->type; | |
| cmd->flow_attr.priority = flow_attr->priority; | |
| cmd->flow_attr.num_of_specs = flow_attr->num_of_specs; | |
| cmd->flow_attr.port = flow_attr->port; | |
| cmd->flow_attr.flags = flow_attr->flags; | |
| kern_spec = cmd + 1; | |
| ib_spec = flow_attr + 1; | |
| for (i = 0; i < flow_attr->num_of_specs; i++) { | |
| err = ib_spec_to_kern_spec(ib_spec, kern_spec); | |
| if (err) { | |
| errno = err; | |
| return err; | |
| } | |
| cmd->flow_attr.size += | |
| ((struct ibv_kern_spec *)kern_spec)->hdr.size; | |
| kern_spec += ((struct ibv_kern_spec *)kern_spec)->hdr.size; | |
| ib_spec += ((struct ibv_flow_spec *)ib_spec)->hdr.size; | |
| } | |
| written_size = sizeof(*cmd) + cmd->flow_attr.size; | |
| if (ucmd) { | |
| memcpy((char *)cmd + written_size, ucmd, ucmd_size); | |
| written_size += ucmd_size; | |
| } | |
| err = execute_cmd_write_ex_full(qp->context, | |
| IB_USER_VERBS_EX_CMD_CREATE_FLOW, cmd, | |
| written_size - ucmd_size, written_size, | |
| &resp, sizeof(resp), sizeof(resp)); | |
| if (err) | |
| return err; | |
| flow_id->context = qp->context; | |
| flow_id->handle = resp.flow_handle; | |
| return 0; | |
| } | |
| int ibv_cmd_modify_wq(struct ibv_wq *wq, struct ibv_wq_attr *attr, | |
| struct ibv_modify_wq *cmd, size_t cmd_size) | |
| { | |
| int err; | |
| if (attr->attr_mask >= IBV_WQ_ATTR_RESERVED) | |
| return EINVAL; | |
| memset(cmd, 0, sizeof(*cmd)); | |
| cmd->curr_wq_state = attr->curr_wq_state; | |
| cmd->wq_state = attr->wq_state; | |
| if (attr->attr_mask & IBV_WQ_ATTR_FLAGS) { | |
| if (attr->flags_mask & ~(IBV_WQ_FLAGS_RESERVED - 1)) | |
| return EOPNOTSUPP; | |
| cmd->flags = attr->flags; | |
| cmd->flags_mask = attr->flags_mask; | |
| } | |
| cmd->wq_handle = wq->handle; | |
| cmd->attr_mask = attr->attr_mask; | |
| err = execute_cmd_write_ex_req( | |
| wq->context, IB_USER_VERBS_EX_CMD_MODIFY_WQ, cmd, cmd_size); | |
| if (err) | |
| return err; | |
| if (attr->attr_mask & IBV_WQ_ATTR_STATE) | |
| wq->state = attr->wq_state; | |
| return 0; | |
| } | |
| int ibv_cmd_create_rwq_ind_table(struct ibv_context *context, | |
| struct ibv_rwq_ind_table_init_attr *init_attr, | |
| struct ibv_rwq_ind_table *rwq_ind_table, | |
| struct ib_uverbs_ex_create_rwq_ind_table_resp *resp, | |
| size_t resp_size) | |
| { | |
| struct ibv_create_rwq_ind_table *cmd; | |
| int err; | |
| unsigned int i; | |
| unsigned int num_tbl_entries; | |
| size_t cmd_size; | |
| if (init_attr->comp_mask >= IBV_CREATE_IND_TABLE_RESERVED) | |
| return EINVAL; | |
| num_tbl_entries = 1 << init_attr->log_ind_tbl_size; | |
| /* The entire message must be size aligned to 8 bytes. */ | |
| cmd_size = sizeof(*cmd) + num_tbl_entries * sizeof(cmd->wq_handles[0]); | |
| cmd_size = (cmd_size + 7) / 8 * 8; | |
| cmd = alloca(cmd_size); | |
| memset(cmd, 0, cmd_size); | |
| for (i = 0; i < num_tbl_entries; i++) | |
| cmd->wq_handles[i] = init_attr->ind_tbl[i]->handle; | |
| cmd->log_ind_tbl_size = init_attr->log_ind_tbl_size; | |
| cmd->comp_mask = 0; | |
| err = execute_cmd_write_ex_full(context, | |
| IB_USER_VERBS_EX_CMD_CREATE_RWQ_IND_TBL, | |
| cmd, cmd_size, cmd_size, resp, | |
| sizeof(*resp), resp_size); | |
| if (err) | |
| return err; | |
| if (resp->response_length < sizeof(*resp)) | |
| return EINVAL; | |
| rwq_ind_table->ind_tbl_handle = resp->ind_tbl_handle; | |
| rwq_ind_table->ind_tbl_num = resp->ind_tbl_num; | |
| rwq_ind_table->context = context; | |
| return 0; | |
| } | |
| int ibv_cmd_modify_cq(struct ibv_cq *cq, | |
| struct ibv_modify_cq_attr *attr, | |
| struct ibv_modify_cq *cmd, | |
| size_t cmd_size) | |
| { | |
| if (attr->attr_mask >= IBV_CQ_ATTR_RESERVED) | |
| return EINVAL; | |
| cmd->cq_handle = cq->handle; | |
| cmd->attr_mask = attr->attr_mask; | |
| cmd->attr.cq_count = attr->moderate.cq_count; | |
| cmd->attr.cq_period = attr->moderate.cq_period; | |
| cmd->reserved = 0; | |
| return execute_cmd_write_ex_req( | |
| cq->context, IB_USER_VERBS_EX_CMD_MODIFY_CQ, cmd, cmd_size); | |
| } |