Resguardo inicial

This commit is contained in:
lastdragon
2026-09-06 12:53:21 -06:00
parent 662efe3c2a
commit ad2088d178
94853 changed files with 43200404 additions and 0 deletions
@@ -0,0 +1,9 @@
# SPDX-License-Identifier: GPL-2.0-only
config MLX5_INFINIBAND
tristate "Mellanox 5th generation network adapters (ConnectX series) support"
depends on NETDEVICES && ETHERNET && PCI && MLX5_CORE
help
This driver provides low-level InfiniBand support for
Mellanox Connect-IB PCI Express host channel adapters (HCAs).
This is required to use InfiniBand protocols such as
IP-over-IB or SRP with these devices.
@@ -0,0 +1,33 @@
# SPDX-License-Identifier: GPL-2.0-only
obj-$(CONFIG_MLX5_INFINIBAND) += mlx5_ib.o
mlx5_ib-y := ah.o \
cmd.o \
cong.o \
counters.o \
cq.o \
data_direct.o \
dm.o \
dmah.o \
doorbell.o \
fs.o \
gsi.o \
ib_virt.o \
mad.o \
main.o \
mem.o \
mr.o \
qp.o \
qpc.o \
restrack.o \
srq.o \
srq_cmd.o \
umr.o \
wr.o
mlx5_ib-$(CONFIG_INFINIBAND_ON_DEMAND_PAGING) += odp.o
mlx5_ib-$(CONFIG_MLX5_ESWITCH) += ib_rep.o
mlx5_ib-$(CONFIG_INFINIBAND_USER_ACCESS) += devx.o \
qos.o \
std_types.o
mlx5_ib-$(CONFIG_MLX5_MACSEC) += macsec.o
+154
View File
@@ -0,0 +1,154 @@
/*
* Copyright (c) 2013-2015, Mellanox Technologies. 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 "mlx5_ib.h"
static __be16 mlx5_ah_get_udp_sport(const struct mlx5_ib_dev *dev,
const struct rdma_ah_attr *ah_attr)
{
enum ib_gid_type gid_type = ah_attr->grh.sgid_attr->gid_type;
__be16 sport;
if ((gid_type == IB_GID_TYPE_ROCE_UDP_ENCAP) &&
(rdma_ah_get_ah_flags(ah_attr) & IB_AH_GRH) &&
(ah_attr->grh.flow_label & IB_GRH_FLOWLABEL_MASK))
sport = cpu_to_be16(
rdma_flow_label_to_udp_sport(ah_attr->grh.flow_label));
else
sport = mlx5_get_roce_udp_sport_min(dev,
ah_attr->grh.sgid_attr);
return sport;
}
static int create_ib_ah(struct mlx5_ib_dev *dev, struct mlx5_ib_ah *ah,
struct rdma_ah_init_attr *init_attr)
{
struct rdma_ah_attr *ah_attr = init_attr->ah_attr;
enum ib_gid_type gid_type;
int rate_val;
if (rdma_ah_get_ah_flags(ah_attr) & IB_AH_GRH) {
const struct ib_global_route *grh = rdma_ah_read_grh(ah_attr);
memcpy(ah->av.rgid, &grh->dgid, 16);
ah->av.grh_gid_fl = cpu_to_be32(grh->flow_label |
(1 << 30) |
grh->sgid_index << 20);
ah->av.hop_limit = grh->hop_limit;
ah->av.tclass = grh->traffic_class;
}
rate_val = mlx5r_ib_rate(dev, rdma_ah_get_static_rate(ah_attr));
if (rate_val < 0)
return rate_val;
ah->av.stat_rate_sl = rate_val << 4;
if (ah_attr->type == RDMA_AH_ATTR_TYPE_ROCE) {
if (init_attr->xmit_slave)
ah->xmit_port =
mlx5_lag_get_slave_port(dev->mdev,
init_attr->xmit_slave);
gid_type = ah_attr->grh.sgid_attr->gid_type;
memcpy(ah->av.rmac, ah_attr->roce.dmac,
sizeof(ah_attr->roce.dmac));
ah->av.udp_sport = mlx5_ah_get_udp_sport(dev, ah_attr);
ah->av.stat_rate_sl |= (rdma_ah_get_sl(ah_attr) & 0x7) << 1;
if (gid_type == IB_GID_TYPE_ROCE_UDP_ENCAP)
#define MLX5_ECN_ENABLED BIT(1)
ah->av.tclass |= MLX5_ECN_ENABLED;
} else {
ah->av.rlid = cpu_to_be16(rdma_ah_get_dlid(ah_attr));
ah->av.fl_mlid = rdma_ah_get_path_bits(ah_attr) & 0x7f;
ah->av.stat_rate_sl |= (rdma_ah_get_sl(ah_attr) & 0xf);
}
return 0;
}
int mlx5_ib_create_ah(struct ib_ah *ibah, struct rdma_ah_init_attr *init_attr,
struct ib_udata *udata)
{
struct rdma_ah_attr *ah_attr = init_attr->ah_attr;
struct mlx5_ib_ah *ah = to_mah(ibah);
struct mlx5_ib_dev *dev = to_mdev(ibah->device);
enum rdma_ah_attr_type ah_type = ah_attr->type;
if ((ah_type == RDMA_AH_ATTR_TYPE_ROCE) &&
!(rdma_ah_get_ah_flags(ah_attr) & IB_AH_GRH))
return -EINVAL;
if (ah_type == RDMA_AH_ATTR_TYPE_ROCE && udata) {
int err;
struct mlx5_ib_create_ah_resp resp = {};
u32 min_resp_len =
offsetofend(struct mlx5_ib_create_ah_resp, dmac);
if (udata->outlen < min_resp_len)
return -EINVAL;
resp.response_length = min_resp_len;
memcpy(resp.dmac, ah_attr->roce.dmac, ETH_ALEN);
err = ib_respond_udata(udata, resp);
if (err)
return err;
}
return create_ib_ah(dev, ah, init_attr);
}
int mlx5_ib_query_ah(struct ib_ah *ibah, struct rdma_ah_attr *ah_attr)
{
struct mlx5_ib_ah *ah = to_mah(ibah);
u32 tmp;
memset(ah_attr, 0, sizeof(*ah_attr));
ah_attr->type = ibah->type;
tmp = be32_to_cpu(ah->av.grh_gid_fl);
if (tmp & (1 << 30)) {
rdma_ah_set_grh(ah_attr, NULL,
tmp & 0xfffff,
(tmp >> 20) & 0xff,
ah->av.hop_limit,
ah->av.tclass);
rdma_ah_set_dgid_raw(ah_attr, ah->av.rgid);
}
rdma_ah_set_dlid(ah_attr, be16_to_cpu(ah->av.rlid));
rdma_ah_set_static_rate(ah_attr, ah->av.stat_rate_sl >> 4);
rdma_ah_set_sl(ah_attr, ah->av.stat_rate_sl & 0xf);
return 0;
}
@@ -0,0 +1,268 @@
// SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB
/*
* Copyright (c) 2017-2020, Mellanox Technologies inc. All rights reserved.
*/
#include "cmd.h"
int mlx5r_cmd_query_special_mkeys(struct mlx5_ib_dev *dev)
{
u32 out[MLX5_ST_SZ_DW(query_special_contexts_out)] = {};
u32 in[MLX5_ST_SZ_DW(query_special_contexts_in)] = {};
bool is_terminate, is_dump, is_null;
int err;
is_terminate = MLX5_CAP_GEN(dev->mdev, terminate_scatter_list_mkey);
is_dump = MLX5_CAP_GEN(dev->mdev, dump_fill_mkey);
is_null = MLX5_CAP_GEN(dev->mdev, null_mkey);
dev->mkeys.terminate_scatter_list_mkey = MLX5_TERMINATE_SCATTER_LIST_LKEY;
if (!is_terminate && !is_dump && !is_null)
return 0;
MLX5_SET(query_special_contexts_in, in, opcode,
MLX5_CMD_OP_QUERY_SPECIAL_CONTEXTS);
err = mlx5_cmd_exec_inout(dev->mdev, query_special_contexts, in, out);
if (err)
return err;
if (is_dump)
dev->mkeys.dump_fill_mkey = MLX5_GET(query_special_contexts_out,
out, dump_fill_mkey);
if (is_null)
dev->mkeys.null_mkey = cpu_to_be32(
MLX5_GET(query_special_contexts_out, out, null_mkey));
if (is_terminate)
dev->mkeys.terminate_scatter_list_mkey =
cpu_to_be32(MLX5_GET(query_special_contexts_out, out,
terminate_scatter_list_mkey));
return 0;
}
int mlx5_cmd_query_cong_params(struct mlx5_core_dev *dev, int cong_point,
void *out)
{
u32 in[MLX5_ST_SZ_DW(query_cong_params_in)] = {};
MLX5_SET(query_cong_params_in, in, opcode,
MLX5_CMD_OP_QUERY_CONG_PARAMS);
MLX5_SET(query_cong_params_in, in, cong_protocol, cong_point);
return mlx5_cmd_exec_inout(dev, query_cong_params, in, out);
}
void mlx5_cmd_destroy_tir(struct mlx5_core_dev *dev, u32 tirn, u16 uid)
{
u32 in[MLX5_ST_SZ_DW(destroy_tir_in)] = {};
MLX5_SET(destroy_tir_in, in, opcode, MLX5_CMD_OP_DESTROY_TIR);
MLX5_SET(destroy_tir_in, in, tirn, tirn);
MLX5_SET(destroy_tir_in, in, uid, uid);
mlx5_cmd_exec_in(dev, destroy_tir, in);
}
void mlx5_cmd_destroy_tis(struct mlx5_core_dev *dev, u32 tisn, u16 uid)
{
u32 in[MLX5_ST_SZ_DW(destroy_tis_in)] = {};
MLX5_SET(destroy_tis_in, in, opcode, MLX5_CMD_OP_DESTROY_TIS);
MLX5_SET(destroy_tis_in, in, tisn, tisn);
MLX5_SET(destroy_tis_in, in, uid, uid);
mlx5_cmd_exec_in(dev, destroy_tis, in);
}
int mlx5_cmd_destroy_rqt(struct mlx5_core_dev *dev, u32 rqtn, u16 uid)
{
u32 in[MLX5_ST_SZ_DW(destroy_rqt_in)] = {};
MLX5_SET(destroy_rqt_in, in, opcode, MLX5_CMD_OP_DESTROY_RQT);
MLX5_SET(destroy_rqt_in, in, rqtn, rqtn);
MLX5_SET(destroy_rqt_in, in, uid, uid);
return mlx5_cmd_exec_in(dev, destroy_rqt, in);
}
int mlx5_cmd_alloc_transport_domain(struct mlx5_core_dev *dev, u32 *tdn,
u16 uid)
{
u32 in[MLX5_ST_SZ_DW(alloc_transport_domain_in)] = {};
u32 out[MLX5_ST_SZ_DW(alloc_transport_domain_out)] = {};
int err;
MLX5_SET(alloc_transport_domain_in, in, opcode,
MLX5_CMD_OP_ALLOC_TRANSPORT_DOMAIN);
MLX5_SET(alloc_transport_domain_in, in, uid, uid);
err = mlx5_cmd_exec_inout(dev, alloc_transport_domain, in, out);
if (!err)
*tdn = MLX5_GET(alloc_transport_domain_out, out,
transport_domain);
return err;
}
void mlx5_cmd_dealloc_transport_domain(struct mlx5_core_dev *dev, u32 tdn,
u16 uid)
{
u32 in[MLX5_ST_SZ_DW(dealloc_transport_domain_in)] = {};
MLX5_SET(dealloc_transport_domain_in, in, opcode,
MLX5_CMD_OP_DEALLOC_TRANSPORT_DOMAIN);
MLX5_SET(dealloc_transport_domain_in, in, uid, uid);
MLX5_SET(dealloc_transport_domain_in, in, transport_domain, tdn);
mlx5_cmd_exec_in(dev, dealloc_transport_domain, in);
}
int mlx5_cmd_dealloc_pd(struct mlx5_core_dev *dev, u32 pdn, u16 uid)
{
u32 in[MLX5_ST_SZ_DW(dealloc_pd_in)] = {};
MLX5_SET(dealloc_pd_in, in, opcode, MLX5_CMD_OP_DEALLOC_PD);
MLX5_SET(dealloc_pd_in, in, pd, pdn);
MLX5_SET(dealloc_pd_in, in, uid, uid);
return mlx5_cmd_exec_in(dev, dealloc_pd, in);
}
int mlx5_cmd_attach_mcg(struct mlx5_core_dev *dev, union ib_gid *mgid,
u32 qpn, u16 uid)
{
u32 in[MLX5_ST_SZ_DW(attach_to_mcg_in)] = {};
void *gid;
MLX5_SET(attach_to_mcg_in, in, opcode, MLX5_CMD_OP_ATTACH_TO_MCG);
MLX5_SET(attach_to_mcg_in, in, qpn, qpn);
MLX5_SET(attach_to_mcg_in, in, uid, uid);
gid = MLX5_ADDR_OF(attach_to_mcg_in, in, multicast_gid);
memcpy(gid, mgid, sizeof(*mgid));
return mlx5_cmd_exec_in(dev, attach_to_mcg, in);
}
int mlx5_cmd_detach_mcg(struct mlx5_core_dev *dev, union ib_gid *mgid,
u32 qpn, u16 uid)
{
u32 in[MLX5_ST_SZ_DW(detach_from_mcg_in)] = {};
void *gid;
MLX5_SET(detach_from_mcg_in, in, opcode, MLX5_CMD_OP_DETACH_FROM_MCG);
MLX5_SET(detach_from_mcg_in, in, qpn, qpn);
MLX5_SET(detach_from_mcg_in, in, uid, uid);
gid = MLX5_ADDR_OF(detach_from_mcg_in, in, multicast_gid);
memcpy(gid, mgid, sizeof(*mgid));
return mlx5_cmd_exec_in(dev, detach_from_mcg, in);
}
int mlx5_cmd_xrcd_alloc(struct mlx5_core_dev *dev, u32 *xrcdn, u16 uid)
{
u32 out[MLX5_ST_SZ_DW(alloc_xrcd_out)] = {};
u32 in[MLX5_ST_SZ_DW(alloc_xrcd_in)] = {};
int err;
MLX5_SET(alloc_xrcd_in, in, opcode, MLX5_CMD_OP_ALLOC_XRCD);
MLX5_SET(alloc_xrcd_in, in, uid, uid);
err = mlx5_cmd_exec_inout(dev, alloc_xrcd, in, out);
if (!err)
*xrcdn = MLX5_GET(alloc_xrcd_out, out, xrcd);
return err;
}
int mlx5_cmd_xrcd_dealloc(struct mlx5_core_dev *dev, u32 xrcdn, u16 uid)
{
u32 in[MLX5_ST_SZ_DW(dealloc_xrcd_in)] = {};
MLX5_SET(dealloc_xrcd_in, in, opcode, MLX5_CMD_OP_DEALLOC_XRCD);
MLX5_SET(dealloc_xrcd_in, in, xrcd, xrcdn);
MLX5_SET(dealloc_xrcd_in, in, uid, uid);
return mlx5_cmd_exec_in(dev, dealloc_xrcd, in);
}
int mlx5_cmd_mad_ifc(struct mlx5_ib_dev *dev, const void *inb, void *outb,
u16 opmod, u8 port)
{
int outlen = MLX5_ST_SZ_BYTES(mad_ifc_out);
int inlen = MLX5_ST_SZ_BYTES(mad_ifc_in);
int err = -ENOMEM;
void *data;
void *resp;
u32 *out;
u32 *in;
in = kzalloc(inlen, GFP_KERNEL);
out = kzalloc(outlen, GFP_KERNEL);
if (!in || !out)
goto out;
MLX5_SET(mad_ifc_in, in, opcode, MLX5_CMD_OP_MAD_IFC);
MLX5_SET(mad_ifc_in, in, op_mod, opmod);
if (dev->ib_dev.type == RDMA_DEVICE_TYPE_SMI) {
MLX5_SET(mad_ifc_in, in, plane_index, port);
MLX5_SET(mad_ifc_in, in, port,
smi_to_native_portnum(dev, port));
} else {
MLX5_SET(mad_ifc_in, in, port, port);
}
data = MLX5_ADDR_OF(mad_ifc_in, in, mad);
memcpy(data, inb, MLX5_FLD_SZ_BYTES(mad_ifc_in, mad));
err = mlx5_cmd_exec_inout(dev->mdev, mad_ifc, in, out);
if (err)
goto out;
resp = MLX5_ADDR_OF(mad_ifc_out, out, response_mad_packet);
memcpy(outb, resp,
MLX5_FLD_SZ_BYTES(mad_ifc_out, response_mad_packet));
out:
kfree(out);
kfree(in);
return err;
}
int mlx5_cmd_uar_alloc(struct mlx5_core_dev *dev, u32 *uarn, u16 uid)
{
u32 out[MLX5_ST_SZ_DW(alloc_uar_out)] = {};
u32 in[MLX5_ST_SZ_DW(alloc_uar_in)] = {};
int err;
MLX5_SET(alloc_uar_in, in, opcode, MLX5_CMD_OP_ALLOC_UAR);
MLX5_SET(alloc_uar_in, in, uid, uid);
err = mlx5_cmd_exec_inout(dev, alloc_uar, in, out);
if (err)
return err;
*uarn = MLX5_GET(alloc_uar_out, out, uar);
return 0;
}
int mlx5_cmd_uar_dealloc(struct mlx5_core_dev *dev, u32 uarn, u16 uid)
{
u32 in[MLX5_ST_SZ_DW(dealloc_uar_in)] = {};
MLX5_SET(dealloc_uar_in, in, opcode, MLX5_CMD_OP_DEALLOC_UAR);
MLX5_SET(dealloc_uar_in, in, uar, uarn);
MLX5_SET(dealloc_uar_in, in, uid, uid);
return mlx5_cmd_exec_in(dev, dealloc_uar, in);
}
int mlx5_cmd_query_vuid(struct mlx5_core_dev *dev, bool data_direct,
char *out_vuid)
{
u8 out[MLX5_ST_SZ_BYTES(query_vuid_out) +
MLX5_ST_SZ_BYTES(array1024_auto)] = {};
u8 in[MLX5_ST_SZ_BYTES(query_vuid_in)] = {};
char *vuid;
int err;
MLX5_SET(query_vuid_in, in, opcode, MLX5_CMD_OPCODE_QUERY_VUID);
MLX5_SET(query_vuid_in, in, vhca_id, MLX5_CAP_GEN(dev, vhca_id));
MLX5_SET(query_vuid_in, in, data_direct, data_direct);
err = mlx5_cmd_exec(dev, in, sizeof(in), out, sizeof(out));
if (err)
return err;
vuid = MLX5_ADDR_OF(query_vuid_out, out, vuid);
memcpy(out_vuid, vuid, MLX5_ST_SZ_BYTES(array1024_auto));
return 0;
}
@@ -0,0 +1,63 @@
/*
* Copyright (c) 2017, Mellanox Technologies. 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.
*/
#ifndef MLX5_IB_CMD_H
#define MLX5_IB_CMD_H
#include "mlx5_ib.h"
#include <linux/kernel.h>
#include <linux/mlx5/driver.h>
int mlx5r_cmd_query_special_mkeys(struct mlx5_ib_dev *dev);
int mlx5_cmd_query_cong_params(struct mlx5_core_dev *dev, int cong_point,
void *out);
int mlx5_cmd_dealloc_pd(struct mlx5_core_dev *dev, u32 pdn, u16 uid);
void mlx5_cmd_destroy_tir(struct mlx5_core_dev *dev, u32 tirn, u16 uid);
void mlx5_cmd_destroy_tis(struct mlx5_core_dev *dev, u32 tisn, u16 uid);
int mlx5_cmd_destroy_rqt(struct mlx5_core_dev *dev, u32 rqtn, u16 uid);
int mlx5_cmd_alloc_transport_domain(struct mlx5_core_dev *dev, u32 *tdn,
u16 uid);
void mlx5_cmd_dealloc_transport_domain(struct mlx5_core_dev *dev, u32 tdn,
u16 uid);
int mlx5_cmd_attach_mcg(struct mlx5_core_dev *dev, union ib_gid *mgid,
u32 qpn, u16 uid);
int mlx5_cmd_detach_mcg(struct mlx5_core_dev *dev, union ib_gid *mgid,
u32 qpn, u16 uid);
int mlx5_cmd_xrcd_alloc(struct mlx5_core_dev *dev, u32 *xrcdn, u16 uid);
int mlx5_cmd_xrcd_dealloc(struct mlx5_core_dev *dev, u32 xrcdn, u16 uid);
int mlx5_cmd_mad_ifc(struct mlx5_ib_dev *dev, const void *inb, void *outb,
u16 opmod, u8 port);
int mlx5_cmd_uar_alloc(struct mlx5_core_dev *dev, u32 *uarn, u16 uid);
int mlx5_cmd_uar_dealloc(struct mlx5_core_dev *dev, u32 uarn, u16 uid);
int mlx5_cmd_query_vuid(struct mlx5_core_dev *dev, bool data_direct,
char *out_vuid);
#endif /* MLX5_IB_CMD_H */
@@ -0,0 +1,491 @@
/*
* Copyright (c) 2013-2017, Mellanox Technologies. 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 <linux/debugfs.h>
#include "mlx5_ib.h"
#include "cmd.h"
enum mlx5_ib_cong_node_type {
MLX5_IB_RROCE_ECN_RP = 1,
MLX5_IB_RROCE_ECN_NP = 2,
MLX5_IB_RROCE_GENERAL = 3,
};
static const char * const mlx5_ib_dbg_cc_name[] = {
"rp_clamp_tgt_rate",
"rp_clamp_tgt_rate_ati",
"rp_time_reset",
"rp_byte_reset",
"rp_threshold",
"rp_ai_rate",
"rp_max_rate",
"rp_hai_rate",
"rp_min_dec_fac",
"rp_min_rate",
"rp_rate_to_set_on_first_cnp",
"rp_dce_tcp_g",
"rp_dce_tcp_rtt",
"rp_rate_reduce_monitor_period",
"rp_initial_alpha_value",
"rp_gd",
"np_min_time_between_cnps",
"np_cnp_dscp",
"np_cnp_prio_mode",
"np_cnp_prio",
"rtt_resp_dscp_valid",
"rtt_resp_dscp",
};
#define MLX5_IB_RP_CLAMP_TGT_RATE_ATTR BIT(1)
#define MLX5_IB_RP_CLAMP_TGT_RATE_ATI_ATTR BIT(2)
#define MLX5_IB_RP_TIME_RESET_ATTR BIT(3)
#define MLX5_IB_RP_BYTE_RESET_ATTR BIT(4)
#define MLX5_IB_RP_THRESHOLD_ATTR BIT(5)
#define MLX5_IB_RP_MAX_RATE_ATTR BIT(6)
#define MLX5_IB_RP_AI_RATE_ATTR BIT(7)
#define MLX5_IB_RP_HAI_RATE_ATTR BIT(8)
#define MLX5_IB_RP_MIN_DEC_FAC_ATTR BIT(9)
#define MLX5_IB_RP_MIN_RATE_ATTR BIT(10)
#define MLX5_IB_RP_RATE_TO_SET_ON_FIRST_CNP_ATTR BIT(11)
#define MLX5_IB_RP_DCE_TCP_G_ATTR BIT(12)
#define MLX5_IB_RP_DCE_TCP_RTT_ATTR BIT(13)
#define MLX5_IB_RP_RATE_REDUCE_MONITOR_PERIOD_ATTR BIT(14)
#define MLX5_IB_RP_INITIAL_ALPHA_VALUE_ATTR BIT(15)
#define MLX5_IB_RP_GD_ATTR BIT(16)
#define MLX5_IB_NP_MIN_TIME_BETWEEN_CNPS_ATTR BIT(2)
#define MLX5_IB_NP_CNP_DSCP_ATTR BIT(3)
#define MLX5_IB_NP_CNP_PRIO_MODE_ATTR BIT(4)
#define MLX5_IB_GENERAL_RTT_RESP_DSCP_ATTR BIT(0)
static enum mlx5_ib_cong_node_type
mlx5_ib_param_to_node(enum mlx5_ib_dbg_cc_types param_offset)
{
if (param_offset <= MLX5_IB_DBG_CC_RP_GD)
return MLX5_IB_RROCE_ECN_RP;
if (param_offset <= MLX5_IB_DBG_CC_NP_CNP_PRIO)
return MLX5_IB_RROCE_ECN_NP;
return MLX5_IB_RROCE_GENERAL;
}
static u32 mlx5_get_cc_param_val(void *field, int offset)
{
switch (offset) {
case MLX5_IB_DBG_CC_RP_CLAMP_TGT_RATE:
return MLX5_GET(cong_control_r_roce_ecn_rp, field,
clamp_tgt_rate);
case MLX5_IB_DBG_CC_RP_CLAMP_TGT_RATE_ATI:
return MLX5_GET(cong_control_r_roce_ecn_rp, field,
clamp_tgt_rate_after_time_inc);
case MLX5_IB_DBG_CC_RP_TIME_RESET:
return MLX5_GET(cong_control_r_roce_ecn_rp, field,
rpg_time_reset);
case MLX5_IB_DBG_CC_RP_BYTE_RESET:
return MLX5_GET(cong_control_r_roce_ecn_rp, field,
rpg_byte_reset);
case MLX5_IB_DBG_CC_RP_THRESHOLD:
return MLX5_GET(cong_control_r_roce_ecn_rp, field,
rpg_threshold);
case MLX5_IB_DBG_CC_RP_AI_RATE:
return MLX5_GET(cong_control_r_roce_ecn_rp, field,
rpg_ai_rate);
case MLX5_IB_DBG_CC_RP_MAX_RATE:
return MLX5_GET(cong_control_r_roce_ecn_rp, field,
rpg_max_rate);
case MLX5_IB_DBG_CC_RP_HAI_RATE:
return MLX5_GET(cong_control_r_roce_ecn_rp, field,
rpg_hai_rate);
case MLX5_IB_DBG_CC_RP_MIN_DEC_FAC:
return MLX5_GET(cong_control_r_roce_ecn_rp, field,
rpg_min_dec_fac);
case MLX5_IB_DBG_CC_RP_MIN_RATE:
return MLX5_GET(cong_control_r_roce_ecn_rp, field,
rpg_min_rate);
case MLX5_IB_DBG_CC_RP_RATE_TO_SET_ON_FIRST_CNP:
return MLX5_GET(cong_control_r_roce_ecn_rp, field,
rate_to_set_on_first_cnp);
case MLX5_IB_DBG_CC_RP_DCE_TCP_G:
return MLX5_GET(cong_control_r_roce_ecn_rp, field,
dce_tcp_g);
case MLX5_IB_DBG_CC_RP_DCE_TCP_RTT:
return MLX5_GET(cong_control_r_roce_ecn_rp, field,
dce_tcp_rtt);
case MLX5_IB_DBG_CC_RP_RATE_REDUCE_MONITOR_PERIOD:
return MLX5_GET(cong_control_r_roce_ecn_rp, field,
rate_reduce_monitor_period);
case MLX5_IB_DBG_CC_RP_INITIAL_ALPHA_VALUE:
return MLX5_GET(cong_control_r_roce_ecn_rp, field,
initial_alpha_value);
case MLX5_IB_DBG_CC_RP_GD:
return MLX5_GET(cong_control_r_roce_ecn_rp, field,
rpg_gd);
case MLX5_IB_DBG_CC_NP_MIN_TIME_BETWEEN_CNPS:
return MLX5_GET(cong_control_r_roce_ecn_np, field,
min_time_between_cnps);
case MLX5_IB_DBG_CC_NP_CNP_DSCP:
return MLX5_GET(cong_control_r_roce_ecn_np, field,
cnp_dscp);
case MLX5_IB_DBG_CC_NP_CNP_PRIO_MODE:
return MLX5_GET(cong_control_r_roce_ecn_np, field,
cnp_prio_mode);
case MLX5_IB_DBG_CC_NP_CNP_PRIO:
return MLX5_GET(cong_control_r_roce_ecn_np, field,
cnp_802p_prio);
case MLX5_IB_DBG_CC_GENERAL_RTT_RESP_DSCP_VALID:
return MLX5_GET(cong_control_r_roce_general, field,
rtt_resp_dscp_valid);
case MLX5_IB_DBG_CC_GENERAL_RTT_RESP_DSCP:
return MLX5_GET(cong_control_r_roce_general, field,
rtt_resp_dscp);
default:
return 0;
}
}
static void mlx5_ib_set_cc_param_mask_val(void *field, int offset,
u32 var, u32 *attr_mask)
{
switch (offset) {
case MLX5_IB_DBG_CC_RP_CLAMP_TGT_RATE:
*attr_mask |= MLX5_IB_RP_CLAMP_TGT_RATE_ATTR;
MLX5_SET(cong_control_r_roce_ecn_rp, field,
clamp_tgt_rate, var);
break;
case MLX5_IB_DBG_CC_RP_CLAMP_TGT_RATE_ATI:
*attr_mask |= MLX5_IB_RP_CLAMP_TGT_RATE_ATI_ATTR;
MLX5_SET(cong_control_r_roce_ecn_rp, field,
clamp_tgt_rate_after_time_inc, var);
break;
case MLX5_IB_DBG_CC_RP_TIME_RESET:
*attr_mask |= MLX5_IB_RP_TIME_RESET_ATTR;
MLX5_SET(cong_control_r_roce_ecn_rp, field,
rpg_time_reset, var);
break;
case MLX5_IB_DBG_CC_RP_BYTE_RESET:
*attr_mask |= MLX5_IB_RP_BYTE_RESET_ATTR;
MLX5_SET(cong_control_r_roce_ecn_rp, field,
rpg_byte_reset, var);
break;
case MLX5_IB_DBG_CC_RP_THRESHOLD:
*attr_mask |= MLX5_IB_RP_THRESHOLD_ATTR;
MLX5_SET(cong_control_r_roce_ecn_rp, field,
rpg_threshold, var);
break;
case MLX5_IB_DBG_CC_RP_AI_RATE:
*attr_mask |= MLX5_IB_RP_AI_RATE_ATTR;
MLX5_SET(cong_control_r_roce_ecn_rp, field,
rpg_ai_rate, var);
break;
case MLX5_IB_DBG_CC_RP_MAX_RATE:
*attr_mask |= MLX5_IB_RP_MAX_RATE_ATTR;
MLX5_SET(cong_control_r_roce_ecn_rp, field,
rpg_max_rate, var);
break;
case MLX5_IB_DBG_CC_RP_HAI_RATE:
*attr_mask |= MLX5_IB_RP_HAI_RATE_ATTR;
MLX5_SET(cong_control_r_roce_ecn_rp, field,
rpg_hai_rate, var);
break;
case MLX5_IB_DBG_CC_RP_MIN_DEC_FAC:
*attr_mask |= MLX5_IB_RP_MIN_DEC_FAC_ATTR;
MLX5_SET(cong_control_r_roce_ecn_rp, field,
rpg_min_dec_fac, var);
break;
case MLX5_IB_DBG_CC_RP_MIN_RATE:
*attr_mask |= MLX5_IB_RP_MIN_RATE_ATTR;
MLX5_SET(cong_control_r_roce_ecn_rp, field,
rpg_min_rate, var);
break;
case MLX5_IB_DBG_CC_RP_RATE_TO_SET_ON_FIRST_CNP:
*attr_mask |= MLX5_IB_RP_RATE_TO_SET_ON_FIRST_CNP_ATTR;
MLX5_SET(cong_control_r_roce_ecn_rp, field,
rate_to_set_on_first_cnp, var);
break;
case MLX5_IB_DBG_CC_RP_DCE_TCP_G:
*attr_mask |= MLX5_IB_RP_DCE_TCP_G_ATTR;
MLX5_SET(cong_control_r_roce_ecn_rp, field,
dce_tcp_g, var);
break;
case MLX5_IB_DBG_CC_RP_DCE_TCP_RTT:
*attr_mask |= MLX5_IB_RP_DCE_TCP_RTT_ATTR;
MLX5_SET(cong_control_r_roce_ecn_rp, field,
dce_tcp_rtt, var);
break;
case MLX5_IB_DBG_CC_RP_RATE_REDUCE_MONITOR_PERIOD:
*attr_mask |= MLX5_IB_RP_RATE_REDUCE_MONITOR_PERIOD_ATTR;
MLX5_SET(cong_control_r_roce_ecn_rp, field,
rate_reduce_monitor_period, var);
break;
case MLX5_IB_DBG_CC_RP_INITIAL_ALPHA_VALUE:
*attr_mask |= MLX5_IB_RP_INITIAL_ALPHA_VALUE_ATTR;
MLX5_SET(cong_control_r_roce_ecn_rp, field,
initial_alpha_value, var);
break;
case MLX5_IB_DBG_CC_RP_GD:
*attr_mask |= MLX5_IB_RP_GD_ATTR;
MLX5_SET(cong_control_r_roce_ecn_rp, field,
rpg_gd, var);
break;
case MLX5_IB_DBG_CC_NP_MIN_TIME_BETWEEN_CNPS:
*attr_mask |= MLX5_IB_NP_MIN_TIME_BETWEEN_CNPS_ATTR;
MLX5_SET(cong_control_r_roce_ecn_np, field,
min_time_between_cnps, var);
break;
case MLX5_IB_DBG_CC_NP_CNP_DSCP:
*attr_mask |= MLX5_IB_NP_CNP_DSCP_ATTR;
MLX5_SET(cong_control_r_roce_ecn_np, field, cnp_dscp, var);
break;
case MLX5_IB_DBG_CC_NP_CNP_PRIO_MODE:
*attr_mask |= MLX5_IB_NP_CNP_PRIO_MODE_ATTR;
MLX5_SET(cong_control_r_roce_ecn_np, field, cnp_prio_mode, var);
break;
case MLX5_IB_DBG_CC_NP_CNP_PRIO:
*attr_mask |= MLX5_IB_NP_CNP_PRIO_MODE_ATTR;
MLX5_SET(cong_control_r_roce_ecn_np, field, cnp_prio_mode, 0);
MLX5_SET(cong_control_r_roce_ecn_np, field, cnp_802p_prio, var);
break;
case MLX5_IB_DBG_CC_GENERAL_RTT_RESP_DSCP_VALID:
*attr_mask |= MLX5_IB_GENERAL_RTT_RESP_DSCP_ATTR;
MLX5_SET(cong_control_r_roce_general, field, rtt_resp_dscp_valid, var);
break;
case MLX5_IB_DBG_CC_GENERAL_RTT_RESP_DSCP:
*attr_mask |= MLX5_IB_GENERAL_RTT_RESP_DSCP_ATTR;
MLX5_SET(cong_control_r_roce_general, field, rtt_resp_dscp_valid, 1);
MLX5_SET(cong_control_r_roce_general, field, rtt_resp_dscp, var);
break;
}
}
static int mlx5_ib_get_cc_params(struct mlx5_ib_dev *dev, u32 port_num,
int offset, u32 *var)
{
int outlen = MLX5_ST_SZ_BYTES(query_cong_params_out);
void *out;
void *field;
int err;
enum mlx5_ib_cong_node_type node;
struct mlx5_core_dev *mdev;
/* Takes a 1-based port number */
mdev = mlx5_ib_get_native_port_mdev(dev, port_num + 1, NULL);
if (!mdev)
return -ENODEV;
out = kvzalloc(outlen, GFP_KERNEL);
if (!out) {
err = -ENOMEM;
goto alloc_err;
}
node = mlx5_ib_param_to_node(offset);
err = mlx5_cmd_query_cong_params(mdev, node, out);
if (err)
goto free;
field = MLX5_ADDR_OF(query_cong_params_out, out, congestion_parameters);
*var = mlx5_get_cc_param_val(field, offset);
free:
kvfree(out);
alloc_err:
mlx5_ib_put_native_port_mdev(dev, port_num + 1);
return err;
}
static int mlx5_ib_set_cc_params(struct mlx5_ib_dev *dev, u32 port_num,
int offset, u32 var)
{
int inlen = MLX5_ST_SZ_BYTES(modify_cong_params_in);
void *in;
void *field;
enum mlx5_ib_cong_node_type node;
struct mlx5_core_dev *mdev;
u32 attr_mask = 0;
int err;
/* Takes a 1-based port number */
mdev = mlx5_ib_get_native_port_mdev(dev, port_num + 1, NULL);
if (!mdev)
return -ENODEV;
in = kvzalloc(inlen, GFP_KERNEL);
if (!in) {
err = -ENOMEM;
goto alloc_err;
}
MLX5_SET(modify_cong_params_in, in, opcode,
MLX5_CMD_OP_MODIFY_CONG_PARAMS);
node = mlx5_ib_param_to_node(offset);
MLX5_SET(modify_cong_params_in, in, cong_protocol, node);
field = MLX5_ADDR_OF(modify_cong_params_in, in, congestion_parameters);
mlx5_ib_set_cc_param_mask_val(field, offset, var, &attr_mask);
field = MLX5_ADDR_OF(modify_cong_params_in, in, field_select);
MLX5_SET(field_select_r_roce_rp, field, field_select_r_roce_rp,
attr_mask);
err = mlx5_cmd_exec_in(dev->mdev, modify_cong_params, in);
kvfree(in);
alloc_err:
mlx5_ib_put_native_port_mdev(dev, port_num + 1);
return err;
}
static ssize_t set_param(struct file *filp, const char __user *buf,
size_t count, loff_t *pos)
{
struct mlx5_ib_dbg_param *param = filp->private_data;
int offset = param->offset;
char lbuf[11] = { };
u32 var;
int ret;
if (count > sizeof(lbuf))
return -EINVAL;
if (copy_from_user(lbuf, buf, count))
return -EFAULT;
lbuf[sizeof(lbuf) - 1] = '\0';
if (kstrtou32(lbuf, 0, &var))
return -EINVAL;
ret = mlx5_ib_set_cc_params(param->dev, param->port_num, offset, var);
return ret ? ret : count;
}
static ssize_t get_param(struct file *filp, char __user *buf, size_t count,
loff_t *pos)
{
struct mlx5_ib_dbg_param *param = filp->private_data;
int offset = param->offset;
u32 var = 0;
int ret;
char lbuf[11];
ret = mlx5_ib_get_cc_params(param->dev, param->port_num, offset, &var);
if (ret)
return ret;
ret = snprintf(lbuf, sizeof(lbuf), "%d\n", var);
if (ret < 0)
return ret;
return simple_read_from_buffer(buf, count, pos, lbuf, ret);
}
static const struct file_operations dbg_cc_fops = {
.owner = THIS_MODULE,
.open = simple_open,
.write = set_param,
.read = get_param,
};
void mlx5_ib_cleanup_cong_debugfs(struct mlx5_ib_dev *dev, u32 port_num)
{
if (!mlx5_debugfs_root ||
!dev->port[port_num].dbg_cc_params ||
!dev->port[port_num].dbg_cc_params->root)
return;
debugfs_remove_recursive(dev->port[port_num].dbg_cc_params->root);
kfree(dev->port[port_num].dbg_cc_params);
dev->port[port_num].dbg_cc_params = NULL;
}
void mlx5_ib_init_cong_debugfs(struct mlx5_ib_dev *dev, u32 port_num)
{
struct mlx5_ib_dbg_cc_params *dbg_cc_params;
struct mlx5_core_dev *mdev;
int i;
if (!mlx5_debugfs_root)
return;
/* Takes a 1-based port number */
mdev = mlx5_ib_get_native_port_mdev(dev, port_num + 1, NULL);
if (!mdev)
return;
if (!MLX5_CAP_GEN(mdev, cc_query_allowed) ||
!MLX5_CAP_GEN(mdev, cc_modify_allowed))
goto put_mdev;
dbg_cc_params = kzalloc_obj(*dbg_cc_params);
if (!dbg_cc_params)
goto err;
dev->port[port_num].dbg_cc_params = dbg_cc_params;
dbg_cc_params->root = debugfs_create_dir("cc_params", mlx5_debugfs_get_dev_root(mdev));
for (i = 0; i < MLX5_IB_DBG_CC_MAX; i++) {
if ((i == MLX5_IB_DBG_CC_GENERAL_RTT_RESP_DSCP_VALID ||
i == MLX5_IB_DBG_CC_GENERAL_RTT_RESP_DSCP))
if (!MLX5_CAP_GEN(mdev, roce) ||
!MLX5_CAP_ROCE(mdev, roce_cc_general))
continue;
dbg_cc_params->params[i].offset = i;
dbg_cc_params->params[i].dev = dev;
dbg_cc_params->params[i].port_num = port_num;
dbg_cc_params->params[i].dentry =
debugfs_create_file(mlx5_ib_dbg_cc_name[i],
0600, dbg_cc_params->root,
&dbg_cc_params->params[i],
&dbg_cc_fops);
}
put_mdev:
mlx5_ib_put_native_port_mdev(dev, port_num + 1);
return;
err:
mlx5_ib_warn(dev, "cong debugfs failure\n");
mlx5_ib_cleanup_cong_debugfs(dev, port_num);
mlx5_ib_put_native_port_mdev(dev, port_num + 1);
/*
* We don't want to fail driver if debugfs failed to initialize,
* so we are not forwarding error to the user.
*/
return;
}
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,19 @@
/* SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB */
/*
* Copyright (c) 2013-2020, Mellanox Technologies inc. All rights reserved.
*/
#ifndef _MLX5_IB_COUNTERS_H
#define _MLX5_IB_COUNTERS_H
#include "mlx5_ib.h"
int mlx5_ib_counters_init(struct mlx5_ib_dev *dev);
void mlx5_ib_counters_cleanup(struct mlx5_ib_dev *dev);
void mlx5_ib_counters_clear_description(struct ib_counters *counters);
int mlx5_ib_flow_counters_set_data(struct ib_counters *ibcounters,
struct mlx5_ib_create_flow *ucmd);
u16 mlx5_ib_get_counters_id(struct mlx5_ib_dev *dev, u32 port_num);
bool mlx5r_is_opfc_shared_and_in_use(struct mlx5_ib_op_fc *opfcs, u32 type,
struct mlx5_ib_op_fc **opfc);
#endif /* _MLX5_IB_COUNTERS_H */
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,227 @@
// SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB
/*
* Copyright (c) 2024, NVIDIA CORPORATION & AFFILIATES. All rights reserved
*/
#include "mlx5_ib.h"
#include "data_direct.h"
static LIST_HEAD(mlx5_data_direct_dev_list);
static LIST_HEAD(mlx5_data_direct_reg_list);
/*
* This mutex should be held when accessing either of the above lists
*/
static DEFINE_MUTEX(mlx5_data_direct_mutex);
struct mlx5_data_direct_registration {
struct mlx5_ib_dev *ibdev;
char vuid[MLX5_ST_SZ_BYTES(array1024_auto) + 1];
struct list_head list;
};
static const struct pci_device_id mlx5_data_direct_pci_table[] = {
{ PCI_VDEVICE(MELLANOX, 0x2100) }, /* ConnectX-8 Data Direct */
{ 0, }
};
static int mlx5_data_direct_vpd_get_vuid(struct mlx5_data_direct_dev *dev)
{
struct pci_dev *pdev = dev->pdev;
unsigned int vpd_size, kw_len;
u8 *vpd_data;
int start;
int ret;
vpd_data = pci_vpd_alloc(pdev, &vpd_size);
if (IS_ERR(vpd_data)) {
pci_err(pdev, "Unable to read VPD, err=%pe\n", vpd_data);
return PTR_ERR(vpd_data);
}
start = pci_vpd_find_ro_info_keyword(vpd_data, vpd_size, "VU", &kw_len);
if (start < 0) {
ret = start;
pci_err(pdev, "VU keyword not found, err=%d\n", ret);
goto end;
}
dev->vuid = kmemdup_nul(vpd_data + start, kw_len, GFP_KERNEL);
ret = dev->vuid ? 0 : -ENOMEM;
end:
kfree(vpd_data);
return ret;
}
static void mlx5_data_direct_shutdown(struct pci_dev *pdev)
{
pci_disable_device(pdev);
}
static int mlx5_data_direct_set_dma_caps(struct pci_dev *pdev)
{
int err;
err = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(64));
if (err) {
dev_warn(&pdev->dev,
"Warning: couldn't set 64-bit PCI DMA mask, err=%d\n", err);
err = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(32));
if (err) {
dev_err(&pdev->dev, "Can't set PCI DMA mask, err=%d\n", err);
return err;
}
}
dma_set_max_seg_size(&pdev->dev, SZ_2G);
return 0;
}
int mlx5_data_direct_ib_reg(struct mlx5_ib_dev *ibdev, char *vuid)
{
struct mlx5_data_direct_registration *reg;
struct mlx5_data_direct_dev *dev;
reg = kzalloc_obj(*reg);
if (!reg)
return -ENOMEM;
reg->ibdev = ibdev;
strscpy(reg->vuid, vuid);
mutex_lock(&mlx5_data_direct_mutex);
list_for_each_entry(dev, &mlx5_data_direct_dev_list, list) {
if (strcmp(dev->vuid, vuid) == 0) {
mlx5_ib_data_direct_bind(ibdev, dev);
break;
}
}
/* Add the registration to its global list, to be used upon bind/unbind
* of its affiliated data direct device
*/
list_add_tail(&reg->list, &mlx5_data_direct_reg_list);
mutex_unlock(&mlx5_data_direct_mutex);
return 0;
}
void mlx5_data_direct_ib_unreg(struct mlx5_ib_dev *ibdev)
{
struct mlx5_data_direct_registration *reg;
mutex_lock(&mlx5_data_direct_mutex);
list_for_each_entry(reg, &mlx5_data_direct_reg_list, list) {
if (reg->ibdev == ibdev) {
list_del(&reg->list);
kfree(reg);
goto end;
}
}
WARN_ON(true);
end:
mutex_unlock(&mlx5_data_direct_mutex);
}
static void mlx5_data_direct_dev_reg(struct mlx5_data_direct_dev *dev)
{
struct mlx5_data_direct_registration *reg;
mutex_lock(&mlx5_data_direct_mutex);
list_for_each_entry(reg, &mlx5_data_direct_reg_list, list) {
if (strcmp(dev->vuid, reg->vuid) == 0)
mlx5_ib_data_direct_bind(reg->ibdev, dev);
}
/* Add the data direct device to the global list, further IB devices may
* use it later as well
*/
list_add_tail(&dev->list, &mlx5_data_direct_dev_list);
mutex_unlock(&mlx5_data_direct_mutex);
}
static void mlx5_data_direct_dev_unreg(struct mlx5_data_direct_dev *dev)
{
struct mlx5_data_direct_registration *reg;
mutex_lock(&mlx5_data_direct_mutex);
/* Prevent any further affiliations */
list_del(&dev->list);
list_for_each_entry(reg, &mlx5_data_direct_reg_list, list) {
if (strcmp(dev->vuid, reg->vuid) == 0)
mlx5_ib_data_direct_unbind(reg->ibdev);
}
mutex_unlock(&mlx5_data_direct_mutex);
}
static int mlx5_data_direct_probe(struct pci_dev *pdev, const struct pci_device_id *id)
{
struct mlx5_data_direct_dev *dev;
int err;
dev = kzalloc_obj(*dev);
if (!dev)
return -ENOMEM;
dev->device = &pdev->dev;
dev->pdev = pdev;
pci_set_drvdata(dev->pdev, dev);
err = pci_enable_device(pdev);
if (err) {
dev_err(dev->device, "Cannot enable PCI device, err=%d\n", err);
goto err;
}
pci_set_master(pdev);
err = mlx5_data_direct_set_dma_caps(pdev);
if (err)
goto err_disable;
if (pci_enable_atomic_ops_to_root(pdev, PCI_EXP_DEVCAP2_ATOMIC_COMP32) &&
pci_enable_atomic_ops_to_root(pdev, PCI_EXP_DEVCAP2_ATOMIC_COMP64) &&
pci_enable_atomic_ops_to_root(pdev, PCI_EXP_DEVCAP2_ATOMIC_COMP128))
dev_dbg(dev->device, "Enabling pci atomics failed\n");
err = mlx5_data_direct_vpd_get_vuid(dev);
if (err)
goto err_disable;
mlx5_data_direct_dev_reg(dev);
return 0;
err_disable:
pci_disable_device(pdev);
err:
kfree(dev);
return err;
}
static void mlx5_data_direct_remove(struct pci_dev *pdev)
{
struct mlx5_data_direct_dev *dev = pci_get_drvdata(pdev);
mlx5_data_direct_dev_unreg(dev);
pci_disable_device(pdev);
kfree(dev->vuid);
kfree(dev);
}
static struct pci_driver mlx5_data_direct_driver = {
.name = KBUILD_MODNAME,
.id_table = mlx5_data_direct_pci_table,
.probe = mlx5_data_direct_probe,
.remove = mlx5_data_direct_remove,
.shutdown = mlx5_data_direct_shutdown,
};
int mlx5_data_direct_driver_register(void)
{
return pci_register_driver(&mlx5_data_direct_driver);
}
void mlx5_data_direct_driver_unregister(void)
{
pci_unregister_driver(&mlx5_data_direct_driver);
}
@@ -0,0 +1,23 @@
/* SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB */
/*
* Copyright (c) 2024, NVIDIA CORPORATION & AFFILIATES. All rights reserved
*/
#ifndef _MLX5_IB_DATA_DIRECT_H
#define _MLX5_IB_DATA_DIRECT_H
struct mlx5_ib_dev;
struct mlx5_data_direct_dev {
struct device *device;
struct pci_dev *pdev;
char *vuid;
struct list_head list;
};
int mlx5_data_direct_ib_reg(struct mlx5_ib_dev *ibdev, char *vuid);
void mlx5_data_direct_ib_unreg(struct mlx5_ib_dev *ibdev);
int mlx5_data_direct_driver_register(void);
void mlx5_data_direct_driver_unregister(void);
#endif
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,50 @@
/* SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB */
/*
* Copyright (c) 2019-2020, Mellanox Technologies inc. All rights reserved.
*/
#ifndef _MLX5_IB_DEVX_H
#define _MLX5_IB_DEVX_H
#include "mlx5_ib.h"
#define MLX5_MAX_DESTROY_INBOX_SIZE_DW MLX5_ST_SZ_DW(delete_fte_in)
struct devx_obj {
struct mlx5_ib_dev *ib_dev;
u64 obj_id;
u32 dinlen; /* destroy inbox length */
u32 dinbox[MLX5_MAX_DESTROY_INBOX_SIZE_DW];
u32 flags;
union {
struct mlx5_ib_mkey mkey;
struct mlx5_core_dct core_dct;
struct mlx5_core_cq core_cq;
u32 flow_counter_bulk_size;
};
struct list_head event_sub; /* holds devx_event_subscription entries */
};
#if IS_ENABLED(CONFIG_INFINIBAND_USER_ACCESS)
int mlx5_ib_devx_create(struct mlx5_ib_dev *dev, bool is_user, u64 req_ucaps);
void mlx5_ib_devx_destroy(struct mlx5_ib_dev *dev, u16 uid);
int mlx5_ib_devx_init(struct mlx5_ib_dev *dev);
void mlx5_ib_devx_cleanup(struct mlx5_ib_dev *dev);
void mlx5_ib_ufile_hw_cleanup(struct ib_uverbs_file *ufile);
#else
static inline int mlx5_ib_devx_create(struct mlx5_ib_dev *dev, bool is_user,
u64 req_ucaps)
{
return -EOPNOTSUPP;
}
static inline void mlx5_ib_devx_destroy(struct mlx5_ib_dev *dev, u16 uid) {}
static inline int mlx5_ib_devx_init(struct mlx5_ib_dev *dev)
{
return 0;
}
static inline void mlx5_ib_devx_cleanup(struct mlx5_ib_dev *dev)
{
}
static inline void mlx5_ib_ufile_hw_cleanup(struct ib_uverbs_file *ufile)
{
}
#endif
#endif /* _MLX5_IB_DEVX_H */
+612
View File
@@ -0,0 +1,612 @@
// SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB
/*
* Copyright (c) 2021, Mellanox Technologies inc. All rights reserved.
*/
#include <rdma/uverbs_std_types.h>
#include "dm.h"
#define UVERBS_MODULE_NAME mlx5_ib
#include <rdma/uverbs_named_ioctl.h>
static int mlx5_cmd_alloc_memic(struct mlx5_dm *dm, phys_addr_t *addr,
u64 length, u32 alignment)
{
struct mlx5_core_dev *dev = dm->dev;
u64 num_memic_hw_pages = MLX5_CAP_DEV_MEM(dev, memic_bar_size)
>> PAGE_SHIFT;
u64 hw_start_addr = MLX5_CAP64_DEV_MEM(dev, memic_bar_start_addr);
u32 max_alignment = MLX5_CAP_DEV_MEM(dev, log_max_memic_addr_alignment);
u32 num_pages = DIV_ROUND_UP(length, PAGE_SIZE);
u32 out[MLX5_ST_SZ_DW(alloc_memic_out)] = {};
u32 in[MLX5_ST_SZ_DW(alloc_memic_in)] = {};
u32 mlx5_alignment;
u64 page_idx = 0;
int ret = 0;
if (!length || (length & MLX5_MEMIC_ALLOC_SIZE_MASK))
return -EINVAL;
/* mlx5 device sets alignment as 64*2^driver_value
* so normalizing is needed.
*/
mlx5_alignment = (alignment < MLX5_MEMIC_BASE_ALIGN) ? 0 :
alignment - MLX5_MEMIC_BASE_ALIGN;
if (mlx5_alignment > max_alignment)
return -EINVAL;
MLX5_SET(alloc_memic_in, in, opcode, MLX5_CMD_OP_ALLOC_MEMIC);
MLX5_SET(alloc_memic_in, in, range_size, num_pages * PAGE_SIZE);
MLX5_SET(alloc_memic_in, in, memic_size, length);
MLX5_SET(alloc_memic_in, in, log_memic_addr_alignment,
mlx5_alignment);
while (page_idx < num_memic_hw_pages) {
spin_lock(&dm->lock);
page_idx = bitmap_find_next_zero_area(dm->memic_alloc_pages,
num_memic_hw_pages,
page_idx,
num_pages, 0);
if (page_idx < num_memic_hw_pages)
bitmap_set(dm->memic_alloc_pages,
page_idx, num_pages);
spin_unlock(&dm->lock);
if (page_idx >= num_memic_hw_pages)
break;
MLX5_SET64(alloc_memic_in, in, range_start_addr,
hw_start_addr + (page_idx * PAGE_SIZE));
ret = mlx5_cmd_exec_inout(dev, alloc_memic, in, out);
if (ret) {
spin_lock(&dm->lock);
bitmap_clear(dm->memic_alloc_pages,
page_idx, num_pages);
spin_unlock(&dm->lock);
if (ret == -EAGAIN) {
page_idx++;
continue;
}
return ret;
}
*addr = dev->bar_addr +
MLX5_GET64(alloc_memic_out, out, memic_start_addr);
return 0;
}
return -ENOMEM;
}
void mlx5_cmd_dealloc_memic(struct mlx5_dm *dm, phys_addr_t addr,
u64 length)
{
struct mlx5_core_dev *dev = dm->dev;
u64 hw_start_addr = MLX5_CAP64_DEV_MEM(dev, memic_bar_start_addr);
u32 num_pages = DIV_ROUND_UP(length, PAGE_SIZE);
u32 in[MLX5_ST_SZ_DW(dealloc_memic_in)] = {};
u64 start_page_idx;
int err;
addr -= dev->bar_addr;
start_page_idx = (addr - hw_start_addr) >> PAGE_SHIFT;
MLX5_SET(dealloc_memic_in, in, opcode, MLX5_CMD_OP_DEALLOC_MEMIC);
MLX5_SET64(dealloc_memic_in, in, memic_start_addr, addr);
MLX5_SET(dealloc_memic_in, in, memic_size, length);
err = mlx5_cmd_exec_in(dev, dealloc_memic, in);
if (err)
return;
spin_lock(&dm->lock);
bitmap_clear(dm->memic_alloc_pages,
start_page_idx, num_pages);
spin_unlock(&dm->lock);
}
void mlx5_cmd_dealloc_memic_op(struct mlx5_dm *dm, phys_addr_t addr,
u8 operation)
{
u32 in[MLX5_ST_SZ_DW(modify_memic_in)] = {};
struct mlx5_core_dev *dev = dm->dev;
MLX5_SET(modify_memic_in, in, opcode, MLX5_CMD_OP_MODIFY_MEMIC);
MLX5_SET(modify_memic_in, in, op_mod, MLX5_MODIFY_MEMIC_OP_MOD_DEALLOC);
MLX5_SET(modify_memic_in, in, memic_operation_type, operation);
MLX5_SET64(modify_memic_in, in, memic_start_addr, addr - dev->bar_addr);
mlx5_cmd_exec_in(dev, modify_memic, in);
}
static int mlx5_cmd_alloc_memic_op(struct mlx5_dm *dm, phys_addr_t addr,
u8 operation, phys_addr_t *op_addr)
{
u32 out[MLX5_ST_SZ_DW(modify_memic_out)] = {};
u32 in[MLX5_ST_SZ_DW(modify_memic_in)] = {};
struct mlx5_core_dev *dev = dm->dev;
int err;
MLX5_SET(modify_memic_in, in, opcode, MLX5_CMD_OP_MODIFY_MEMIC);
MLX5_SET(modify_memic_in, in, op_mod, MLX5_MODIFY_MEMIC_OP_MOD_ALLOC);
MLX5_SET(modify_memic_in, in, memic_operation_type, operation);
MLX5_SET64(modify_memic_in, in, memic_start_addr, addr - dev->bar_addr);
err = mlx5_cmd_exec_inout(dev, modify_memic, in, out);
if (err)
return err;
*op_addr = dev->bar_addr +
MLX5_GET64(modify_memic_out, out, memic_operation_addr);
return 0;
}
static int add_dm_mmap_entry(struct ib_ucontext *context,
struct mlx5_user_mmap_entry *mentry, u8 mmap_flag,
size_t size, u64 address)
{
mentry->mmap_flag = mmap_flag;
mentry->address = address;
return rdma_user_mmap_entry_insert_range(
context, &mentry->rdma_entry, size,
MLX5_IB_MMAP_DEVICE_MEM << 16,
(MLX5_IB_MMAP_DEVICE_MEM << 16) + (1UL << 16) - 1);
}
static void mlx5_ib_dm_memic_free(struct kref *kref)
{
struct mlx5_ib_dm_memic *dm =
container_of(kref, struct mlx5_ib_dm_memic, ref);
struct mlx5_ib_dev *dev = to_mdev(dm->base.ibdm.device);
mlx5_cmd_dealloc_memic(&dev->dm, dm->base.dev_addr, dm->base.size);
kfree(dm);
}
static int copy_op_to_user(struct mlx5_ib_dm_op_entry *op_entry,
struct uverbs_attr_bundle *attrs)
{
u64 start_offset;
u16 page_idx;
int err;
page_idx = op_entry->mentry.rdma_entry.start_pgoff & 0xFFFF;
start_offset = op_entry->op_addr & ~PAGE_MASK;
err = uverbs_copy_to(attrs, MLX5_IB_ATTR_DM_MAP_OP_ADDR_RESP_PAGE_INDEX,
&page_idx, sizeof(page_idx));
if (err)
return err;
return uverbs_copy_to(attrs,
MLX5_IB_ATTR_DM_MAP_OP_ADDR_RESP_START_OFFSET,
&start_offset, sizeof(start_offset));
}
static int map_existing_op(struct mlx5_ib_dm_memic *dm, u8 op,
struct uverbs_attr_bundle *attrs)
{
struct mlx5_ib_dm_op_entry *op_entry;
op_entry = xa_load(&dm->ops, op);
if (!op_entry)
return -ENOENT;
return copy_op_to_user(op_entry, attrs);
}
static int UVERBS_HANDLER(MLX5_IB_METHOD_DM_MAP_OP_ADDR)(
struct uverbs_attr_bundle *attrs)
{
struct ib_uobject *uobj = uverbs_attr_get_uobject(
attrs, MLX5_IB_ATTR_DM_MAP_OP_ADDR_REQ_HANDLE);
struct mlx5_ib_dev *dev = to_mdev(uobj->context->device);
struct ib_dm *ibdm = uobj->object;
struct mlx5_ib_dm_memic *dm = to_memic(ibdm);
struct mlx5_ib_dm_op_entry *op_entry;
int err;
u8 op;
err = uverbs_copy_from(&op, attrs, MLX5_IB_ATTR_DM_MAP_OP_ADDR_REQ_OP);
if (err)
return err;
if (op >= BITS_PER_TYPE(u32))
return -EOPNOTSUPP;
if (!(MLX5_CAP_DEV_MEM(dev->mdev, memic_operations) & BIT(op)))
return -EOPNOTSUPP;
mutex_lock(&dm->ops_xa_lock);
err = map_existing_op(dm, op, attrs);
if (!err || err != -ENOENT)
goto err_unlock;
op_entry = kzalloc_obj(*op_entry);
if (!op_entry)
goto err_unlock;
err = mlx5_cmd_alloc_memic_op(&dev->dm, dm->base.dev_addr, op,
&op_entry->op_addr);
if (err) {
kfree(op_entry);
goto err_unlock;
}
op_entry->op = op;
op_entry->dm = dm;
err = add_dm_mmap_entry(uobj->context, &op_entry->mentry,
MLX5_IB_MMAP_TYPE_MEMIC_OP, dm->base.size,
op_entry->op_addr & PAGE_MASK);
if (err) {
mlx5_cmd_dealloc_memic_op(&dev->dm, dm->base.dev_addr, op);
kfree(op_entry);
goto err_unlock;
}
/* From this point, entry will be freed by mmap_free */
kref_get(&dm->ref);
err = copy_op_to_user(op_entry, attrs);
if (err)
goto err_remove;
err = xa_insert(&dm->ops, op, op_entry, GFP_KERNEL);
if (err)
goto err_remove;
mutex_unlock(&dm->ops_xa_lock);
return 0;
err_remove:
rdma_user_mmap_entry_remove(&op_entry->mentry.rdma_entry);
err_unlock:
mutex_unlock(&dm->ops_xa_lock);
return err;
}
static struct ib_dm *handle_alloc_dm_memic(struct ib_ucontext *ctx,
struct ib_dm_alloc_attr *attr,
struct uverbs_attr_bundle *attrs)
{
struct mlx5_dm *dm_db = &to_mdev(ctx->device)->dm;
struct mlx5_ib_dm_memic *dm;
u64 start_offset;
u16 page_idx;
int err;
u64 address;
if (!dm_db || !MLX5_CAP_DEV_MEM(dm_db->dev, memic))
return ERR_PTR(-EOPNOTSUPP);
dm = kzalloc_obj(*dm);
if (!dm)
return ERR_PTR(-ENOMEM);
dm->base.type = MLX5_IB_UAPI_DM_TYPE_MEMIC;
dm->base.size = roundup(attr->length, MLX5_MEMIC_BASE_SIZE);
dm->base.ibdm.device = ctx->device;
kref_init(&dm->ref);
xa_init(&dm->ops);
mutex_init(&dm->ops_xa_lock);
dm->req_length = attr->length;
err = mlx5_cmd_alloc_memic(dm_db, &dm->base.dev_addr,
dm->base.size, attr->alignment);
if (err) {
kfree(dm);
return ERR_PTR(err);
}
address = dm->base.dev_addr & PAGE_MASK;
err = add_dm_mmap_entry(ctx, &dm->mentry, MLX5_IB_MMAP_TYPE_MEMIC,
dm->base.size, address);
if (err) {
mlx5_cmd_dealloc_memic(dm_db, dm->base.dev_addr, dm->base.size);
kfree(dm);
return ERR_PTR(err);
}
page_idx = dm->mentry.rdma_entry.start_pgoff & 0xFFFF;
err = uverbs_copy_to(attrs, MLX5_IB_ATTR_ALLOC_DM_RESP_PAGE_INDEX,
&page_idx, sizeof(page_idx));
if (err)
goto err_copy;
start_offset = dm->base.dev_addr & ~PAGE_MASK;
err = uverbs_copy_to(attrs,
MLX5_IB_ATTR_ALLOC_DM_RESP_START_OFFSET,
&start_offset, sizeof(start_offset));
if (err)
goto err_copy;
return &dm->base.ibdm;
err_copy:
rdma_user_mmap_entry_remove(&dm->mentry.rdma_entry);
return ERR_PTR(err);
}
static enum mlx5_sw_icm_type get_icm_type(int uapi_type)
{
switch (uapi_type) {
case MLX5_IB_UAPI_DM_TYPE_HEADER_MODIFY_SW_ICM:
return MLX5_SW_ICM_TYPE_HEADER_MODIFY;
case MLX5_IB_UAPI_DM_TYPE_HEADER_MODIFY_PATTERN_SW_ICM:
return MLX5_SW_ICM_TYPE_HEADER_MODIFY_PATTERN;
case MLX5_IB_UAPI_DM_TYPE_ENCAP_SW_ICM:
return MLX5_SW_ICM_TYPE_SW_ENCAP;
case MLX5_IB_UAPI_DM_TYPE_STEERING_SW_ICM:
default:
return MLX5_SW_ICM_TYPE_STEERING;
}
}
static struct ib_dm *handle_alloc_dm_sw_icm(struct ib_ucontext *ctx,
struct ib_dm_alloc_attr *attr,
struct uverbs_attr_bundle *attrs,
int type)
{
struct mlx5_core_dev *dev = to_mdev(ctx->device)->mdev;
enum mlx5_sw_icm_type icm_type;
struct mlx5_ib_dm_icm *dm;
u64 act_size;
int err;
if (!capable(CAP_SYS_RAWIO) || !capable(CAP_NET_RAW))
return ERR_PTR(-EPERM);
switch (type) {
case MLX5_IB_UAPI_DM_TYPE_STEERING_SW_ICM:
case MLX5_IB_UAPI_DM_TYPE_HEADER_MODIFY_SW_ICM:
case MLX5_IB_UAPI_DM_TYPE_ENCAP_SW_ICM:
if (!(MLX5_CAP_FLOWTABLE_NIC_RX(dev, sw_owner) ||
MLX5_CAP_FLOWTABLE_NIC_TX(dev, sw_owner) ||
MLX5_CAP_FLOWTABLE_NIC_RX(dev, sw_owner_v2) ||
MLX5_CAP_FLOWTABLE_NIC_TX(dev, sw_owner_v2)))
return ERR_PTR(-EOPNOTSUPP);
break;
case MLX5_IB_UAPI_DM_TYPE_HEADER_MODIFY_PATTERN_SW_ICM:
if (!MLX5_CAP_FLOWTABLE_NIC_RX(dev, sw_owner_v2) ||
!MLX5_CAP_FLOWTABLE_NIC_TX(dev, sw_owner_v2))
return ERR_PTR(-EOPNOTSUPP);
break;
default:
return ERR_PTR(-EOPNOTSUPP);
}
dm = kzalloc_obj(*dm);
if (!dm)
return ERR_PTR(-ENOMEM);
dm->base.type = type;
dm->base.ibdm.device = ctx->device;
/* Allocation size must a multiple of the basic block size
* and a power of 2.
*/
act_size = round_up(attr->length, MLX5_SW_ICM_BLOCK_SIZE(dev));
act_size = roundup_pow_of_two(act_size);
dm->base.size = act_size;
icm_type = get_icm_type(type);
err = mlx5_dm_sw_icm_alloc(dev, icm_type, act_size, attr->alignment,
to_mucontext(ctx)->devx_uid,
&dm->base.dev_addr, &dm->obj_id);
if (err)
goto free;
err = uverbs_copy_to(attrs, MLX5_IB_ATTR_ALLOC_DM_RESP_START_OFFSET,
&dm->base.dev_addr, sizeof(dm->base.dev_addr));
if (err) {
mlx5_dm_sw_icm_dealloc(dev, icm_type, dm->base.size,
to_mucontext(ctx)->devx_uid,
dm->base.dev_addr, dm->obj_id);
goto free;
}
return &dm->base.ibdm;
free:
kfree(dm);
return ERR_PTR(err);
}
struct ib_dm *mlx5_ib_alloc_dm(struct ib_device *ibdev,
struct ib_ucontext *context,
struct ib_dm_alloc_attr *attr,
struct uverbs_attr_bundle *attrs)
{
enum mlx5_ib_uapi_dm_type type;
int err;
err = uverbs_get_const_default(&type, attrs,
MLX5_IB_ATTR_ALLOC_DM_REQ_TYPE,
MLX5_IB_UAPI_DM_TYPE_MEMIC);
if (err)
return ERR_PTR(err);
mlx5_ib_dbg(to_mdev(ibdev), "alloc_dm req: dm_type=%d user_length=0x%llx log_alignment=%d\n",
type, attr->length, attr->alignment);
switch (type) {
case MLX5_IB_UAPI_DM_TYPE_MEMIC:
return handle_alloc_dm_memic(context, attr, attrs);
case MLX5_IB_UAPI_DM_TYPE_STEERING_SW_ICM:
case MLX5_IB_UAPI_DM_TYPE_HEADER_MODIFY_SW_ICM:
case MLX5_IB_UAPI_DM_TYPE_HEADER_MODIFY_PATTERN_SW_ICM:
case MLX5_IB_UAPI_DM_TYPE_ENCAP_SW_ICM:
return handle_alloc_dm_sw_icm(context, attr, attrs, type);
default:
return ERR_PTR(-EOPNOTSUPP);
}
}
static void dm_memic_remove_ops(struct mlx5_ib_dm_memic *dm)
{
struct mlx5_ib_dm_op_entry *entry;
unsigned long idx;
mutex_lock(&dm->ops_xa_lock);
xa_for_each(&dm->ops, idx, entry) {
xa_erase(&dm->ops, idx);
rdma_user_mmap_entry_remove(&entry->mentry.rdma_entry);
}
mutex_unlock(&dm->ops_xa_lock);
}
static void mlx5_dm_memic_dealloc(struct mlx5_ib_dm_memic *dm)
{
dm_memic_remove_ops(dm);
rdma_user_mmap_entry_remove(&dm->mentry.rdma_entry);
}
static int mlx5_dm_icm_dealloc(struct mlx5_ib_ucontext *ctx,
struct mlx5_ib_dm_icm *dm)
{
enum mlx5_sw_icm_type type = get_icm_type(dm->base.type);
struct mlx5_core_dev *dev = to_mdev(dm->base.ibdm.device)->mdev;
int err;
err = mlx5_dm_sw_icm_dealloc(dev, type, dm->base.size, ctx->devx_uid,
dm->base.dev_addr, dm->obj_id);
if (!err)
kfree(dm);
return 0;
}
static int mlx5_ib_dealloc_dm(struct ib_dm *ibdm,
struct uverbs_attr_bundle *attrs)
{
struct mlx5_ib_ucontext *ctx = rdma_udata_to_drv_context(
&attrs->driver_udata, struct mlx5_ib_ucontext, ibucontext);
struct mlx5_ib_dm *dm = to_mdm(ibdm);
switch (dm->type) {
case MLX5_IB_UAPI_DM_TYPE_MEMIC:
mlx5_dm_memic_dealloc(to_memic(ibdm));
return 0;
case MLX5_IB_UAPI_DM_TYPE_STEERING_SW_ICM:
case MLX5_IB_UAPI_DM_TYPE_HEADER_MODIFY_SW_ICM:
case MLX5_IB_UAPI_DM_TYPE_HEADER_MODIFY_PATTERN_SW_ICM:
case MLX5_IB_UAPI_DM_TYPE_ENCAP_SW_ICM:
return mlx5_dm_icm_dealloc(ctx, to_icm(ibdm));
default:
return -EOPNOTSUPP;
}
}
static int UVERBS_HANDLER(MLX5_IB_METHOD_DM_QUERY)(
struct uverbs_attr_bundle *attrs)
{
struct ib_dm *ibdm =
uverbs_attr_get_obj(attrs, MLX5_IB_ATTR_QUERY_DM_REQ_HANDLE);
struct mlx5_ib_dm *dm = to_mdm(ibdm);
struct mlx5_ib_dm_memic *memic;
u64 start_offset;
u16 page_idx;
int err;
if (dm->type != MLX5_IB_UAPI_DM_TYPE_MEMIC)
return -EOPNOTSUPP;
memic = to_memic(ibdm);
page_idx = memic->mentry.rdma_entry.start_pgoff & 0xFFFF;
err = uverbs_copy_to(attrs, MLX5_IB_ATTR_QUERY_DM_RESP_PAGE_INDEX,
&page_idx, sizeof(page_idx));
if (err)
return err;
start_offset = memic->base.dev_addr & ~PAGE_MASK;
err = uverbs_copy_to(attrs, MLX5_IB_ATTR_QUERY_DM_RESP_START_OFFSET,
&start_offset, sizeof(start_offset));
if (err)
return err;
return uverbs_copy_to(attrs, MLX5_IB_ATTR_QUERY_DM_RESP_LENGTH,
&memic->req_length,
sizeof(memic->req_length));
}
void mlx5_ib_dm_mmap_free(struct mlx5_ib_dev *dev,
struct mlx5_user_mmap_entry *mentry)
{
struct mlx5_ib_dm_op_entry *op_entry;
struct mlx5_ib_dm_memic *mdm;
switch (mentry->mmap_flag) {
case MLX5_IB_MMAP_TYPE_MEMIC:
mdm = container_of(mentry, struct mlx5_ib_dm_memic, mentry);
kref_put(&mdm->ref, mlx5_ib_dm_memic_free);
break;
case MLX5_IB_MMAP_TYPE_MEMIC_OP:
op_entry = container_of(mentry, struct mlx5_ib_dm_op_entry,
mentry);
mdm = op_entry->dm;
mlx5_cmd_dealloc_memic_op(&dev->dm, mdm->base.dev_addr,
op_entry->op);
kfree(op_entry);
kref_put(&mdm->ref, mlx5_ib_dm_memic_free);
break;
default:
WARN_ON(true);
}
}
DECLARE_UVERBS_NAMED_METHOD(
MLX5_IB_METHOD_DM_QUERY,
UVERBS_ATTR_IDR(MLX5_IB_ATTR_QUERY_DM_REQ_HANDLE, UVERBS_OBJECT_DM,
UVERBS_ACCESS_READ, UA_MANDATORY),
UVERBS_ATTR_PTR_OUT(MLX5_IB_ATTR_QUERY_DM_RESP_START_OFFSET,
UVERBS_ATTR_TYPE(u64), UA_MANDATORY),
UVERBS_ATTR_PTR_OUT(MLX5_IB_ATTR_QUERY_DM_RESP_PAGE_INDEX,
UVERBS_ATTR_TYPE(u16), UA_MANDATORY),
UVERBS_ATTR_PTR_OUT(MLX5_IB_ATTR_QUERY_DM_RESP_LENGTH,
UVERBS_ATTR_TYPE(u64), UA_MANDATORY));
ADD_UVERBS_ATTRIBUTES_SIMPLE(
mlx5_ib_dm, UVERBS_OBJECT_DM, UVERBS_METHOD_DM_ALLOC,
UVERBS_ATTR_PTR_OUT(MLX5_IB_ATTR_ALLOC_DM_RESP_START_OFFSET,
UVERBS_ATTR_TYPE(u64), UA_MANDATORY),
UVERBS_ATTR_PTR_OUT(MLX5_IB_ATTR_ALLOC_DM_RESP_PAGE_INDEX,
UVERBS_ATTR_TYPE(u16), UA_OPTIONAL),
UVERBS_ATTR_CONST_IN(MLX5_IB_ATTR_ALLOC_DM_REQ_TYPE,
enum mlx5_ib_uapi_dm_type, UA_OPTIONAL));
DECLARE_UVERBS_NAMED_METHOD(
MLX5_IB_METHOD_DM_MAP_OP_ADDR,
UVERBS_ATTR_IDR(MLX5_IB_ATTR_DM_MAP_OP_ADDR_REQ_HANDLE,
UVERBS_OBJECT_DM,
UVERBS_ACCESS_READ,
UA_MANDATORY),
UVERBS_ATTR_PTR_IN(MLX5_IB_ATTR_DM_MAP_OP_ADDR_REQ_OP,
UVERBS_ATTR_TYPE(u8),
UA_MANDATORY),
UVERBS_ATTR_PTR_OUT(MLX5_IB_ATTR_DM_MAP_OP_ADDR_RESP_START_OFFSET,
UVERBS_ATTR_TYPE(u64),
UA_MANDATORY),
UVERBS_ATTR_PTR_OUT(MLX5_IB_ATTR_DM_MAP_OP_ADDR_RESP_PAGE_INDEX,
UVERBS_ATTR_TYPE(u16),
UA_OPTIONAL));
DECLARE_UVERBS_GLOBAL_METHODS(UVERBS_OBJECT_DM,
&UVERBS_METHOD(MLX5_IB_METHOD_DM_MAP_OP_ADDR),
&UVERBS_METHOD(MLX5_IB_METHOD_DM_QUERY));
const struct uapi_definition mlx5_ib_dm_defs[] = {
UAPI_DEF_CHAIN_OBJ_TREE(UVERBS_OBJECT_DM, &mlx5_ib_dm),
UAPI_DEF_CHAIN_OBJ_TREE_NAMED(UVERBS_OBJECT_DM),
{},
};
const struct ib_device_ops mlx5_ib_dev_dm_ops = {
.alloc_dm = mlx5_ib_alloc_dm,
.dealloc_dm = mlx5_ib_dealloc_dm,
.reg_dm_mr = mlx5_ib_reg_dm_mr,
};
@@ -0,0 +1,68 @@
/* SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB */
/*
* Copyright (c) 2021, Mellanox Technologies inc. All rights reserved.
*/
#ifndef _MLX5_IB_DM_H
#define _MLX5_IB_DM_H
#include "mlx5_ib.h"
extern const struct ib_device_ops mlx5_ib_dev_dm_ops;
extern const struct uapi_definition mlx5_ib_dm_defs[];
struct mlx5_ib_dm {
struct ib_dm ibdm;
u32 type;
phys_addr_t dev_addr;
size_t size;
};
struct mlx5_ib_dm_op_entry {
struct mlx5_user_mmap_entry mentry;
phys_addr_t op_addr;
struct mlx5_ib_dm_memic *dm;
u8 op;
};
struct mlx5_ib_dm_memic {
struct mlx5_ib_dm base;
struct mlx5_user_mmap_entry mentry;
struct xarray ops;
struct mutex ops_xa_lock;
struct kref ref;
size_t req_length;
};
struct mlx5_ib_dm_icm {
struct mlx5_ib_dm base;
u32 obj_id;
};
static inline struct mlx5_ib_dm *to_mdm(struct ib_dm *ibdm)
{
return container_of(ibdm, struct mlx5_ib_dm, ibdm);
}
static inline struct mlx5_ib_dm_memic *to_memic(struct ib_dm *ibdm)
{
return container_of(ibdm, struct mlx5_ib_dm_memic, base.ibdm);
}
static inline struct mlx5_ib_dm_icm *to_icm(struct ib_dm *ibdm)
{
return container_of(ibdm, struct mlx5_ib_dm_icm, base.ibdm);
}
struct ib_dm *mlx5_ib_alloc_dm(struct ib_device *ibdev,
struct ib_ucontext *context,
struct ib_dm_alloc_attr *attr,
struct uverbs_attr_bundle *attrs);
void mlx5_ib_dm_mmap_free(struct mlx5_ib_dev *dev,
struct mlx5_user_mmap_entry *mentry);
void mlx5_cmd_dealloc_memic(struct mlx5_dm *dm, phys_addr_t addr,
u64 length);
void mlx5_cmd_dealloc_memic_op(struct mlx5_dm *dm, phys_addr_t addr,
u8 operation);
#endif /* _MLX5_IB_DM_H */
@@ -0,0 +1,54 @@
// SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB
/*
* Copyright (c) 2025, NVIDIA CORPORATION & AFFILIATES. All rights reserved
*/
#include <rdma/uverbs_std_types.h>
#include <linux/pci-tph.h>
#include "dmah.h"
#define UVERBS_MODULE_NAME mlx5_ib
#include <rdma/uverbs_named_ioctl.h>
static int mlx5_ib_alloc_dmah(struct ib_dmah *ibdmah,
struct uverbs_attr_bundle *attrs)
{
struct mlx5_core_dev *mdev = to_mdev(ibdmah->device)->mdev;
struct mlx5_ib_dmah *dmah = to_mdmah(ibdmah);
u16 st_bits = BIT(IB_DMAH_CPU_ID_EXISTS) |
BIT(IB_DMAH_MEM_TYPE_EXISTS);
int err;
/* PH is a must for TPH following PCIe spec 6.2-1.0 */
if (!(ibdmah->valid_fields & BIT(IB_DMAH_PH_EXISTS)))
return -EINVAL;
/* ST is optional; however, partial data for it is not allowed */
if (ibdmah->valid_fields & st_bits) {
if ((ibdmah->valid_fields & st_bits) != st_bits)
return -EINVAL;
err = mlx5_st_alloc_index(mdev, ibdmah->mem_type,
ibdmah->cpu_id, &dmah->st_index);
if (err)
return err;
}
return 0;
}
static int mlx5_ib_dealloc_dmah(struct ib_dmah *ibdmah,
struct uverbs_attr_bundle *attrs)
{
struct mlx5_ib_dmah *dmah = to_mdmah(ibdmah);
struct mlx5_core_dev *mdev = to_mdev(ibdmah->device)->mdev;
if (ibdmah->valid_fields & BIT(IB_DMAH_CPU_ID_EXISTS))
return mlx5_st_dealloc_index(mdev, dmah->st_index);
return 0;
}
const struct ib_device_ops mlx5_ib_dev_dmah_ops = {
.alloc_dmah = mlx5_ib_alloc_dmah,
.dealloc_dmah = mlx5_ib_dealloc_dmah,
};
@@ -0,0 +1,23 @@
/* SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB */
/*
* Copyright (c) 2025, NVIDIA CORPORATION & AFFILIATES. All rights reserved
*/
#ifndef _MLX5_IB_DMAH_H
#define _MLX5_IB_DMAH_H
#include "mlx5_ib.h"
extern const struct ib_device_ops mlx5_ib_dev_dmah_ops;
struct mlx5_ib_dmah {
struct ib_dmah ibdmah;
u16 st_index;
};
static inline struct mlx5_ib_dmah *to_mdmah(struct ib_dmah *ibdmah)
{
return container_of(ibdmah, struct mlx5_ib_dmah, ibdmah);
}
#endif /* _MLX5_IB_DMAH_H */
@@ -0,0 +1,172 @@
/*
* Copyright (c) 2013-2015, Mellanox Technologies. 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 <linux/kref.h>
#include <linux/slab.h>
#include <linux/sched/mm.h>
#include <rdma/ib_umem.h>
#include "mlx5_ib.h"
#define MLX5_IB_DBR_SIZE (sizeof(__be32) * 2)
struct mlx5_ib_user_db_page {
struct list_head list;
struct ib_umem *umem;
struct ib_uverbs_buffer_desc desc;
int refcnt;
struct mm_struct *mm;
};
static int mlx5_ib_db_map_user_desc(struct mlx5_ib_ucontext *context,
const struct ib_uverbs_buffer_desc *desc,
struct mlx5_db *db)
{
struct mlx5_ib_user_db_page *page;
struct ib_umem *umem;
int err = 0;
if (desc->length < MLX5_IB_DBR_SIZE)
return -EINVAL;
/*
* For VA descriptors the umem is normalized to a single PAGE_SIZE
* region, so reject offsets that would place the 8-byte DBR
* straddling the page boundary.
*/
if (desc->type == IB_UVERBS_BUFFER_TYPE_VA &&
(desc->addr & ~PAGE_MASK) > PAGE_SIZE - MLX5_IB_DBR_SIZE)
return -EINVAL;
mutex_lock(&context->db_page_mutex);
/*
* Only VA-typed descriptors are eligible to share a per-page
* doorbell umem; FD-typed descriptors are pinned individually.
*/
if (desc->type == IB_UVERBS_BUFFER_TYPE_VA) {
list_for_each_entry(page, &context->db_page_list, list) {
if (current->mm != page->mm)
continue;
if (page->desc.addr == (desc->addr & PAGE_MASK))
goto found;
}
}
page = kzalloc_obj(*page);
if (!page) {
err = -ENOMEM;
goto out;
}
page->desc = *desc;
/*
* Normalize VA descriptors to a page-aligned PAGE_SIZE region so
* multiple DBRs that fall in the same user page share one umem.
*/
if (page->desc.type == IB_UVERBS_BUFFER_TYPE_VA) {
page->desc.addr &= PAGE_MASK;
page->desc.length = PAGE_SIZE;
}
umem = ib_umem_get_desc(context->ibucontext.device, &page->desc, 0);
if (IS_ERR(umem)) {
err = PTR_ERR(umem);
kfree(page);
goto out;
}
/*
* The 8-byte DBR is programmed to the device as one DMA address,
* so it must live in a single contiguous DMA segment.
*/
if (!ib_umem_is_contiguous(umem)) {
ib_umem_release(umem);
kfree(page);
err = -EINVAL;
goto out;
}
page->umem = umem;
if (page->desc.type == IB_UVERBS_BUFFER_TYPE_VA) {
mmgrab(current->mm);
page->mm = current->mm;
}
list_add(&page->list, &context->db_page_list);
found:
db->dma = sg_dma_address(page->umem->sgt_append.sgt.sgl) +
(desc->addr & ~PAGE_MASK);
db->u.user_page = page;
++page->refcnt;
out:
mutex_unlock(&context->db_page_mutex);
return err;
}
int mlx5_ib_db_map_user(struct mlx5_ib_ucontext *context,
const struct uverbs_attr_bundle *attrs, u16 attr_id,
unsigned long virt, struct mlx5_db *db)
{
struct ib_uverbs_buffer_desc desc = {
.type = IB_UVERBS_BUFFER_TYPE_VA,
.addr = virt,
.length = MLX5_IB_DBR_SIZE,
};
if (attrs) {
int err;
err = uverbs_get_buffer_desc(attrs, attr_id, &desc);
if (err && err != -ENOENT)
return err;
}
return mlx5_ib_db_map_user_desc(context, &desc, db);
}
void mlx5_ib_db_unmap_user(struct mlx5_ib_ucontext *context, struct mlx5_db *db)
{
mutex_lock(&context->db_page_mutex);
if (!--db->u.user_page->refcnt) {
list_del(&db->u.user_page->list);
if (db->u.user_page->mm)
mmdrop(db->u.user_page->mm);
ib_umem_release(db->u.user_page->umem);
kfree(db->u.user_page);
}
mutex_unlock(&context->db_page_mutex);
}
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,36 @@
/* SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB */
/*
* Copyright (c) 2013-2020, Mellanox Technologies inc. All rights reserved.
*/
#ifndef _MLX5_IB_FS_H
#define _MLX5_IB_FS_H
#include "mlx5_ib.h"
int mlx5_ib_fs_init(struct mlx5_ib_dev *dev);
void mlx5_ib_fs_cleanup_anchor(struct mlx5_ib_dev *dev);
static inline void mlx5_ib_fs_cleanup(struct mlx5_ib_dev *dev)
{
int i;
/* When a steering anchor is created, a special flow table is also
* created for the user to reference. Since the user can reference it,
* the kernel cannot trust that when the user destroys the steering
* anchor, they no longer reference the flow table.
*
* To address this issue, when a user destroys a steering anchor, only
* the flow steering rule in the table is destroyed, but the table
* itself is kept to deal with the above scenario. The remaining
* resources are only removed when the RDMA device is destroyed, which
* is a safe assumption that all references are gone.
*/
mlx5_ib_fs_cleanup_anchor(dev);
for (i = 0; i < MLX5_RDMA_TRANSPORT_BYPASS_PRIO; i++)
kfree(dev->flow_db->rdma_transport_tx[i]);
for (i = 0; i < MLX5_RDMA_TRANSPORT_BYPASS_PRIO; i++)
kfree(dev->flow_db->rdma_transport_rx[i]);
kfree(dev->flow_db);
}
#endif /* _MLX5_IB_FS_H */
@@ -0,0 +1,491 @@
/*
* Copyright (c) 2016, Mellanox Technologies. 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 "mlx5_ib.h"
struct mlx5_ib_gsi_wr {
struct ib_cqe cqe;
struct ib_wc wc;
bool completed:1;
};
static bool mlx5_ib_deth_sqpn_cap(struct mlx5_ib_dev *dev)
{
return MLX5_CAP_GEN(dev->mdev, set_deth_sqpn);
}
/* Call with gsi->lock locked */
static void generate_completions(struct mlx5_ib_qp *mqp)
{
struct mlx5_ib_gsi_qp *gsi = &mqp->gsi;
struct ib_cq *gsi_cq = mqp->ibqp.send_cq;
struct mlx5_ib_gsi_wr *wr;
u32 index;
for (index = gsi->outstanding_ci; index != gsi->outstanding_pi;
index++) {
wr = &gsi->outstanding_wrs[index % gsi->cap.max_send_wr];
if (!wr->completed)
break;
WARN_ON_ONCE(mlx5_ib_generate_wc(gsi_cq, &wr->wc));
wr->completed = false;
}
gsi->outstanding_ci = index;
}
static void handle_single_completion(struct ib_cq *cq, struct ib_wc *wc)
{
struct mlx5_ib_gsi_qp *gsi = cq->cq_context;
struct mlx5_ib_gsi_wr *wr =
container_of(wc->wr_cqe, struct mlx5_ib_gsi_wr, cqe);
struct mlx5_ib_qp *mqp = container_of(gsi, struct mlx5_ib_qp, gsi);
u64 wr_id;
unsigned long flags;
spin_lock_irqsave(&gsi->lock, flags);
wr->completed = true;
wr_id = wr->wc.wr_id;
wr->wc = *wc;
wr->wc.wr_id = wr_id;
wr->wc.qp = &mqp->ibqp;
generate_completions(mqp);
spin_unlock_irqrestore(&gsi->lock, flags);
}
int mlx5_ib_create_gsi(struct ib_pd *pd, struct mlx5_ib_qp *mqp,
struct ib_qp_init_attr *attr)
{
struct mlx5_ib_dev *dev = to_mdev(pd->device);
struct mlx5_ib_gsi_qp *gsi;
struct ib_qp_init_attr hw_init_attr = *attr;
const u8 port_num = attr->port_num;
int num_qps = 0;
int ret;
if (mlx5_ib_deth_sqpn_cap(dev)) {
if (MLX5_CAP_GEN(dev->mdev,
port_type) == MLX5_CAP_PORT_TYPE_IB)
num_qps = pd->device->attrs.max_pkeys;
else if (dev->lag_active)
num_qps = dev->lag_ports;
}
gsi = &mqp->gsi;
gsi->tx_qps = kzalloc_objs(*gsi->tx_qps, num_qps);
if (!gsi->tx_qps)
return -ENOMEM;
gsi->outstanding_wrs =
kzalloc_objs(*gsi->outstanding_wrs, attr->cap.max_send_wr);
if (!gsi->outstanding_wrs) {
ret = -ENOMEM;
goto err_free_tx;
}
if (dev->devr.ports[port_num - 1].gsi) {
mlx5_ib_warn(dev, "GSI QP already exists on port %d\n",
port_num);
ret = -EBUSY;
goto err_free_wrs;
}
gsi->num_qps = num_qps;
spin_lock_init(&gsi->lock);
gsi->cap = attr->cap;
gsi->port_num = port_num;
gsi->cq = ib_alloc_cq(pd->device, gsi, attr->cap.max_send_wr, 0,
IB_POLL_SOFTIRQ);
if (IS_ERR(gsi->cq)) {
mlx5_ib_warn(dev,
"unable to create send CQ for GSI QP. error %pe\n",
gsi->cq);
ret = PTR_ERR(gsi->cq);
goto err_free_wrs;
}
hw_init_attr.qp_type = MLX5_IB_QPT_HW_GSI;
hw_init_attr.send_cq = gsi->cq;
if (num_qps) {
hw_init_attr.cap.max_send_wr = 0;
hw_init_attr.cap.max_send_sge = 0;
hw_init_attr.cap.max_inline_data = 0;
}
gsi->rx_qp = ib_create_qp(pd, &hw_init_attr);
if (IS_ERR(gsi->rx_qp)) {
mlx5_ib_warn(dev,
"unable to create hardware GSI QP. error %pe\n",
gsi->rx_qp);
ret = PTR_ERR(gsi->rx_qp);
goto err_destroy_cq;
}
dev->devr.ports[attr->port_num - 1].gsi = gsi;
return 0;
err_destroy_cq:
ib_free_cq(gsi->cq);
err_free_wrs:
kfree(gsi->outstanding_wrs);
err_free_tx:
kfree(gsi->tx_qps);
return ret;
}
int mlx5_ib_destroy_gsi(struct mlx5_ib_qp *mqp)
{
struct mlx5_ib_dev *dev = to_mdev(mqp->ibqp.device);
struct mlx5_ib_gsi_qp *gsi = &mqp->gsi;
const int port_num = gsi->port_num;
int qp_index;
int ret;
ret = ib_destroy_qp(gsi->rx_qp);
if (ret) {
mlx5_ib_warn(dev, "unable to destroy hardware GSI QP. error %d\n",
ret);
return ret;
}
dev->devr.ports[port_num - 1].gsi = NULL;
gsi->rx_qp = NULL;
for (qp_index = 0; qp_index < gsi->num_qps; ++qp_index) {
if (!gsi->tx_qps[qp_index])
continue;
WARN_ON_ONCE(ib_destroy_qp(gsi->tx_qps[qp_index]));
gsi->tx_qps[qp_index] = NULL;
}
ib_free_cq(gsi->cq);
kfree(gsi->outstanding_wrs);
kfree(gsi->tx_qps);
return 0;
}
static struct ib_qp *create_gsi_ud_qp(struct mlx5_ib_gsi_qp *gsi)
{
struct ib_pd *pd = gsi->rx_qp->pd;
struct ib_qp_init_attr init_attr = {
.event_handler = gsi->rx_qp->event_handler,
.qp_context = gsi->rx_qp->qp_context,
.send_cq = gsi->cq,
.recv_cq = gsi->rx_qp->recv_cq,
.cap = {
.max_send_wr = gsi->cap.max_send_wr,
.max_send_sge = gsi->cap.max_send_sge,
.max_inline_data = gsi->cap.max_inline_data,
},
.qp_type = IB_QPT_UD,
.create_flags = MLX5_IB_QP_CREATE_SQPN_QP1,
};
return ib_create_qp(pd, &init_attr);
}
static int modify_to_rts(struct mlx5_ib_gsi_qp *gsi, struct ib_qp *qp,
u16 pkey_index)
{
struct mlx5_ib_dev *dev = to_mdev(qp->device);
struct ib_qp_attr attr;
int mask;
int ret;
mask = IB_QP_STATE | IB_QP_PKEY_INDEX | IB_QP_QKEY | IB_QP_PORT;
attr.qp_state = IB_QPS_INIT;
attr.pkey_index = pkey_index;
attr.qkey = IB_QP1_QKEY;
attr.port_num = gsi->port_num;
ret = ib_modify_qp(qp, &attr, mask);
if (ret) {
mlx5_ib_err(dev, "could not change QP%d state to INIT: %d\n",
qp->qp_num, ret);
return ret;
}
attr.qp_state = IB_QPS_RTR;
ret = ib_modify_qp(qp, &attr, IB_QP_STATE);
if (ret) {
mlx5_ib_err(dev, "could not change QP%d state to RTR: %d\n",
qp->qp_num, ret);
return ret;
}
attr.qp_state = IB_QPS_RTS;
attr.sq_psn = 0;
ret = ib_modify_qp(qp, &attr, IB_QP_STATE | IB_QP_SQ_PSN);
if (ret) {
mlx5_ib_err(dev, "could not change QP%d state to RTS: %d\n",
qp->qp_num, ret);
return ret;
}
return 0;
}
static void setup_qp(struct mlx5_ib_gsi_qp *gsi, u16 qp_index)
{
struct ib_device *device = gsi->rx_qp->device;
struct mlx5_ib_dev *dev = to_mdev(device);
int pkey_index = qp_index;
struct mlx5_ib_qp *mqp;
struct ib_qp *qp;
unsigned long flags;
u16 pkey;
int ret;
if (MLX5_CAP_GEN(dev->mdev, port_type) != MLX5_CAP_PORT_TYPE_IB)
pkey_index = 0;
ret = ib_query_pkey(device, gsi->port_num, pkey_index, &pkey);
if (ret) {
mlx5_ib_warn(dev, "unable to read P_Key at port %d, index %d\n",
gsi->port_num, qp_index);
return;
}
if (!pkey) {
mlx5_ib_dbg(dev, "invalid P_Key at port %d, index %d. Skipping.\n",
gsi->port_num, qp_index);
return;
}
spin_lock_irqsave(&gsi->lock, flags);
qp = gsi->tx_qps[qp_index];
spin_unlock_irqrestore(&gsi->lock, flags);
if (qp) {
mlx5_ib_dbg(dev, "already existing GSI TX QP at port %d, index %d. Skipping\n",
gsi->port_num, qp_index);
return;
}
qp = create_gsi_ud_qp(gsi);
if (IS_ERR(qp)) {
mlx5_ib_warn(dev,
"unable to create hardware UD QP for GSI: %pe\n",
qp);
return;
}
mqp = to_mqp(qp);
if (dev->lag_active)
mqp->gsi_lag_port = qp_index + 1;
ret = modify_to_rts(gsi, qp, pkey_index);
if (ret)
goto err_destroy_qp;
spin_lock_irqsave(&gsi->lock, flags);
WARN_ON_ONCE(gsi->tx_qps[qp_index]);
gsi->tx_qps[qp_index] = qp;
spin_unlock_irqrestore(&gsi->lock, flags);
return;
err_destroy_qp:
WARN_ON_ONCE(qp);
}
int mlx5_ib_gsi_modify_qp(struct ib_qp *qp, struct ib_qp_attr *attr,
int attr_mask)
{
struct mlx5_ib_dev *dev = to_mdev(qp->device);
struct mlx5_ib_qp *mqp = to_mqp(qp);
struct mlx5_ib_gsi_qp *gsi = &mqp->gsi;
u16 qp_index;
int ret;
mlx5_ib_dbg(dev, "modifying GSI QP to state %d\n", attr->qp_state);
ret = ib_modify_qp(gsi->rx_qp, attr, attr_mask);
if (ret) {
mlx5_ib_warn(dev, "unable to modify GSI rx QP: %d\n", ret);
return ret;
}
if (to_mqp(gsi->rx_qp)->state != IB_QPS_RTS)
return 0;
for (qp_index = 0; qp_index < gsi->num_qps; ++qp_index)
setup_qp(gsi, qp_index);
return 0;
}
int mlx5_ib_gsi_query_qp(struct ib_qp *qp, struct ib_qp_attr *qp_attr,
int qp_attr_mask,
struct ib_qp_init_attr *qp_init_attr)
{
struct mlx5_ib_qp *mqp = to_mqp(qp);
struct mlx5_ib_gsi_qp *gsi = &mqp->gsi;
int ret;
ret = ib_query_qp(gsi->rx_qp, qp_attr, qp_attr_mask, qp_init_attr);
qp_init_attr->cap = gsi->cap;
return ret;
}
/* Call with gsi->lock locked */
static int mlx5_ib_add_outstanding_wr(struct mlx5_ib_qp *mqp,
struct ib_ud_wr *wr, struct ib_wc *wc)
{
struct mlx5_ib_gsi_qp *gsi = &mqp->gsi;
struct mlx5_ib_dev *dev = to_mdev(gsi->rx_qp->device);
struct mlx5_ib_gsi_wr *gsi_wr;
if (gsi->outstanding_pi == gsi->outstanding_ci + gsi->cap.max_send_wr) {
mlx5_ib_warn(dev, "no available GSI work request.\n");
return -ENOMEM;
}
gsi_wr = &gsi->outstanding_wrs[gsi->outstanding_pi %
gsi->cap.max_send_wr];
gsi->outstanding_pi++;
if (!wc) {
memset(&gsi_wr->wc, 0, sizeof(gsi_wr->wc));
gsi_wr->wc.pkey_index = wr->pkey_index;
gsi_wr->wc.wr_id = wr->wr.wr_id;
} else {
gsi_wr->wc = *wc;
gsi_wr->completed = true;
}
gsi_wr->cqe.done = &handle_single_completion;
wr->wr.wr_cqe = &gsi_wr->cqe;
return 0;
}
/* Call with gsi->lock locked */
static int mlx5_ib_gsi_silent_drop(struct mlx5_ib_qp *mqp, struct ib_ud_wr *wr)
{
struct ib_wc wc = {
{ .wr_id = wr->wr.wr_id },
.status = IB_WC_SUCCESS,
.opcode = IB_WC_SEND,
.qp = &mqp->ibqp,
};
int ret;
ret = mlx5_ib_add_outstanding_wr(mqp, wr, &wc);
if (ret)
return ret;
generate_completions(mqp);
return 0;
}
/* Call with gsi->lock locked */
static struct ib_qp *get_tx_qp(struct mlx5_ib_gsi_qp *gsi, struct ib_ud_wr *wr)
{
struct mlx5_ib_dev *dev = to_mdev(gsi->rx_qp->device);
struct mlx5_ib_ah *ah = to_mah(wr->ah);
int qp_index = wr->pkey_index;
if (!gsi->num_qps)
return gsi->rx_qp;
if (dev->lag_active && ah->xmit_port)
qp_index = ah->xmit_port - 1;
if (qp_index >= gsi->num_qps)
return NULL;
return gsi->tx_qps[qp_index];
}
int mlx5_ib_gsi_post_send(struct ib_qp *qp, const struct ib_send_wr *wr,
const struct ib_send_wr **bad_wr)
{
struct mlx5_ib_qp *mqp = to_mqp(qp);
struct mlx5_ib_gsi_qp *gsi = &mqp->gsi;
struct ib_qp *tx_qp;
unsigned long flags;
int ret;
for (; wr; wr = wr->next) {
struct ib_ud_wr cur_wr = *ud_wr(wr);
cur_wr.wr.next = NULL;
spin_lock_irqsave(&gsi->lock, flags);
tx_qp = get_tx_qp(gsi, &cur_wr);
if (!tx_qp) {
ret = mlx5_ib_gsi_silent_drop(mqp, &cur_wr);
if (ret)
goto err;
spin_unlock_irqrestore(&gsi->lock, flags);
continue;
}
ret = mlx5_ib_add_outstanding_wr(mqp, &cur_wr, NULL);
if (ret)
goto err;
ret = ib_post_send(tx_qp, &cur_wr.wr, bad_wr);
if (ret) {
/* Undo the effect of adding the outstanding wr */
gsi->outstanding_pi--;
goto err;
}
spin_unlock_irqrestore(&gsi->lock, flags);
}
return 0;
err:
spin_unlock_irqrestore(&gsi->lock, flags);
*bad_wr = wr;
return ret;
}
int mlx5_ib_gsi_post_recv(struct ib_qp *qp, const struct ib_recv_wr *wr,
const struct ib_recv_wr **bad_wr)
{
struct mlx5_ib_qp *mqp = to_mqp(qp);
struct mlx5_ib_gsi_qp *gsi = &mqp->gsi;
return ib_post_recv(gsi->rx_qp, wr, bad_wr);
}
void mlx5_ib_gsi_pkey_change(struct mlx5_ib_gsi_qp *gsi)
{
u16 qp_index;
for (qp_index = 0; qp_index < gsi->num_qps; ++qp_index)
setup_qp(gsi, qp_index);
}
@@ -0,0 +1,363 @@
// SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB
/*
* Copyright (c) 2018 Mellanox Technologies. All rights reserved.
*/
#include <linux/mlx5/lag.h>
#include <linux/mlx5/vport.h>
#include "ib_rep.h"
#include "srq.h"
static int
mlx5_ib_set_vport_rep(struct mlx5_core_dev *dev,
struct mlx5_core_dev *rep_dev,
struct mlx5_eswitch_rep *rep,
int vport_index)
{
struct mlx5_ib_dev *ibdev;
struct net_device *ndev;
int ret;
ibdev = mlx5_eswitch_uplink_get_proto_dev(dev->priv.eswitch, REP_IB);
if (!ibdev)
return -EINVAL;
ibdev->port[vport_index].rep = rep;
rep->rep_data[REP_IB].priv = ibdev;
ndev = mlx5_ib_get_rep_netdev(rep->esw, rep->vport);
ret = ib_device_set_netdev(&ibdev->ib_dev, ndev, vport_index + 1);
if (ret)
return ret;
/* Only Vports that are not native to the LAG master eswitch need to add
* demux rule.
*/
if (mlx5_eswitch_get_total_vports(dev) > vport_index)
return 0;
return mlx5_lag_demux_rule_add(rep_dev, rep->vport, vport_index);
}
static void mlx5_ib_register_peer_vport_reps(struct mlx5_core_dev *mdev);
static void mlx5_ib_num_ports_update(struct mlx5_core_dev *dev, u32 *num_ports)
{
struct mlx5_core_dev *peer_dev;
int i;
mlx5_lag_for_each_peer_mdev(dev, peer_dev, i) {
u32 peer_num_ports = mlx5_eswitch_get_total_vports(peer_dev);
if (mlx5_lag_is_mpesw(peer_dev))
*num_ports += peer_num_ports;
else
/* Only 1 ib port is the representor for all uplinks */
*num_ports += peer_num_ports - 1;
}
}
static int mlx5_ib_set_owner_transport(struct mlx5_core_dev *cur_owner,
struct mlx5_core_dev *new_owner)
{
int ret;
if (!MLX5_CAP_FLOWTABLE_RDMA_TRANSPORT_TX(cur_owner, ft_support) ||
!MLX5_CAP_FLOWTABLE_RDMA_TRANSPORT_RX(cur_owner, ft_support))
return 0;
if (!MLX5_CAP_ADV_RDMA(new_owner, rdma_transport_manager) ||
!MLX5_CAP_ADV_RDMA(new_owner, rdma_transport_manager_other_eswitch))
return 0;
ret = mlx5_fs_set_root_dev(cur_owner, new_owner,
FS_FT_RDMA_TRANSPORT_TX);
if (ret)
return ret;
ret = mlx5_fs_set_root_dev(cur_owner, new_owner,
FS_FT_RDMA_TRANSPORT_RX);
if (ret) {
mlx5_fs_set_root_dev(cur_owner, cur_owner,
FS_FT_RDMA_TRANSPORT_TX);
return ret;
}
return 0;
}
static void mlx5_ib_release_transport(struct mlx5_core_dev *dev)
{
struct mlx5_core_dev *peer_dev;
int i, ret;
mlx5_lag_for_each_peer_mdev(dev, peer_dev, i) {
ret = mlx5_ib_set_owner_transport(peer_dev, peer_dev);
WARN_ON_ONCE(ret);
}
}
static int mlx5_ib_take_transport(struct mlx5_core_dev *dev)
{
struct mlx5_core_dev *peer_dev;
int ret;
int i;
mlx5_lag_for_each_peer_mdev(dev, peer_dev, i) {
ret = mlx5_ib_set_owner_transport(peer_dev, dev);
if (ret) {
mlx5_ib_release_transport(dev);
return ret;
}
}
return 0;
}
static int
mlx5_ib_vport_rep_load(struct mlx5_core_dev *dev, struct mlx5_eswitch_rep *rep)
{
u32 num_ports = mlx5_eswitch_get_total_vports(dev);
struct mlx5_core_dev *lag_master = dev;
const struct mlx5_ib_profile *profile;
struct mlx5_core_dev *peer_dev;
struct mlx5_ib_dev *ibdev;
int new_uplink = false;
int vport_index;
int ret;
int i;
vport_index = rep->vport_index;
if (mlx5_lag_is_shared_fdb(dev)) {
if (mlx5_lag_is_master(dev)) {
mlx5_ib_num_ports_update(dev, &num_ports);
} else {
if (rep->vport == MLX5_VPORT_UPLINK) {
if (!mlx5_lag_is_mpesw(dev))
return 0;
new_uplink = true;
}
mlx5_lag_for_each_peer_mdev(dev, peer_dev, i) {
u32 peer_n_ports = mlx5_eswitch_get_total_vports(peer_dev);
if (mlx5_lag_is_master(peer_dev))
lag_master = peer_dev;
else if (!mlx5_lag_is_mpesw(peer_dev))
/* Only 1 ib port is the representor for all uplinks */
peer_n_ports--;
if (mlx5_lag_get_dev_seq(peer_dev) <
mlx5_lag_get_dev_seq(dev))
vport_index += peer_n_ports;
}
}
}
if (rep->vport == MLX5_VPORT_UPLINK && !new_uplink)
profile = &raw_eth_profile;
else
return mlx5_ib_set_vport_rep(lag_master, dev, rep, vport_index);
if (mlx5_lag_is_shared_fdb(dev)) {
ret = mlx5_ib_take_transport(lag_master);
if (ret)
return ret;
}
ibdev = ib_alloc_device_with_net(mlx5_ib_dev, ib_dev,
mlx5_core_net(lag_master));
if (!ibdev) {
ret = -ENOMEM;
goto release_transport;
}
ibdev->port = kzalloc_objs(*ibdev->port, num_ports);
if (!ibdev->port) {
ret = -ENOMEM;
goto fail_port;
}
ibdev->is_rep = true;
vport_index = rep->vport_index;
ibdev->port[vport_index].rep = rep;
ibdev->mdev = lag_master;
ibdev->num_ports = num_ports;
ibdev->ib_dev.phys_port_cnt = num_ports;
ret = ib_device_set_netdev(&ibdev->ib_dev,
mlx5_ib_get_rep_netdev(lag_master->priv.eswitch,
rep->vport),
vport_index + 1);
if (ret)
goto fail_add;
ret = __mlx5_ib_add(ibdev, profile);
if (ret)
goto fail_add;
rep->rep_data[REP_IB].priv = ibdev;
if (mlx5_lag_is_shared_fdb(lag_master))
mlx5_ib_register_peer_vport_reps(lag_master);
return 0;
fail_add:
kfree(ibdev->port);
fail_port:
ib_dealloc_device(&ibdev->ib_dev);
release_transport:
if (mlx5_lag_is_shared_fdb(lag_master))
mlx5_ib_release_transport(lag_master);
return ret;
}
static void *mlx5_ib_rep_to_dev(struct mlx5_eswitch_rep *rep)
{
return rep->rep_data[REP_IB].priv;
}
static void
mlx5_ib_vport_rep_unload(struct mlx5_eswitch_rep *rep)
{
struct mlx5_core_dev *mdev = mlx5_eswitch_get_core_dev(rep->esw);
struct mlx5_ib_dev *dev = mlx5_ib_rep_to_dev(rep);
int vport_index = rep->vport_index;
struct mlx5_ib_port *port;
int i;
if (WARN_ON(!mdev))
return;
if (!dev)
return;
if (mlx5_lag_is_shared_fdb(mdev) &&
!mlx5_lag_is_master(mdev)) {
if (rep->vport == MLX5_VPORT_UPLINK && !mlx5_lag_is_mpesw(mdev))
return;
for (i = 0; i < dev->num_ports; i++) {
if (dev->port[i].rep == rep)
break;
}
if (WARN_ON(i == dev->num_ports))
return;
vport_index = i;
}
mlx5_lag_demux_rule_del(mdev, vport_index);
port = &dev->port[vport_index];
ib_device_set_netdev(&dev->ib_dev, NULL, vport_index + 1);
rep->rep_data[REP_IB].priv = NULL;
port->rep = NULL;
if (rep->vport == MLX5_VPORT_UPLINK) {
if (mlx5_lag_is_shared_fdb(mdev) && !mlx5_lag_is_master(mdev))
return;
if (mlx5_lag_is_shared_fdb(mdev)) {
struct mlx5_core_dev *peer_mdev;
struct mlx5_eswitch *esw;
/* Called while the master E-Switch reps_lock is held. */
mlx5_lag_for_each_peer_mdev(mdev, peer_mdev, i) {
esw = peer_mdev->priv.eswitch;
mlx5_eswitch_unregister_vport_reps_nested(esw, REP_IB);
}
mlx5_ib_release_transport(mdev);
}
__mlx5_ib_remove(dev, dev->profile, MLX5_IB_STAGE_MAX);
}
}
static const struct mlx5_eswitch_rep_ops rep_ops = {
.load = mlx5_ib_vport_rep_load,
.unload = mlx5_ib_vport_rep_unload,
.get_proto_dev = mlx5_ib_rep_to_dev,
};
static void mlx5_ib_register_peer_vport_reps(struct mlx5_core_dev *mdev)
{
struct mlx5_core_dev *peer_mdev;
struct mlx5_eswitch *esw;
int i;
/* Called while the master E-Switch reps_lock is held. */
mlx5_lag_for_each_peer_mdev(mdev, peer_mdev, i) {
esw = peer_mdev->priv.eswitch;
mlx5_eswitch_register_vport_reps_nested(esw, &rep_ops, REP_IB);
}
}
struct net_device *mlx5_ib_get_rep_netdev(struct mlx5_eswitch *esw,
u16 vport_num)
{
return mlx5_eswitch_get_proto_dev(esw, vport_num, REP_ETH);
}
struct mlx5_flow_handle *create_flow_rule_vport_sq(struct mlx5_ib_dev *dev,
struct mlx5_ib_sq *sq,
u32 port)
{
struct mlx5_eswitch *esw = dev->mdev->priv.eswitch;
struct mlx5_eswitch_rep *rep;
if (!dev->is_rep || !port)
return NULL;
if (!dev->port[port - 1].rep)
return ERR_PTR(-EINVAL);
rep = dev->port[port - 1].rep;
return mlx5_eswitch_add_send_to_vport_rule(esw, esw, rep, sq->base.mqp.qpn);
}
static int mlx5r_rep_probe(struct auxiliary_device *adev,
const struct auxiliary_device_id *id)
{
struct mlx5_adev *idev = container_of(adev, struct mlx5_adev, adev);
struct mlx5_core_dev *mdev = idev->mdev;
struct mlx5_eswitch *esw;
esw = mdev->priv.eswitch;
mlx5_eswitch_register_vport_reps(esw, &rep_ops, REP_IB);
return 0;
}
static void mlx5r_rep_remove(struct auxiliary_device *adev)
{
struct mlx5_adev *idev = container_of(adev, struct mlx5_adev, adev);
struct mlx5_core_dev *mdev = idev->mdev;
struct mlx5_eswitch *esw;
esw = mdev->priv.eswitch;
mlx5_eswitch_unregister_vport_reps(esw, REP_IB);
}
static const struct auxiliary_device_id mlx5r_rep_id_table[] = {
{ .name = MLX5_ADEV_NAME ".rdma-rep", },
{},
};
MODULE_DEVICE_TABLE(auxiliary, mlx5r_rep_id_table);
static struct auxiliary_driver mlx5r_rep_driver = {
.name = "rep",
.probe = mlx5r_rep_probe,
.remove = mlx5r_rep_remove,
.id_table = mlx5r_rep_id_table,
};
int mlx5r_rep_init(void)
{
return auxiliary_driver_register(&mlx5r_rep_driver);
}
void mlx5r_rep_cleanup(void)
{
auxiliary_driver_unregister(&mlx5r_rep_driver);
}
@@ -0,0 +1,40 @@
/* SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB */
/*
* Copyright (c) 2018 Mellanox Technologies. All rights reserved.
*/
#ifndef __MLX5_IB_REP_H__
#define __MLX5_IB_REP_H__
#include <linux/mlx5/eswitch.h>
#include "mlx5_ib.h"
extern const struct mlx5_ib_profile raw_eth_profile;
#ifdef CONFIG_MLX5_ESWITCH
int mlx5r_rep_init(void);
void mlx5r_rep_cleanup(void);
struct mlx5_flow_handle *create_flow_rule_vport_sq(struct mlx5_ib_dev *dev,
struct mlx5_ib_sq *sq,
u32 port);
struct net_device *mlx5_ib_get_rep_netdev(struct mlx5_eswitch *esw,
u16 vport_num);
#else /* CONFIG_MLX5_ESWITCH */
static inline int mlx5r_rep_init(void) { return 0; }
static inline void mlx5r_rep_cleanup(void) {}
static inline
struct mlx5_flow_handle *create_flow_rule_vport_sq(struct mlx5_ib_dev *dev,
struct mlx5_ib_sq *sq,
u32 port)
{
return NULL;
}
static inline
struct net_device *mlx5_ib_get_rep_netdev(struct mlx5_eswitch *esw,
u16 vport_num)
{
return NULL;
}
#endif
#endif /* __MLX5_IB_REP_H__ */
@@ -0,0 +1,224 @@
/*
* Copyright (c) 2016, Mellanox Technologies. 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 <linux/mlx5/vport.h>
#include "mlx5_ib.h"
static inline u32 mlx_to_net_policy(enum port_state_policy mlx_policy)
{
switch (mlx_policy) {
case MLX5_POLICY_DOWN:
return IFLA_VF_LINK_STATE_DISABLE;
case MLX5_POLICY_UP:
return IFLA_VF_LINK_STATE_ENABLE;
case MLX5_POLICY_FOLLOW:
return IFLA_VF_LINK_STATE_AUTO;
default:
return __IFLA_VF_LINK_STATE_MAX;
}
}
int mlx5_ib_get_vf_config(struct ib_device *device, int vf, u32 port,
struct ifla_vf_info *info)
{
struct mlx5_ib_dev *dev = to_mdev(device);
struct mlx5_core_dev *mdev = dev->mdev;
struct mlx5_hca_vport_context *rep;
int err;
rep = kzalloc_obj(*rep);
if (!rep)
return -ENOMEM;
err = mlx5_query_hca_vport_context(mdev, 1, 1, vf + 1, rep);
if (err) {
mlx5_ib_warn(dev, "failed to query port policy for vf %d (%d)\n",
vf, err);
goto free;
}
memset(info, 0, sizeof(*info));
info->linkstate = mlx_to_net_policy(rep->policy);
if (info->linkstate == __IFLA_VF_LINK_STATE_MAX)
err = -EINVAL;
free:
kfree(rep);
return err;
}
static inline enum port_state_policy net_to_mlx_policy(int policy)
{
switch (policy) {
case IFLA_VF_LINK_STATE_DISABLE:
return MLX5_POLICY_DOWN;
case IFLA_VF_LINK_STATE_ENABLE:
return MLX5_POLICY_UP;
case IFLA_VF_LINK_STATE_AUTO:
return MLX5_POLICY_FOLLOW;
default:
return MLX5_POLICY_INVALID;
}
}
int mlx5_ib_set_vf_link_state(struct ib_device *device, int vf,
u32 port, int state)
{
struct mlx5_ib_dev *dev = to_mdev(device);
struct mlx5_core_dev *mdev = dev->mdev;
struct mlx5_hca_vport_context *in;
struct mlx5_vf_context *vfs_ctx = mdev->priv.sriov.vfs_ctx;
int err;
in = kzalloc_obj(*in);
if (!in)
return -ENOMEM;
in->policy = net_to_mlx_policy(state);
if (in->policy == MLX5_POLICY_INVALID) {
err = -EINVAL;
goto out;
}
in->field_select = MLX5_HCA_VPORT_SEL_STATE_POLICY;
err = mlx5_core_modify_hca_vport_context(mdev, 1, 1, vf + 1, in);
if (!err)
vfs_ctx[vf].policy = in->policy;
out:
kfree(in);
return err;
}
int mlx5_ib_get_vf_stats(struct ib_device *device, int vf,
u32 port, struct ifla_vf_stats *stats)
{
int out_sz = MLX5_ST_SZ_BYTES(query_vport_counter_out);
struct mlx5_core_dev *mdev;
struct mlx5_ib_dev *dev;
void *out;
int err;
dev = to_mdev(device);
mdev = dev->mdev;
out = kzalloc(out_sz, GFP_KERNEL);
if (!out)
return -ENOMEM;
err = mlx5_core_query_vport_counter(mdev, true, vf, port, out);
if (err)
goto ex;
stats->rx_packets = MLX5_GET64_PR(query_vport_counter_out, out, received_ib_unicast.packets);
stats->tx_packets = MLX5_GET64_PR(query_vport_counter_out, out, transmitted_ib_unicast.packets);
stats->rx_bytes = MLX5_GET64_PR(query_vport_counter_out, out, received_ib_unicast.octets);
stats->tx_bytes = MLX5_GET64_PR(query_vport_counter_out, out, transmitted_ib_unicast.octets);
stats->multicast = MLX5_GET64_PR(query_vport_counter_out, out, received_ib_multicast.packets);
ex:
kfree(out);
return err;
}
static int set_vf_node_guid(struct ib_device *device, int vf, u32 port,
u64 guid)
{
struct mlx5_ib_dev *dev = to_mdev(device);
struct mlx5_core_dev *mdev = dev->mdev;
struct mlx5_hca_vport_context *in;
struct mlx5_vf_context *vfs_ctx = mdev->priv.sriov.vfs_ctx;
int err;
in = kzalloc_obj(*in);
if (!in)
return -ENOMEM;
in->field_select = MLX5_HCA_VPORT_SEL_NODE_GUID;
in->node_guid = guid;
err = mlx5_core_modify_hca_vport_context(mdev, 1, 1, vf + 1, in);
if (!err) {
vfs_ctx[vf].node_guid = guid;
vfs_ctx[vf].node_guid_valid = 1;
}
kfree(in);
return err;
}
static int set_vf_port_guid(struct ib_device *device, int vf, u32 port,
u64 guid)
{
struct mlx5_ib_dev *dev = to_mdev(device);
struct mlx5_core_dev *mdev = dev->mdev;
struct mlx5_hca_vport_context *in;
struct mlx5_vf_context *vfs_ctx = mdev->priv.sriov.vfs_ctx;
int err;
in = kzalloc_obj(*in);
if (!in)
return -ENOMEM;
in->field_select = MLX5_HCA_VPORT_SEL_PORT_GUID;
in->port_guid = guid;
err = mlx5_core_modify_hca_vport_context(mdev, 1, 1, vf + 1, in);
if (!err) {
vfs_ctx[vf].port_guid = guid;
vfs_ctx[vf].port_guid_valid = 1;
}
kfree(in);
return err;
}
int mlx5_ib_set_vf_guid(struct ib_device *device, int vf, u32 port,
u64 guid, int type)
{
if (type == IFLA_VF_IB_NODE_GUID)
return set_vf_node_guid(device, vf, port, guid);
else if (type == IFLA_VF_IB_PORT_GUID)
return set_vf_port_guid(device, vf, port, guid);
return -EINVAL;
}
int mlx5_ib_get_vf_guid(struct ib_device *device, int vf, u32 port,
struct ifla_vf_guid *node_guid,
struct ifla_vf_guid *port_guid)
{
struct mlx5_ib_dev *dev = to_mdev(device);
struct mlx5_core_dev *mdev = dev->mdev;
struct mlx5_vf_context *vfs_ctx = mdev->priv.sriov.vfs_ctx;
node_guid->guid =
vfs_ctx[vf].node_guid_valid ? vfs_ctx[vf].node_guid : 0;
port_guid->guid =
vfs_ctx[vf].port_guid_valid ? vfs_ctx[vf].port_guid : 0;
return 0;
}
@@ -0,0 +1,363 @@
// SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB
/* Copyright (c) 2022, NVIDIA CORPORATION & AFFILIATES. */
#include "macsec.h"
#include <linux/mlx5/macsec.h>
struct mlx5_reserved_gids {
int macsec_index;
const struct ib_gid_attr *physical_gid;
};
struct mlx5_roce_gids {
struct list_head roce_gid_list_entry;
u16 gid_idx;
union {
struct sockaddr_in sockaddr_in;
struct sockaddr_in6 sockaddr_in6;
} addr;
};
struct mlx5_macsec_device {
struct list_head macsec_devices_list_entry;
void *macdev;
struct list_head macsec_roce_gids;
struct list_head tx_rules_list;
struct list_head rx_rules_list;
};
static void cleanup_macsec_device(struct mlx5_macsec_device *macsec_device)
{
if (!list_empty(&macsec_device->tx_rules_list) ||
!list_empty(&macsec_device->rx_rules_list) ||
!list_empty(&macsec_device->macsec_roce_gids))
return;
list_del(&macsec_device->macsec_devices_list_entry);
kfree(macsec_device);
}
static struct mlx5_macsec_device *get_macsec_device(void *macdev,
struct list_head *macsec_devices_list)
{
struct mlx5_macsec_device *iter, *macsec_device = NULL;
list_for_each_entry(iter, macsec_devices_list, macsec_devices_list_entry) {
if (iter->macdev == macdev) {
macsec_device = iter;
break;
}
}
if (macsec_device)
return macsec_device;
macsec_device = kzalloc_obj(*macsec_device);
if (!macsec_device)
return NULL;
macsec_device->macdev = macdev;
INIT_LIST_HEAD(&macsec_device->tx_rules_list);
INIT_LIST_HEAD(&macsec_device->rx_rules_list);
INIT_LIST_HEAD(&macsec_device->macsec_roce_gids);
list_add(&macsec_device->macsec_devices_list_entry, macsec_devices_list);
return macsec_device;
}
static void mlx5_macsec_del_roce_gid(struct mlx5_macsec_device *macsec_device, u16 gid_idx)
{
struct mlx5_roce_gids *current_gid, *next_gid;
list_for_each_entry_safe(current_gid, next_gid, &macsec_device->macsec_roce_gids,
roce_gid_list_entry)
if (current_gid->gid_idx == gid_idx) {
list_del(&current_gid->roce_gid_list_entry);
kfree(current_gid);
}
}
static void mlx5_macsec_save_roce_gid(struct mlx5_macsec_device *macsec_device,
const struct sockaddr *addr, u16 gid_idx)
{
struct mlx5_roce_gids *roce_gids;
roce_gids = kzalloc_obj(*roce_gids);
if (!roce_gids)
return;
roce_gids->gid_idx = gid_idx;
if (addr->sa_family == AF_INET)
memcpy(&roce_gids->addr.sockaddr_in, addr, sizeof(roce_gids->addr.sockaddr_in));
else
memcpy(&roce_gids->addr.sockaddr_in6, addr, sizeof(roce_gids->addr.sockaddr_in6));
list_add_tail(&roce_gids->roce_gid_list_entry, &macsec_device->macsec_roce_gids);
}
static void handle_macsec_gids(struct list_head *macsec_devices_list,
struct mlx5_macsec_event_data *data)
{
struct mlx5_macsec_device *macsec_device;
struct mlx5_roce_gids *gid;
macsec_device = get_macsec_device(data->macdev, macsec_devices_list);
if (!macsec_device)
return;
list_for_each_entry(gid, &macsec_device->macsec_roce_gids, roce_gid_list_entry) {
mlx5_macsec_add_roce_sa_rules(data->fs_id, (struct sockaddr *)&gid->addr,
gid->gid_idx, &macsec_device->tx_rules_list,
&macsec_device->rx_rules_list, data->macsec_fs,
data->is_tx);
}
}
static void del_sa_roce_rule(struct list_head *macsec_devices_list,
struct mlx5_macsec_event_data *data)
{
struct mlx5_macsec_device *macsec_device;
macsec_device = get_macsec_device(data->macdev, macsec_devices_list);
WARN_ON(!macsec_device);
mlx5_macsec_del_roce_sa_rules(data->fs_id, data->macsec_fs,
&macsec_device->tx_rules_list,
&macsec_device->rx_rules_list, data->is_tx);
}
static int macsec_event(struct notifier_block *nb, unsigned long event, void *data)
{
struct mlx5_macsec *macsec = container_of(nb, struct mlx5_macsec, blocking_events_nb);
mutex_lock(&macsec->lock);
switch (event) {
case MLX5_DRIVER_EVENT_MACSEC_SA_ADDED:
handle_macsec_gids(&macsec->macsec_devices_list, data);
break;
case MLX5_DRIVER_EVENT_MACSEC_SA_DELETED:
del_sa_roce_rule(&macsec->macsec_devices_list, data);
break;
default:
mutex_unlock(&macsec->lock);
return NOTIFY_DONE;
}
mutex_unlock(&macsec->lock);
return NOTIFY_OK;
}
void mlx5r_macsec_event_register(struct mlx5_ib_dev *dev)
{
if (!mlx5_is_macsec_roce_supported(dev->mdev)) {
mlx5_ib_dbg(dev, "RoCE MACsec not supported due to capabilities\n");
return;
}
dev->macsec.blocking_events_nb.notifier_call = macsec_event;
blocking_notifier_chain_register(&dev->mdev->macsec_nh,
&dev->macsec.blocking_events_nb);
}
void mlx5r_macsec_event_unregister(struct mlx5_ib_dev *dev)
{
if (!mlx5_is_macsec_roce_supported(dev->mdev)) {
mlx5_ib_dbg(dev, "RoCE MACsec not supported due to capabilities\n");
return;
}
blocking_notifier_chain_unregister(&dev->mdev->macsec_nh,
&dev->macsec.blocking_events_nb);
}
int mlx5r_macsec_init_gids_and_devlist(struct mlx5_ib_dev *dev)
{
int i, j, max_gids;
if (!mlx5_is_macsec_roce_supported(dev->mdev)) {
mlx5_ib_dbg(dev, "RoCE MACsec not supported due to capabilities\n");
return 0;
}
max_gids = MLX5_CAP_ROCE(dev->mdev, roce_address_table_size);
for (i = 0; i < dev->num_ports; i++) {
dev->port[i].reserved_gids = kzalloc_objs(*dev->port[i].reserved_gids,
max_gids);
if (!dev->port[i].reserved_gids)
goto err;
for (j = 0; j < max_gids; j++)
dev->port[i].reserved_gids[j].macsec_index = -1;
}
INIT_LIST_HEAD(&dev->macsec.macsec_devices_list);
mutex_init(&dev->macsec.lock);
return 0;
err:
while (i >= 0) {
kfree(dev->port[i].reserved_gids);
i--;
}
return -ENOMEM;
}
void mlx5r_macsec_dealloc_gids(struct mlx5_ib_dev *dev)
{
int i;
if (!mlx5_is_macsec_roce_supported(dev->mdev))
mlx5_ib_dbg(dev, "RoCE MACsec not supported due to capabilities\n");
for (i = 0; i < dev->num_ports; i++)
kfree(dev->port[i].reserved_gids);
mutex_destroy(&dev->macsec.lock);
}
int mlx5r_add_gid_macsec_operations(const struct ib_gid_attr *attr)
{
struct mlx5_ib_dev *dev = to_mdev(attr->device);
struct mlx5_macsec_device *macsec_device;
const struct ib_gid_attr *physical_gid;
struct mlx5_reserved_gids *mgids;
struct net_device *ndev;
int ret = 0;
union {
struct sockaddr_in sockaddr_in;
struct sockaddr_in6 sockaddr_in6;
} addr;
if (attr->gid_type != IB_GID_TYPE_ROCE_UDP_ENCAP)
return 0;
if (!mlx5_is_macsec_roce_supported(dev->mdev)) {
mlx5_ib_dbg(dev, "RoCE MACsec not supported due to capabilities\n");
return 0;
}
rcu_read_lock();
ndev = rcu_dereference(attr->ndev);
if (!ndev) {
rcu_read_unlock();
return -ENODEV;
}
if (!netif_is_macsec(ndev) || !macsec_netdev_is_offloaded(ndev)) {
rcu_read_unlock();
return 0;
}
dev_hold(ndev);
rcu_read_unlock();
mutex_lock(&dev->macsec.lock);
macsec_device = get_macsec_device(ndev, &dev->macsec.macsec_devices_list);
if (!macsec_device) {
ret = -ENOMEM;
goto dev_err;
}
physical_gid = rdma_find_gid(attr->device, &attr->gid,
attr->gid_type, NULL);
if (!IS_ERR(physical_gid)) {
ret = set_roce_addr(to_mdev(physical_gid->device),
physical_gid->port_num,
physical_gid->index, NULL,
physical_gid);
if (ret)
goto gid_err;
mgids = &dev->port[attr->port_num - 1].reserved_gids[physical_gid->index];
mgids->macsec_index = attr->index;
mgids->physical_gid = physical_gid;
}
/* Proceed with adding steering rules, regardless if there was gid ambiguity or not.*/
rdma_gid2ip((struct sockaddr *)&addr, &attr->gid);
ret = mlx5_macsec_add_roce_rule(ndev, (struct sockaddr *)&addr, attr->index,
&macsec_device->tx_rules_list,
&macsec_device->rx_rules_list, dev->mdev->macsec_fs);
if (ret && !IS_ERR(physical_gid))
goto rule_err;
mlx5_macsec_save_roce_gid(macsec_device, (struct sockaddr *)&addr, attr->index);
dev_put(ndev);
mutex_unlock(&dev->macsec.lock);
return ret;
rule_err:
set_roce_addr(to_mdev(physical_gid->device), physical_gid->port_num,
physical_gid->index, &physical_gid->gid, physical_gid);
mgids->macsec_index = -1;
gid_err:
rdma_put_gid_attr(physical_gid);
cleanup_macsec_device(macsec_device);
dev_err:
dev_put(ndev);
mutex_unlock(&dev->macsec.lock);
return ret;
}
void mlx5r_del_gid_macsec_operations(const struct ib_gid_attr *attr)
{
struct mlx5_ib_dev *dev = to_mdev(attr->device);
struct mlx5_macsec_device *macsec_device;
struct mlx5_reserved_gids *mgids;
struct net_device *ndev;
int i, max_gids;
if (attr->gid_type != IB_GID_TYPE_ROCE_UDP_ENCAP)
return;
if (!mlx5_is_macsec_roce_supported(dev->mdev)) {
mlx5_ib_dbg(dev, "RoCE MACsec not supported due to capabilities\n");
return;
}
mgids = &dev->port[attr->port_num - 1].reserved_gids[attr->index];
if (mgids->macsec_index != -1) { /* Checking if physical gid has ambiguous IP */
rdma_put_gid_attr(mgids->physical_gid);
mgids->macsec_index = -1;
return;
}
rcu_read_lock();
ndev = rcu_dereference(attr->ndev);
if (!ndev) {
rcu_read_unlock();
return;
}
if (!netif_is_macsec(ndev) || !macsec_netdev_is_offloaded(ndev)) {
rcu_read_unlock();
return;
}
dev_hold(ndev);
rcu_read_unlock();
mutex_lock(&dev->macsec.lock);
max_gids = MLX5_CAP_ROCE(dev->mdev, roce_address_table_size);
for (i = 0; i < max_gids; i++) { /* Checking if macsec gid has ambiguous IP */
mgids = &dev->port[attr->port_num - 1].reserved_gids[i];
if (mgids->macsec_index == attr->index) {
const struct ib_gid_attr *physical_gid = mgids->physical_gid;
set_roce_addr(to_mdev(physical_gid->device),
physical_gid->port_num,
physical_gid->index,
&physical_gid->gid, physical_gid);
rdma_put_gid_attr(physical_gid);
mgids->macsec_index = -1;
break;
}
}
macsec_device = get_macsec_device(ndev, &dev->macsec.macsec_devices_list);
mlx5_macsec_del_roce_rule(attr->index, dev->mdev->macsec_fs,
&macsec_device->tx_rules_list, &macsec_device->rx_rules_list);
mlx5_macsec_del_roce_gid(macsec_device, attr->index);
cleanup_macsec_device(macsec_device);
dev_put(ndev);
mutex_unlock(&dev->macsec.lock);
}
@@ -0,0 +1,29 @@
/* SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB */
/* Copyright (c) 2022, NVIDIA CORPORATION & AFFILIATES. */
#ifndef __MLX5_MACSEC_H__
#define __MLX5_MACSEC_H__
#include <net/macsec.h>
#include <rdma/ib_cache.h>
#include <rdma/ib_addr.h>
#include "mlx5_ib.h"
#ifdef CONFIG_MLX5_MACSEC
struct mlx5_reserved_gids;
int mlx5r_add_gid_macsec_operations(const struct ib_gid_attr *attr);
void mlx5r_del_gid_macsec_operations(const struct ib_gid_attr *attr);
int mlx5r_macsec_init_gids_and_devlist(struct mlx5_ib_dev *dev);
void mlx5r_macsec_dealloc_gids(struct mlx5_ib_dev *dev);
void mlx5r_macsec_event_register(struct mlx5_ib_dev *dev);
void mlx5r_macsec_event_unregister(struct mlx5_ib_dev *dev);
#else
static inline int mlx5r_add_gid_macsec_operations(const struct ib_gid_attr *attr) { return 0; }
static inline void mlx5r_del_gid_macsec_operations(const struct ib_gid_attr *attr) {}
static inline int mlx5r_macsec_init_gids_and_devlist(struct mlx5_ib_dev *dev) { return 0; }
static inline void mlx5r_macsec_dealloc_gids(struct mlx5_ib_dev *dev) {}
static inline void mlx5r_macsec_event_register(struct mlx5_ib_dev *dev) {}
static inline void mlx5r_macsec_event_unregister(struct mlx5_ib_dev *dev) {}
#endif
#endif /* __MLX5_MACSEC_H__ */
@@ -0,0 +1,713 @@
/*
* Copyright (c) 2013-2015, Mellanox Technologies. 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 <linux/mlx5/vport.h>
#include <rdma/ib_mad.h>
#include <rdma/ib_smi.h>
#include <rdma/ib_pma.h>
#include "mlx5_ib.h"
#include "cmd.h"
enum {
MLX5_IB_VENDOR_CLASS1 = 0x9,
MLX5_IB_VENDOR_CLASS2 = 0xa
};
static bool can_do_mad_ifc(struct mlx5_ib_dev *dev, u32 port_num,
struct ib_mad *in_mad)
{
if (in_mad->mad_hdr.mgmt_class != IB_MGMT_CLASS_SUBN_LID_ROUTED &&
in_mad->mad_hdr.mgmt_class != IB_MGMT_CLASS_SUBN_DIRECTED_ROUTE)
return true;
return dev->port_caps[port_num - 1].has_smi;
}
static int mlx5_MAD_IFC(struct mlx5_ib_dev *dev, int ignore_mkey,
int ignore_bkey, u32 port, const struct ib_wc *in_wc,
const struct ib_grh *in_grh, const void *in_mad,
void *response_mad)
{
u8 op_modifier = 0;
if (!can_do_mad_ifc(dev, port, (struct ib_mad *)in_mad))
return -EPERM;
/* Key check traps can't be generated unless we have in_wc to
* tell us where to send the trap.
*/
if (ignore_mkey || !in_wc)
op_modifier |= 0x1;
if (ignore_bkey || !in_wc)
op_modifier |= 0x2;
return mlx5_cmd_mad_ifc(dev, in_mad, response_mad, op_modifier,
port);
}
static void pma_cnt_ext_assign(struct ib_pma_portcounters_ext *pma_cnt_ext,
void *out)
{
#define MLX5_SUM_CNT(p, cntr1, cntr2) \
(MLX5_GET64(query_vport_counter_out, p, cntr1) + \
MLX5_GET64(query_vport_counter_out, p, cntr2))
pma_cnt_ext->port_xmit_data =
cpu_to_be64(MLX5_SUM_CNT(out, transmitted_ib_unicast.octets,
transmitted_ib_multicast.octets) >> 2);
pma_cnt_ext->port_rcv_data =
cpu_to_be64(MLX5_SUM_CNT(out, received_ib_unicast.octets,
received_ib_multicast.octets) >> 2);
pma_cnt_ext->port_xmit_packets =
cpu_to_be64(MLX5_SUM_CNT(out, transmitted_ib_unicast.packets,
transmitted_ib_multicast.packets));
pma_cnt_ext->port_rcv_packets =
cpu_to_be64(MLX5_SUM_CNT(out, received_ib_unicast.packets,
received_ib_multicast.packets));
pma_cnt_ext->port_unicast_xmit_packets =
MLX5_GET64_BE(query_vport_counter_out,
out, transmitted_ib_unicast.packets);
pma_cnt_ext->port_unicast_rcv_packets =
MLX5_GET64_BE(query_vport_counter_out,
out, received_ib_unicast.packets);
pma_cnt_ext->port_multicast_xmit_packets =
MLX5_GET64_BE(query_vport_counter_out,
out, transmitted_ib_multicast.packets);
pma_cnt_ext->port_multicast_rcv_packets =
MLX5_GET64_BE(query_vport_counter_out,
out, received_ib_multicast.packets);
}
static void pma_cnt_assign(struct ib_pma_portcounters *pma_cnt,
void *out)
{
/* Traffic counters will be reported in
* their 64bit form via ib_pma_portcounters_ext by default.
*/
void *out_pma = MLX5_ADDR_OF(ppcnt_reg, out,
counter_set);
#define MLX5_ASSIGN_PMA_CNTR(counter_var, counter_name) { \
counter_var = MLX5_GET_BE(typeof(counter_var), \
ib_port_cntrs_grp_data_layout, \
out_pma, counter_name); \
}
MLX5_ASSIGN_PMA_CNTR(pma_cnt->symbol_error_counter,
symbol_error_counter);
MLX5_ASSIGN_PMA_CNTR(pma_cnt->link_error_recovery_counter,
link_error_recovery_counter);
MLX5_ASSIGN_PMA_CNTR(pma_cnt->link_downed_counter,
link_downed_counter);
MLX5_ASSIGN_PMA_CNTR(pma_cnt->port_rcv_errors,
port_rcv_errors);
MLX5_ASSIGN_PMA_CNTR(pma_cnt->port_rcv_remphys_errors,
port_rcv_remote_physical_errors);
MLX5_ASSIGN_PMA_CNTR(pma_cnt->port_rcv_switch_relay_errors,
port_rcv_switch_relay_errors);
MLX5_ASSIGN_PMA_CNTR(pma_cnt->port_xmit_discards,
port_xmit_discards);
MLX5_ASSIGN_PMA_CNTR(pma_cnt->port_xmit_constraint_errors,
port_xmit_constraint_errors);
MLX5_ASSIGN_PMA_CNTR(pma_cnt->port_xmit_wait,
port_xmit_wait);
MLX5_ASSIGN_PMA_CNTR(pma_cnt->port_rcv_constraint_errors,
port_rcv_constraint_errors);
MLX5_ASSIGN_PMA_CNTR(pma_cnt->link_overrun_errors,
link_overrun_errors);
MLX5_ASSIGN_PMA_CNTR(pma_cnt->vl15_dropped,
vl_15_dropped);
}
static void pma_cnt_ext_assign_ppcnt(struct ib_pma_portcounters_ext *cnt_ext,
void *out)
{
void *out_pma = MLX5_ADDR_OF(ppcnt_reg, out,
counter_set);
#define MLX5_GET_EXT_CNTR(counter_name) \
MLX5_GET64(ib_ext_port_cntrs_grp_data_layout, \
out_pma, counter_name##_high)
cnt_ext->port_xmit_data =
cpu_to_be64(MLX5_GET_EXT_CNTR(port_xmit_data) >> 2);
cnt_ext->port_rcv_data =
cpu_to_be64(MLX5_GET_EXT_CNTR(port_rcv_data) >> 2);
cnt_ext->port_xmit_packets =
cpu_to_be64(MLX5_GET_EXT_CNTR(port_xmit_pkts));
cnt_ext->port_rcv_packets =
cpu_to_be64(MLX5_GET_EXT_CNTR(port_rcv_pkts));
cnt_ext->port_unicast_xmit_packets =
cpu_to_be64(MLX5_GET_EXT_CNTR(port_unicast_xmit_pkts));
cnt_ext->port_unicast_rcv_packets =
cpu_to_be64(MLX5_GET_EXT_CNTR(port_unicast_rcv_pkts));
cnt_ext->port_multicast_xmit_packets =
cpu_to_be64(MLX5_GET_EXT_CNTR(port_multicast_xmit_pkts));
cnt_ext->port_multicast_rcv_packets =
cpu_to_be64(MLX5_GET_EXT_CNTR(port_multicast_rcv_pkts));
}
static int query_ib_ppcnt(struct mlx5_core_dev *dev, u8 port_num, u8 plane_num,
void *out, size_t sz, bool ext)
{
u32 *in;
int err;
in = kvzalloc(sz, GFP_KERNEL);
if (!in) {
err = -ENOMEM;
return err;
}
MLX5_SET(ppcnt_reg, in, local_port, port_num);
MLX5_SET(ppcnt_reg, in, plane_ind, plane_num);
if (ext)
MLX5_SET(ppcnt_reg, in, grp,
MLX5_INFINIBAND_EXTENDED_PORT_COUNTERS_GROUP);
else
MLX5_SET(ppcnt_reg, in, grp,
MLX5_INFINIBAND_PORT_COUNTERS_GROUP);
err = mlx5_core_access_reg(dev, in, sz, out,
sz, MLX5_REG_PPCNT, 0, 0);
kvfree(in);
return err;
}
static int process_pma_cmd(struct mlx5_ib_dev *dev, u32 port_num,
const struct ib_mad *in_mad, struct ib_mad *out_mad)
{
struct mlx5_core_dev *mdev;
bool native_port = true;
u32 mdev_port_num;
void *out_cnt;
int err;
mdev = mlx5_ib_get_native_port_mdev(dev, port_num, &mdev_port_num);
if (!mdev) {
/* Fail to get the native port, likely due to 2nd port is still
* unaffiliated. In such case default to 1st port and attached
* PF device.
*/
native_port = false;
mdev = dev->mdev;
mdev_port_num = 1;
}
if (MLX5_CAP_GEN(dev->mdev, num_ports) == 1 &&
!mlx5_core_mp_enabled(mdev) &&
dev->ib_dev.type != RDMA_DEVICE_TYPE_SMI) {
/* set local port to one for Function-Per-Port HCA. */
mdev = dev->mdev;
mdev_port_num = 1;
}
/* Declaring support of extended counters */
if (in_mad->mad_hdr.attr_id == IB_PMA_CLASS_PORT_INFO) {
struct ib_class_port_info cpi = {};
cpi.capability_mask = IB_PMA_CLASS_CAP_EXT_WIDTH;
memcpy((out_mad->data + 40), &cpi, sizeof(cpi));
err = IB_MAD_RESULT_SUCCESS | IB_MAD_RESULT_REPLY;
goto done;
}
if (in_mad->mad_hdr.attr_id == IB_PMA_PORT_COUNTERS_EXT) {
struct ib_pma_portcounters_ext *pma_cnt_ext =
(struct ib_pma_portcounters_ext *)(out_mad->data + 40);
int sz = max(MLX5_ST_SZ_BYTES(query_vport_counter_out),
MLX5_ST_SZ_BYTES(ppcnt_reg));
out_cnt = kvzalloc(sz, GFP_KERNEL);
if (!out_cnt) {
err = IB_MAD_RESULT_FAILURE;
goto done;
}
if (dev->ib_dev.type == RDMA_DEVICE_TYPE_SMI) {
err = query_ib_ppcnt(mdev, mdev_port_num,
port_num, out_cnt, sz, 1);
if (!err)
pma_cnt_ext_assign_ppcnt(pma_cnt_ext, out_cnt);
} else {
err = mlx5_core_query_vport_counter(mdev, 0, 0,
mdev_port_num,
out_cnt);
if (!err)
pma_cnt_ext_assign(pma_cnt_ext, out_cnt);
}
} else {
struct ib_pma_portcounters *pma_cnt =
(struct ib_pma_portcounters *)(out_mad->data + 40);
int sz = MLX5_ST_SZ_BYTES(ppcnt_reg);
out_cnt = kvzalloc(sz, GFP_KERNEL);
if (!out_cnt) {
err = IB_MAD_RESULT_FAILURE;
goto done;
}
if (dev->ib_dev.type == RDMA_DEVICE_TYPE_SMI)
err = query_ib_ppcnt(mdev, mdev_port_num, port_num,
out_cnt, sz, 0);
else
err = query_ib_ppcnt(mdev, mdev_port_num, 0,
out_cnt, sz, 0);
if (!err)
pma_cnt_assign(pma_cnt, out_cnt);
}
kvfree(out_cnt);
err = err ? IB_MAD_RESULT_FAILURE :
IB_MAD_RESULT_SUCCESS | IB_MAD_RESULT_REPLY;
done:
if (native_port)
mlx5_ib_put_native_port_mdev(dev, port_num);
return err;
}
int mlx5_ib_process_mad(struct ib_device *ibdev, int mad_flags, u32 port_num,
const struct ib_wc *in_wc, const struct ib_grh *in_grh,
const struct ib_mad *in, struct ib_mad *out,
size_t *out_mad_size, u16 *out_mad_pkey_index)
{
struct mlx5_ib_dev *dev = to_mdev(ibdev);
u8 mgmt_class = in->mad_hdr.mgmt_class;
u8 method = in->mad_hdr.method;
u16 slid;
int err;
slid = in_wc ? ib_lid_cpu16(in_wc->slid) :
be16_to_cpu(IB_LID_PERMISSIVE);
if (method == IB_MGMT_METHOD_TRAP && !slid)
return IB_MAD_RESULT_SUCCESS | IB_MAD_RESULT_CONSUMED;
switch (mgmt_class) {
case IB_MGMT_CLASS_SUBN_LID_ROUTED:
case IB_MGMT_CLASS_SUBN_DIRECTED_ROUTE: {
if (method != IB_MGMT_METHOD_GET &&
method != IB_MGMT_METHOD_SET &&
method != IB_MGMT_METHOD_TRAP_REPRESS)
return IB_MAD_RESULT_SUCCESS;
/* Don't process SMInfo queries -- the SMA can't handle them.
*/
if (in->mad_hdr.attr_id == IB_SMP_ATTR_SM_INFO)
return IB_MAD_RESULT_SUCCESS;
} break;
case IB_MGMT_CLASS_PERF_MGMT:
if (MLX5_CAP_GEN(dev->mdev, vport_counters) &&
method == IB_MGMT_METHOD_GET)
return process_pma_cmd(dev, port_num, in, out);
fallthrough;
case MLX5_IB_VENDOR_CLASS1:
case MLX5_IB_VENDOR_CLASS2:
case IB_MGMT_CLASS_CONG_MGMT: {
if (method != IB_MGMT_METHOD_GET &&
method != IB_MGMT_METHOD_SET)
return IB_MAD_RESULT_SUCCESS;
} break;
default:
return IB_MAD_RESULT_SUCCESS;
}
err = mlx5_MAD_IFC(to_mdev(ibdev), mad_flags & IB_MAD_IGNORE_MKEY,
mad_flags & IB_MAD_IGNORE_BKEY, port_num, in_wc,
in_grh, in, out);
if (err)
return IB_MAD_RESULT_FAILURE;
/* set return bit in status of directed route responses */
if (mgmt_class == IB_MGMT_CLASS_SUBN_DIRECTED_ROUTE)
out->mad_hdr.status |= cpu_to_be16(1 << 15);
if (method == IB_MGMT_METHOD_TRAP_REPRESS)
/* no response for trap repress */
return IB_MAD_RESULT_SUCCESS | IB_MAD_RESULT_CONSUMED;
return IB_MAD_RESULT_SUCCESS | IB_MAD_RESULT_REPLY;
}
int mlx5_query_ext_port_caps(struct mlx5_ib_dev *dev, unsigned int port)
{
struct ib_smp *in_mad;
struct ib_smp *out_mad;
int err = -ENOMEM;
u16 packet_error;
in_mad = kzalloc_obj(*in_mad);
out_mad = kmalloc_obj(*out_mad);
if (!in_mad || !out_mad)
goto out;
ib_init_query_mad(in_mad);
in_mad->attr_id = MLX5_ATTR_EXTENDED_PORT_INFO;
in_mad->attr_mod = cpu_to_be32(port);
err = mlx5_MAD_IFC(dev, 1, 1, 1, NULL, NULL, in_mad, out_mad);
packet_error = be16_to_cpu(out_mad->status);
dev->port_caps[port - 1].ext_port_cap = (!err && !packet_error) ?
MLX_EXT_PORT_CAP_FLAG_EXTENDED_PORT_INFO : 0;
out:
kfree(in_mad);
kfree(out_mad);
return err;
}
static int mlx5_query_mad_ifc_smp_attr_node_info(struct ib_device *ibdev,
struct ib_smp *out_mad)
{
struct ib_smp *in_mad;
int err;
in_mad = kzalloc_obj(*in_mad);
if (!in_mad)
return -ENOMEM;
ib_init_query_mad(in_mad);
in_mad->attr_id = IB_SMP_ATTR_NODE_INFO;
err = mlx5_MAD_IFC(to_mdev(ibdev), 1, 1, 1, NULL, NULL, in_mad,
out_mad);
kfree(in_mad);
return err;
}
int mlx5_query_mad_ifc_system_image_guid(struct ib_device *ibdev,
__be64 *sys_image_guid)
{
struct ib_smp *out_mad;
int err;
out_mad = kmalloc_obj(*out_mad);
if (!out_mad)
return -ENOMEM;
err = mlx5_query_mad_ifc_smp_attr_node_info(ibdev, out_mad);
if (err)
goto out;
memcpy(sys_image_guid, out_mad->data + 4, 8);
out:
kfree(out_mad);
return err;
}
int mlx5_query_mad_ifc_max_pkeys(struct ib_device *ibdev,
u16 *max_pkeys)
{
struct ib_smp *out_mad;
int err;
out_mad = kmalloc_obj(*out_mad);
if (!out_mad)
return -ENOMEM;
err = mlx5_query_mad_ifc_smp_attr_node_info(ibdev, out_mad);
if (err)
goto out;
*max_pkeys = be16_to_cpup((__be16 *)(out_mad->data + 28));
out:
kfree(out_mad);
return err;
}
int mlx5_query_mad_ifc_vendor_id(struct ib_device *ibdev,
u32 *vendor_id)
{
struct ib_smp *out_mad;
int err;
out_mad = kmalloc_obj(*out_mad);
if (!out_mad)
return -ENOMEM;
err = mlx5_query_mad_ifc_smp_attr_node_info(ibdev, out_mad);
if (err)
goto out;
*vendor_id = be32_to_cpup((__be32 *)(out_mad->data + 36)) & 0xffff;
out:
kfree(out_mad);
return err;
}
int mlx5_query_mad_ifc_node_desc(struct mlx5_ib_dev *dev, char *node_desc)
{
struct ib_smp *in_mad;
struct ib_smp *out_mad;
int err = -ENOMEM;
in_mad = kzalloc_obj(*in_mad);
out_mad = kmalloc_obj(*out_mad);
if (!in_mad || !out_mad)
goto out;
ib_init_query_mad(in_mad);
in_mad->attr_id = IB_SMP_ATTR_NODE_DESC;
err = mlx5_MAD_IFC(dev, 1, 1, 1, NULL, NULL, in_mad, out_mad);
if (err)
goto out;
memcpy(node_desc, out_mad->data, IB_DEVICE_NODE_DESC_MAX);
out:
kfree(in_mad);
kfree(out_mad);
return err;
}
int mlx5_query_mad_ifc_node_guid(struct mlx5_ib_dev *dev, __be64 *node_guid)
{
struct ib_smp *in_mad;
struct ib_smp *out_mad;
int err = -ENOMEM;
in_mad = kzalloc_obj(*in_mad);
out_mad = kmalloc_obj(*out_mad);
if (!in_mad || !out_mad)
goto out;
ib_init_query_mad(in_mad);
in_mad->attr_id = IB_SMP_ATTR_NODE_INFO;
err = mlx5_MAD_IFC(dev, 1, 1, 1, NULL, NULL, in_mad, out_mad);
if (err)
goto out;
memcpy(node_guid, out_mad->data + 12, 8);
out:
kfree(in_mad);
kfree(out_mad);
return err;
}
int mlx5_query_mad_ifc_pkey(struct ib_device *ibdev, u32 port, u16 index,
u16 *pkey)
{
struct ib_smp *in_mad;
struct ib_smp *out_mad;
int err = -ENOMEM;
in_mad = kzalloc_obj(*in_mad);
out_mad = kmalloc_obj(*out_mad);
if (!in_mad || !out_mad)
goto out;
ib_init_query_mad(in_mad);
in_mad->attr_id = IB_SMP_ATTR_PKEY_TABLE;
in_mad->attr_mod = cpu_to_be32(index / 32);
err = mlx5_MAD_IFC(to_mdev(ibdev), 1, 1, port, NULL, NULL, in_mad,
out_mad);
if (err)
goto out;
*pkey = be16_to_cpu(((__be16 *)out_mad->data)[index % 32]);
out:
kfree(in_mad);
kfree(out_mad);
return err;
}
int mlx5_query_mad_ifc_gids(struct ib_device *ibdev, u32 port, int index,
union ib_gid *gid)
{
struct ib_smp *in_mad;
struct ib_smp *out_mad;
int err = -ENOMEM;
in_mad = kzalloc_obj(*in_mad);
out_mad = kmalloc_obj(*out_mad);
if (!in_mad || !out_mad)
goto out;
ib_init_query_mad(in_mad);
in_mad->attr_id = IB_SMP_ATTR_PORT_INFO;
in_mad->attr_mod = cpu_to_be32(port);
err = mlx5_MAD_IFC(to_mdev(ibdev), 1, 1, port, NULL, NULL, in_mad,
out_mad);
if (err)
goto out;
memcpy(gid->raw, out_mad->data + 8, 8);
ib_init_query_mad(in_mad);
in_mad->attr_id = IB_SMP_ATTR_GUID_INFO;
in_mad->attr_mod = cpu_to_be32(index / 8);
err = mlx5_MAD_IFC(to_mdev(ibdev), 1, 1, port, NULL, NULL, in_mad,
out_mad);
if (err)
goto out;
memcpy(gid->raw + 8, out_mad->data + (index % 8) * 8, 8);
out:
kfree(in_mad);
kfree(out_mad);
return err;
}
int mlx5_query_mad_ifc_port(struct ib_device *ibdev, u32 port,
struct ib_port_attr *props)
{
struct mlx5_ib_dev *dev = to_mdev(ibdev);
struct mlx5_core_dev *mdev = dev->mdev;
struct ib_smp *in_mad;
struct ib_smp *out_mad;
int ext_active_speed;
int err = -ENOMEM;
in_mad = kzalloc_obj(*in_mad);
out_mad = kmalloc_obj(*out_mad);
if (!in_mad || !out_mad)
goto out;
/* props being zeroed by the caller, avoid zeroing it here */
ib_init_query_mad(in_mad);
in_mad->attr_id = IB_SMP_ATTR_PORT_INFO;
in_mad->attr_mod = cpu_to_be32(port);
err = mlx5_MAD_IFC(dev, 1, 1, port, NULL, NULL, in_mad, out_mad);
if (err) {
mlx5_ib_warn(dev, "err %d\n", err);
goto out;
}
props->lid = be16_to_cpup((__be16 *)(out_mad->data + 16));
props->lmc = out_mad->data[34] & 0x7;
props->sm_lid = be16_to_cpup((__be16 *)(out_mad->data + 18));
props->sm_sl = out_mad->data[36] & 0xf;
props->state = out_mad->data[32] & 0xf;
props->phys_state = out_mad->data[33] >> 4;
props->port_cap_flags = be32_to_cpup((__be32 *)(out_mad->data + 20));
props->gid_tbl_len = out_mad->data[50];
props->max_msg_sz = 1 << MLX5_CAP_GEN(mdev, log_max_msg);
props->pkey_tbl_len = dev->pkey_table_len;
props->bad_pkey_cntr = be16_to_cpup((__be16 *)(out_mad->data + 46));
props->qkey_viol_cntr = be16_to_cpup((__be16 *)(out_mad->data + 48));
props->active_width = out_mad->data[31] & 0xf;
props->active_speed = out_mad->data[35] >> 4;
props->max_mtu = out_mad->data[41] & 0xf;
props->active_mtu = out_mad->data[36] >> 4;
props->subnet_timeout = out_mad->data[51] & 0x1f;
props->max_vl_num = out_mad->data[37] >> 4;
props->init_type_reply = out_mad->data[41] >> 4;
if (props->port_cap_flags & IB_PORT_CAP_MASK2_SUP) {
props->port_cap_flags2 =
be16_to_cpup((__be16 *)(out_mad->data + 60));
if (props->port_cap_flags2 & IB_PORT_LINK_WIDTH_2X_SUP)
props->active_width = out_mad->data[31] & 0x1f;
}
/* Check if extended speeds (EDR/FDR/...) are supported */
if (props->port_cap_flags & IB_PORT_EXTENDED_SPEEDS_SUP) {
ext_active_speed = out_mad->data[62] >> 4;
switch (ext_active_speed) {
case 1:
props->active_speed = 16; /* FDR */
break;
case 2:
props->active_speed = 32; /* EDR */
break;
case 4:
if (props->port_cap_flags & IB_PORT_CAP_MASK2_SUP &&
props->port_cap_flags2 & IB_PORT_LINK_SPEED_HDR_SUP)
props->active_speed = IB_SPEED_HDR;
break;
case 8:
if (props->port_cap_flags & IB_PORT_CAP_MASK2_SUP &&
props->port_cap_flags2 & IB_PORT_LINK_SPEED_NDR_SUP)
props->active_speed = IB_SPEED_NDR;
break;
}
}
/* Check if extended speeds 2 (XDR/...) are supported */
if (props->port_cap_flags & IB_PORT_CAP_MASK2_SUP &&
props->port_cap_flags2 & IB_PORT_EXTENDED_SPEEDS2_SUP) {
ext_active_speed = (out_mad->data[56] >> 4) & 0x6;
switch (ext_active_speed) {
case 2:
if (props->port_cap_flags2 & IB_PORT_LINK_SPEED_XDR_SUP)
props->active_speed = IB_SPEED_XDR;
break;
}
}
/* If reported active speed is QDR, check if is FDR-10 */
if (props->active_speed == 4) {
if (dev->port_caps[port - 1].ext_port_cap &
MLX_EXT_PORT_CAP_FLAG_EXTENDED_PORT_INFO) {
ib_init_query_mad(in_mad);
in_mad->attr_id = MLX5_ATTR_EXTENDED_PORT_INFO;
in_mad->attr_mod = cpu_to_be32(port);
err = mlx5_MAD_IFC(dev, 1, 1, port,
NULL, NULL, in_mad, out_mad);
if (err)
goto out;
/* Checking LinkSpeedActive for FDR-10 */
if (out_mad->data[15] & 0x1)
props->active_speed = 8;
}
}
out:
kfree(in_mad);
kfree(out_mad);
return err;
}
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,96 @@
/*
* Copyright (c) 2013-2015, Mellanox Technologies. 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 <rdma/ib_umem_odp.h>
#include <rdma/iter.h>
#include "mlx5_ib.h"
/*
* Fill in a physical address list. ib_umem_num_dma_blocks() entries will be
* filled in the pas array.
*/
void mlx5_ib_populate_pas(struct ib_umem *umem, size_t page_size, __be64 *pas,
u64 access_flags)
{
struct ib_block_iter biter;
rdma_umem_for_each_dma_block (umem, &biter, page_size) {
*pas = cpu_to_be64(rdma_block_iter_dma_address(&biter) |
access_flags);
pas++;
}
}
/*
* Compute the page shift and page_offset for mailboxes that use a quantized
* page_offset. The granulatity of the page offset scales according to page
* size.
*/
unsigned long __mlx5_umem_find_best_quantized_pgoff(
struct ib_umem *umem, unsigned long pgsz_bitmap,
unsigned int page_offset_bits, u64 pgoff_bitmask, unsigned int scale,
unsigned int *page_offset_quantized)
{
const u64 page_offset_mask = (1UL << page_offset_bits) - 1;
unsigned long page_size;
u64 page_offset;
page_size = ib_umem_find_best_pgoff(umem, pgsz_bitmap, pgoff_bitmask);
if (!page_size)
return 0;
/*
* page size is the largest possible page size.
*
* Reduce the page_size, and thus the page_offset and quanta, until the
* page_offset fits into the mailbox field. Once page_size < scale this
* loop is guaranteed to terminate.
*/
page_offset = ib_umem_dma_offset(umem, page_size);
while (page_offset & ~(u64)(page_offset_mask * (page_size / scale))) {
page_size /= 2;
page_offset = ib_umem_dma_offset(umem, page_size);
}
/*
* The address is not aligned, or otherwise cannot be represented by the
* page_offset.
*/
if (!(pgsz_bitmap & page_size))
return 0;
*page_offset_quantized =
(unsigned long)page_offset / (page_size / scale);
if (WARN_ON(*page_offset_quantized > page_offset_mask))
return 0;
return page_size;
}
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,133 @@
// SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB
/*
* Copyright (c) 2020, Mellanox Technologies inc. All rights reserved.
*/
#include <rdma/uverbs_ioctl.h>
#include <rdma/mlx5_user_ioctl_cmds.h>
#include <rdma/mlx5_user_ioctl_verbs.h>
#include <linux/mlx5/driver.h>
#include "mlx5_ib.h"
#define UVERBS_MODULE_NAME mlx5_ib
#include <rdma/uverbs_named_ioctl.h>
static bool pp_is_supported(struct ib_device *device)
{
struct mlx5_ib_dev *dev = to_mdev(device);
return (MLX5_CAP_GEN(dev->mdev, qos) &&
MLX5_CAP_QOS(dev->mdev, packet_pacing) &&
MLX5_CAP_QOS(dev->mdev, packet_pacing_uid));
}
static int UVERBS_HANDLER(MLX5_IB_METHOD_PP_OBJ_ALLOC)(
struct uverbs_attr_bundle *attrs)
{
u8 rl_raw[MLX5_ST_SZ_BYTES(set_pp_rate_limit_context)] = {};
struct ib_uobject *uobj = uverbs_attr_get_uobject(attrs,
MLX5_IB_ATTR_PP_OBJ_ALLOC_HANDLE);
struct mlx5_ib_dev *dev;
struct mlx5_ib_ucontext *c;
struct mlx5_ib_pp *pp_entry;
void *in_ctx;
u16 uid;
int inlen;
u32 flags;
int err;
c = to_mucontext(ib_uverbs_get_ucontext(attrs));
if (IS_ERR(c))
return PTR_ERR(c);
/* The allocated entry can be used only by a DEVX context */
if (!c->devx_uid)
return -EINVAL;
dev = to_mdev(c->ibucontext.device);
pp_entry = kzalloc_obj(*pp_entry);
if (!pp_entry)
return -ENOMEM;
in_ctx = uverbs_attr_get_alloced_ptr(attrs,
MLX5_IB_ATTR_PP_OBJ_ALLOC_CTX);
inlen = uverbs_attr_get_len(attrs,
MLX5_IB_ATTR_PP_OBJ_ALLOC_CTX);
memcpy(rl_raw, in_ctx, inlen);
err = uverbs_get_flags32(&flags, attrs,
MLX5_IB_ATTR_PP_OBJ_ALLOC_FLAGS,
MLX5_IB_UAPI_PP_ALLOC_FLAGS_DEDICATED_INDEX);
if (err)
goto err;
uid = (flags & MLX5_IB_UAPI_PP_ALLOC_FLAGS_DEDICATED_INDEX) ?
c->devx_uid : MLX5_SHARED_RESOURCE_UID;
err = mlx5_rl_add_rate_raw(dev->mdev, rl_raw, uid,
(flags & MLX5_IB_UAPI_PP_ALLOC_FLAGS_DEDICATED_INDEX),
&pp_entry->index);
if (err)
goto err;
pp_entry->mdev = dev->mdev;
uobj->object = pp_entry;
uverbs_finalize_uobj_create(attrs, MLX5_IB_ATTR_PP_OBJ_ALLOC_HANDLE);
err = uverbs_copy_to(attrs, MLX5_IB_ATTR_PP_OBJ_ALLOC_INDEX,
&pp_entry->index, sizeof(pp_entry->index));
return err;
err:
kfree(pp_entry);
return err;
}
static int pp_obj_cleanup(struct ib_uobject *uobject,
enum rdma_remove_reason why,
struct uverbs_attr_bundle *attrs)
{
struct mlx5_ib_pp *pp_entry = uobject->object;
mlx5_rl_remove_rate_raw(pp_entry->mdev, pp_entry->index);
kfree(pp_entry);
return 0;
}
DECLARE_UVERBS_NAMED_METHOD(
MLX5_IB_METHOD_PP_OBJ_ALLOC,
UVERBS_ATTR_IDR(MLX5_IB_ATTR_PP_OBJ_ALLOC_HANDLE,
MLX5_IB_OBJECT_PP,
UVERBS_ACCESS_NEW,
UA_MANDATORY),
UVERBS_ATTR_PTR_IN(
MLX5_IB_ATTR_PP_OBJ_ALLOC_CTX,
UVERBS_ATTR_SIZE(1,
MLX5_ST_SZ_BYTES(set_pp_rate_limit_context)),
UA_MANDATORY,
UA_ALLOC_AND_COPY),
UVERBS_ATTR_FLAGS_IN(MLX5_IB_ATTR_PP_OBJ_ALLOC_FLAGS,
enum mlx5_ib_uapi_pp_alloc_flags,
UA_MANDATORY),
UVERBS_ATTR_PTR_OUT(MLX5_IB_ATTR_PP_OBJ_ALLOC_INDEX,
UVERBS_ATTR_TYPE(u16),
UA_MANDATORY));
DECLARE_UVERBS_NAMED_METHOD_DESTROY(
MLX5_IB_METHOD_PP_OBJ_DESTROY,
UVERBS_ATTR_IDR(MLX5_IB_ATTR_PP_OBJ_DESTROY_HANDLE,
MLX5_IB_OBJECT_PP,
UVERBS_ACCESS_DESTROY,
UA_MANDATORY));
DECLARE_UVERBS_NAMED_OBJECT(MLX5_IB_OBJECT_PP,
UVERBS_TYPE_ALLOC_IDR(pp_obj_cleanup),
&UVERBS_METHOD(MLX5_IB_METHOD_PP_OBJ_ALLOC),
&UVERBS_METHOD(MLX5_IB_METHOD_PP_OBJ_DESTROY));
const struct uapi_definition mlx5_ib_qos_defs[] = {
UAPI_DEF_CHAIN_OBJ_TREE_NAMED(
MLX5_IB_OBJECT_PP,
UAPI_DEF_IS_OBJ_SUPPORTED(pp_is_supported)),
{},
};
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,60 @@
/* SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB */
/*
* Copyright (c) 2013-2020, Mellanox Technologies inc. All rights reserved.
*/
#ifndef _MLX5_IB_QP_H
#define _MLX5_IB_QP_H
struct mlx5_ib_dev;
struct mlx5_qp_table {
struct notifier_block nb;
struct xarray dct_xa;
/* protect radix tree
*/
spinlock_t lock;
struct radix_tree_root tree;
};
int mlx5_init_qp_table(struct mlx5_ib_dev *dev);
void mlx5_cleanup_qp_table(struct mlx5_ib_dev *dev);
int mlx5_core_create_dct(struct mlx5_ib_dev *dev, struct mlx5_core_dct *qp,
u32 *in, int inlen, u32 *out, int outlen);
int mlx5_qpc_create_qp(struct mlx5_ib_dev *dev, struct mlx5_core_qp *qp,
u32 *in, int inlen, u32 *out);
int mlx5_core_qp_modify(struct mlx5_ib_dev *dev, u16 opcode, u32 opt_param_mask,
void *qpc, struct mlx5_core_qp *qp, u32 *ece);
int mlx5_core_destroy_qp(struct mlx5_ib_dev *dev, struct mlx5_core_qp *qp);
int mlx5_core_destroy_dct(struct mlx5_ib_dev *dev, struct mlx5_core_dct *dct);
int mlx5_core_qp_query(struct mlx5_ib_dev *dev, struct mlx5_core_qp *qp,
u32 *out, int outlen, bool qpc_ext);
int mlx5_core_dct_query(struct mlx5_ib_dev *dev, struct mlx5_core_dct *dct,
u32 *out, int outlen);
int mlx5_core_set_delay_drop(struct mlx5_ib_dev *dev, u32 timeout_usec);
int mlx5_core_destroy_rq_tracked(struct mlx5_ib_dev *dev,
struct mlx5_core_qp *rq);
int mlx5_core_create_sq_tracked(struct mlx5_ib_dev *dev, u32 *in, int inlen,
struct mlx5_core_qp *sq);
void mlx5_core_destroy_sq_tracked(struct mlx5_ib_dev *dev,
struct mlx5_core_qp *sq);
int mlx5_core_create_rq_tracked(struct mlx5_ib_dev *dev, u32 *in, int inlen,
struct mlx5_core_qp *rq);
struct mlx5_core_rsc_common *mlx5_core_res_hold(struct mlx5_ib_dev *dev,
int res_num,
enum mlx5_res_type res_type);
void mlx5_core_res_put(struct mlx5_core_rsc_common *res);
int mlx5_core_xrcd_alloc(struct mlx5_ib_dev *dev, u32 *xrcdn);
int mlx5_core_xrcd_dealloc(struct mlx5_ib_dev *dev, u32 xrcdn);
int mlx5_ib_qp_set_counter(struct ib_qp *qp, struct rdma_counter *counter);
int mlx5_ib_qp_event_init(void);
void mlx5_ib_qp_event_cleanup(void);
int mlx5r_ib_rate(struct mlx5_ib_dev *dev, u8 rate);
#endif /* _MLX5_IB_QP_H */
@@ -0,0 +1,704 @@
// SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB
/*
* Copyright (c) 2013-2020, Mellanox Technologies inc. All rights reserved.
*/
#include <linux/gfp.h>
#include <linux/mlx5/qp.h>
#include <linux/mlx5/driver.h>
#include "mlx5_ib.h"
#include "qp.h"
static int mlx5_core_drain_dct(struct mlx5_ib_dev *dev,
struct mlx5_core_dct *dct);
static struct mlx5_core_rsc_common *
mlx5_get_rsc(struct mlx5_qp_table *table, u32 rsn)
{
struct mlx5_core_rsc_common *common;
unsigned long flags;
spin_lock_irqsave(&table->lock, flags);
common = radix_tree_lookup(&table->tree, rsn);
if (common && !common->invalid)
refcount_inc(&common->refcount);
else
common = NULL;
spin_unlock_irqrestore(&table->lock, flags);
return common;
}
void mlx5_core_put_rsc(struct mlx5_core_rsc_common *common)
{
if (refcount_dec_and_test(&common->refcount))
complete(&common->free);
}
static u64 qp_allowed_event_types(void)
{
u64 mask;
mask = BIT(MLX5_EVENT_TYPE_PATH_MIG) |
BIT(MLX5_EVENT_TYPE_COMM_EST) |
BIT(MLX5_EVENT_TYPE_SQ_DRAINED) |
BIT(MLX5_EVENT_TYPE_SRQ_LAST_WQE) |
BIT(MLX5_EVENT_TYPE_WQ_CATAS_ERROR) |
BIT(MLX5_EVENT_TYPE_PATH_MIG_FAILED) |
BIT(MLX5_EVENT_TYPE_WQ_INVAL_REQ_ERROR) |
BIT(MLX5_EVENT_TYPE_WQ_ACCESS_ERROR);
return mask;
}
static u64 rq_allowed_event_types(void)
{
u64 mask;
mask = BIT(MLX5_EVENT_TYPE_SRQ_LAST_WQE) |
BIT(MLX5_EVENT_TYPE_WQ_CATAS_ERROR);
return mask;
}
static u64 sq_allowed_event_types(void)
{
return BIT(MLX5_EVENT_TYPE_WQ_CATAS_ERROR);
}
static u64 dct_allowed_event_types(void)
{
return BIT(MLX5_EVENT_TYPE_DCT_DRAINED);
}
static bool is_event_type_allowed(int rsc_type, int event_type)
{
switch (rsc_type) {
case MLX5_EVENT_QUEUE_TYPE_QP:
return BIT(event_type) & qp_allowed_event_types();
case MLX5_EVENT_QUEUE_TYPE_RQ:
return BIT(event_type) & rq_allowed_event_types();
case MLX5_EVENT_QUEUE_TYPE_SQ:
return BIT(event_type) & sq_allowed_event_types();
case MLX5_EVENT_QUEUE_TYPE_DCT:
return BIT(event_type) & dct_allowed_event_types();
default:
WARN(1, "Event arrived for unknown resource type");
return false;
}
}
static int dct_event_notifier(struct mlx5_ib_dev *dev, struct mlx5_eqe *eqe)
{
struct mlx5_core_dct *dct;
unsigned long flags;
u32 qpn;
qpn = be32_to_cpu(eqe->data.dct.dctn) & 0xFFFFFF;
xa_lock_irqsave(&dev->qp_table.dct_xa, flags);
dct = xa_load(&dev->qp_table.dct_xa, qpn);
if (dct)
complete(&dct->drained);
xa_unlock_irqrestore(&dev->qp_table.dct_xa, flags);
return NOTIFY_OK;
}
static int rsc_event_notifier(struct notifier_block *nb,
unsigned long type, void *data)
{
struct mlx5_ib_dev *dev =
container_of(nb, struct mlx5_ib_dev, qp_table.nb);
struct mlx5_core_rsc_common *common;
struct mlx5_eqe *eqe = data;
u8 event_type = (u8)type;
struct mlx5_core_qp *qp;
u32 rsn;
switch (event_type) {
case MLX5_EVENT_TYPE_DCT_DRAINED:
return dct_event_notifier(dev, eqe);
case MLX5_EVENT_TYPE_PATH_MIG:
case MLX5_EVENT_TYPE_COMM_EST:
case MLX5_EVENT_TYPE_SQ_DRAINED:
case MLX5_EVENT_TYPE_SRQ_LAST_WQE:
case MLX5_EVENT_TYPE_WQ_CATAS_ERROR:
case MLX5_EVENT_TYPE_PATH_MIG_FAILED:
case MLX5_EVENT_TYPE_WQ_INVAL_REQ_ERROR:
case MLX5_EVENT_TYPE_WQ_ACCESS_ERROR:
rsn = be32_to_cpu(eqe->data.qp_srq.qp_srq_n) & 0xffffff;
rsn |= (eqe->data.qp_srq.type << MLX5_USER_INDEX_LEN);
break;
default:
return NOTIFY_DONE;
}
common = mlx5_get_rsc(&dev->qp_table, rsn);
if (!common)
return NOTIFY_OK;
if (!is_event_type_allowed((rsn >> MLX5_USER_INDEX_LEN), event_type))
goto out;
switch (common->res) {
case MLX5_RES_QP:
case MLX5_RES_RQ:
case MLX5_RES_SQ:
qp = (struct mlx5_core_qp *)common;
qp->event(qp, event_type);
/* Need to put resource in event handler */
return NOTIFY_OK;
default:
break;
}
out:
mlx5_core_put_rsc(common);
return NOTIFY_OK;
}
static int create_resource_common(struct mlx5_ib_dev *dev,
struct mlx5_core_qp *qp, int rsc_type)
{
struct mlx5_qp_table *table = &dev->qp_table;
int err;
qp->common.res = rsc_type;
spin_lock_irq(&table->lock);
err = radix_tree_insert(&table->tree,
qp->qpn | (rsc_type << MLX5_USER_INDEX_LEN),
qp);
spin_unlock_irq(&table->lock);
if (err)
return err;
refcount_set(&qp->common.refcount, 1);
init_completion(&qp->common.free);
qp->pid = current->pid;
return 0;
}
static void modify_resource_common_state(struct mlx5_ib_dev *dev,
struct mlx5_core_qp *qp,
bool invalid)
{
struct mlx5_qp_table *table = &dev->qp_table;
unsigned long flags;
spin_lock_irqsave(&table->lock, flags);
qp->common.invalid = invalid;
spin_unlock_irqrestore(&table->lock, flags);
}
static void destroy_resource_common(struct mlx5_ib_dev *dev,
struct mlx5_core_qp *qp)
{
struct mlx5_qp_table *table = &dev->qp_table;
unsigned long flags;
spin_lock_irqsave(&table->lock, flags);
radix_tree_delete(&table->tree,
qp->qpn | (qp->common.res << MLX5_USER_INDEX_LEN));
spin_unlock_irqrestore(&table->lock, flags);
mlx5_core_put_rsc((struct mlx5_core_rsc_common *)qp);
wait_for_completion(&qp->common.free);
}
static int _mlx5_core_destroy_dct(struct mlx5_ib_dev *dev,
struct mlx5_core_dct *dct)
{
u32 in[MLX5_ST_SZ_DW(destroy_dct_in)] = {};
struct mlx5_core_qp *qp = &dct->mqp;
MLX5_SET(destroy_dct_in, in, opcode, MLX5_CMD_OP_DESTROY_DCT);
MLX5_SET(destroy_dct_in, in, dctn, qp->qpn);
MLX5_SET(destroy_dct_in, in, uid, qp->uid);
return mlx5_cmd_exec_in(dev->mdev, destroy_dct, in);
}
int mlx5_core_create_dct(struct mlx5_ib_dev *dev, struct mlx5_core_dct *dct,
u32 *in, int inlen, u32 *out, int outlen)
{
struct mlx5_core_qp *qp = &dct->mqp;
int err;
init_completion(&dct->drained);
MLX5_SET(create_dct_in, in, opcode, MLX5_CMD_OP_CREATE_DCT);
err = mlx5_cmd_do(dev->mdev, in, inlen, out, outlen);
if (err)
return err;
qp->qpn = MLX5_GET(create_dct_out, out, dctn);
qp->uid = MLX5_GET(create_dct_in, in, uid);
err = xa_err(xa_store_irq(&dev->qp_table.dct_xa, qp->qpn, dct, GFP_KERNEL));
if (err)
goto err_cmd;
return 0;
err_cmd:
_mlx5_core_destroy_dct(dev, dct);
return err;
}
int mlx5_qpc_create_qp(struct mlx5_ib_dev *dev, struct mlx5_core_qp *qp,
u32 *in, int inlen, u32 *out)
{
u32 din[MLX5_ST_SZ_DW(destroy_qp_in)] = {};
int err;
MLX5_SET(create_qp_in, in, opcode, MLX5_CMD_OP_CREATE_QP);
err = mlx5_cmd_exec(dev->mdev, in, inlen, out,
MLX5_ST_SZ_BYTES(create_qp_out));
if (err)
return err;
qp->uid = MLX5_GET(create_qp_in, in, uid);
qp->qpn = MLX5_GET(create_qp_out, out, qpn);
err = create_resource_common(dev, qp, MLX5_RES_QP);
if (err)
goto err_cmd;
if (dev->ib_dev.type != RDMA_DEVICE_TYPE_SMI)
mlx5_debug_qp_add(dev->mdev, qp);
return 0;
err_cmd:
MLX5_SET(destroy_qp_in, din, opcode, MLX5_CMD_OP_DESTROY_QP);
MLX5_SET(destroy_qp_in, din, qpn, qp->qpn);
MLX5_SET(destroy_qp_in, din, uid, qp->uid);
mlx5_cmd_exec_in(dev->mdev, destroy_qp, din);
return err;
}
static int mlx5_core_drain_dct(struct mlx5_ib_dev *dev,
struct mlx5_core_dct *dct)
{
u32 in[MLX5_ST_SZ_DW(drain_dct_in)] = {};
struct mlx5_core_qp *qp = &dct->mqp;
MLX5_SET(drain_dct_in, in, opcode, MLX5_CMD_OP_DRAIN_DCT);
MLX5_SET(drain_dct_in, in, dctn, qp->qpn);
MLX5_SET(drain_dct_in, in, uid, qp->uid);
return mlx5_cmd_exec_in(dev->mdev, drain_dct, in);
}
int mlx5_core_destroy_dct(struct mlx5_ib_dev *dev,
struct mlx5_core_dct *dct)
{
struct mlx5_qp_table *table = &dev->qp_table;
struct mlx5_core_dct *tmp;
int err;
err = mlx5_core_drain_dct(dev, dct);
if (err) {
if (dev->mdev->state == MLX5_DEVICE_STATE_INTERNAL_ERROR)
goto destroy;
return err;
}
wait_for_completion(&dct->drained);
destroy:
tmp = xa_cmpxchg_irq(&table->dct_xa, dct->mqp.qpn, dct, XA_ZERO_ENTRY, GFP_KERNEL);
if (WARN_ON(tmp != dct))
return xa_err(tmp) ?: -EINVAL;
err = _mlx5_core_destroy_dct(dev, dct);
if (err) {
xa_cmpxchg_irq(&table->dct_xa, dct->mqp.qpn, XA_ZERO_ENTRY, dct, 0);
return err;
}
/*
* A race can occur where a concurrent create gets the same dctn
* (after hardware released it) and overwrites XA_ZERO_ENTRY with
* its new DCT before we reach here. In that case, we must not erase
* the entry as it now belongs to the new DCT.
*/
xa_cmpxchg_irq(&table->dct_xa, dct->mqp.qpn, XA_ZERO_ENTRY, NULL, 0);
return 0;
}
int mlx5_core_destroy_qp(struct mlx5_ib_dev *dev, struct mlx5_core_qp *qp)
{
u32 in[MLX5_ST_SZ_DW(destroy_qp_in)] = {};
if (dev->ib_dev.type != RDMA_DEVICE_TYPE_SMI)
mlx5_debug_qp_remove(dev->mdev, qp);
destroy_resource_common(dev, qp);
MLX5_SET(destroy_qp_in, in, opcode, MLX5_CMD_OP_DESTROY_QP);
MLX5_SET(destroy_qp_in, in, qpn, qp->qpn);
MLX5_SET(destroy_qp_in, in, uid, qp->uid);
return mlx5_cmd_exec_in(dev->mdev, destroy_qp, in);
}
int mlx5_core_set_delay_drop(struct mlx5_ib_dev *dev,
u32 timeout_usec)
{
u32 in[MLX5_ST_SZ_DW(set_delay_drop_params_in)] = {};
MLX5_SET(set_delay_drop_params_in, in, opcode,
MLX5_CMD_OP_SET_DELAY_DROP_PARAMS);
MLX5_SET(set_delay_drop_params_in, in, delay_drop_timeout,
timeout_usec / 100);
return mlx5_cmd_exec_in(dev->mdev, set_delay_drop_params, in);
}
struct mbox_info {
u32 *in;
u32 *out;
int inlen;
int outlen;
};
static int mbox_alloc(struct mbox_info *mbox, int inlen, int outlen)
{
mbox->inlen = inlen;
mbox->outlen = outlen;
mbox->in = kzalloc(mbox->inlen, GFP_KERNEL);
mbox->out = kzalloc(mbox->outlen, GFP_KERNEL);
if (!mbox->in || !mbox->out) {
kfree(mbox->in);
kfree(mbox->out);
return -ENOMEM;
}
return 0;
}
static void mbox_free(struct mbox_info *mbox)
{
kfree(mbox->in);
kfree(mbox->out);
}
static int get_ece_from_mbox(void *out, u16 opcode)
{
int ece = 0;
switch (opcode) {
case MLX5_CMD_OP_INIT2INIT_QP:
ece = MLX5_GET(init2init_qp_out, out, ece);
break;
case MLX5_CMD_OP_INIT2RTR_QP:
ece = MLX5_GET(init2rtr_qp_out, out, ece);
break;
case MLX5_CMD_OP_RTR2RTS_QP:
ece = MLX5_GET(rtr2rts_qp_out, out, ece);
break;
case MLX5_CMD_OP_RTS2RTS_QP:
ece = MLX5_GET(rts2rts_qp_out, out, ece);
break;
case MLX5_CMD_OP_RST2INIT_QP:
ece = MLX5_GET(rst2init_qp_out, out, ece);
break;
default:
break;
}
return ece;
}
static int modify_qp_mbox_alloc(struct mlx5_core_dev *dev, u16 opcode, int qpn,
u32 opt_param_mask, void *qpc,
struct mbox_info *mbox, u16 uid, u32 ece)
{
mbox->out = NULL;
mbox->in = NULL;
#define MBOX_ALLOC(mbox, typ) \
mbox_alloc(mbox, MLX5_ST_SZ_BYTES(typ##_in), MLX5_ST_SZ_BYTES(typ##_out))
#define MOD_QP_IN_SET(typ, in, _opcode, _qpn, _uid) \
do { \
MLX5_SET(typ##_in, in, opcode, _opcode); \
MLX5_SET(typ##_in, in, qpn, _qpn); \
MLX5_SET(typ##_in, in, uid, _uid); \
} while (0)
#define MOD_QP_IN_SET_QPC(typ, in, _opcode, _qpn, _opt_p, _qpc, _uid) \
do { \
MOD_QP_IN_SET(typ, in, _opcode, _qpn, _uid); \
MLX5_SET(typ##_in, in, opt_param_mask, _opt_p); \
memcpy(MLX5_ADDR_OF(typ##_in, in, qpc), _qpc, \
MLX5_ST_SZ_BYTES(qpc)); \
} while (0)
switch (opcode) {
/* 2RST & 2ERR */
case MLX5_CMD_OP_2RST_QP:
if (MBOX_ALLOC(mbox, qp_2rst))
return -ENOMEM;
MOD_QP_IN_SET(qp_2rst, mbox->in, opcode, qpn, uid);
break;
case MLX5_CMD_OP_2ERR_QP:
if (MBOX_ALLOC(mbox, qp_2err))
return -ENOMEM;
MOD_QP_IN_SET(qp_2err, mbox->in, opcode, qpn, uid);
break;
/* MODIFY with QPC */
case MLX5_CMD_OP_RST2INIT_QP:
if (MBOX_ALLOC(mbox, rst2init_qp))
return -ENOMEM;
MOD_QP_IN_SET_QPC(rst2init_qp, mbox->in, opcode, qpn,
opt_param_mask, qpc, uid);
MLX5_SET(rst2init_qp_in, mbox->in, ece, ece);
break;
case MLX5_CMD_OP_INIT2RTR_QP:
if (MBOX_ALLOC(mbox, init2rtr_qp))
return -ENOMEM;
MOD_QP_IN_SET_QPC(init2rtr_qp, mbox->in, opcode, qpn,
opt_param_mask, qpc, uid);
MLX5_SET(init2rtr_qp_in, mbox->in, ece, ece);
break;
case MLX5_CMD_OP_RTR2RTS_QP:
if (MBOX_ALLOC(mbox, rtr2rts_qp))
return -ENOMEM;
MOD_QP_IN_SET_QPC(rtr2rts_qp, mbox->in, opcode, qpn,
opt_param_mask, qpc, uid);
MLX5_SET(rtr2rts_qp_in, mbox->in, ece, ece);
break;
case MLX5_CMD_OP_RTS2RTS_QP:
if (MBOX_ALLOC(mbox, rts2rts_qp))
return -ENOMEM;
MOD_QP_IN_SET_QPC(rts2rts_qp, mbox->in, opcode, qpn,
opt_param_mask, qpc, uid);
MLX5_SET(rts2rts_qp_in, mbox->in, ece, ece);
break;
case MLX5_CMD_OP_SQERR2RTS_QP:
if (MBOX_ALLOC(mbox, sqerr2rts_qp))
return -ENOMEM;
MOD_QP_IN_SET_QPC(sqerr2rts_qp, mbox->in, opcode, qpn,
opt_param_mask, qpc, uid);
break;
case MLX5_CMD_OP_SQD_RTS_QP:
if (MBOX_ALLOC(mbox, sqd2rts_qp))
return -ENOMEM;
MOD_QP_IN_SET_QPC(sqd2rts_qp, mbox->in, opcode, qpn,
opt_param_mask, qpc, uid);
break;
case MLX5_CMD_OP_INIT2INIT_QP:
if (MBOX_ALLOC(mbox, init2init_qp))
return -ENOMEM;
MOD_QP_IN_SET_QPC(init2init_qp, mbox->in, opcode, qpn,
opt_param_mask, qpc, uid);
MLX5_SET(init2init_qp_in, mbox->in, ece, ece);
break;
default:
return -EINVAL;
}
return 0;
}
int mlx5_core_qp_modify(struct mlx5_ib_dev *dev, u16 opcode, u32 opt_param_mask,
void *qpc, struct mlx5_core_qp *qp, u32 *ece)
{
struct mbox_info mbox;
int err;
err = modify_qp_mbox_alloc(dev->mdev, opcode, qp->qpn, opt_param_mask,
qpc, &mbox, qp->uid, (ece) ? *ece : 0);
if (err)
return err;
err = mlx5_cmd_exec(dev->mdev, mbox.in, mbox.inlen, mbox.out,
mbox.outlen);
if (ece)
*ece = get_ece_from_mbox(mbox.out, opcode);
mbox_free(&mbox);
return err;
}
int mlx5_init_qp_table(struct mlx5_ib_dev *dev)
{
struct mlx5_qp_table *table = &dev->qp_table;
spin_lock_init(&table->lock);
INIT_RADIX_TREE(&table->tree, GFP_ATOMIC);
xa_init(&table->dct_xa);
if (dev->ib_dev.type != RDMA_DEVICE_TYPE_SMI)
mlx5_qp_debugfs_init(dev->mdev);
table->nb.notifier_call = rsc_event_notifier;
mlx5_notifier_register(dev->mdev, &table->nb);
return 0;
}
void mlx5_cleanup_qp_table(struct mlx5_ib_dev *dev)
{
struct mlx5_qp_table *table = &dev->qp_table;
mlx5_notifier_unregister(dev->mdev, &table->nb);
if (dev->ib_dev.type != RDMA_DEVICE_TYPE_SMI)
mlx5_qp_debugfs_cleanup(dev->mdev);
}
int mlx5_core_qp_query(struct mlx5_ib_dev *dev, struct mlx5_core_qp *qp,
u32 *out, int outlen, bool qpc_ext)
{
u32 in[MLX5_ST_SZ_DW(query_qp_in)] = {};
MLX5_SET(query_qp_in, in, opcode, MLX5_CMD_OP_QUERY_QP);
MLX5_SET(query_qp_in, in, qpn, qp->qpn);
MLX5_SET(query_qp_in, in, qpc_ext, qpc_ext);
return mlx5_cmd_exec(dev->mdev, in, sizeof(in), out, outlen);
}
int mlx5_core_dct_query(struct mlx5_ib_dev *dev, struct mlx5_core_dct *dct,
u32 *out, int outlen)
{
u32 in[MLX5_ST_SZ_DW(query_dct_in)] = {};
struct mlx5_core_qp *qp = &dct->mqp;
MLX5_SET(query_dct_in, in, opcode, MLX5_CMD_OP_QUERY_DCT);
MLX5_SET(query_dct_in, in, dctn, qp->qpn);
return mlx5_cmd_exec(dev->mdev, (void *)&in, sizeof(in), (void *)out,
outlen);
}
int mlx5_core_xrcd_alloc(struct mlx5_ib_dev *dev, u32 *xrcdn)
{
u32 out[MLX5_ST_SZ_DW(alloc_xrcd_out)] = {};
u32 in[MLX5_ST_SZ_DW(alloc_xrcd_in)] = {};
int err;
MLX5_SET(alloc_xrcd_in, in, opcode, MLX5_CMD_OP_ALLOC_XRCD);
err = mlx5_cmd_exec_inout(dev->mdev, alloc_xrcd, in, out);
if (!err)
*xrcdn = MLX5_GET(alloc_xrcd_out, out, xrcd);
return err;
}
int mlx5_core_xrcd_dealloc(struct mlx5_ib_dev *dev, u32 xrcdn)
{
u32 in[MLX5_ST_SZ_DW(dealloc_xrcd_in)] = {};
MLX5_SET(dealloc_xrcd_in, in, opcode, MLX5_CMD_OP_DEALLOC_XRCD);
MLX5_SET(dealloc_xrcd_in, in, xrcd, xrcdn);
return mlx5_cmd_exec_in(dev->mdev, dealloc_xrcd, in);
}
static int destroy_rq_tracked(struct mlx5_ib_dev *dev, u32 rqn, u16 uid)
{
u32 in[MLX5_ST_SZ_DW(destroy_rq_in)] = {};
MLX5_SET(destroy_rq_in, in, opcode, MLX5_CMD_OP_DESTROY_RQ);
MLX5_SET(destroy_rq_in, in, rqn, rqn);
MLX5_SET(destroy_rq_in, in, uid, uid);
return mlx5_cmd_exec_in(dev->mdev, destroy_rq, in);
}
int mlx5_core_create_rq_tracked(struct mlx5_ib_dev *dev, u32 *in, int inlen,
struct mlx5_core_qp *rq)
{
int err;
u32 rqn;
err = mlx5_core_create_rq(dev->mdev, in, inlen, &rqn);
if (err)
return err;
rq->uid = MLX5_GET(create_rq_in, in, uid);
rq->qpn = rqn;
err = create_resource_common(dev, rq, MLX5_RES_RQ);
if (err)
goto err_destroy_rq;
return 0;
err_destroy_rq:
destroy_rq_tracked(dev, rq->qpn, rq->uid);
return err;
}
int mlx5_core_destroy_rq_tracked(struct mlx5_ib_dev *dev,
struct mlx5_core_qp *rq)
{
int ret;
/* The rq destruction can be called again in case it fails, hence we
* mark the common resource as invalid and only once FW destruction
* is completed successfully we actually destroy the resources.
*/
modify_resource_common_state(dev, rq, true);
ret = destroy_rq_tracked(dev, rq->qpn, rq->uid);
if (ret) {
modify_resource_common_state(dev, rq, false);
return ret;
}
destroy_resource_common(dev, rq);
return 0;
}
static void destroy_sq_tracked(struct mlx5_ib_dev *dev, u32 sqn, u16 uid)
{
u32 in[MLX5_ST_SZ_DW(destroy_sq_in)] = {};
MLX5_SET(destroy_sq_in, in, opcode, MLX5_CMD_OP_DESTROY_SQ);
MLX5_SET(destroy_sq_in, in, sqn, sqn);
MLX5_SET(destroy_sq_in, in, uid, uid);
mlx5_cmd_exec_in(dev->mdev, destroy_sq, in);
}
int mlx5_core_create_sq_tracked(struct mlx5_ib_dev *dev, u32 *in, int inlen,
struct mlx5_core_qp *sq)
{
u32 out[MLX5_ST_SZ_DW(create_sq_out)] = {};
int err;
MLX5_SET(create_sq_in, in, opcode, MLX5_CMD_OP_CREATE_SQ);
err = mlx5_cmd_exec(dev->mdev, in, inlen, out, sizeof(out));
if (err)
return err;
sq->qpn = MLX5_GET(create_sq_out, out, sqn);
sq->uid = MLX5_GET(create_sq_in, in, uid);
err = create_resource_common(dev, sq, MLX5_RES_SQ);
if (err)
goto err_destroy_sq;
return 0;
err_destroy_sq:
destroy_sq_tracked(dev, sq->qpn, sq->uid);
return err;
}
void mlx5_core_destroy_sq_tracked(struct mlx5_ib_dev *dev,
struct mlx5_core_qp *sq)
{
destroy_resource_common(dev, sq);
destroy_sq_tracked(dev, sq->qpn, sq->uid);
}
struct mlx5_core_rsc_common *mlx5_core_res_hold(struct mlx5_ib_dev *dev,
int res_num,
enum mlx5_res_type res_type)
{
u32 rsn = res_num | (res_type << MLX5_USER_INDEX_LEN);
struct mlx5_qp_table *table = &dev->qp_table;
return mlx5_get_rsc(table, rsn);
}
void mlx5_core_res_put(struct mlx5_core_rsc_common *res)
{
mlx5_core_put_rsc(res);
}
@@ -0,0 +1,214 @@
// SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB
/*
* Copyright (c) 2019-2020, Mellanox Technologies Ltd. All rights reserved.
*/
#include <uapi/rdma/rdma_netlink.h>
#include <linux/mlx5/rsc_dump.h>
#include <rdma/ib_umem_odp.h>
#include <rdma/restrack.h>
#include "mlx5_ib.h"
#include "restrack.h"
#define MAX_DUMP_SIZE 1024
static int dump_rsc(struct mlx5_core_dev *dev, enum mlx5_sgmt_type type,
int index, void *data, int *data_len)
{
struct mlx5_core_dev *mdev = dev;
struct mlx5_rsc_dump_cmd *cmd;
struct mlx5_rsc_key key = {};
struct page *page;
int offset = 0;
int err = 0;
int cmd_err;
int size;
page = alloc_page(GFP_KERNEL);
if (!page)
return -ENOMEM;
key.size = PAGE_SIZE;
key.rsc = type;
key.index1 = index;
key.num_of_obj1 = 1;
cmd = mlx5_rsc_dump_cmd_create(mdev, &key);
if (IS_ERR(cmd)) {
err = PTR_ERR(cmd);
goto free_page;
}
do {
cmd_err = mlx5_rsc_dump_next(mdev, cmd, page, &size);
if (cmd_err < 0 || size + offset > MAX_DUMP_SIZE) {
err = cmd_err;
goto destroy_cmd;
}
memcpy(data + offset, page_address(page), size);
offset += size;
} while (cmd_err > 0);
*data_len = offset;
destroy_cmd:
mlx5_rsc_dump_cmd_destroy(cmd);
free_page:
__free_page(page);
return err;
}
static int fill_res_raw(struct sk_buff *msg, struct mlx5_ib_dev *dev,
enum mlx5_sgmt_type type, u32 key)
{
int len = 0;
void *data;
int err;
data = kzalloc(MAX_DUMP_SIZE, GFP_KERNEL);
if (!data)
return -ENOMEM;
err = dump_rsc(dev->mdev, type, key, data, &len);
if (err)
goto out;
err = nla_put(msg, RDMA_NLDEV_ATTR_RES_RAW, len, data);
out:
kfree(data);
return err;
}
static int fill_stat_mr_entry(struct sk_buff *msg, struct ib_mr *ibmr)
{
struct mlx5_ib_mr *mr = to_mmr(ibmr);
struct nlattr *table_attr;
if (!(mr->access_flags & IB_ACCESS_ON_DEMAND))
return 0;
table_attr = nla_nest_start(msg,
RDMA_NLDEV_ATTR_STAT_HWCOUNTERS);
if (!table_attr)
goto err;
if (rdma_nl_stat_hwcounter_entry(msg, "page_faults",
atomic64_read(&mr->odp_stats.faults)))
goto err_table;
if (rdma_nl_stat_hwcounter_entry(
msg, "page_faults_handled",
atomic64_read(&mr->odp_stats.faults_handled)))
goto err_table;
if (rdma_nl_stat_hwcounter_entry(
msg, "page_invalidations",
atomic64_read(&mr->odp_stats.invalidations)))
goto err_table;
if (rdma_nl_stat_hwcounter_entry(
msg, "page_invalidations_handled",
atomic64_read(&mr->odp_stats.invalidations_handled)))
goto err_table;
if (rdma_nl_stat_hwcounter_entry(msg, "page_prefetch",
atomic64_read(&mr->odp_stats.prefetch)))
goto err_table;
nla_nest_end(msg, table_attr);
return 0;
err_table:
nla_nest_cancel(msg, table_attr);
err:
return -EMSGSIZE;
}
static int fill_res_mr_entry_raw(struct sk_buff *msg, struct ib_mr *ibmr)
{
struct mlx5_ib_mr *mr = to_mmr(ibmr);
return fill_res_raw(msg, mr_to_mdev(mr), MLX5_SGMT_TYPE_PRM_QUERY_MKEY,
mlx5_mkey_to_idx(mr->mmkey.key));
}
static int fill_res_mr_entry(struct sk_buff *msg, struct ib_mr *ibmr)
{
struct mlx5_ib_mr *mr = to_mmr(ibmr);
struct nlattr *table_attr;
if (!(mr->access_flags & IB_ACCESS_ON_DEMAND))
return 0;
table_attr = nla_nest_start(msg, RDMA_NLDEV_ATTR_DRIVER);
if (!table_attr)
goto err;
if (mr->is_odp_implicit) {
if (rdma_nl_put_driver_string(msg, "odp", "implicit"))
goto err;
} else {
if (rdma_nl_put_driver_string(msg, "odp", "explicit"))
goto err;
}
nla_nest_end(msg, table_attr);
return 0;
err:
nla_nest_cancel(msg, table_attr);
return -EMSGSIZE;
}
static int fill_res_cq_entry_raw(struct sk_buff *msg, struct ib_cq *ibcq)
{
struct mlx5_ib_dev *dev = to_mdev(ibcq->device);
struct mlx5_ib_cq *cq = to_mcq(ibcq);
return fill_res_raw(msg, dev, MLX5_SGMT_TYPE_PRM_QUERY_CQ, cq->mcq.cqn);
}
static int fill_res_qp_entry(struct sk_buff *msg, struct ib_qp *ibqp)
{
struct mlx5_ib_qp *qp = to_mqp(ibqp);
int ret;
if (qp->type < IB_QPT_DRIVER)
return 0;
switch (qp->type) {
case MLX5_IB_QPT_REG_UMR:
ret = nla_put_string(msg, RDMA_NLDEV_ATTR_RES_SUBTYPE,
"REG_UMR");
break;
case MLX5_IB_QPT_DCI:
ret = nla_put_string(msg, RDMA_NLDEV_ATTR_RES_SUBTYPE, "DCI");
break;
default:
return 0;
}
if (ret)
return ret;
return nla_put_u8(msg, RDMA_NLDEV_ATTR_RES_TYPE, IB_QPT_DRIVER);
}
static int fill_res_qp_entry_raw(struct sk_buff *msg, struct ib_qp *ibqp)
{
struct mlx5_ib_dev *dev = to_mdev(ibqp->device);
return fill_res_raw(msg, dev, MLX5_SGMT_TYPE_PRM_QUERY_QP,
ibqp->qp_num);
}
static const struct ib_device_ops restrack_ops = {
.fill_res_cq_entry_raw = fill_res_cq_entry_raw,
.fill_res_mr_entry = fill_res_mr_entry,
.fill_res_mr_entry_raw = fill_res_mr_entry_raw,
.fill_res_qp_entry = fill_res_qp_entry,
.fill_res_qp_entry_raw = fill_res_qp_entry_raw,
.fill_stat_mr_entry = fill_stat_mr_entry,
};
int mlx5_ib_restrack_init(struct mlx5_ib_dev *dev)
{
ib_set_device_ops(&dev->ib_dev, &restrack_ops);
return 0;
}
@@ -0,0 +1,13 @@
/* SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB */
/*
* Copyright (c) 2013-2020, Mellanox Technologies Ltd. All rights reserved.
*/
#ifndef _MLX5_IB_RESTRACK_H
#define _MLX5_IB_RESTRACK_H
#include "mlx5_ib.h"
int mlx5_ib_restrack_init(struct mlx5_ib_dev *dev);
#endif /* _MLX5_IB_RESTRACK_H */
@@ -0,0 +1,464 @@
// SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB
/*
* Copyright (c) 2013-2018, Mellanox Technologies inc. All rights reserved.
*/
#include <linux/mlx5/qp.h>
#include <linux/slab.h>
#include <rdma/ib_umem.h>
#include <rdma/ib_user_verbs.h>
#include "mlx5_ib.h"
#include "srq.h"
static void *get_wqe(struct mlx5_ib_srq *srq, int n)
{
return mlx5_frag_buf_get_wqe(&srq->fbc, n);
}
static void mlx5_ib_srq_event(struct mlx5_core_srq *srq, enum mlx5_event type)
{
struct ib_event event;
struct ib_srq *ibsrq = &to_mibsrq(srq)->ibsrq;
if (ibsrq->event_handler) {
event.device = ibsrq->device;
event.element.srq = ibsrq;
switch (type) {
case MLX5_EVENT_TYPE_SRQ_RQ_LIMIT:
event.event = IB_EVENT_SRQ_LIMIT_REACHED;
break;
case MLX5_EVENT_TYPE_SRQ_CATAS_ERROR:
event.event = IB_EVENT_SRQ_ERR;
break;
default:
pr_warn("mlx5_ib: Unexpected event type %d on SRQ %06x\n",
type, srq->srqn);
return;
}
ibsrq->event_handler(&event, ibsrq->srq_context);
}
}
static int create_srq_user(struct ib_pd *pd, struct mlx5_ib_srq *srq,
struct mlx5_srq_attr *in,
struct ib_udata *udata, int buf_size)
{
struct mlx5_ib_dev *dev = to_mdev(pd->device);
struct mlx5_ib_create_srq ucmd;
struct mlx5_ib_ucontext *ucontext = rdma_udata_to_drv_context(
udata, struct mlx5_ib_ucontext, ibucontext);
int err;
u32 uidx = MLX5_IB_DEFAULT_UIDX;
err = ib_copy_validate_udata_in(udata, ucmd, flags);
if (err)
return err;
if (ucmd.reserved0 || ucmd.reserved1)
return -EINVAL;
if (in->type != IB_SRQT_BASIC) {
err = get_srq_user_index(ucontext, &ucmd, udata->inlen, &uidx);
if (err)
return err;
}
srq->wq_sig = !!(ucmd.flags & MLX5_SRQ_FLAG_SIGNATURE);
srq->umem = ib_umem_get_va(pd->device, ucmd.buf_addr, buf_size, 0);
if (IS_ERR(srq->umem)) {
mlx5_ib_dbg(dev, "failed umem get, size %d\n", buf_size);
err = PTR_ERR(srq->umem);
return err;
}
in->umem = srq->umem;
err = mlx5_ib_db_map_user(ucontext, NULL, 0, ucmd.db_addr, &srq->db);
if (err) {
mlx5_ib_dbg(dev, "map doorbell failed\n");
goto err_umem;
}
in->uid = (in->type != IB_SRQT_XRC) ? to_mpd(pd)->uid : 0;
if (MLX5_CAP_GEN(dev->mdev, cqe_version) == MLX5_CQE_VERSION_V1 &&
in->type != IB_SRQT_BASIC)
in->user_index = uidx;
return 0;
err_umem:
ib_umem_release(srq->umem);
return err;
}
static int create_srq_kernel(struct mlx5_ib_dev *dev, struct mlx5_ib_srq *srq,
struct mlx5_srq_attr *in, int buf_size)
{
int err;
int i;
struct mlx5_wqe_srq_next_seg *next;
err = mlx5_db_alloc(dev->mdev, &srq->db);
if (err) {
mlx5_ib_warn(dev, "alloc dbell rec failed\n");
return err;
}
if (mlx5_frag_buf_alloc_node(dev->mdev, buf_size, &srq->buf,
dev->mdev->priv.numa_node)) {
mlx5_ib_dbg(dev, "buf alloc failed\n");
err = -ENOMEM;
goto err_db;
}
mlx5_init_fbc(srq->buf.frags, srq->msrq.wqe_shift, ilog2(srq->msrq.max),
&srq->fbc);
srq->head = 0;
srq->tail = srq->msrq.max - 1;
srq->wqe_ctr = 0;
for (i = 0; i < srq->msrq.max; i++) {
next = get_wqe(srq, i);
next->next_wqe_index =
cpu_to_be16((i + 1) & (srq->msrq.max - 1));
}
mlx5_ib_dbg(dev, "srq->buf.page_shift = %d\n", srq->buf.page_shift);
in->pas = kvcalloc(srq->buf.npages, sizeof(*in->pas), GFP_KERNEL);
if (!in->pas) {
err = -ENOMEM;
goto err_buf;
}
mlx5_fill_page_frag_array(&srq->buf, in->pas);
srq->wrid = kvmalloc_array(srq->msrq.max, sizeof(u64), GFP_KERNEL);
if (!srq->wrid) {
err = -ENOMEM;
goto err_in;
}
srq->wq_sig = 0;
in->log_page_size = srq->buf.page_shift - MLX5_ADAPTER_PAGE_SHIFT;
if (MLX5_CAP_GEN(dev->mdev, cqe_version) == MLX5_CQE_VERSION_V1 &&
in->type != IB_SRQT_BASIC)
in->user_index = MLX5_IB_DEFAULT_UIDX;
return 0;
err_in:
kvfree(in->pas);
err_buf:
mlx5_frag_buf_free(dev->mdev, &srq->buf);
err_db:
mlx5_db_free(dev->mdev, &srq->db);
return err;
}
static void destroy_srq_user(struct ib_pd *pd, struct mlx5_ib_srq *srq,
struct ib_udata *udata)
{
mlx5_ib_db_unmap_user(
rdma_udata_to_drv_context(
udata,
struct mlx5_ib_ucontext,
ibucontext),
&srq->db);
ib_umem_release(srq->umem);
}
static void destroy_srq_kernel(struct mlx5_ib_dev *dev, struct mlx5_ib_srq *srq)
{
kvfree(srq->wrid);
mlx5_frag_buf_free(dev->mdev, &srq->buf);
mlx5_db_free(dev->mdev, &srq->db);
}
int mlx5_ib_create_srq(struct ib_srq *ib_srq,
struct ib_srq_init_attr *init_attr,
struct ib_udata *udata)
{
struct mlx5_ib_dev *dev = to_mdev(ib_srq->device);
struct mlx5_ib_srq *srq = to_msrq(ib_srq);
size_t desc_size;
size_t buf_size;
int err;
struct mlx5_srq_attr in = {};
__u32 max_srq_wqes = 1 << MLX5_CAP_GEN(dev->mdev, log_max_srq_sz);
__u32 max_sge_sz = MLX5_CAP_GEN(dev->mdev, max_wqe_sz_rq) /
sizeof(struct mlx5_wqe_data_seg);
if (init_attr->srq_type != IB_SRQT_BASIC &&
init_attr->srq_type != IB_SRQT_XRC &&
init_attr->srq_type != IB_SRQT_TM)
return -EOPNOTSUPP;
/* Sanity check SRQ and sge size before proceeding */
if (init_attr->attr.max_wr >= max_srq_wqes ||
init_attr->attr.max_sge > max_sge_sz) {
mlx5_ib_dbg(dev, "max_wr %d,wr_cap %d,max_sge %d, sge_cap:%d\n",
init_attr->attr.max_wr, max_srq_wqes,
init_attr->attr.max_sge, max_sge_sz);
return -EINVAL;
}
err = mlx5_ib_dev_res_cq_init(dev);
if (err)
return err;
mutex_init(&srq->mutex);
spin_lock_init(&srq->lock);
srq->msrq.max = roundup_pow_of_two(init_attr->attr.max_wr + 1);
srq->msrq.max_gs = init_attr->attr.max_sge;
desc_size = sizeof(struct mlx5_wqe_srq_next_seg) +
srq->msrq.max_gs * sizeof(struct mlx5_wqe_data_seg);
if (desc_size == 0 || srq->msrq.max_gs > desc_size)
return -EINVAL;
desc_size = roundup_pow_of_two(desc_size);
desc_size = max_t(size_t, 32, desc_size);
if (desc_size < sizeof(struct mlx5_wqe_srq_next_seg))
return -EINVAL;
srq->msrq.max_avail_gather = (desc_size - sizeof(struct mlx5_wqe_srq_next_seg)) /
sizeof(struct mlx5_wqe_data_seg);
srq->msrq.wqe_shift = ilog2(desc_size);
buf_size = srq->msrq.max * desc_size;
if (buf_size < desc_size)
return -EINVAL;
in.type = init_attr->srq_type;
if (udata)
err = create_srq_user(ib_srq->pd, srq, &in, udata, buf_size);
else
err = create_srq_kernel(dev, srq, &in, buf_size);
if (err) {
mlx5_ib_warn(dev, "create srq %s failed, err %d\n",
udata ? "user" : "kernel", err);
return err;
}
in.log_size = ilog2(srq->msrq.max);
in.wqe_shift = srq->msrq.wqe_shift - 4;
if (srq->wq_sig)
in.flags |= MLX5_SRQ_FLAG_WQ_SIG;
if (init_attr->srq_type == IB_SRQT_XRC && init_attr->ext.xrc.xrcd)
in.xrcd = to_mxrcd(init_attr->ext.xrc.xrcd)->xrcdn;
else
in.xrcd = dev->devr.xrcdn0;
if (init_attr->srq_type == IB_SRQT_TM) {
in.tm_log_list_size =
ilog2(init_attr->ext.tag_matching.max_num_tags) + 1;
if (in.tm_log_list_size >
MLX5_CAP_GEN(dev->mdev, log_tag_matching_list_sz)) {
mlx5_ib_dbg(dev, "TM SRQ max_num_tags exceeding limit\n");
err = -EINVAL;
goto err_usr_kern_srq;
}
in.flags |= MLX5_SRQ_FLAG_RNDV;
}
if (ib_srq_has_cq(init_attr->srq_type))
in.cqn = to_mcq(init_attr->ext.cq)->mcq.cqn;
else
in.cqn = to_mcq(dev->devr.c0)->mcq.cqn;
in.pd = to_mpd(ib_srq->pd)->pdn;
in.db_record = srq->db.dma;
err = mlx5_cmd_create_srq(dev, &srq->msrq, &in);
kvfree(in.pas);
if (err) {
mlx5_ib_dbg(dev, "create SRQ failed, err %d\n", err);
goto err_usr_kern_srq;
}
mlx5_ib_dbg(dev, "create SRQ with srqn 0x%x\n", srq->msrq.srqn);
srq->msrq.event = mlx5_ib_srq_event;
srq->ibsrq.ext.xrc.srq_num = srq->msrq.srqn;
if (udata) {
struct mlx5_ib_create_srq_resp resp = {
.srqn = srq->msrq.srqn,
};
err = ib_respond_udata(udata, resp);
if (err)
goto err_core;
}
init_attr->attr.max_wr = srq->msrq.max - 1;
return 0;
err_core:
mlx5_cmd_destroy_srq(dev, &srq->msrq);
err_usr_kern_srq:
if (udata)
destroy_srq_user(ib_srq->pd, srq, udata);
else
destroy_srq_kernel(dev, srq);
return err;
}
int mlx5_ib_modify_srq(struct ib_srq *ibsrq, struct ib_srq_attr *attr,
enum ib_srq_attr_mask attr_mask, struct ib_udata *udata)
{
struct mlx5_ib_dev *dev = to_mdev(ibsrq->device);
struct mlx5_ib_srq *srq = to_msrq(ibsrq);
int ret;
/* We don't support resizing SRQs yet */
if (attr_mask & IB_SRQ_MAX_WR)
return -EINVAL;
if (attr_mask & IB_SRQ_LIMIT) {
if (attr->srq_limit >= srq->msrq.max)
return -EINVAL;
mutex_lock(&srq->mutex);
ret = mlx5_cmd_arm_srq(dev, &srq->msrq, attr->srq_limit, 1);
mutex_unlock(&srq->mutex);
if (ret)
return ret;
}
return 0;
}
int mlx5_ib_query_srq(struct ib_srq *ibsrq, struct ib_srq_attr *srq_attr)
{
struct mlx5_ib_dev *dev = to_mdev(ibsrq->device);
struct mlx5_ib_srq *srq = to_msrq(ibsrq);
int ret;
struct mlx5_srq_attr *out;
out = kzalloc_obj(*out);
if (!out)
return -ENOMEM;
ret = mlx5_cmd_query_srq(dev, &srq->msrq, out);
if (ret)
goto out_box;
srq_attr->srq_limit = out->lwm;
srq_attr->max_wr = srq->msrq.max - 1;
srq_attr->max_sge = srq->msrq.max_gs;
out_box:
kfree(out);
return ret;
}
int mlx5_ib_destroy_srq(struct ib_srq *srq, struct ib_udata *udata)
{
struct mlx5_ib_dev *dev = to_mdev(srq->device);
struct mlx5_ib_srq *msrq = to_msrq(srq);
int ret;
ret = mlx5_cmd_destroy_srq(dev, &msrq->msrq);
if (ret)
return ret;
if (udata)
destroy_srq_user(srq->pd, msrq, udata);
else
destroy_srq_kernel(dev, msrq);
return 0;
}
void mlx5_ib_free_srq_wqe(struct mlx5_ib_srq *srq, int wqe_index)
{
struct mlx5_wqe_srq_next_seg *next;
/* always called with interrupts disabled. */
spin_lock(&srq->lock);
next = get_wqe(srq, srq->tail);
next->next_wqe_index = cpu_to_be16(wqe_index);
srq->tail = wqe_index;
spin_unlock(&srq->lock);
}
int mlx5_ib_post_srq_recv(struct ib_srq *ibsrq, const struct ib_recv_wr *wr,
const struct ib_recv_wr **bad_wr)
{
struct mlx5_ib_srq *srq = to_msrq(ibsrq);
struct mlx5_wqe_srq_next_seg *next;
struct mlx5_wqe_data_seg *scat;
struct mlx5_ib_dev *dev = to_mdev(ibsrq->device);
struct mlx5_core_dev *mdev = dev->mdev;
unsigned long flags;
int err = 0;
int nreq;
int i;
spin_lock_irqsave(&srq->lock, flags);
if (mdev->state == MLX5_DEVICE_STATE_INTERNAL_ERROR) {
err = -EIO;
*bad_wr = wr;
goto out;
}
for (nreq = 0; wr; nreq++, wr = wr->next) {
if (unlikely(wr->num_sge > srq->msrq.max_gs)) {
err = -EINVAL;
*bad_wr = wr;
break;
}
if (unlikely(srq->head == srq->tail)) {
err = -ENOMEM;
*bad_wr = wr;
break;
}
srq->wrid[srq->head] = wr->wr_id;
next = get_wqe(srq, srq->head);
srq->head = be16_to_cpu(next->next_wqe_index);
scat = (struct mlx5_wqe_data_seg *)(next + 1);
for (i = 0; i < wr->num_sge; i++) {
scat[i].byte_count = cpu_to_be32(wr->sg_list[i].length);
scat[i].lkey = cpu_to_be32(wr->sg_list[i].lkey);
scat[i].addr = cpu_to_be64(wr->sg_list[i].addr);
}
if (i < srq->msrq.max_avail_gather) {
scat[i].byte_count = 0;
scat[i].lkey = dev->mkeys.terminate_scatter_list_mkey;
scat[i].addr = 0;
}
}
if (likely(nreq)) {
srq->wqe_ctr += nreq;
/* Make sure that descriptors are written before
* doorbell record.
*/
wmb();
*srq->db.db = cpu_to_be32(srq->wqe_ctr);
}
out:
spin_unlock_irqrestore(&srq->lock, flags);
return err;
}
@@ -0,0 +1,69 @@
/* SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB */
/*
* Copyright (c) 2013-2018, Mellanox Technologies. All rights reserved.
*/
#ifndef MLX5_IB_SRQ_H
#define MLX5_IB_SRQ_H
enum {
MLX5_SRQ_FLAG_ERR = (1 << 0),
MLX5_SRQ_FLAG_WQ_SIG = (1 << 1),
MLX5_SRQ_FLAG_RNDV = (1 << 2),
};
struct mlx5_srq_attr {
u32 type;
u32 flags;
u32 log_size;
u32 wqe_shift;
u32 log_page_size;
u32 wqe_cnt;
u32 srqn;
u32 xrcd;
u32 page_offset;
u32 cqn;
u32 pd;
u32 lwm;
u32 user_index;
u64 db_record;
__be64 *pas;
struct ib_umem *umem;
u32 tm_log_list_size;
u32 tm_next_tag;
u32 tm_hw_phase_cnt;
u32 tm_sw_phase_cnt;
u16 uid;
};
struct mlx5_ib_dev;
struct mlx5_core_srq {
struct mlx5_core_rsc_common common; /* must be first */
u32 srqn;
int max;
size_t max_gs;
size_t max_avail_gather;
int wqe_shift;
void (*event)(struct mlx5_core_srq *srq, enum mlx5_event e);
u16 uid;
};
struct mlx5_srq_table {
struct notifier_block nb;
struct xarray array;
};
int mlx5_cmd_create_srq(struct mlx5_ib_dev *dev, struct mlx5_core_srq *srq,
struct mlx5_srq_attr *in);
int mlx5_cmd_destroy_srq(struct mlx5_ib_dev *dev, struct mlx5_core_srq *srq);
int mlx5_cmd_query_srq(struct mlx5_ib_dev *dev, struct mlx5_core_srq *srq,
struct mlx5_srq_attr *out);
int mlx5_cmd_arm_srq(struct mlx5_ib_dev *dev, struct mlx5_core_srq *srq,
u16 lwm, int is_srq);
struct mlx5_core_srq *mlx5_cmd_get_srq(struct mlx5_ib_dev *dev, u32 srqn);
int mlx5_init_srq_table(struct mlx5_ib_dev *dev);
void mlx5_cleanup_srq_table(struct mlx5_ib_dev *dev);
#endif /* MLX5_IB_SRQ_H */
@@ -0,0 +1,781 @@
// SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB
/*
* Copyright (c) 2013-2018, Mellanox Technologies inc. All rights reserved.
*/
#include <linux/kernel.h>
#include <linux/mlx5/driver.h>
#include "mlx5_ib.h"
#include "srq.h"
#include "qp.h"
static int get_pas_size(struct mlx5_srq_attr *in)
{
u32 log_page_size = in->log_page_size + 12;
u32 log_srq_size = in->log_size;
u32 log_rq_stride = in->wqe_shift;
u32 page_offset = in->page_offset;
u32 po_quanta = 1 << (log_page_size - 6);
u32 rq_sz = 1 << (log_srq_size + 4 + log_rq_stride);
u32 page_size = 1 << log_page_size;
u32 rq_sz_po = rq_sz + (page_offset * po_quanta);
u32 rq_num_pas = DIV_ROUND_UP(rq_sz_po, page_size);
return rq_num_pas * sizeof(u64);
}
static void set_wq(void *wq, struct mlx5_srq_attr *in)
{
MLX5_SET(wq, wq, wq_signature, !!(in->flags
& MLX5_SRQ_FLAG_WQ_SIG));
MLX5_SET(wq, wq, log_wq_pg_sz, in->log_page_size);
MLX5_SET(wq, wq, log_wq_stride, in->wqe_shift + 4);
MLX5_SET(wq, wq, log_wq_sz, in->log_size);
MLX5_SET(wq, wq, page_offset, in->page_offset);
MLX5_SET(wq, wq, lwm, in->lwm);
MLX5_SET(wq, wq, pd, in->pd);
MLX5_SET64(wq, wq, dbr_addr, in->db_record);
}
static void set_srqc(void *srqc, struct mlx5_srq_attr *in)
{
MLX5_SET(srqc, srqc, wq_signature, !!(in->flags
& MLX5_SRQ_FLAG_WQ_SIG));
MLX5_SET(srqc, srqc, log_page_size, in->log_page_size);
MLX5_SET(srqc, srqc, log_rq_stride, in->wqe_shift);
MLX5_SET(srqc, srqc, log_srq_size, in->log_size);
MLX5_SET(srqc, srqc, page_offset, in->page_offset);
MLX5_SET(srqc, srqc, lwm, in->lwm);
MLX5_SET(srqc, srqc, pd, in->pd);
MLX5_SET64(srqc, srqc, dbr_addr, in->db_record);
MLX5_SET(srqc, srqc, xrcd, in->xrcd);
MLX5_SET(srqc, srqc, cqn, in->cqn);
}
static void get_wq(void *wq, struct mlx5_srq_attr *in)
{
if (MLX5_GET(wq, wq, wq_signature))
in->flags &= MLX5_SRQ_FLAG_WQ_SIG;
in->log_page_size = MLX5_GET(wq, wq, log_wq_pg_sz);
in->wqe_shift = MLX5_GET(wq, wq, log_wq_stride) - 4;
in->log_size = MLX5_GET(wq, wq, log_wq_sz);
in->page_offset = MLX5_GET(wq, wq, page_offset);
in->lwm = MLX5_GET(wq, wq, lwm);
in->pd = MLX5_GET(wq, wq, pd);
in->db_record = MLX5_GET64(wq, wq, dbr_addr);
}
static void get_srqc(void *srqc, struct mlx5_srq_attr *in)
{
if (MLX5_GET(srqc, srqc, wq_signature))
in->flags &= MLX5_SRQ_FLAG_WQ_SIG;
in->log_page_size = MLX5_GET(srqc, srqc, log_page_size);
in->wqe_shift = MLX5_GET(srqc, srqc, log_rq_stride);
in->log_size = MLX5_GET(srqc, srqc, log_srq_size);
in->page_offset = MLX5_GET(srqc, srqc, page_offset);
in->lwm = MLX5_GET(srqc, srqc, lwm);
in->pd = MLX5_GET(srqc, srqc, pd);
in->db_record = MLX5_GET64(srqc, srqc, dbr_addr);
}
struct mlx5_core_srq *mlx5_cmd_get_srq(struct mlx5_ib_dev *dev, u32 srqn)
{
struct mlx5_srq_table *table = &dev->srq_table;
struct mlx5_core_srq *srq;
xa_lock_irq(&table->array);
srq = xa_load(&table->array, srqn);
if (srq)
refcount_inc(&srq->common.refcount);
xa_unlock_irq(&table->array);
return srq;
}
static int __set_srq_page_size(struct mlx5_srq_attr *in,
unsigned long page_size)
{
if (!page_size)
return -EINVAL;
in->log_page_size = order_base_2(page_size) - MLX5_ADAPTER_PAGE_SHIFT;
if (WARN_ON(get_pas_size(in) !=
ib_umem_num_dma_blocks(in->umem, page_size) * sizeof(u64)))
return -EINVAL;
return 0;
}
#define set_srq_page_size(in, typ, log_pgsz_fld) \
__set_srq_page_size(in, mlx5_umem_find_best_quantized_pgoff( \
(in)->umem, typ, log_pgsz_fld, \
MLX5_ADAPTER_PAGE_SHIFT, page_offset, \
64, &(in)->page_offset))
static int create_srq_cmd(struct mlx5_ib_dev *dev, struct mlx5_core_srq *srq,
struct mlx5_srq_attr *in)
{
u32 create_out[MLX5_ST_SZ_DW(create_srq_out)] = {0};
void *create_in;
void *srqc;
void *pas;
int pas_size;
int inlen;
int err;
if (in->umem) {
err = set_srq_page_size(in, srqc, log_page_size);
if (err)
return err;
}
pas_size = get_pas_size(in);
inlen = MLX5_ST_SZ_BYTES(create_srq_in) + pas_size;
create_in = kvzalloc(inlen, GFP_KERNEL);
if (!create_in)
return -ENOMEM;
MLX5_SET(create_srq_in, create_in, uid, in->uid);
srqc = MLX5_ADDR_OF(create_srq_in, create_in, srq_context_entry);
pas = MLX5_ADDR_OF(create_srq_in, create_in, pas);
set_srqc(srqc, in);
if (in->umem)
mlx5_ib_populate_pas(
in->umem,
1UL << (in->log_page_size + MLX5_ADAPTER_PAGE_SHIFT),
pas, 0);
else
memcpy(pas, in->pas, pas_size);
MLX5_SET(create_srq_in, create_in, opcode,
MLX5_CMD_OP_CREATE_SRQ);
err = mlx5_cmd_exec(dev->mdev, create_in, inlen, create_out,
sizeof(create_out));
kvfree(create_in);
if (!err) {
srq->srqn = MLX5_GET(create_srq_out, create_out, srqn);
srq->uid = in->uid;
}
return err;
}
static int destroy_srq_cmd(struct mlx5_ib_dev *dev, struct mlx5_core_srq *srq)
{
u32 in[MLX5_ST_SZ_DW(destroy_srq_in)] = {};
MLX5_SET(destroy_srq_in, in, opcode, MLX5_CMD_OP_DESTROY_SRQ);
MLX5_SET(destroy_srq_in, in, srqn, srq->srqn);
MLX5_SET(destroy_srq_in, in, uid, srq->uid);
return mlx5_cmd_exec_in(dev->mdev, destroy_srq, in);
}
static int arm_srq_cmd(struct mlx5_ib_dev *dev, struct mlx5_core_srq *srq,
u16 lwm, int is_srq)
{
u32 in[MLX5_ST_SZ_DW(arm_rq_in)] = {};
MLX5_SET(arm_rq_in, in, opcode, MLX5_CMD_OP_ARM_RQ);
MLX5_SET(arm_rq_in, in, op_mod, MLX5_ARM_RQ_IN_OP_MOD_SRQ);
MLX5_SET(arm_rq_in, in, srq_number, srq->srqn);
MLX5_SET(arm_rq_in, in, lwm, lwm);
MLX5_SET(arm_rq_in, in, uid, srq->uid);
return mlx5_cmd_exec_in(dev->mdev, arm_rq, in);
}
static int query_srq_cmd(struct mlx5_ib_dev *dev, struct mlx5_core_srq *srq,
struct mlx5_srq_attr *out)
{
u32 in[MLX5_ST_SZ_DW(query_srq_in)] = {};
u32 *srq_out;
void *srqc;
int err;
srq_out = kvzalloc(MLX5_ST_SZ_BYTES(query_srq_out), GFP_KERNEL);
if (!srq_out)
return -ENOMEM;
MLX5_SET(query_srq_in, in, opcode, MLX5_CMD_OP_QUERY_SRQ);
MLX5_SET(query_srq_in, in, srqn, srq->srqn);
err = mlx5_cmd_exec_inout(dev->mdev, query_srq, in, srq_out);
if (err)
goto out;
srqc = MLX5_ADDR_OF(query_srq_out, srq_out, srq_context_entry);
get_srqc(srqc, out);
if (MLX5_GET(srqc, srqc, state) != MLX5_SRQC_STATE_GOOD)
out->flags |= MLX5_SRQ_FLAG_ERR;
out:
kvfree(srq_out);
return err;
}
static int create_xrc_srq_cmd(struct mlx5_ib_dev *dev,
struct mlx5_core_srq *srq,
struct mlx5_srq_attr *in)
{
u32 create_out[MLX5_ST_SZ_DW(create_xrc_srq_out)];
void *create_in;
void *xrc_srqc;
void *pas;
int pas_size;
int inlen;
int err;
if (in->umem) {
err = set_srq_page_size(in, xrc_srqc, log_page_size);
if (err)
return err;
}
pas_size = get_pas_size(in);
inlen = MLX5_ST_SZ_BYTES(create_xrc_srq_in) + pas_size;
create_in = kvzalloc(inlen, GFP_KERNEL);
if (!create_in)
return -ENOMEM;
MLX5_SET(create_xrc_srq_in, create_in, uid, in->uid);
xrc_srqc = MLX5_ADDR_OF(create_xrc_srq_in, create_in,
xrc_srq_context_entry);
pas = MLX5_ADDR_OF(create_xrc_srq_in, create_in, pas);
set_srqc(xrc_srqc, in);
MLX5_SET(xrc_srqc, xrc_srqc, user_index, in->user_index);
if (in->umem)
mlx5_ib_populate_pas(
in->umem,
1UL << (in->log_page_size + MLX5_ADAPTER_PAGE_SHIFT),
pas, 0);
else
memcpy(pas, in->pas, pas_size);
MLX5_SET(create_xrc_srq_in, create_in, opcode,
MLX5_CMD_OP_CREATE_XRC_SRQ);
memset(create_out, 0, sizeof(create_out));
err = mlx5_cmd_exec(dev->mdev, create_in, inlen, create_out,
sizeof(create_out));
if (err)
goto out;
srq->srqn = MLX5_GET(create_xrc_srq_out, create_out, xrc_srqn);
srq->uid = in->uid;
out:
kvfree(create_in);
return err;
}
static int destroy_xrc_srq_cmd(struct mlx5_ib_dev *dev,
struct mlx5_core_srq *srq)
{
u32 in[MLX5_ST_SZ_DW(destroy_xrc_srq_in)] = {};
MLX5_SET(destroy_xrc_srq_in, in, opcode, MLX5_CMD_OP_DESTROY_XRC_SRQ);
MLX5_SET(destroy_xrc_srq_in, in, xrc_srqn, srq->srqn);
MLX5_SET(destroy_xrc_srq_in, in, uid, srq->uid);
return mlx5_cmd_exec_in(dev->mdev, destroy_xrc_srq, in);
}
static int arm_xrc_srq_cmd(struct mlx5_ib_dev *dev, struct mlx5_core_srq *srq,
u16 lwm)
{
u32 in[MLX5_ST_SZ_DW(arm_xrc_srq_in)] = {};
MLX5_SET(arm_xrc_srq_in, in, opcode, MLX5_CMD_OP_ARM_XRC_SRQ);
MLX5_SET(arm_xrc_srq_in, in, op_mod,
MLX5_ARM_XRC_SRQ_IN_OP_MOD_XRC_SRQ);
MLX5_SET(arm_xrc_srq_in, in, xrc_srqn, srq->srqn);
MLX5_SET(arm_xrc_srq_in, in, lwm, lwm);
MLX5_SET(arm_xrc_srq_in, in, uid, srq->uid);
return mlx5_cmd_exec_in(dev->mdev, arm_xrc_srq, in);
}
static int query_xrc_srq_cmd(struct mlx5_ib_dev *dev,
struct mlx5_core_srq *srq,
struct mlx5_srq_attr *out)
{
u32 in[MLX5_ST_SZ_DW(query_xrc_srq_in)] = {};
u32 *xrcsrq_out;
void *xrc_srqc;
int err;
xrcsrq_out = kvzalloc(MLX5_ST_SZ_BYTES(query_xrc_srq_out), GFP_KERNEL);
if (!xrcsrq_out)
return -ENOMEM;
MLX5_SET(query_xrc_srq_in, in, opcode, MLX5_CMD_OP_QUERY_XRC_SRQ);
MLX5_SET(query_xrc_srq_in, in, xrc_srqn, srq->srqn);
err = mlx5_cmd_exec_inout(dev->mdev, query_xrc_srq, in, xrcsrq_out);
if (err)
goto out;
xrc_srqc = MLX5_ADDR_OF(query_xrc_srq_out, xrcsrq_out,
xrc_srq_context_entry);
get_srqc(xrc_srqc, out);
if (MLX5_GET(xrc_srqc, xrc_srqc, state) != MLX5_XRC_SRQC_STATE_GOOD)
out->flags |= MLX5_SRQ_FLAG_ERR;
out:
kvfree(xrcsrq_out);
return err;
}
static int create_rmp_cmd(struct mlx5_ib_dev *dev, struct mlx5_core_srq *srq,
struct mlx5_srq_attr *in)
{
void *create_out = NULL;
void *create_in = NULL;
void *rmpc;
void *wq;
void *pas;
int pas_size;
int outlen;
int inlen;
int err;
if (in->umem) {
err = set_srq_page_size(in, wq, log_wq_pg_sz);
if (err)
return err;
}
pas_size = get_pas_size(in);
inlen = MLX5_ST_SZ_BYTES(create_rmp_in) + pas_size;
outlen = MLX5_ST_SZ_BYTES(create_rmp_out);
create_in = kvzalloc(inlen, GFP_KERNEL);
create_out = kvzalloc(outlen, GFP_KERNEL);
if (!create_in || !create_out) {
err = -ENOMEM;
goto out;
}
rmpc = MLX5_ADDR_OF(create_rmp_in, create_in, ctx);
wq = MLX5_ADDR_OF(rmpc, rmpc, wq);
MLX5_SET(rmpc, rmpc, state, MLX5_RMPC_STATE_RDY);
MLX5_SET(create_rmp_in, create_in, uid, in->uid);
pas = MLX5_ADDR_OF(rmpc, rmpc, wq.pas);
set_wq(wq, in);
if (in->umem)
mlx5_ib_populate_pas(
in->umem,
1UL << (in->log_page_size + MLX5_ADAPTER_PAGE_SHIFT),
pas, 0);
else
memcpy(pas, in->pas, pas_size);
MLX5_SET(create_rmp_in, create_in, opcode, MLX5_CMD_OP_CREATE_RMP);
err = mlx5_cmd_exec(dev->mdev, create_in, inlen, create_out, outlen);
if (!err) {
srq->srqn = MLX5_GET(create_rmp_out, create_out, rmpn);
srq->uid = in->uid;
}
out:
kvfree(create_in);
kvfree(create_out);
return err;
}
static int destroy_rmp_cmd(struct mlx5_ib_dev *dev, struct mlx5_core_srq *srq)
{
u32 in[MLX5_ST_SZ_DW(destroy_rmp_in)] = {};
MLX5_SET(destroy_rmp_in, in, opcode, MLX5_CMD_OP_DESTROY_RMP);
MLX5_SET(destroy_rmp_in, in, rmpn, srq->srqn);
MLX5_SET(destroy_rmp_in, in, uid, srq->uid);
return mlx5_cmd_exec_in(dev->mdev, destroy_rmp, in);
}
static int arm_rmp_cmd(struct mlx5_ib_dev *dev, struct mlx5_core_srq *srq,
u16 lwm)
{
void *out = NULL;
void *in = NULL;
void *rmpc;
void *wq;
void *bitmask;
int outlen;
int inlen;
int err;
inlen = MLX5_ST_SZ_BYTES(modify_rmp_in);
outlen = MLX5_ST_SZ_BYTES(modify_rmp_out);
in = kvzalloc(inlen, GFP_KERNEL);
out = kvzalloc(outlen, GFP_KERNEL);
if (!in || !out) {
err = -ENOMEM;
goto out;
}
rmpc = MLX5_ADDR_OF(modify_rmp_in, in, ctx);
bitmask = MLX5_ADDR_OF(modify_rmp_in, in, bitmask);
wq = MLX5_ADDR_OF(rmpc, rmpc, wq);
MLX5_SET(modify_rmp_in, in, rmp_state, MLX5_RMPC_STATE_RDY);
MLX5_SET(modify_rmp_in, in, rmpn, srq->srqn);
MLX5_SET(modify_rmp_in, in, uid, srq->uid);
MLX5_SET(wq, wq, lwm, lwm);
MLX5_SET(rmp_bitmask, bitmask, lwm, 1);
MLX5_SET(rmpc, rmpc, state, MLX5_RMPC_STATE_RDY);
MLX5_SET(modify_rmp_in, in, opcode, MLX5_CMD_OP_MODIFY_RMP);
err = mlx5_cmd_exec_inout(dev->mdev, modify_rmp, in, out);
out:
kvfree(in);
kvfree(out);
return err;
}
static int query_rmp_cmd(struct mlx5_ib_dev *dev, struct mlx5_core_srq *srq,
struct mlx5_srq_attr *out)
{
u32 *rmp_out = NULL;
u32 *rmp_in = NULL;
void *rmpc;
int outlen;
int inlen;
int err;
outlen = MLX5_ST_SZ_BYTES(query_rmp_out);
inlen = MLX5_ST_SZ_BYTES(query_rmp_in);
rmp_out = kvzalloc(outlen, GFP_KERNEL);
rmp_in = kvzalloc(inlen, GFP_KERNEL);
if (!rmp_out || !rmp_in) {
err = -ENOMEM;
goto out;
}
MLX5_SET(query_rmp_in, rmp_in, opcode, MLX5_CMD_OP_QUERY_RMP);
MLX5_SET(query_rmp_in, rmp_in, rmpn, srq->srqn);
err = mlx5_cmd_exec_inout(dev->mdev, query_rmp, rmp_in, rmp_out);
if (err)
goto out;
rmpc = MLX5_ADDR_OF(query_rmp_out, rmp_out, rmp_context);
get_wq(MLX5_ADDR_OF(rmpc, rmpc, wq), out);
if (MLX5_GET(rmpc, rmpc, state) != MLX5_RMPC_STATE_RDY)
out->flags |= MLX5_SRQ_FLAG_ERR;
out:
kvfree(rmp_out);
kvfree(rmp_in);
return err;
}
static int create_xrq_cmd(struct mlx5_ib_dev *dev, struct mlx5_core_srq *srq,
struct mlx5_srq_attr *in)
{
u32 create_out[MLX5_ST_SZ_DW(create_xrq_out)] = {0};
void *create_in;
void *xrqc;
void *wq;
void *pas;
int pas_size;
int inlen;
int err;
if (in->umem) {
err = set_srq_page_size(in, wq, log_wq_pg_sz);
if (err)
return err;
}
pas_size = get_pas_size(in);
inlen = MLX5_ST_SZ_BYTES(create_xrq_in) + pas_size;
create_in = kvzalloc(inlen, GFP_KERNEL);
if (!create_in)
return -ENOMEM;
xrqc = MLX5_ADDR_OF(create_xrq_in, create_in, xrq_context);
wq = MLX5_ADDR_OF(xrqc, xrqc, wq);
pas = MLX5_ADDR_OF(xrqc, xrqc, wq.pas);
set_wq(wq, in);
if (in->umem)
mlx5_ib_populate_pas(
in->umem,
1UL << (in->log_page_size + MLX5_ADAPTER_PAGE_SHIFT),
pas, 0);
else
memcpy(pas, in->pas, pas_size);
if (in->type == IB_SRQT_TM) {
MLX5_SET(xrqc, xrqc, topology, MLX5_XRQC_TOPOLOGY_TAG_MATCHING);
if (in->flags & MLX5_SRQ_FLAG_RNDV)
MLX5_SET(xrqc, xrqc, offload, MLX5_XRQC_OFFLOAD_RNDV);
MLX5_SET(xrqc, xrqc,
tag_matching_topology_context.log_matching_list_sz,
in->tm_log_list_size);
}
MLX5_SET(xrqc, xrqc, user_index, in->user_index);
MLX5_SET(xrqc, xrqc, cqn, in->cqn);
MLX5_SET(create_xrq_in, create_in, opcode, MLX5_CMD_OP_CREATE_XRQ);
MLX5_SET(create_xrq_in, create_in, uid, in->uid);
err = mlx5_cmd_exec(dev->mdev, create_in, inlen, create_out,
sizeof(create_out));
kvfree(create_in);
if (!err) {
srq->srqn = MLX5_GET(create_xrq_out, create_out, xrqn);
srq->uid = in->uid;
}
return err;
}
static int destroy_xrq_cmd(struct mlx5_ib_dev *dev, struct mlx5_core_srq *srq)
{
u32 in[MLX5_ST_SZ_DW(destroy_xrq_in)] = {};
MLX5_SET(destroy_xrq_in, in, opcode, MLX5_CMD_OP_DESTROY_XRQ);
MLX5_SET(destroy_xrq_in, in, xrqn, srq->srqn);
MLX5_SET(destroy_xrq_in, in, uid, srq->uid);
return mlx5_cmd_exec_in(dev->mdev, destroy_xrq, in);
}
static int arm_xrq_cmd(struct mlx5_ib_dev *dev,
struct mlx5_core_srq *srq,
u16 lwm)
{
u32 in[MLX5_ST_SZ_DW(arm_rq_in)] = {};
MLX5_SET(arm_rq_in, in, opcode, MLX5_CMD_OP_ARM_RQ);
MLX5_SET(arm_rq_in, in, op_mod, MLX5_ARM_RQ_IN_OP_MOD_XRQ);
MLX5_SET(arm_rq_in, in, srq_number, srq->srqn);
MLX5_SET(arm_rq_in, in, lwm, lwm);
MLX5_SET(arm_rq_in, in, uid, srq->uid);
return mlx5_cmd_exec_in(dev->mdev, arm_rq, in);
}
static int query_xrq_cmd(struct mlx5_ib_dev *dev, struct mlx5_core_srq *srq,
struct mlx5_srq_attr *out)
{
u32 in[MLX5_ST_SZ_DW(query_xrq_in)] = {};
u32 *xrq_out;
int outlen = MLX5_ST_SZ_BYTES(query_xrq_out);
void *xrqc;
int err;
xrq_out = kvzalloc(outlen, GFP_KERNEL);
if (!xrq_out)
return -ENOMEM;
MLX5_SET(query_xrq_in, in, opcode, MLX5_CMD_OP_QUERY_XRQ);
MLX5_SET(query_xrq_in, in, xrqn, srq->srqn);
err = mlx5_cmd_exec_inout(dev->mdev, query_xrq, in, xrq_out);
if (err)
goto out;
xrqc = MLX5_ADDR_OF(query_xrq_out, xrq_out, xrq_context);
get_wq(MLX5_ADDR_OF(xrqc, xrqc, wq), out);
if (MLX5_GET(xrqc, xrqc, state) != MLX5_XRQC_STATE_GOOD)
out->flags |= MLX5_SRQ_FLAG_ERR;
out->tm_next_tag =
MLX5_GET(xrqc, xrqc,
tag_matching_topology_context.append_next_index);
out->tm_hw_phase_cnt =
MLX5_GET(xrqc, xrqc,
tag_matching_topology_context.hw_phase_cnt);
out->tm_sw_phase_cnt =
MLX5_GET(xrqc, xrqc,
tag_matching_topology_context.sw_phase_cnt);
out:
kvfree(xrq_out);
return err;
}
static int create_srq_split(struct mlx5_ib_dev *dev, struct mlx5_core_srq *srq,
struct mlx5_srq_attr *in)
{
if (!dev->mdev->issi)
return create_srq_cmd(dev, srq, in);
switch (srq->common.res) {
case MLX5_RES_XSRQ:
return create_xrc_srq_cmd(dev, srq, in);
case MLX5_RES_XRQ:
return create_xrq_cmd(dev, srq, in);
default:
return create_rmp_cmd(dev, srq, in);
}
}
static int destroy_srq_split(struct mlx5_ib_dev *dev, struct mlx5_core_srq *srq)
{
if (!dev->mdev->issi)
return destroy_srq_cmd(dev, srq);
switch (srq->common.res) {
case MLX5_RES_XSRQ:
return destroy_xrc_srq_cmd(dev, srq);
case MLX5_RES_XRQ:
return destroy_xrq_cmd(dev, srq);
default:
return destroy_rmp_cmd(dev, srq);
}
}
int mlx5_cmd_create_srq(struct mlx5_ib_dev *dev, struct mlx5_core_srq *srq,
struct mlx5_srq_attr *in)
{
struct mlx5_srq_table *table = &dev->srq_table;
int err;
switch (in->type) {
case IB_SRQT_XRC:
srq->common.res = MLX5_RES_XSRQ;
break;
case IB_SRQT_TM:
srq->common.res = MLX5_RES_XRQ;
break;
default:
srq->common.res = MLX5_RES_SRQ;
}
err = create_srq_split(dev, srq, in);
if (err)
return err;
refcount_set(&srq->common.refcount, 1);
init_completion(&srq->common.free);
err = xa_err(xa_store_irq(&table->array, srq->srqn, srq, GFP_KERNEL));
if (err)
goto err_destroy_srq_split;
return 0;
err_destroy_srq_split:
destroy_srq_split(dev, srq);
return err;
}
int mlx5_cmd_destroy_srq(struct mlx5_ib_dev *dev, struct mlx5_core_srq *srq)
{
struct mlx5_srq_table *table = &dev->srq_table;
struct mlx5_core_srq *tmp;
int err;
/* Delete entry, but leave index occupied */
tmp = xa_cmpxchg_irq(&table->array, srq->srqn, srq, XA_ZERO_ENTRY, 0);
if (WARN_ON(tmp != srq))
return xa_err(tmp) ?: -EINVAL;
err = destroy_srq_split(dev, srq);
if (err) {
/*
* We don't need to check returned result for an error,
* because we are storing in pre-allocated space xarray
* entry and it can't fail at this stage.
*/
xa_cmpxchg_irq(&table->array, srq->srqn, XA_ZERO_ENTRY, srq, 0);
return err;
}
/*
* A race can occur where a concurrent create gets the same srqn
* (after hardware released it) and overwrites XA_ZERO_ENTRY with
* its new SRQ before we reach here. In that case, we must not erase
* the entry as it now belongs to the new SRQ.
*/
xa_cmpxchg_irq(&table->array, srq->srqn, XA_ZERO_ENTRY, NULL, 0);
mlx5_core_res_put(&srq->common);
wait_for_completion(&srq->common.free);
return 0;
}
int mlx5_cmd_query_srq(struct mlx5_ib_dev *dev, struct mlx5_core_srq *srq,
struct mlx5_srq_attr *out)
{
if (!dev->mdev->issi)
return query_srq_cmd(dev, srq, out);
switch (srq->common.res) {
case MLX5_RES_XSRQ:
return query_xrc_srq_cmd(dev, srq, out);
case MLX5_RES_XRQ:
return query_xrq_cmd(dev, srq, out);
default:
return query_rmp_cmd(dev, srq, out);
}
}
int mlx5_cmd_arm_srq(struct mlx5_ib_dev *dev, struct mlx5_core_srq *srq,
u16 lwm, int is_srq)
{
if (!dev->mdev->issi)
return arm_srq_cmd(dev, srq, lwm, is_srq);
switch (srq->common.res) {
case MLX5_RES_XSRQ:
return arm_xrc_srq_cmd(dev, srq, lwm);
case MLX5_RES_XRQ:
return arm_xrq_cmd(dev, srq, lwm);
default:
return arm_rmp_cmd(dev, srq, lwm);
}
}
static int srq_event_notifier(struct notifier_block *nb,
unsigned long type, void *data)
{
struct mlx5_srq_table *table;
struct mlx5_core_srq *srq;
struct mlx5_eqe *eqe;
u32 srqn;
if (type != MLX5_EVENT_TYPE_SRQ_CATAS_ERROR &&
type != MLX5_EVENT_TYPE_SRQ_RQ_LIMIT)
return NOTIFY_DONE;
table = container_of(nb, struct mlx5_srq_table, nb);
eqe = data;
srqn = be32_to_cpu(eqe->data.qp_srq.qp_srq_n) & 0xffffff;
xa_lock(&table->array);
srq = xa_load(&table->array, srqn);
if (srq)
refcount_inc(&srq->common.refcount);
xa_unlock(&table->array);
if (!srq)
return NOTIFY_OK;
srq->event(srq, eqe->type);
mlx5_core_res_put(&srq->common);
return NOTIFY_OK;
}
int mlx5_init_srq_table(struct mlx5_ib_dev *dev)
{
struct mlx5_srq_table *table = &dev->srq_table;
memset(table, 0, sizeof(*table));
xa_init_flags(&table->array, XA_FLAGS_LOCK_IRQ);
table->nb.notifier_call = srq_event_notifier;
mlx5_notifier_register(dev->mdev, &table->nb);
return 0;
}
void mlx5_cleanup_srq_table(struct mlx5_ib_dev *dev)
{
struct mlx5_srq_table *table = &dev->srq_table;
mlx5_notifier_unregister(dev->mdev, &table->nb);
}
@@ -0,0 +1,277 @@
// SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB
/*
* Copyright (c) 2020, Mellanox Technologies inc. All rights reserved.
*/
#include <rdma/uverbs_ioctl.h>
#include <rdma/mlx5_user_ioctl_cmds.h>
#include <rdma/mlx5_user_ioctl_verbs.h>
#include <linux/mlx5/driver.h>
#include <linux/mlx5/eswitch.h>
#include <linux/mlx5/vport.h>
#include "mlx5_ib.h"
#include "data_direct.h"
#define UVERBS_MODULE_NAME mlx5_ib
#include <rdma/uverbs_named_ioctl.h>
static int UVERBS_HANDLER(MLX5_IB_METHOD_PD_QUERY)(
struct uverbs_attr_bundle *attrs)
{
struct ib_pd *pd =
uverbs_attr_get_obj(attrs, MLX5_IB_ATTR_QUERY_PD_HANDLE);
struct mlx5_ib_pd *mpd = to_mpd(pd);
return uverbs_copy_to(attrs, MLX5_IB_ATTR_QUERY_PD_RESP_PDN,
&mpd->pdn, sizeof(mpd->pdn));
}
static int fill_vport_icm_addr(struct mlx5_core_dev *mdev, u16 vport,
struct mlx5_ib_uapi_query_port *info)
{
u32 out[MLX5_ST_SZ_DW(query_esw_vport_context_out)] = {};
u32 in[MLX5_ST_SZ_DW(query_esw_vport_context_in)] = {};
bool sw_owner_supp;
u64 icm_rx;
u64 icm_tx;
int err;
sw_owner_supp = MLX5_CAP_ESW_FLOWTABLE_FDB(mdev, sw_owner) ||
MLX5_CAP_ESW_FLOWTABLE_FDB(mdev, sw_owner_v2);
if (vport == MLX5_VPORT_UPLINK) {
icm_rx = MLX5_CAP64_ESW_FLOWTABLE(mdev,
sw_steering_uplink_icm_address_rx);
icm_tx = MLX5_CAP64_ESW_FLOWTABLE(mdev,
sw_steering_uplink_icm_address_tx);
} else {
MLX5_SET(query_esw_vport_context_in, in, opcode,
MLX5_CMD_OP_QUERY_ESW_VPORT_CONTEXT);
MLX5_SET(query_esw_vport_context_in, in, vport_number, vport);
MLX5_SET(query_esw_vport_context_in, in, other_vport, true);
err = mlx5_cmd_exec_inout(mdev, query_esw_vport_context, in,
out);
if (err)
return err;
icm_rx = MLX5_GET64(
query_esw_vport_context_out, out,
esw_vport_context.sw_steering_vport_icm_address_rx);
icm_tx = MLX5_GET64(
query_esw_vport_context_out, out,
esw_vport_context.sw_steering_vport_icm_address_tx);
}
if (sw_owner_supp && icm_rx) {
info->vport_steering_icm_rx = icm_rx;
info->flags |=
MLX5_IB_UAPI_QUERY_PORT_VPORT_STEERING_ICM_RX;
}
if (sw_owner_supp && icm_tx) {
info->vport_steering_icm_tx = icm_tx;
info->flags |=
MLX5_IB_UAPI_QUERY_PORT_VPORT_STEERING_ICM_TX;
}
return 0;
}
static int fill_vport_vhca_id(struct mlx5_core_dev *mdev, u16 vport,
struct mlx5_ib_uapi_query_port *info)
{
int err = mlx5_vport_get_vhca_id(mdev, vport, &info->vport_vhca_id);
if (err)
return err;
info->flags |= MLX5_IB_UAPI_QUERY_PORT_VPORT_VHCA_ID;
return 0;
}
static int fill_multiport_info(struct mlx5_ib_dev *dev, u32 port_num,
struct mlx5_ib_uapi_query_port *info)
{
struct mlx5_core_dev *mdev;
mdev = mlx5_ib_get_native_port_mdev(dev, port_num, NULL);
if (!mdev)
return -EINVAL;
info->vport_vhca_id = MLX5_CAP_GEN(mdev, vhca_id);
info->flags |= MLX5_IB_UAPI_QUERY_PORT_VPORT_VHCA_ID;
mlx5_ib_put_native_port_mdev(dev, port_num);
return 0;
}
static int fill_switchdev_info(struct mlx5_ib_dev *dev, u32 port_num,
struct mlx5_ib_uapi_query_port *info)
{
struct mlx5_eswitch_rep *rep;
struct mlx5_core_dev *mdev;
int err;
rep = dev->port[port_num - 1].rep;
if (!rep)
return -EOPNOTSUPP;
mdev = mlx5_eswitch_get_core_dev(rep->esw);
if (!mdev)
return -EINVAL;
info->vport = rep->vport;
info->flags |= MLX5_IB_UAPI_QUERY_PORT_VPORT;
if (rep->vport != MLX5_VPORT_UPLINK) {
err = fill_vport_vhca_id(mdev, rep->vport, info);
if (err)
return err;
}
info->esw_owner_vhca_id = MLX5_CAP_GEN(mdev, vhca_id);
info->flags |= MLX5_IB_UAPI_QUERY_PORT_ESW_OWNER_VHCA_ID;
err = fill_vport_icm_addr(mdev, rep->vport, info);
if (err)
return err;
if (mlx5_eswitch_vport_match_metadata_enabled(rep->esw)) {
info->reg_c0.value = mlx5_eswitch_get_vport_metadata_for_match(
rep->esw, rep->vport);
info->reg_c0.mask = mlx5_eswitch_get_vport_metadata_mask();
info->flags |= MLX5_IB_UAPI_QUERY_PORT_VPORT_REG_C0;
}
return 0;
}
static int UVERBS_HANDLER(MLX5_IB_METHOD_QUERY_PORT)(
struct uverbs_attr_bundle *attrs)
{
struct mlx5_ib_uapi_query_port info = {};
struct mlx5_ib_ucontext *c;
struct mlx5_ib_dev *dev;
u32 port_num;
int ret;
if (uverbs_copy_from(&port_num, attrs,
MLX5_IB_ATTR_QUERY_PORT_PORT_NUM))
return -EFAULT;
c = to_mucontext(ib_uverbs_get_ucontext(attrs));
if (IS_ERR(c))
return PTR_ERR(c);
dev = to_mdev(c->ibucontext.device);
if (!rdma_is_port_valid(&dev->ib_dev, port_num))
return -EINVAL;
if (mlx5_eswitch_mode(dev->mdev) == MLX5_ESWITCH_OFFLOADS) {
ret = fill_switchdev_info(dev, port_num, &info);
if (ret)
return ret;
} else if (mlx5_core_mp_enabled(dev->mdev)) {
ret = fill_multiport_info(dev, port_num, &info);
if (ret)
return ret;
}
return uverbs_copy_to_struct_or_zero(attrs, MLX5_IB_ATTR_QUERY_PORT, &info,
sizeof(info));
}
static int UVERBS_HANDLER(MLX5_IB_METHOD_GET_DATA_DIRECT_SYSFS_PATH)(
struct uverbs_attr_bundle *attrs)
{
struct mlx5_data_direct_dev *data_direct_dev;
struct mlx5_ib_ucontext *c;
struct mlx5_ib_dev *dev;
int out_len = uverbs_attr_get_len(attrs,
MLX5_IB_ATTR_GET_DATA_DIRECT_SYSFS_PATH);
u32 dev_path_len;
char *dev_path = NULL;
int ret;
c = to_mucontext(ib_uverbs_get_ucontext(attrs));
if (IS_ERR(c))
return PTR_ERR(c);
dev = to_mdev(c->ibucontext.device);
mutex_lock(&dev->data_direct_lock);
data_direct_dev = dev->data_direct_dev;
if (!data_direct_dev) {
ret = -ENODEV;
goto end;
}
dev_path = kobject_get_path(&data_direct_dev->device->kobj, GFP_KERNEL);
if (!dev_path) {
ret = -ENOMEM;
goto end;
}
dev_path_len = strlen(dev_path) + 1;
if (dev_path_len > out_len) {
ret = -ENOSPC;
goto end;
}
ret = uverbs_copy_to(attrs, MLX5_IB_ATTR_GET_DATA_DIRECT_SYSFS_PATH, dev_path,
dev_path_len);
end:
kfree(dev_path);
mutex_unlock(&dev->data_direct_lock);
return ret;
}
DECLARE_UVERBS_NAMED_METHOD(
MLX5_IB_METHOD_QUERY_PORT,
UVERBS_ATTR_PTR_IN(MLX5_IB_ATTR_QUERY_PORT_PORT_NUM,
UVERBS_ATTR_TYPE(u32), UA_MANDATORY),
UVERBS_ATTR_PTR_OUT(
MLX5_IB_ATTR_QUERY_PORT,
UVERBS_ATTR_STRUCT(struct mlx5_ib_uapi_query_port,
reg_c0),
UA_MANDATORY));
DECLARE_UVERBS_NAMED_METHOD(
MLX5_IB_METHOD_GET_DATA_DIRECT_SYSFS_PATH,
UVERBS_ATTR_PTR_OUT(
MLX5_IB_ATTR_GET_DATA_DIRECT_SYSFS_PATH,
UVERBS_ATTR_MIN_SIZE(0),
UA_MANDATORY));
ADD_UVERBS_METHODS(mlx5_ib_device,
UVERBS_OBJECT_DEVICE,
&UVERBS_METHOD(MLX5_IB_METHOD_QUERY_PORT),
&UVERBS_METHOD(MLX5_IB_METHOD_GET_DATA_DIRECT_SYSFS_PATH));
DECLARE_UVERBS_NAMED_METHOD(
MLX5_IB_METHOD_PD_QUERY,
UVERBS_ATTR_IDR(MLX5_IB_ATTR_QUERY_PD_HANDLE,
UVERBS_OBJECT_PD,
UVERBS_ACCESS_READ,
UA_MANDATORY),
UVERBS_ATTR_PTR_OUT(MLX5_IB_ATTR_QUERY_PD_RESP_PDN,
UVERBS_ATTR_TYPE(u32),
UA_MANDATORY));
ADD_UVERBS_METHODS(mlx5_ib_pd,
UVERBS_OBJECT_PD,
&UVERBS_METHOD(MLX5_IB_METHOD_PD_QUERY));
const struct uapi_definition mlx5_ib_std_types_defs[] = {
UAPI_DEF_CHAIN_OBJ_TREE(
UVERBS_OBJECT_PD,
&mlx5_ib_pd),
UAPI_DEF_CHAIN_OBJ_TREE(
UVERBS_OBJECT_DEVICE,
&mlx5_ib_device),
{},
};
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,113 @@
/* SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB */
/* Copyright (c) 2022, NVIDIA CORPORATION & AFFILIATES. */
#ifndef _MLX5_IB_UMR_H
#define _MLX5_IB_UMR_H
#include "mlx5_ib.h"
#define MLX5_MAX_UMR_SHIFT 16
#define MLX5_MAX_UMR_PAGES (1 << MLX5_MAX_UMR_SHIFT)
#define MLX5_MAX_UMR_EXTENDED_SHIFT 43
#define MLX5_IB_UMR_OCTOWORD 16
#define MLX5_IB_UMR_XLT_ALIGNMENT 64
int mlx5r_umr_resource_init(struct mlx5_ib_dev *dev);
void mlx5r_umr_resource_cleanup(struct mlx5_ib_dev *dev);
int mlx5r_umr_init(struct mlx5_ib_dev *dev);
void mlx5r_umr_cleanup(struct mlx5_ib_dev *dev);
static inline bool mlx5r_umr_can_load_pas(struct mlx5_ib_dev *dev,
size_t length)
{
/*
* umr_check_mkey_mask() rejects MLX5_MKEY_MASK_PAGE_SIZE which is
* always set if MLX5_IB_SEND_UMR_UPDATE_TRANSLATION (aka
* MLX5_IB_UPD_XLT_ADDR and MLX5_IB_UPD_XLT_ENABLE) is set. Thus, a mkey
* can never be enabled without this capability. Simplify this weird
* quirky hardware by just saying it can't use PAS lists with UMR at
* all.
*/
if (MLX5_CAP_GEN(dev->mdev, umr_modify_entity_size_disabled))
return false;
/*
* length is the size of the MR in bytes when mlx5_ib_update_xlt() is
* used.
*/
if (!MLX5_CAP_GEN(dev->mdev, umr_extended_translation_offset) &&
length >= MLX5_MAX_UMR_PAGES * PAGE_SIZE)
return false;
return true;
}
/*
* true if an existing MR can be reconfigured to new access_flags using UMR.
* Older HW cannot use UMR to update certain elements of the MKC. See
* get_umr_update_access_mask() and umr_check_mkey_mask()
*/
static inline bool mlx5r_umr_can_reconfig(struct mlx5_ib_dev *dev,
unsigned int current_access_flags,
unsigned int target_access_flags)
{
unsigned int diffs = current_access_flags ^ target_access_flags;
if ((diffs & IB_ACCESS_REMOTE_ATOMIC) &&
MLX5_CAP_GEN(dev->mdev, atomic) &&
MLX5_CAP_GEN(dev->mdev, umr_modify_atomic_disabled))
return false;
if ((diffs & IB_ACCESS_RELAXED_ORDERING) &&
MLX5_CAP_GEN(dev->mdev, relaxed_ordering_write) &&
!MLX5_CAP_GEN(dev->mdev, relaxed_ordering_write_umr))
return false;
if ((diffs & IB_ACCESS_RELAXED_ORDERING) &&
(MLX5_CAP_GEN(dev->mdev, relaxed_ordering_read) ||
MLX5_CAP_GEN(dev->mdev, relaxed_ordering_read_pci_enabled)) &&
!MLX5_CAP_GEN(dev->mdev, relaxed_ordering_read_umr))
return false;
return true;
}
static inline u64 mlx5r_umr_get_xlt_octo(u64 bytes)
{
return ALIGN(bytes, MLX5_IB_UMR_XLT_ALIGNMENT) /
MLX5_IB_UMR_OCTOWORD;
}
struct mlx5r_umr_context {
struct ib_cqe cqe;
enum ib_wc_status status;
struct completion done;
};
struct mlx5r_umr_wqe {
struct mlx5_wqe_umr_ctrl_seg ctrl_seg;
struct mlx5_mkey_seg mkey_seg;
struct mlx5_wqe_data_seg data_seg;
};
int mlx5r_umr_revoke_mr(struct mlx5_ib_mr *mr);
int mlx5r_umr_rereg_pd_access(struct mlx5_ib_mr *mr, struct ib_pd *pd,
int access_flags);
int mlx5r_umr_update_data_direct_ksm_pas_range(struct mlx5_ib_mr *mr,
unsigned int flags,
size_t start_block,
size_t nblocks);
int mlx5r_umr_update_data_direct_ksm_pas(struct mlx5_ib_mr *mr, unsigned int flags);
int mlx5r_umr_update_mr_pas_range(struct mlx5_ib_mr *mr, unsigned int flags,
u32 pdn, size_t start_block, size_t nblocks);
int mlx5r_umr_update_mr_pas(struct mlx5_ib_mr *mr, unsigned int flags, u32 pdn);
int mlx5r_umr_update_xlt(struct mlx5_ib_mr *mr, u64 idx, int npages,
int page_shift, int flags, u32 pdn);
int mlx5r_umr_update_mr_page_shift(struct mlx5_ib_mr *mr,
unsigned int page_shift);
int mlx5r_umr_dmabuf_update_pgsz(struct mlx5_ib_mr *mr, u32 xlt_flags, u32 pdn,
unsigned int page_shift);
#endif /* _MLX5_IB_UMR_H */
File diff suppressed because it is too large Load Diff
+136
View File
@@ -0,0 +1,136 @@
/* SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB */
/*
* Copyright (c) 2020, Mellanox Technologies inc. All rights reserved.
*/
#ifndef _MLX5_IB_WR_H
#define _MLX5_IB_WR_H
#include "mlx5_ib.h"
enum {
MLX5_IB_SQ_UMR_INLINE_THRESHOLD = 64,
};
struct mlx5_wqe_eth_pad {
u8 rsvd0[16];
};
/* get_sq_edge - Get the next nearby edge.
*
* An 'edge' is defined as the first following address after the end
* of the fragment or the SQ. Accordingly, during the WQE construction
* which repetitively increases the pointer to write the next data, it
* simply should check if it gets to an edge.
*
* @sq - SQ buffer.
* @idx - Stride index in the SQ buffer.
*
* Return:
* The new edge.
*/
static inline void *get_sq_edge(struct mlx5_ib_wq *sq, u32 idx)
{
void *fragment_end;
fragment_end = mlx5_frag_buf_get_wqe
(&sq->fbc,
mlx5_frag_buf_get_idx_last_contig_stride(&sq->fbc, idx));
return fragment_end + MLX5_SEND_WQE_BB;
}
/* handle_post_send_edge - Check if we get to SQ edge. If yes, update to the
* next nearby edge and get new address translation for current WQE position.
* @sq: SQ buffer.
* @seg: Current WQE position (16B aligned).
* @wqe_sz: Total current WQE size [16B].
* @cur_edge: Updated current edge.
*/
static inline void handle_post_send_edge(struct mlx5_ib_wq *sq, void **seg,
u32 wqe_sz, void **cur_edge)
{
u32 idx;
if (likely(*seg != *cur_edge))
return;
idx = (sq->cur_post + (wqe_sz >> 2)) & (sq->wqe_cnt - 1);
*cur_edge = get_sq_edge(sq, idx);
*seg = mlx5_frag_buf_get_wqe(&sq->fbc, idx);
}
/* mlx5r_memcpy_send_wqe - copy data from src to WQE and update the relevant
* WQ's pointers. At the end @seg is aligned to 16B regardless the copied size.
* @sq: SQ buffer.
* @cur_edge: Updated current edge.
* @seg: Current WQE position (16B aligned).
* @wqe_sz: Total current WQE size [16B].
* @src: Pointer to copy from.
* @n: Number of bytes to copy.
*/
static inline void mlx5r_memcpy_send_wqe(struct mlx5_ib_wq *sq, void **cur_edge,
void **seg, u32 *wqe_sz,
const void *src, size_t n)
{
while (likely(n)) {
size_t leftlen = *cur_edge - *seg;
size_t copysz = min_t(size_t, leftlen, n);
size_t stride;
memcpy(*seg, src, copysz);
n -= copysz;
src += copysz;
stride = !n ? ALIGN(copysz, 16) : copysz;
*seg += stride;
*wqe_sz += stride >> 4;
handle_post_send_edge(sq, seg, *wqe_sz, cur_edge);
}
}
int mlx5r_wq_overflow(struct mlx5_ib_wq *wq, int nreq, struct ib_cq *ib_cq);
int mlx5r_begin_wqe(struct mlx5_ib_qp *qp, void **seg,
struct mlx5_wqe_ctrl_seg **ctrl, unsigned int *idx,
int *size, void **cur_edge, int nreq, __be32 general_id,
bool send_signaled, bool solicited);
void mlx5r_finish_wqe(struct mlx5_ib_qp *qp, struct mlx5_wqe_ctrl_seg *ctrl,
void *seg, u8 size, void *cur_edge, unsigned int idx,
u64 wr_id, int nreq, u8 fence, u32 mlx5_opcode);
void mlx5r_ring_db(struct mlx5_ib_qp *qp, unsigned int nreq,
struct mlx5_wqe_ctrl_seg *ctrl);
int mlx5_ib_post_send(struct ib_qp *ibqp, const struct ib_send_wr *wr,
const struct ib_send_wr **bad_wr, bool drain);
int mlx5_ib_post_recv(struct ib_qp *ibqp, const struct ib_recv_wr *wr,
const struct ib_recv_wr **bad_wr, bool drain);
static inline int mlx5_ib_post_send_nodrain(struct ib_qp *ibqp,
const struct ib_send_wr *wr,
const struct ib_send_wr **bad_wr)
{
return mlx5_ib_post_send(ibqp, wr, bad_wr, false);
}
static inline int mlx5_ib_post_send_drain(struct ib_qp *ibqp,
const struct ib_send_wr *wr,
const struct ib_send_wr **bad_wr)
{
return mlx5_ib_post_send(ibqp, wr, bad_wr, true);
}
static inline int mlx5_ib_post_recv_nodrain(struct ib_qp *ibqp,
const struct ib_recv_wr *wr,
const struct ib_recv_wr **bad_wr)
{
return mlx5_ib_post_recv(ibqp, wr, bad_wr, false);
}
static inline int mlx5_ib_post_recv_drain(struct ib_qp *ibqp,
const struct ib_recv_wr *wr,
const struct ib_recv_wr **bad_wr)
{
return mlx5_ib_post_recv(ibqp, wr, bad_wr, true);
}
#endif /* _MLX5_IB_WR_H */