blob: 815333691c383185664ac33f2fc3dda22d4baa9c [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 <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;
}