| /* |
| * 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 <arpa/inet.h> |
| #include "pvrdma.h" |
| |
| int pvrdma_query_device(struct ibv_context *context, |
| const struct ibv_query_device_ex_input *input, |
| struct ibv_device_attr_ex *attr, size_t attr_size) |
| { |
| struct ib_uverbs_ex_query_device_resp resp; |
| size_t resp_size = sizeof(resp); |
| uint64_t raw_fw_ver; |
| unsigned major, minor, sub_minor; |
| int ret; |
| |
| ret = ibv_cmd_query_device_any(context, input, attr, attr_size, &resp, |
| &resp_size); |
| if (ret) |
| return ret; |
| |
| raw_fw_ver = resp.base.fw_ver; |
| major = (raw_fw_ver >> 32) & 0xffff; |
| minor = (raw_fw_ver >> 16) & 0xffff; |
| sub_minor = raw_fw_ver & 0xffff; |
| |
| snprintf(attr->orig_attr.fw_ver, sizeof(attr->orig_attr.fw_ver), |
| "%d.%d.%03d", major, minor, sub_minor); |
| |
| return 0; |
| } |
| |
| int pvrdma_query_port(struct ibv_context *context, uint8_t port, |
| struct ibv_port_attr *attr) |
| { |
| struct ibv_query_port cmd; |
| |
| return ibv_cmd_query_port(context, port, attr, &cmd, sizeof(cmd)); |
| } |
| |
| struct ibv_pd *pvrdma_alloc_pd(struct ibv_context *context) |
| { |
| struct ibv_alloc_pd cmd; |
| struct user_pvrdma_alloc_pd_resp resp; |
| struct pvrdma_pd *pd; |
| |
| pd = malloc(sizeof(*pd)); |
| if (!pd) |
| return NULL; |
| |
| if (ibv_cmd_alloc_pd(context, &pd->ibv_pd, &cmd, sizeof(cmd), |
| &resp.ibv_resp, sizeof(resp))) { |
| free(pd); |
| return NULL; |
| } |
| |
| pd->pdn = resp.pdn; |
| |
| return &pd->ibv_pd; |
| } |
| |
| int pvrdma_free_pd(struct ibv_pd *pd) |
| { |
| int ret; |
| |
| ret = ibv_cmd_dealloc_pd(pd); |
| if (ret) |
| return ret; |
| |
| free(to_vpd(pd)); |
| |
| return 0; |
| } |
| |
| struct ibv_mr *pvrdma_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; |
| struct ib_uverbs_reg_mr_resp resp; |
| int ret; |
| |
| vmr = malloc(sizeof(*vmr)); |
| if (!vmr) |
| return NULL; |
| |
| ret = ibv_cmd_reg_mr(pd, addr, length, hca_va, access, vmr, &cmd, |
| sizeof(cmd), &resp, sizeof(resp)); |
| if (ret) { |
| free(vmr); |
| return NULL; |
| } |
| |
| return &vmr->ibv_mr; |
| } |
| |
| int pvrdma_dereg_mr(struct verbs_mr *vmr) |
| { |
| int ret; |
| |
| ret = ibv_cmd_dereg_mr(vmr); |
| if (ret) |
| return ret; |
| |
| free(vmr); |
| |
| return 0; |
| } |
| |
| static int is_multicast_gid(const union ibv_gid *gid) |
| { |
| return gid->raw[0] == 0xff; |
| } |
| |
| static int is_link_local_gid(const union ibv_gid *gid) |
| { |
| return gid->global.subnet_prefix == htobe64(0xfe80000000000000ULL); |
| } |
| |
| static int is_ipv6_addr_v4mapped(const struct in6_addr *a) |
| { |
| return IN6_IS_ADDR_V4MAPPED(&a->s6_addr32) || |
| /* IPv4 encoded multicast addresses */ |
| (a->s6_addr32[0] == htobe32(0xff0e0000) && |
| ((a->s6_addr32[1] | |
| (a->s6_addr32[2] ^ htobe32(0x0000ffff))) == 0UL)); |
| } |
| |
| static int set_mac_from_gid(const union ibv_gid *gid, |
| __u8 mac[6]) |
| { |
| if (is_link_local_gid(gid)) { |
| /* |
| * The MAC is embedded in GID[8-10,13-15] with the |
| * 7th most significant bit inverted. |
| */ |
| memcpy(mac, gid->raw + 8, 3); |
| memcpy(mac + 3, gid->raw + 13, 3); |
| mac[0] ^= 2; |
| |
| return 0; |
| } |
| |
| return 1; |
| } |
| |
| struct ibv_ah *pvrdma_create_ah(struct ibv_pd *pd, |
| struct ibv_ah_attr *attr) |
| { |
| struct pvrdma_ah *ah; |
| struct pvrdma_av *av; |
| struct ibv_port_attr port_attr; |
| |
| if (!attr->is_global) |
| return NULL; |
| |
| if (ibv_query_port(pd->context, attr->port_num, &port_attr)) |
| return NULL; |
| |
| if (port_attr.link_layer == IBV_LINK_LAYER_UNSPECIFIED || |
| port_attr.link_layer == IBV_LINK_LAYER_INFINIBAND) |
| return NULL; |
| |
| if (port_attr.link_layer == IBV_LINK_LAYER_ETHERNET && |
| (!is_link_local_gid(&attr->grh.dgid) && |
| !is_multicast_gid(&attr->grh.dgid) && |
| !is_ipv6_addr_v4mapped((struct in6_addr *)attr->grh.dgid.raw))) |
| return NULL; |
| |
| ah = calloc(1, sizeof(*ah)); |
| if (!ah) |
| return NULL; |
| |
| av = &ah->av; |
| av->port_pd = to_vpd(pd)->pdn | (attr->port_num << 24); |
| av->src_path_bits = attr->src_path_bits; |
| av->src_path_bits |= 0x80; |
| av->gid_index = attr->grh.sgid_index; |
| av->hop_limit = attr->grh.hop_limit; |
| av->sl_tclass_flowlabel = (attr->grh.traffic_class << 20) | |
| attr->grh.flow_label; |
| memcpy(av->dgid, attr->grh.dgid.raw, 16); |
| |
| if (port_attr.port_cap_flags & IBV_PORT_IP_BASED_GIDS) { |
| if (!ibv_resolve_eth_l2_from_gid(pd->context, attr, |
| av->dmac, NULL)) |
| return &ah->ibv_ah; |
| } else { |
| if (!set_mac_from_gid(&attr->grh.dgid, av->dmac)) |
| return &ah->ibv_ah; |
| } |
| |
| free(ah); |
| return NULL; |
| } |
| |
| int pvrdma_destroy_ah(struct ibv_ah *ah) |
| { |
| free(to_vah(ah)); |
| |
| return 0; |
| } |