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,10 @@
# SPDX-License-Identifier: GPL-2.0-only
config MANA_INFINIBAND
tristate "Microsoft Azure Network Adapter support"
depends on NETDEVICES && ETHERNET && PCI && MICROSOFT_MANA
help
This driver provides low-level RDMA support for Microsoft Azure
Network Adapter (MANA). MANA supports RDMA features that can be used
for workloads (e.g. DPDK, MPI etc) that uses RDMA verbs to directly
access hardware from user-mode processes in Microsoft Azure cloud
environment.
@@ -0,0 +1,4 @@
# SPDX-License-Identifier: GPL-2.0-only
obj-$(CONFIG_MANA_INFINIBAND) += mana_ib.o
mana_ib-y := device.o main.o wq.o qp.o cq.o mr.o ah.o wr.o counters.o
@@ -0,0 +1,58 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) 2024, Microsoft Corporation. All rights reserved.
*/
#include "mana_ib.h"
int mana_ib_create_ah(struct ib_ah *ibah, struct rdma_ah_init_attr *attr,
struct ib_udata *udata)
{
struct mana_ib_dev *mdev = container_of(ibah->device, struct mana_ib_dev, ib_dev);
struct mana_ib_ah *ah = container_of(ibah, struct mana_ib_ah, ibah);
struct rdma_ah_attr *ah_attr = attr->ah_attr;
const struct ib_global_route *grh;
enum rdma_network_type ntype;
if (ah_attr->type != RDMA_AH_ATTR_TYPE_ROCE ||
!(rdma_ah_get_ah_flags(ah_attr) & IB_AH_GRH))
return -EINVAL;
if (udata)
return -EINVAL;
ah->av = dma_pool_zalloc(mdev->av_pool, GFP_ATOMIC, &ah->dma_handle);
if (!ah->av)
return -ENOMEM;
grh = rdma_ah_read_grh(ah_attr);
ntype = rdma_gid_attr_network_type(grh->sgid_attr);
copy_in_reverse(ah->av->dest_mac, ah_attr->roce.dmac, ETH_ALEN);
ah->av->udp_src_port = rdma_flow_label_to_udp_sport(grh->flow_label);
ah->av->hop_limit = grh->hop_limit;
ah->av->dscp = (grh->traffic_class >> 2) & 0x3f;
ah->av->is_ipv6 = (ntype == RDMA_NETWORK_IPV6);
if (ah->av->is_ipv6) {
copy_in_reverse(ah->av->dest_ip, grh->dgid.raw, 16);
copy_in_reverse(ah->av->src_ip, grh->sgid_attr->gid.raw, 16);
} else {
ah->av->dest_ip[10] = 0xFF;
ah->av->dest_ip[11] = 0xFF;
copy_in_reverse(&ah->av->dest_ip[12], &grh->dgid.raw[12], 4);
copy_in_reverse(&ah->av->src_ip[12], &grh->sgid_attr->gid.raw[12], 4);
}
return 0;
}
int mana_ib_destroy_ah(struct ib_ah *ibah, u32 flags)
{
struct mana_ib_dev *mdev = container_of(ibah->device, struct mana_ib_dev, ib_dev);
struct mana_ib_ah *ah = container_of(ibah, struct mana_ib_ah, ibah);
dma_pool_free(mdev->av_pool, ah->av, ah->dma_handle);
return 0;
}
@@ -0,0 +1,179 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) 2024, Microsoft Corporation. All rights reserved.
*/
#include "counters.h"
static const struct rdma_stat_desc mana_ib_port_stats_desc[] = {
[MANA_IB_REQUESTER_TIMEOUT].name = "requester_timeout",
[MANA_IB_REQUESTER_OOS_NAK].name = "requester_oos_nak",
[MANA_IB_REQUESTER_RNR_NAK].name = "requester_rnr_nak",
[MANA_IB_RESPONDER_RNR_NAK].name = "responder_rnr_nak",
[MANA_IB_RESPONDER_OOS].name = "responder_oos",
[MANA_IB_RESPONDER_DUP_REQUEST].name = "responder_dup_request",
[MANA_IB_REQUESTER_IMPLICIT_NAK].name = "requester_implicit_nak",
[MANA_IB_REQUESTER_READRESP_PSN_MISMATCH].name = "requester_readresp_psn_mismatch",
[MANA_IB_NAK_INV_REQ].name = "nak_inv_req",
[MANA_IB_NAK_ACCESS_ERR].name = "nak_access_error",
[MANA_IB_NAK_OPP_ERR].name = "nak_opp_error",
[MANA_IB_NAK_INV_READ].name = "nak_inv_read",
[MANA_IB_RESPONDER_LOCAL_LEN_ERR].name = "responder_local_len_error",
[MANA_IB_REQUESTOR_LOCAL_PROT_ERR].name = "requestor_local_prot_error",
[MANA_IB_RESPONDER_REM_ACCESS_ERR].name = "responder_rem_access_error",
[MANA_IB_RESPONDER_LOCAL_QP_ERR].name = "responder_local_qp_error",
[MANA_IB_RESPONDER_MALFORMED_WQE].name = "responder_malformed_wqe",
[MANA_IB_GENERAL_HW_ERR].name = "general_hw_error",
[MANA_IB_REQUESTER_RNR_NAK_RETRIES_EXCEEDED].name = "requester_rnr_nak_retries_exceeded",
[MANA_IB_REQUESTER_RETRIES_EXCEEDED].name = "requester_retries_exceeded",
[MANA_IB_TOTAL_FATAL_ERR].name = "total_fatal_error",
[MANA_IB_RECEIVED_CNPS].name = "received_cnps",
[MANA_IB_NUM_QPS_CONGESTED].name = "num_qps_congested",
[MANA_IB_RATE_INC_EVENTS].name = "rate_inc_events",
[MANA_IB_NUM_QPS_RECOVERED].name = "num_qps_recovered",
[MANA_IB_CURRENT_RATE].name = "current_rate",
[MANA_IB_DUP_RX_REQ].name = "dup_rx_requests",
[MANA_IB_TX_BYTES].name = "tx_bytes",
[MANA_IB_RX_BYTES].name = "rx_bytes",
[MANA_IB_RX_SEND_REQ].name = "rx_send_requests",
[MANA_IB_RX_WRITE_REQ].name = "rx_write_requests",
[MANA_IB_RX_READ_REQ].name = "rx_read_requests",
[MANA_IB_TX_PKT].name = "tx_packets",
[MANA_IB_RX_PKT].name = "rx_packets",
};
static const struct rdma_stat_desc mana_ib_device_stats_desc[] = {
[MANA_IB_SENT_CNPS].name = "sent_cnps",
[MANA_IB_RECEIVED_ECNS].name = "received_ecns",
[MANA_IB_RECEIVED_CNP_COUNT].name = "received_cnp_count",
[MANA_IB_QP_CONGESTED_EVENTS].name = "qp_congested_events",
[MANA_IB_QP_RECOVERED_EVENTS].name = "qp_recovered_events",
[MANA_IB_DEV_RATE_INC_EVENTS].name = "rate_inc_events",
};
struct rdma_hw_stats *mana_ib_alloc_hw_device_stats(struct ib_device *ibdev)
{
return rdma_alloc_hw_stats_struct(mana_ib_device_stats_desc,
ARRAY_SIZE(mana_ib_device_stats_desc),
RDMA_HW_STATS_DEFAULT_LIFESPAN);
}
struct rdma_hw_stats *mana_ib_alloc_hw_port_stats(struct ib_device *ibdev,
u32 port_num)
{
return rdma_alloc_hw_stats_struct(mana_ib_port_stats_desc,
ARRAY_SIZE(mana_ib_port_stats_desc),
RDMA_HW_STATS_DEFAULT_LIFESPAN);
}
static int mana_ib_get_hw_device_stats(struct ib_device *ibdev, struct rdma_hw_stats *stats)
{
struct mana_ib_dev *mdev = container_of(ibdev, struct mana_ib_dev,
ib_dev);
struct mana_rnic_query_device_cntrs_resp resp = {};
struct mana_rnic_query_device_cntrs_req req = {};
int err;
mana_gd_init_req_hdr(&req.hdr, MANA_IB_QUERY_DEVICE_COUNTERS,
sizeof(req), sizeof(resp));
req.hdr.dev_id = mdev->gdma_dev->dev_id;
req.adapter = mdev->adapter_handle;
err = mana_gd_send_request(mdev_to_gc(mdev), sizeof(req), &req,
sizeof(resp), &resp);
if (err) {
ibdev_err(&mdev->ib_dev, "Failed to query device counters err %d",
err);
return err;
}
stats->value[MANA_IB_SENT_CNPS] = resp.sent_cnps;
stats->value[MANA_IB_RECEIVED_ECNS] = resp.received_ecns;
stats->value[MANA_IB_RECEIVED_CNP_COUNT] = resp.received_cnp_count;
stats->value[MANA_IB_QP_CONGESTED_EVENTS] = resp.qp_congested_events;
stats->value[MANA_IB_QP_RECOVERED_EVENTS] = resp.qp_recovered_events;
stats->value[MANA_IB_DEV_RATE_INC_EVENTS] = resp.rate_inc_events;
return ARRAY_SIZE(mana_ib_device_stats_desc);
}
static int mana_ib_get_hw_port_stats(struct ib_device *ibdev, struct rdma_hw_stats *stats,
u32 port_num)
{
struct mana_ib_dev *mdev = container_of(ibdev, struct mana_ib_dev,
ib_dev);
struct mana_rnic_query_vf_cntrs_resp resp = {};
struct mana_rnic_query_vf_cntrs_req req = {};
int err;
mana_gd_init_req_hdr(&req.hdr, MANA_IB_QUERY_VF_COUNTERS,
sizeof(req), sizeof(resp));
req.hdr.resp.msg_version = GDMA_MESSAGE_V2;
req.hdr.dev_id = mdev->gdma_dev->dev_id;
req.adapter = mdev->adapter_handle;
err = mana_gd_send_request(mdev_to_gc(mdev), sizeof(req), &req,
sizeof(resp), &resp);
if (err) {
ibdev_err(&mdev->ib_dev, "Failed to query vf counters err %d",
err);
return err;
}
stats->value[MANA_IB_REQUESTER_TIMEOUT] = resp.requester_timeout;
stats->value[MANA_IB_REQUESTER_OOS_NAK] = resp.requester_oos_nak;
stats->value[MANA_IB_REQUESTER_RNR_NAK] = resp.requester_rnr_nak;
stats->value[MANA_IB_RESPONDER_RNR_NAK] = resp.responder_rnr_nak;
stats->value[MANA_IB_RESPONDER_OOS] = resp.responder_oos;
stats->value[MANA_IB_RESPONDER_DUP_REQUEST] = resp.responder_dup_request;
stats->value[MANA_IB_REQUESTER_IMPLICIT_NAK] =
resp.requester_implicit_nak;
stats->value[MANA_IB_REQUESTER_READRESP_PSN_MISMATCH] =
resp.requester_readresp_psn_mismatch;
stats->value[MANA_IB_NAK_INV_REQ] = resp.nak_inv_req;
stats->value[MANA_IB_NAK_ACCESS_ERR] = resp.nak_access_err;
stats->value[MANA_IB_NAK_OPP_ERR] = resp.nak_opp_err;
stats->value[MANA_IB_NAK_INV_READ] = resp.nak_inv_read;
stats->value[MANA_IB_RESPONDER_LOCAL_LEN_ERR] =
resp.responder_local_len_err;
stats->value[MANA_IB_REQUESTOR_LOCAL_PROT_ERR] =
resp.requestor_local_prot_err;
stats->value[MANA_IB_RESPONDER_REM_ACCESS_ERR] =
resp.responder_rem_access_err;
stats->value[MANA_IB_RESPONDER_LOCAL_QP_ERR] =
resp.responder_local_qp_err;
stats->value[MANA_IB_RESPONDER_MALFORMED_WQE] =
resp.responder_malformed_wqe;
stats->value[MANA_IB_GENERAL_HW_ERR] = resp.general_hw_err;
stats->value[MANA_IB_REQUESTER_RNR_NAK_RETRIES_EXCEEDED] =
resp.requester_rnr_nak_retries_exceeded;
stats->value[MANA_IB_REQUESTER_RETRIES_EXCEEDED] =
resp.requester_retries_exceeded;
stats->value[MANA_IB_TOTAL_FATAL_ERR] = resp.total_fatal_err;
stats->value[MANA_IB_RECEIVED_CNPS] = resp.received_cnps;
stats->value[MANA_IB_NUM_QPS_CONGESTED] = resp.num_qps_congested;
stats->value[MANA_IB_RATE_INC_EVENTS] = resp.rate_inc_events;
stats->value[MANA_IB_NUM_QPS_RECOVERED] = resp.num_qps_recovered;
stats->value[MANA_IB_CURRENT_RATE] = resp.current_rate;
stats->value[MANA_IB_DUP_RX_REQ] = resp.dup_rx_req;
stats->value[MANA_IB_TX_BYTES] = resp.tx_bytes;
stats->value[MANA_IB_RX_BYTES] = resp.rx_bytes;
stats->value[MANA_IB_RX_SEND_REQ] = resp.rx_send_req;
stats->value[MANA_IB_RX_WRITE_REQ] = resp.rx_write_req;
stats->value[MANA_IB_RX_READ_REQ] = resp.rx_read_req;
stats->value[MANA_IB_TX_PKT] = resp.tx_pkt;
stats->value[MANA_IB_RX_PKT] = resp.rx_pkt;
return ARRAY_SIZE(mana_ib_port_stats_desc);
}
int mana_ib_get_hw_stats(struct ib_device *ibdev, struct rdma_hw_stats *stats,
u32 port_num, int index)
{
if (!port_num)
return mana_ib_get_hw_device_stats(ibdev, stats);
else
return mana_ib_get_hw_port_stats(ibdev, stats, port_num);
}
@@ -0,0 +1,62 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright (c) 2024 Microsoft Corporation. All rights reserved.
*/
#ifndef _COUNTERS_H_
#define _COUNTERS_H_
#include "mana_ib.h"
enum mana_ib_port_counters {
MANA_IB_REQUESTER_TIMEOUT,
MANA_IB_REQUESTER_OOS_NAK,
MANA_IB_REQUESTER_RNR_NAK,
MANA_IB_RESPONDER_RNR_NAK,
MANA_IB_RESPONDER_OOS,
MANA_IB_RESPONDER_DUP_REQUEST,
MANA_IB_REQUESTER_IMPLICIT_NAK,
MANA_IB_REQUESTER_READRESP_PSN_MISMATCH,
MANA_IB_NAK_INV_REQ,
MANA_IB_NAK_ACCESS_ERR,
MANA_IB_NAK_OPP_ERR,
MANA_IB_NAK_INV_READ,
MANA_IB_RESPONDER_LOCAL_LEN_ERR,
MANA_IB_REQUESTOR_LOCAL_PROT_ERR,
MANA_IB_RESPONDER_REM_ACCESS_ERR,
MANA_IB_RESPONDER_LOCAL_QP_ERR,
MANA_IB_RESPONDER_MALFORMED_WQE,
MANA_IB_GENERAL_HW_ERR,
MANA_IB_REQUESTER_RNR_NAK_RETRIES_EXCEEDED,
MANA_IB_REQUESTER_RETRIES_EXCEEDED,
MANA_IB_TOTAL_FATAL_ERR,
MANA_IB_RECEIVED_CNPS,
MANA_IB_NUM_QPS_CONGESTED,
MANA_IB_RATE_INC_EVENTS,
MANA_IB_NUM_QPS_RECOVERED,
MANA_IB_CURRENT_RATE,
MANA_IB_DUP_RX_REQ,
MANA_IB_TX_BYTES,
MANA_IB_RX_BYTES,
MANA_IB_RX_SEND_REQ,
MANA_IB_RX_WRITE_REQ,
MANA_IB_RX_READ_REQ,
MANA_IB_TX_PKT,
MANA_IB_RX_PKT,
};
enum mana_ib_device_counters {
MANA_IB_SENT_CNPS,
MANA_IB_RECEIVED_ECNS,
MANA_IB_RECEIVED_CNP_COUNT,
MANA_IB_QP_CONGESTED_EVENTS,
MANA_IB_QP_RECOVERED_EVENTS,
MANA_IB_DEV_RATE_INC_EVENTS,
};
struct rdma_hw_stats *mana_ib_alloc_hw_port_stats(struct ib_device *ibdev,
u32 port_num);
struct rdma_hw_stats *mana_ib_alloc_hw_device_stats(struct ib_device *ibdev);
int mana_ib_get_hw_stats(struct ib_device *ibdev, struct rdma_hw_stats *stats,
u32 port_num, int index);
#endif /* _COUNTERS_H_ */
+338
View File
@@ -0,0 +1,338 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) 2022, Microsoft Corporation. All rights reserved.
*/
#include "mana_ib.h"
int mana_ib_create_cq(struct ib_cq *ibcq, const struct ib_cq_init_attr *attr,
struct uverbs_attr_bundle *attrs)
{
struct ib_udata *udata = &attrs->driver_udata;
struct mana_ib_cq *cq = container_of(ibcq, struct mana_ib_cq, ibcq);
struct mana_ib_create_cq_resp resp = {};
struct mana_ib_ucontext *mana_ucontext;
struct ib_device *ibdev = ibcq->device;
struct mana_ib_create_cq ucmd;
struct mana_ib_dev *mdev;
bool is_rnic_cq;
u32 doorbell;
u32 buf_size;
int err;
mdev = container_of(ibdev, struct mana_ib_dev, ib_dev);
cq->comp_vector = attr->comp_vector % ibdev->num_comp_vectors;
cq->cq_handle = INVALID_MANA_HANDLE;
is_rnic_cq = mana_ib_is_rnic(mdev);
if (udata) {
err = ib_copy_validate_udata_in(udata, ucmd, buf_addr);
if (err)
return err;
if ((!is_rnic_cq && attr->cqe > mdev->adapter_caps.max_qp_wr) ||
attr->cqe > U32_MAX / COMP_ENTRY_SIZE) {
ibdev_dbg(ibdev, "CQE %d exceeding limit\n", attr->cqe);
return -EINVAL;
}
cq->cqe = attr->cqe;
err = mana_ib_create_queue(mdev, ucmd.buf_addr, cq->cqe * COMP_ENTRY_SIZE,
&cq->queue);
if (err) {
ibdev_dbg(ibdev, "Failed to create queue for create cq, %d\n", err);
return err;
}
mana_ucontext = rdma_udata_to_drv_context(udata, struct mana_ib_ucontext,
ibucontext);
doorbell = mana_ucontext->doorbell;
} else {
if (attr->cqe > U32_MAX / COMP_ENTRY_SIZE / 2 + 1) {
ibdev_dbg(ibdev, "CQE %d exceeding limit\n", attr->cqe);
return -EINVAL;
}
buf_size = MANA_PAGE_ALIGN(roundup_pow_of_two(attr->cqe * COMP_ENTRY_SIZE));
cq->cqe = buf_size / COMP_ENTRY_SIZE;
err = mana_ib_create_kernel_queue(mdev, buf_size, GDMA_CQ, &cq->queue);
if (err) {
ibdev_dbg(ibdev, "Failed to create kernel queue for create cq, %d\n", err);
return err;
}
doorbell = mdev->gdma_dev->doorbell;
}
if (is_rnic_cq) {
err = mana_ib_gd_create_cq(mdev, cq, doorbell);
if (err) {
ibdev_dbg(ibdev, "Failed to create RNIC cq, %d\n", err);
goto err_destroy_queue;
}
err = mana_ib_install_cq_cb(mdev, cq);
if (err) {
ibdev_dbg(ibdev, "Failed to install cq callback, %d\n", err);
goto err_destroy_rnic_cq;
}
}
if (udata) {
resp.cqid = cq->queue.id;
err = ib_respond_udata(udata, resp);
if (err)
goto err_remove_cq_cb;
}
spin_lock_init(&cq->cq_lock);
INIT_LIST_HEAD(&cq->list_send_qp);
INIT_LIST_HEAD(&cq->list_recv_qp);
return 0;
err_remove_cq_cb:
mana_ib_remove_cq_cb(mdev, cq);
err_destroy_rnic_cq:
mana_ib_gd_destroy_cq(mdev, cq);
err_destroy_queue:
mana_ib_destroy_queue(mdev, &cq->queue);
return err;
}
int mana_ib_destroy_cq(struct ib_cq *ibcq, struct ib_udata *udata)
{
struct mana_ib_cq *cq = container_of(ibcq, struct mana_ib_cq, ibcq);
struct ib_device *ibdev = ibcq->device;
struct mana_ib_dev *mdev;
mdev = container_of(ibdev, struct mana_ib_dev, ib_dev);
mana_ib_remove_cq_cb(mdev, cq);
/* Ignore return code as there is not much we can do about it.
* The error message is printed inside.
*/
mana_ib_gd_destroy_cq(mdev, cq);
mana_ib_destroy_queue(mdev, &cq->queue);
return 0;
}
static void mana_ib_cq_handler(void *ctx, struct gdma_queue *gdma_cq)
{
struct mana_ib_cq *cq = ctx;
if (cq->ibcq.comp_handler)
cq->ibcq.comp_handler(&cq->ibcq, cq->ibcq.cq_context);
}
int mana_ib_install_cq_cb(struct mana_ib_dev *mdev, struct mana_ib_cq *cq)
{
struct gdma_context *gc = mdev_to_gc(mdev);
struct gdma_queue *gdma_cq;
if (cq->queue.id >= gc->max_num_cqs)
return -EINVAL;
/* Create CQ table entry, sharing a CQ between WQs is not supported */
if (gc->cq_table[cq->queue.id])
return -EINVAL;
if (cq->queue.kmem)
gdma_cq = cq->queue.kmem;
else
gdma_cq = kzalloc_obj(*gdma_cq);
if (!gdma_cq)
return -ENOMEM;
gdma_cq->cq.context = cq;
gdma_cq->type = GDMA_CQ;
gdma_cq->cq.callback = mana_ib_cq_handler;
gdma_cq->id = cq->queue.id;
gc->cq_table[cq->queue.id] = gdma_cq;
return 0;
}
void mana_ib_remove_cq_cb(struct mana_ib_dev *mdev, struct mana_ib_cq *cq)
{
struct gdma_context *gc = mdev_to_gc(mdev);
if (cq->queue.id >= gc->max_num_cqs || cq->queue.id == INVALID_QUEUE_ID)
return;
if (cq->queue.kmem)
/* Then it will be cleaned and removed by the mana */
return;
kfree(gc->cq_table[cq->queue.id]);
gc->cq_table[cq->queue.id] = NULL;
}
int mana_ib_arm_cq(struct ib_cq *ibcq, enum ib_cq_notify_flags flags)
{
struct mana_ib_cq *cq = container_of(ibcq, struct mana_ib_cq, ibcq);
struct gdma_queue *gdma_cq = cq->queue.kmem;
if (!gdma_cq)
return -EINVAL;
mana_gd_ring_cq(gdma_cq, SET_ARM_BIT);
return 0;
}
static inline void handle_ud_sq_cqe(struct mana_ib_qp *qp, struct gdma_comp *cqe)
{
struct mana_rdma_cqe *rdma_cqe = (struct mana_rdma_cqe *)cqe->cqe_data;
struct gdma_queue *wq = qp->ud_qp.queues[MANA_UD_SEND_QUEUE].kmem;
struct ud_sq_shadow_wqe *shadow_wqe;
shadow_wqe = shadow_queue_get_next_to_complete(&qp->shadow_sq);
if (!shadow_wqe)
return;
shadow_wqe->header.error_code = rdma_cqe->ud_send.vendor_error;
wq->tail += shadow_wqe->header.posted_wqe_size;
shadow_queue_advance_next_to_complete(&qp->shadow_sq);
}
static inline void handle_ud_rq_cqe(struct mana_ib_qp *qp, struct gdma_comp *cqe)
{
struct mana_rdma_cqe *rdma_cqe = (struct mana_rdma_cqe *)cqe->cqe_data;
struct gdma_queue *wq = qp->ud_qp.queues[MANA_UD_RECV_QUEUE].kmem;
struct ud_rq_shadow_wqe *shadow_wqe;
shadow_wqe = shadow_queue_get_next_to_complete(&qp->shadow_rq);
if (!shadow_wqe)
return;
shadow_wqe->byte_len = rdma_cqe->ud_recv.msg_len;
shadow_wqe->src_qpn = rdma_cqe->ud_recv.src_qpn;
shadow_wqe->header.error_code = IB_WC_SUCCESS;
wq->tail += shadow_wqe->header.posted_wqe_size;
shadow_queue_advance_next_to_complete(&qp->shadow_rq);
}
static void mana_handle_cqe(struct mana_ib_dev *mdev, struct gdma_comp *cqe)
{
struct mana_ib_qp *qp = mana_get_qp_ref(mdev, cqe->wq_num, cqe->is_sq);
if (!qp)
return;
if (qp->ibqp.qp_type == IB_QPT_GSI || qp->ibqp.qp_type == IB_QPT_UD) {
if (cqe->is_sq)
handle_ud_sq_cqe(qp, cqe);
else
handle_ud_rq_cqe(qp, cqe);
}
mana_put_qp_ref(qp);
}
static void fill_verbs_from_shadow_wqe(struct mana_ib_qp *qp, struct ib_wc *wc,
const struct shadow_wqe_header *shadow_wqe)
{
const struct ud_rq_shadow_wqe *ud_wqe = (const struct ud_rq_shadow_wqe *)shadow_wqe;
wc->wr_id = shadow_wqe->wr_id;
wc->status = shadow_wqe->error_code;
wc->opcode = shadow_wqe->opcode;
wc->vendor_err = shadow_wqe->error_code;
wc->wc_flags = 0;
wc->qp = &qp->ibqp;
wc->pkey_index = 0;
if (shadow_wqe->opcode == IB_WC_RECV) {
wc->byte_len = ud_wqe->byte_len;
wc->src_qp = ud_wqe->src_qpn;
wc->wc_flags |= IB_WC_GRH;
}
}
static int mana_process_completions(struct mana_ib_cq *cq, int nwc, struct ib_wc *wc)
{
struct shadow_wqe_header *shadow_wqe;
struct mana_ib_qp *qp;
int wc_index = 0;
/* process send shadow queue completions */
list_for_each_entry(qp, &cq->list_send_qp, cq_send_list) {
while ((shadow_wqe = shadow_queue_get_next_to_consume(&qp->shadow_sq))
!= NULL) {
if (wc_index >= nwc)
goto out;
fill_verbs_from_shadow_wqe(qp, &wc[wc_index], shadow_wqe);
shadow_queue_advance_consumer(&qp->shadow_sq);
wc_index++;
}
}
/* process recv shadow queue completions */
list_for_each_entry(qp, &cq->list_recv_qp, cq_recv_list) {
while ((shadow_wqe = shadow_queue_get_next_to_consume(&qp->shadow_rq))
!= NULL) {
if (wc_index >= nwc)
goto out;
fill_verbs_from_shadow_wqe(qp, &wc[wc_index], shadow_wqe);
shadow_queue_advance_consumer(&qp->shadow_rq);
wc_index++;
}
}
out:
return wc_index;
}
void mana_drain_gsi_sqs(struct mana_ib_dev *mdev)
{
struct mana_ib_qp *qp = mana_get_qp_ref(mdev, MANA_GSI_QPN, false);
struct ud_sq_shadow_wqe *shadow_wqe;
struct mana_ib_cq *cq;
unsigned long flags;
if (!qp)
return;
cq = container_of(qp->ibqp.send_cq, struct mana_ib_cq, ibcq);
spin_lock_irqsave(&cq->cq_lock, flags);
while ((shadow_wqe = shadow_queue_get_next_to_complete(&qp->shadow_sq))
!= NULL) {
shadow_wqe->header.error_code = IB_WC_GENERAL_ERR;
shadow_queue_advance_next_to_complete(&qp->shadow_sq);
}
spin_unlock_irqrestore(&cq->cq_lock, flags);
if (cq->ibcq.comp_handler)
cq->ibcq.comp_handler(&cq->ibcq, cq->ibcq.cq_context);
mana_put_qp_ref(qp);
}
int mana_ib_poll_cq(struct ib_cq *ibcq, int num_entries, struct ib_wc *wc)
{
struct mana_ib_cq *cq = container_of(ibcq, struct mana_ib_cq, ibcq);
struct mana_ib_dev *mdev = container_of(ibcq->device, struct mana_ib_dev, ib_dev);
struct gdma_queue *queue = cq->queue.kmem;
struct gdma_comp gdma_cqe;
unsigned long flags;
int num_polled = 0;
int comp_read, i;
spin_lock_irqsave(&cq->cq_lock, flags);
for (i = 0; i < num_entries; i++) {
comp_read = mana_gd_poll_cq(queue, &gdma_cqe, 1);
if (comp_read < 1)
break;
mana_handle_cqe(mdev, &gdma_cqe);
}
num_polled = mana_process_completions(cq, num_entries, wc);
spin_unlock_irqrestore(&cq->cq_lock, flags);
return num_polled;
}
@@ -0,0 +1,271 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) 2022, Microsoft Corporation. All rights reserved.
*/
#include "mana_ib.h"
#include <net/mana/mana_auxiliary.h>
#include <net/addrconf.h>
MODULE_DESCRIPTION("Microsoft Azure Network Adapter IB driver");
MODULE_LICENSE("GPL");
MODULE_IMPORT_NS("NET_MANA");
static const struct ib_device_ops mana_ib_dev_ops = {
.owner = THIS_MODULE,
.driver_id = RDMA_DRIVER_MANA,
.uverbs_abi_ver = MANA_IB_UVERBS_ABI_VERSION,
.add_gid = mana_ib_gd_add_gid,
.alloc_mw = mana_ib_alloc_mw,
.alloc_pd = mana_ib_alloc_pd,
.alloc_ucontext = mana_ib_alloc_ucontext,
.create_ah = mana_ib_create_ah,
.create_cq = mana_ib_create_cq,
.create_qp = mana_ib_create_qp,
.create_rwq_ind_table = mana_ib_create_rwq_ind_table,
.create_wq = mana_ib_create_wq,
.dealloc_mw = mana_ib_dealloc_mw,
.dealloc_pd = mana_ib_dealloc_pd,
.dealloc_ucontext = mana_ib_dealloc_ucontext,
.del_gid = mana_ib_gd_del_gid,
.dereg_mr = mana_ib_dereg_mr,
.destroy_ah = mana_ib_destroy_ah,
.destroy_cq = mana_ib_destroy_cq,
.destroy_qp = mana_ib_destroy_qp,
.destroy_rwq_ind_table = mana_ib_destroy_rwq_ind_table,
.destroy_wq = mana_ib_destroy_wq,
.disassociate_ucontext = mana_ib_disassociate_ucontext,
.get_dma_mr = mana_ib_get_dma_mr,
.get_link_layer = mana_ib_get_link_layer,
.get_port_immutable = mana_ib_get_port_immutable,
.mmap = mana_ib_mmap,
.modify_qp = mana_ib_modify_qp,
.modify_wq = mana_ib_modify_wq,
.poll_cq = mana_ib_poll_cq,
.post_recv = mana_ib_post_recv,
.post_send = mana_ib_post_send,
.query_device = mana_ib_query_device,
.query_gid = mana_ib_query_gid,
.query_pkey = mana_ib_query_pkey,
.query_port = mana_ib_query_port,
.reg_user_mr = mana_ib_reg_user_mr,
.reg_user_mr_dmabuf = mana_ib_reg_user_mr_dmabuf,
.req_notify_cq = mana_ib_arm_cq,
INIT_RDMA_OBJ_SIZE(ib_ah, mana_ib_ah, ibah),
INIT_RDMA_OBJ_SIZE(ib_cq, mana_ib_cq, ibcq),
INIT_RDMA_OBJ_SIZE(ib_mw, mana_ib_mw, ibmw),
INIT_RDMA_OBJ_SIZE(ib_pd, mana_ib_pd, ibpd),
INIT_RDMA_OBJ_SIZE(ib_qp, mana_ib_qp, ibqp),
INIT_RDMA_OBJ_SIZE(ib_ucontext, mana_ib_ucontext, ibucontext),
INIT_RDMA_OBJ_SIZE(ib_rwq_ind_table, mana_ib_rwq_ind_table,
ib_ind_table),
};
static const struct ib_device_ops mana_ib_stats_ops = {
.alloc_hw_port_stats = mana_ib_alloc_hw_port_stats,
.get_hw_stats = mana_ib_get_hw_stats,
};
static const struct ib_device_ops mana_ib_device_stats_ops = {
.alloc_hw_device_stats = mana_ib_alloc_hw_device_stats,
};
const struct ib_device_ops mana_ib_dev_dm_ops = {
.alloc_dm = mana_ib_alloc_dm,
.dealloc_dm = mana_ib_dealloc_dm,
.reg_dm_mr = mana_ib_reg_dm_mr,
};
static int mana_ib_netdev_event(struct notifier_block *this,
unsigned long event, void *ptr)
{
struct mana_ib_dev *dev = container_of(this, struct mana_ib_dev, nb);
struct net_device *event_dev = netdev_notifier_info_to_dev(ptr);
struct gdma_context *gc = dev->gdma_dev->gdma_context;
struct mana_context *mc = gc->mana.driver_data;
struct net_device *ndev;
int i;
/* Only process events from our parent device */
for (i = 0; i < dev->ib_dev.phys_port_cnt; i++)
if (event_dev == mc->ports[i]) {
switch (event) {
case NETDEV_CHANGEUPPER:
ndev = mana_get_primary_netdev(mc, i, &dev->dev_tracker);
/*
* RDMA core will setup GID based on updated netdev.
* It's not possible to race with the core as rtnl lock is being
* held.
*/
ib_device_set_netdev(&dev->ib_dev, ndev, i + 1);
/* mana_get_primary_netdev() returns ndev with refcount held */
if (ndev)
netdev_put(ndev, &dev->dev_tracker);
return NOTIFY_OK;
default:
return NOTIFY_DONE;
}
}
return NOTIFY_DONE;
}
static int mana_ib_probe(struct auxiliary_device *adev,
const struct auxiliary_device_id *id)
{
struct mana_adev *madev = container_of(adev, struct mana_adev, adev);
struct gdma_context *gc = madev->mdev->gdma_context;
struct mana_context *mc = gc->mana.driver_data;
struct gdma_dev *mdev = madev->mdev;
struct net_device *ndev;
struct mana_ib_dev *dev;
u8 mac_addr[ETH_ALEN];
int ret, i;
dev = ib_alloc_device(mana_ib_dev, ib_dev);
if (!dev)
return -ENOMEM;
ib_set_device_ops(&dev->ib_dev, &mana_ib_dev_ops);
dev->ib_dev.node_type = RDMA_NODE_IB_CA;
dev->ib_dev.num_comp_vectors = gc->max_num_queues;
dev->ib_dev.dev.parent = gc->dev;
dev->gdma_dev = mdev;
xa_init_flags(&dev->qp_table_wq, XA_FLAGS_LOCK_IRQ);
if (mana_ib_is_rnic(dev)) {
dev->ib_dev.phys_port_cnt = 1;
addrconf_addr_eui48((u8 *)&dev->ib_dev.node_guid, mc->ports[0]->dev_addr);
ret = mana_ib_gd_query_adapter_caps(dev);
if (ret) {
ibdev_err(&dev->ib_dev, "Failed to query device caps, ret %d", ret);
goto free_ib_device;
}
ib_set_device_ops(&dev->ib_dev, &mana_ib_stats_ops);
if (dev->adapter_caps.feature_flags & MANA_IB_FEATURE_DEV_COUNTERS_SUPPORT)
ib_set_device_ops(&dev->ib_dev, &mana_ib_device_stats_ops);
ib_set_device_ops(&dev->ib_dev, &mana_ib_dev_dm_ops);
ret = mana_ib_create_eqs(dev);
if (ret) {
ibdev_err(&dev->ib_dev, "Failed to create EQs, ret %d", ret);
goto free_ib_device;
}
ret = mana_ib_gd_create_rnic_adapter(dev);
if (ret)
goto destroy_eqs;
if (dev->adapter_caps.feature_flags & MANA_IB_FEATURE_MULTI_PORTS_SUPPORT)
dev->ib_dev.phys_port_cnt = mc->num_ports;
for (i = 0; i < dev->ib_dev.phys_port_cnt; i++) {
ndev = mana_get_primary_netdev(mc, i, &dev->dev_tracker);
if (!ndev) {
ret = -ENODEV;
ibdev_err(&dev->ib_dev,
"Failed to get netdev for IB port %d", i + 1);
goto destroy_rnic;
}
ether_addr_copy(mac_addr, ndev->dev_addr);
ret = ib_device_set_netdev(&dev->ib_dev, ndev, i + 1);
/* mana_get_primary_netdev() returns ndev with refcount held */
netdev_put(ndev, &dev->dev_tracker);
if (ret) {
ibdev_err(&dev->ib_dev, "Failed to set ib netdev, ret %d", ret);
goto destroy_rnic;
}
ret = mana_ib_gd_config_mac(dev, ADDR_OP_ADD, mac_addr);
if (ret) {
ibdev_err(&dev->ib_dev, "Failed to add Mac address, ret %d", ret);
goto destroy_rnic;
}
}
dev->nb.notifier_call = mana_ib_netdev_event;
ret = register_netdevice_notifier(&dev->nb);
if (ret) {
ibdev_err(&dev->ib_dev, "Failed to register net notifier, %d", ret);
goto destroy_rnic;
}
} else {
dev->ib_dev.phys_port_cnt = mc->num_ports;
ret = mana_eth_query_adapter_caps(dev);
if (ret) {
ibdev_err(&dev->ib_dev, "Failed to query ETH device caps, ret %d", ret);
goto free_ib_device;
}
}
dev->av_pool = dma_pool_create("mana_ib_av", gc->dev, MANA_AV_BUFFER_SIZE,
MANA_AV_BUFFER_SIZE, 0);
if (!dev->av_pool) {
ret = -ENOMEM;
goto deregister_net_notifier;
}
ibdev_dbg(&dev->ib_dev, "mdev=%p id=%d num_ports=%d\n", mdev,
mdev->dev_id.as_uint32, dev->ib_dev.phys_port_cnt);
ret = ib_register_device(&dev->ib_dev, mana_ib_is_rnic(dev) ? "mana_%d" : "manae_%d",
gc->dev);
if (ret)
goto deallocate_pool;
dev_set_drvdata(&adev->dev, dev);
return 0;
deallocate_pool:
dma_pool_destroy(dev->av_pool);
deregister_net_notifier:
if (mana_ib_is_rnic(dev))
unregister_netdevice_notifier(&dev->nb);
destroy_rnic:
if (mana_ib_is_rnic(dev))
mana_ib_gd_destroy_rnic_adapter(dev);
destroy_eqs:
if (mana_ib_is_rnic(dev))
mana_ib_destroy_eqs(dev);
free_ib_device:
xa_destroy(&dev->qp_table_wq);
ib_dealloc_device(&dev->ib_dev);
return ret;
}
static void mana_ib_remove(struct auxiliary_device *adev)
{
struct mana_ib_dev *dev = dev_get_drvdata(&adev->dev);
if (mana_ib_is_rnic(dev))
mana_drain_gsi_sqs(dev);
ib_unregister_device(&dev->ib_dev);
dma_pool_destroy(dev->av_pool);
if (mana_ib_is_rnic(dev)) {
unregister_netdevice_notifier(&dev->nb);
mana_ib_gd_destroy_rnic_adapter(dev);
mana_ib_destroy_eqs(dev);
}
xa_destroy(&dev->qp_table_wq);
ib_dealloc_device(&dev->ib_dev);
}
static const struct auxiliary_device_id mana_id_table[] = {
{ .name = "mana.rdma", },
{ .name = "mana.eth", },
{},
};
MODULE_DEVICE_TABLE(auxiliary, mana_id_table);
static struct auxiliary_driver mana_driver = {
.probe = mana_ib_probe,
.remove = mana_ib_remove,
.id_table = mana_id_table,
};
module_auxiliary_driver(mana_driver);
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,772 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright (c) 2022 Microsoft Corporation. All rights reserved.
*/
#ifndef _MANA_IB_H_
#define _MANA_IB_H_
#include <rdma/ib_verbs.h>
#include <rdma/ib_mad.h>
#include <rdma/iter.h>
#include <rdma/mana-abi.h>
#include <rdma/uverbs_ioctl.h>
#include <linux/dmapool.h>
#include <net/mana/mana.h>
#include "shadow_queue.h"
#include "counters.h"
#define PAGE_SZ_BM \
(SZ_4K | SZ_8K | SZ_16K | SZ_32K | SZ_64K | SZ_128K | SZ_256K | \
SZ_512K | SZ_1M | SZ_2M)
/* MANA doesn't have any limit for MR size */
#define MANA_IB_MAX_MR_SIZE U64_MAX
/* Send queue ID mask */
#define MANA_SENDQ_MASK BIT(31)
/*
* The hardware limit of number of MRs is greater than maximum number of MRs
* that can possibly represent in 24 bits
*/
#define MANA_IB_MAX_MR 0xFFFFFFu
/*
* The CA timeout is approx. 260ms (4us * 2^(DELAY))
*/
#define MANA_CA_ACK_DELAY 16
/*
* The buffer used for writing AV
*/
#define MANA_AV_BUFFER_SIZE 64
#define MANA_GSI_QPN (1)
struct mana_ib_adapter_caps {
u32 max_sq_id;
u32 max_rq_id;
u32 max_cq_id;
u32 max_qp_count;
u32 max_cq_count;
u32 max_mr_count;
u32 max_pd_count;
u32 max_inbound_read_limit;
u32 max_outbound_read_limit;
u32 mw_count;
u32 max_srq_count;
u32 max_qp_wr;
u32 max_send_sge_count;
u32 max_recv_sge_count;
u32 max_inline_data_size;
u64 feature_flags;
u64 page_size_cap;
};
struct mana_ib_queue {
struct ib_umem *umem;
struct gdma_queue *kmem;
u64 gdma_region;
u64 id;
};
struct mana_ib_dev {
struct ib_device ib_dev;
struct gdma_dev *gdma_dev;
mana_handle_t adapter_handle;
struct gdma_queue *fatal_err_eq;
struct gdma_queue **eqs;
struct xarray qp_table_wq;
struct mana_ib_adapter_caps adapter_caps;
struct dma_pool *av_pool;
netdevice_tracker dev_tracker;
struct notifier_block nb;
};
struct mana_ib_wq {
struct ib_wq ibwq;
struct mana_ib_queue queue;
int wqe;
u32 wq_buf_size;
mana_handle_t rx_object;
};
struct mana_ib_pd {
struct ib_pd ibpd;
u32 pdn;
mana_handle_t pd_handle;
/* Mutex for sharing access to vport_use_count */
struct mutex vport_mutex;
int vport_use_count;
/* Port bound to this PD for raw QP usage. Only valid when
* vport_use_count > 0. A PD can only be associated with a
* single physical port because per-port EQs and vport
* configuration are tied to the PD's refcount.
*/
u32 vport_port;
/* Only one RSS QP is allowed per vport because each RSS QP
* overwrites the vport steering config (indirection table /
* hash key) and mana_disable_vport_rx() on destroy would
* blackhole traffic for any other RSS QP on the same vport.
*/
bool has_rss_qp;
bool tx_shortform_allowed;
u32 tx_vp_offset;
};
struct mana_ib_av {
u8 dest_ip[16];
u8 dest_mac[ETH_ALEN];
u16 udp_src_port;
u8 src_ip[16];
u32 hop_limit : 8;
u32 reserved1 : 12;
u32 dscp : 6;
u32 reserved2 : 5;
u32 is_ipv6 : 1;
u32 reserved3 : 32;
};
struct mana_ib_ah {
struct ib_ah ibah;
struct mana_ib_av *av;
dma_addr_t dma_handle;
};
struct mana_ib_mw {
struct ib_mw ibmw;
mana_handle_t mw_handle;
};
struct mana_ib_mr {
struct ib_mr ibmr;
struct ib_umem *umem;
mana_handle_t mr_handle;
};
struct mana_ib_dm {
struct ib_dm ibdm;
mana_handle_t dm_handle;
};
struct mana_ib_cq {
struct ib_cq ibcq;
struct mana_ib_queue queue;
/* protects CQ polling */
spinlock_t cq_lock;
struct list_head list_send_qp;
struct list_head list_recv_qp;
int cqe;
u32 comp_vector;
mana_handle_t cq_handle;
};
enum mana_rc_queue_type {
MANA_RC_SEND_QUEUE_REQUESTER = 0,
MANA_RC_SEND_QUEUE_RESPONDER,
MANA_RC_SEND_QUEUE_FMR,
MANA_RC_RECV_QUEUE_REQUESTER,
MANA_RC_RECV_QUEUE_RESPONDER,
MANA_RC_QUEUE_TYPE_MAX,
};
struct mana_ib_rc_qp {
struct mana_ib_queue queues[MANA_RC_QUEUE_TYPE_MAX];
};
enum mana_ud_queue_type {
MANA_UD_SEND_QUEUE = 0,
MANA_UD_RECV_QUEUE,
MANA_UD_QUEUE_TYPE_MAX,
};
struct mana_ib_ud_qp {
struct mana_ib_queue queues[MANA_UD_QUEUE_TYPE_MAX];
u32 sq_psn;
};
struct mana_ib_qp {
struct ib_qp ibqp;
mana_handle_t qp_handle;
union {
struct mana_ib_queue raw_sq;
struct mana_ib_rc_qp rc_qp;
struct mana_ib_ud_qp ud_qp;
};
/* The port on the IB device, starting with 1 */
u32 port;
struct list_head cq_send_list;
struct list_head cq_recv_list;
struct shadow_queue shadow_rq;
struct shadow_queue shadow_sq;
refcount_t refcount;
struct completion free;
};
struct mana_ib_ucontext {
struct ib_ucontext ibucontext;
u32 doorbell;
};
struct mana_ib_rwq_ind_table {
struct ib_rwq_ind_table ib_ind_table;
};
enum mana_ib_command_code {
MANA_IB_GET_ADAPTER_CAP = 0x30001,
MANA_IB_CREATE_ADAPTER = 0x30002,
MANA_IB_DESTROY_ADAPTER = 0x30003,
MANA_IB_CONFIG_IP_ADDR = 0x30004,
MANA_IB_CONFIG_MAC_ADDR = 0x30005,
MANA_IB_CREATE_UD_QP = 0x30006,
MANA_IB_DESTROY_UD_QP = 0x30007,
MANA_IB_CREATE_CQ = 0x30008,
MANA_IB_DESTROY_CQ = 0x30009,
MANA_IB_CREATE_RC_QP = 0x3000a,
MANA_IB_DESTROY_RC_QP = 0x3000b,
MANA_IB_SET_QP_STATE = 0x3000d,
MANA_IB_QUERY_VF_COUNTERS = 0x30022,
MANA_IB_QUERY_DEVICE_COUNTERS = 0x30023,
};
struct mana_ib_query_adapter_caps_req {
struct gdma_req_hdr hdr;
}; /*HW Data */
enum mana_ib_adapter_features {
MANA_IB_FEATURE_CLIENT_ERROR_CQE_SUPPORT = BIT(4),
MANA_IB_FEATURE_DEV_COUNTERS_SUPPORT = BIT(5),
MANA_IB_FEATURE_MULTI_PORTS_SUPPORT = BIT(6),
};
struct mana_ib_query_adapter_caps_resp {
struct gdma_resp_hdr hdr;
u32 max_sq_id;
u32 max_rq_id;
u32 max_cq_id;
u32 max_qp_count;
u32 max_cq_count;
u32 max_mr_count;
u32 max_pd_count;
u32 max_inbound_read_limit;
u32 max_outbound_read_limit;
u32 mw_count;
u32 max_srq_count;
u32 max_requester_sq_size;
u32 max_responder_sq_size;
u32 max_requester_rq_size;
u32 max_responder_rq_size;
u32 max_send_sge_count;
u32 max_recv_sge_count;
u32 max_inline_data_size;
u64 feature_flags;
}; /* HW Data */
enum mana_ib_adapter_features_request {
MANA_IB_FEATURE_CLIENT_ERROR_CQE_REQUEST = BIT(1),
}; /*HW Data */
struct mana_rnic_create_adapter_req {
struct gdma_req_hdr hdr;
u32 notify_eq_id;
u32 reserved;
u64 feature_flags;
}; /*HW Data */
struct mana_rnic_create_adapter_resp {
struct gdma_resp_hdr hdr;
mana_handle_t adapter;
}; /* HW Data */
struct mana_rnic_destroy_adapter_req {
struct gdma_req_hdr hdr;
mana_handle_t adapter;
}; /*HW Data */
struct mana_rnic_destroy_adapter_resp {
struct gdma_resp_hdr hdr;
}; /* HW Data */
enum mana_ib_addr_op {
ADDR_OP_ADD = 1,
ADDR_OP_REMOVE = 2,
};
enum sgid_entry_type {
SGID_TYPE_IPV4 = 1,
SGID_TYPE_IPV6 = 2,
};
struct mana_rnic_config_addr_req {
struct gdma_req_hdr hdr;
mana_handle_t adapter;
enum mana_ib_addr_op op;
enum sgid_entry_type sgid_type;
u8 ip_addr[16];
}; /* HW Data */
struct mana_rnic_config_addr_resp {
struct gdma_resp_hdr hdr;
}; /* HW Data */
struct mana_rnic_config_mac_addr_req {
struct gdma_req_hdr hdr;
mana_handle_t adapter;
enum mana_ib_addr_op op;
u8 mac_addr[ETH_ALEN];
u8 reserved[6];
}; /* HW Data */
struct mana_rnic_config_mac_addr_resp {
struct gdma_resp_hdr hdr;
}; /* HW Data */
struct mana_rnic_create_cq_req {
struct gdma_req_hdr hdr;
mana_handle_t adapter;
u64 gdma_region;
u32 eq_id;
u32 doorbell_page;
}; /* HW Data */
struct mana_rnic_create_cq_resp {
struct gdma_resp_hdr hdr;
mana_handle_t cq_handle;
u32 cq_id;
u32 reserved;
}; /* HW Data */
struct mana_rnic_destroy_cq_req {
struct gdma_req_hdr hdr;
mana_handle_t adapter;
mana_handle_t cq_handle;
}; /* HW Data */
struct mana_rnic_destroy_cq_resp {
struct gdma_resp_hdr hdr;
}; /* HW Data */
enum mana_rnic_create_rc_flags {
MANA_RC_FLAG_NO_FMR = 2,
};
struct mana_rnic_create_qp_req {
struct gdma_req_hdr hdr;
mana_handle_t adapter;
mana_handle_t pd_handle;
mana_handle_t send_cq_handle;
mana_handle_t recv_cq_handle;
u64 dma_region[MANA_RC_QUEUE_TYPE_MAX];
u64 deprecated[2];
u64 flags;
u32 doorbell_page;
u32 max_send_wr;
u32 max_recv_wr;
u32 max_send_sge;
u32 max_recv_sge;
u32 reserved;
}; /* HW Data */
struct mana_rnic_create_qp_resp {
struct gdma_resp_hdr hdr;
mana_handle_t rc_qp_handle;
u32 queue_ids[MANA_RC_QUEUE_TYPE_MAX];
u32 reserved;
}; /* HW Data*/
struct mana_rnic_destroy_rc_qp_req {
struct gdma_req_hdr hdr;
mana_handle_t adapter;
mana_handle_t rc_qp_handle;
}; /* HW Data */
struct mana_rnic_destroy_rc_qp_resp {
struct gdma_resp_hdr hdr;
}; /* HW Data */
struct mana_rnic_create_udqp_req {
struct gdma_req_hdr hdr;
mana_handle_t adapter;
mana_handle_t pd_handle;
mana_handle_t send_cq_handle;
mana_handle_t recv_cq_handle;
u64 dma_region[MANA_UD_QUEUE_TYPE_MAX];
u32 qp_type;
u32 doorbell_page;
u32 max_send_wr;
u32 max_recv_wr;
u32 max_send_sge;
u32 max_recv_sge;
}; /* HW Data */
struct mana_rnic_create_udqp_resp {
struct gdma_resp_hdr hdr;
mana_handle_t qp_handle;
u32 queue_ids[MANA_UD_QUEUE_TYPE_MAX];
}; /* HW Data*/
struct mana_rnic_destroy_udqp_req {
struct gdma_req_hdr hdr;
mana_handle_t adapter;
mana_handle_t qp_handle;
}; /* HW Data */
struct mana_rnic_destroy_udqp_resp {
struct gdma_resp_hdr hdr;
}; /* HW Data */
struct mana_ib_ah_attr {
u8 src_addr[16];
u8 dest_addr[16];
u8 src_mac[ETH_ALEN];
u8 dest_mac[ETH_ALEN];
u8 src_addr_type;
u8 dest_addr_type;
u8 hop_limit;
u8 traffic_class;
u16 src_port;
u16 dest_port;
u32 flow_label;
};
struct mana_rnic_set_qp_state_req {
struct gdma_req_hdr hdr;
mana_handle_t adapter;
mana_handle_t qp_handle;
u64 attr_mask;
u32 qp_state;
u32 path_mtu;
u32 rq_psn;
u32 sq_psn;
u32 dest_qpn;
u32 max_dest_rd_atomic;
u32 retry_cnt;
u32 rnr_retry;
u32 min_rnr_timer;
u32 rate_limit;
struct mana_ib_ah_attr ah_attr;
u64 reserved1;
u32 qkey;
u32 qp_access_flags;
u8 local_ack_timeout;
u8 max_rd_atomic;
u16 reserved2;
u32 reserved3;
}; /* HW Data */
struct mana_rnic_set_qp_state_resp {
struct gdma_resp_hdr hdr;
}; /* HW Data */
enum WQE_OPCODE_TYPES {
WQE_TYPE_UD_SEND = 0,
WQE_TYPE_UD_RECV = 8,
}; /* HW DATA */
struct rdma_send_oob {
u32 wqe_type : 5;
u32 fence : 1;
u32 signaled : 1;
u32 solicited : 1;
u32 psn : 24;
u32 ssn_or_rqpn : 24;
u32 reserved1 : 8;
union {
struct {
u32 remote_qkey;
u32 immediate;
u32 reserved1;
u32 reserved2;
} ud_send;
};
}; /* HW DATA */
struct mana_rdma_cqe {
union {
struct {
u8 cqe_type;
u8 data[GDMA_COMP_DATA_SIZE - 1];
};
struct {
u32 cqe_type : 8;
u32 vendor_error : 9;
u32 reserved1 : 15;
u32 sge_offset : 5;
u32 tx_wqe_offset : 27;
} ud_send;
struct {
u32 cqe_type : 8;
u32 reserved1 : 24;
u32 msg_len;
u32 src_qpn : 24;
u32 reserved2 : 8;
u32 imm_data;
u32 rx_wqe_offset;
} ud_recv;
};
}; /* HW DATA */
struct mana_rnic_query_vf_cntrs_req {
struct gdma_req_hdr hdr;
mana_handle_t adapter;
}; /* HW Data */
struct mana_rnic_query_vf_cntrs_resp {
struct gdma_resp_hdr hdr;
u64 requester_timeout;
u64 requester_oos_nak;
u64 requester_rnr_nak;
u64 responder_rnr_nak;
u64 responder_oos;
u64 responder_dup_request;
u64 requester_implicit_nak;
u64 requester_readresp_psn_mismatch;
u64 nak_inv_req;
u64 nak_access_err;
u64 nak_opp_err;
u64 nak_inv_read;
u64 responder_local_len_err;
u64 requestor_local_prot_err;
u64 responder_rem_access_err;
u64 responder_local_qp_err;
u64 responder_malformed_wqe;
u64 general_hw_err;
u64 requester_rnr_nak_retries_exceeded;
u64 requester_retries_exceeded;
u64 total_fatal_err;
u64 received_cnps;
u64 num_qps_congested;
u64 rate_inc_events;
u64 num_qps_recovered;
u64 current_rate;
u64 dup_rx_req;
u64 tx_bytes;
u64 rx_bytes;
u64 rx_send_req;
u64 rx_write_req;
u64 rx_read_req;
u64 tx_pkt;
u64 rx_pkt;
}; /* HW Data */
struct mana_rnic_query_device_cntrs_req {
struct gdma_req_hdr hdr;
mana_handle_t adapter;
}; /* HW Data */
struct mana_rnic_query_device_cntrs_resp {
struct gdma_resp_hdr hdr;
u32 sent_cnps;
u32 received_ecns;
u32 reserved1;
u32 received_cnp_count;
u32 qp_congested_events;
u32 qp_recovered_events;
u32 rate_inc_events;
u32 reserved2;
}; /* HW Data */
static inline struct gdma_context *mdev_to_gc(struct mana_ib_dev *mdev)
{
return mdev->gdma_dev->gdma_context;
}
static inline struct mana_ib_qp *mana_get_qp_ref(struct mana_ib_dev *mdev,
u32 qid, bool is_sq)
{
struct mana_ib_qp *qp;
unsigned long flag;
if (is_sq)
qid |= MANA_SENDQ_MASK;
xa_lock_irqsave(&mdev->qp_table_wq, flag);
qp = xa_load(&mdev->qp_table_wq, qid);
if (qp)
refcount_inc(&qp->refcount);
xa_unlock_irqrestore(&mdev->qp_table_wq, flag);
return qp;
}
static inline void mana_put_qp_ref(struct mana_ib_qp *qp)
{
if (refcount_dec_and_test(&qp->refcount))
complete(&qp->free);
}
static inline bool mana_ib_is_rnic(struct mana_ib_dev *mdev)
{
return mdev->gdma_dev->dev_id.type == GDMA_DEVICE_MANA_IB;
}
static inline struct net_device *mana_ib_get_netdev(struct ib_device *ibdev, u32 port)
{
struct mana_ib_dev *mdev = container_of(ibdev, struct mana_ib_dev, ib_dev);
struct gdma_context *gc = mdev_to_gc(mdev);
struct mana_context *mc = gc->mana.driver_data;
if (port < 1 || port > mc->num_ports)
return NULL;
return mc->ports[port - 1];
}
static inline void copy_in_reverse(u8 *dst, const u8 *src, u32 size)
{
u32 i;
for (i = 0; i < size; i++)
dst[size - 1 - i] = src[i];
}
int mana_ib_install_cq_cb(struct mana_ib_dev *mdev, struct mana_ib_cq *cq);
void mana_ib_remove_cq_cb(struct mana_ib_dev *mdev, struct mana_ib_cq *cq);
int mana_ib_create_zero_offset_dma_region(struct mana_ib_dev *dev, struct ib_umem *umem,
mana_handle_t *gdma_region);
int mana_ib_create_dma_region(struct mana_ib_dev *dev, struct ib_umem *umem,
mana_handle_t *gdma_region, u64 virt);
int mana_ib_gd_destroy_dma_region(struct mana_ib_dev *dev,
mana_handle_t gdma_region);
int mana_ib_create_kernel_queue(struct mana_ib_dev *mdev, u32 size, enum gdma_queue_type type,
struct mana_ib_queue *queue);
int mana_ib_create_queue(struct mana_ib_dev *mdev, u64 addr, u32 size,
struct mana_ib_queue *queue);
void mana_ib_destroy_queue(struct mana_ib_dev *mdev, struct mana_ib_queue *queue);
struct ib_wq *mana_ib_create_wq(struct ib_pd *pd,
struct ib_wq_init_attr *init_attr,
struct ib_udata *udata);
int mana_ib_modify_wq(struct ib_wq *wq, struct ib_wq_attr *wq_attr,
u32 wq_attr_mask, struct ib_udata *udata);
int mana_ib_destroy_wq(struct ib_wq *ibwq, struct ib_udata *udata);
int mana_ib_create_rwq_ind_table(struct ib_rwq_ind_table *ib_rwq_ind_table,
struct ib_rwq_ind_table_init_attr *init_attr,
struct ib_udata *udata);
int mana_ib_destroy_rwq_ind_table(struct ib_rwq_ind_table *ib_rwq_ind_tbl);
struct ib_mr *mana_ib_get_dma_mr(struct ib_pd *ibpd, int access_flags);
struct ib_mr *mana_ib_reg_user_mr(struct ib_pd *pd, u64 start, u64 length,
u64 iova, int access_flags,
struct ib_dmah *dmah,
struct ib_udata *udata);
int mana_ib_dereg_mr(struct ib_mr *ibmr, struct ib_udata *udata);
int mana_ib_create_qp(struct ib_qp *qp, struct ib_qp_init_attr *qp_init_attr,
struct ib_udata *udata);
int mana_ib_modify_qp(struct ib_qp *ibqp, struct ib_qp_attr *attr,
int attr_mask, struct ib_udata *udata);
int mana_ib_destroy_qp(struct ib_qp *ibqp, struct ib_udata *udata);
int mana_ib_cfg_vport(struct mana_ib_dev *dev, u32 port_id,
struct mana_ib_pd *pd, u32 doorbell_id);
void mana_ib_uncfg_vport(struct mana_ib_dev *dev, struct mana_ib_pd *pd,
u32 port);
int mana_ib_create_cq(struct ib_cq *ibcq, const struct ib_cq_init_attr *attr,
struct uverbs_attr_bundle *attrs);
int mana_ib_destroy_cq(struct ib_cq *ibcq, struct ib_udata *udata);
int mana_ib_alloc_pd(struct ib_pd *ibpd, struct ib_udata *udata);
int mana_ib_dealloc_pd(struct ib_pd *ibpd, struct ib_udata *udata);
int mana_ib_alloc_ucontext(struct ib_ucontext *ibcontext,
struct ib_udata *udata);
void mana_ib_dealloc_ucontext(struct ib_ucontext *ibcontext);
int mana_ib_mmap(struct ib_ucontext *ibcontext, struct vm_area_struct *vma);
int mana_ib_get_port_immutable(struct ib_device *ibdev, u32 port_num,
struct ib_port_immutable *immutable);
int mana_ib_query_device(struct ib_device *ibdev, struct ib_device_attr *props,
struct ib_udata *uhw);
int mana_ib_query_port(struct ib_device *ibdev, u32 port,
struct ib_port_attr *props);
int mana_ib_query_gid(struct ib_device *ibdev, u32 port, int index,
union ib_gid *gid);
void mana_ib_disassociate_ucontext(struct ib_ucontext *ibcontext);
int mana_ib_gd_query_adapter_caps(struct mana_ib_dev *mdev);
int mana_eth_query_adapter_caps(struct mana_ib_dev *mdev);
int mana_ib_create_eqs(struct mana_ib_dev *mdev);
void mana_ib_destroy_eqs(struct mana_ib_dev *mdev);
int mana_ib_gd_create_rnic_adapter(struct mana_ib_dev *mdev);
int mana_ib_gd_destroy_rnic_adapter(struct mana_ib_dev *mdev);
int mana_ib_query_pkey(struct ib_device *ibdev, u32 port, u16 index, u16 *pkey);
enum rdma_link_layer mana_ib_get_link_layer(struct ib_device *device, u32 port_num);
int mana_ib_gd_add_gid(const struct ib_gid_attr *attr, void **context);
int mana_ib_gd_del_gid(const struct ib_gid_attr *attr, void **context);
int mana_ib_gd_config_mac(struct mana_ib_dev *mdev, enum mana_ib_addr_op op, u8 *mac);
int mana_ib_gd_create_cq(struct mana_ib_dev *mdev, struct mana_ib_cq *cq, u32 doorbell);
int mana_ib_gd_destroy_cq(struct mana_ib_dev *mdev, struct mana_ib_cq *cq);
int mana_ib_gd_create_rc_qp(struct mana_ib_dev *mdev, struct mana_ib_qp *qp,
struct ib_qp_init_attr *attr, u32 doorbell, u64 flags);
int mana_ib_gd_destroy_rc_qp(struct mana_ib_dev *mdev, struct mana_ib_qp *qp);
int mana_ib_gd_create_ud_qp(struct mana_ib_dev *mdev, struct mana_ib_qp *qp,
struct ib_qp_init_attr *attr, u32 doorbell, u32 type);
int mana_ib_gd_destroy_ud_qp(struct mana_ib_dev *mdev, struct mana_ib_qp *qp);
int mana_ib_create_ah(struct ib_ah *ibah, struct rdma_ah_init_attr *init_attr,
struct ib_udata *udata);
int mana_ib_destroy_ah(struct ib_ah *ah, u32 flags);
int mana_ib_post_recv(struct ib_qp *ibqp, const struct ib_recv_wr *wr,
const struct ib_recv_wr **bad_wr);
int mana_ib_post_send(struct ib_qp *ibqp, const struct ib_send_wr *wr,
const struct ib_send_wr **bad_wr);
void mana_drain_gsi_sqs(struct mana_ib_dev *mdev);
int mana_ib_poll_cq(struct ib_cq *ibcq, int num_entries, struct ib_wc *wc);
int mana_ib_arm_cq(struct ib_cq *ibcq, enum ib_cq_notify_flags flags);
int mana_ib_alloc_mw(struct ib_mw *mw, struct ib_udata *udata);
int mana_ib_dealloc_mw(struct ib_mw *mw);
struct ib_mr *mana_ib_reg_user_mr_dmabuf(struct ib_pd *ibpd, u64 start, u64 length,
u64 iova, int fd, int mr_access_flags,
struct ib_dmah *dmah,
struct uverbs_attr_bundle *attrs);
struct ib_dm *mana_ib_alloc_dm(struct ib_device *dev, struct ib_ucontext *context,
struct ib_dm_alloc_attr *attr, struct uverbs_attr_bundle *attrs);
int mana_ib_dealloc_dm(struct ib_dm *dm, struct uverbs_attr_bundle *attrs);
struct ib_mr *mana_ib_reg_dm_mr(struct ib_pd *pd, struct ib_dm *dm, struct ib_dm_mr_attr *attr,
struct uverbs_attr_bundle *attrs);
#endif
+471
View File
@@ -0,0 +1,471 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) 2022, Microsoft Corporation. All rights reserved.
*/
#include "mana_ib.h"
#define VALID_MR_FLAGS (IB_ACCESS_LOCAL_WRITE | IB_ACCESS_REMOTE_WRITE | IB_ACCESS_REMOTE_READ |\
IB_ACCESS_REMOTE_ATOMIC | IB_ACCESS_MW_BIND | IB_ZERO_BASED)
#define VALID_DMA_MR_FLAGS (IB_ACCESS_LOCAL_WRITE)
static enum gdma_mr_access_flags
mana_ib_verbs_to_gdma_access_flags(int access_flags)
{
enum gdma_mr_access_flags flags = GDMA_ACCESS_FLAG_LOCAL_READ;
if (access_flags & IB_ACCESS_LOCAL_WRITE)
flags |= GDMA_ACCESS_FLAG_LOCAL_WRITE;
if (access_flags & IB_ACCESS_REMOTE_WRITE)
flags |= GDMA_ACCESS_FLAG_REMOTE_WRITE;
if (access_flags & IB_ACCESS_REMOTE_READ)
flags |= GDMA_ACCESS_FLAG_REMOTE_READ;
if (access_flags & IB_ACCESS_REMOTE_ATOMIC)
flags |= GDMA_ACCESS_FLAG_REMOTE_ATOMIC;
if (access_flags & IB_ACCESS_MW_BIND)
flags |= GDMA_ACCESS_FLAG_BIND_MW;
return flags;
}
static int mana_ib_gd_create_mr(struct mana_ib_dev *dev, struct mana_ib_mr *mr,
struct gdma_create_mr_params *mr_params)
{
struct gdma_create_mr_response resp = {};
struct gdma_create_mr_request req = {};
struct gdma_context *gc = mdev_to_gc(dev);
int err;
mana_gd_init_req_hdr(&req.hdr, GDMA_CREATE_MR, sizeof(req),
sizeof(resp));
req.hdr.req.msg_version = GDMA_MESSAGE_V2;
req.pd_handle = mr_params->pd_handle;
req.mr_type = mr_params->mr_type;
switch (mr_params->mr_type) {
case GDMA_MR_TYPE_GPA:
break;
case GDMA_MR_TYPE_GVA:
req.gva.dma_region_handle = mr_params->gva.dma_region_handle;
req.gva.virtual_address = mr_params->gva.virtual_address;
req.gva.access_flags = mr_params->gva.access_flags;
break;
case GDMA_MR_TYPE_ZBVA:
req.zbva.dma_region_handle = mr_params->zbva.dma_region_handle;
req.zbva.access_flags = mr_params->zbva.access_flags;
break;
case GDMA_MR_TYPE_DM:
req.da_ext.length = mr_params->da.length;
req.da.dm_handle = mr_params->da.dm_handle;
req.da.offset = mr_params->da.offset;
req.da.access_flags = mr_params->da.access_flags;
break;
default:
ibdev_dbg(&dev->ib_dev,
"invalid param (GDMA_MR_TYPE) passed, type %d\n",
req.mr_type);
return -EINVAL;
}
err = mana_gd_send_request(gc, sizeof(req), &req, sizeof(resp), &resp);
if (err)
return err;
mr->ibmr.lkey = resp.lkey;
mr->ibmr.rkey = resp.rkey;
mr->mr_handle = resp.mr_handle;
return 0;
}
static int mana_ib_gd_destroy_mr(struct mana_ib_dev *dev, u64 mr_handle)
{
struct gdma_destroy_mr_response resp = {};
struct gdma_destroy_mr_request req = {};
struct gdma_context *gc = mdev_to_gc(dev);
mana_gd_init_req_hdr(&req.hdr, GDMA_DESTROY_MR, sizeof(req),
sizeof(resp));
req.mr_handle = mr_handle;
return mana_gd_send_request(gc, sizeof(req), &req, sizeof(resp), &resp);
}
struct ib_mr *mana_ib_reg_user_mr(struct ib_pd *ibpd, u64 start, u64 length,
u64 iova, int access_flags,
struct ib_dmah *dmah,
struct ib_udata *udata)
{
struct mana_ib_pd *pd = container_of(ibpd, struct mana_ib_pd, ibpd);
struct gdma_create_mr_params mr_params = {};
struct ib_device *ibdev = ibpd->device;
struct mana_ib_dev *dev;
struct mana_ib_mr *mr;
u64 dma_region_handle;
int err;
if (dmah)
return ERR_PTR(-EOPNOTSUPP);
dev = container_of(ibdev, struct mana_ib_dev, ib_dev);
ibdev_dbg(ibdev,
"start 0x%llx, iova 0x%llx length 0x%llx access_flags 0x%x",
start, iova, length, access_flags);
access_flags &= ~IB_ACCESS_OPTIONAL;
if (access_flags & ~VALID_MR_FLAGS)
return ERR_PTR(-EINVAL);
mr = kzalloc_obj(*mr);
if (!mr)
return ERR_PTR(-ENOMEM);
mr->umem = ib_umem_get_va(ibdev, start, length, access_flags);
if (IS_ERR(mr->umem)) {
err = PTR_ERR(mr->umem);
ibdev_dbg(ibdev,
"Failed to get umem for register user-mr, %pe\n",
mr->umem);
goto err_free;
}
err = mana_ib_create_dma_region(dev, mr->umem, &dma_region_handle, iova);
if (err) {
ibdev_dbg(ibdev, "Failed create dma region for user-mr, %d\n",
err);
goto err_umem;
}
ibdev_dbg(ibdev,
"created dma region for user-mr 0x%llx\n",
dma_region_handle);
mr_params.pd_handle = pd->pd_handle;
if (access_flags & IB_ZERO_BASED) {
mr_params.mr_type = GDMA_MR_TYPE_ZBVA;
mr_params.zbva.dma_region_handle = dma_region_handle;
mr_params.zbva.access_flags =
mana_ib_verbs_to_gdma_access_flags(access_flags);
} else {
mr_params.mr_type = GDMA_MR_TYPE_GVA;
mr_params.gva.dma_region_handle = dma_region_handle;
mr_params.gva.virtual_address = iova;
mr_params.gva.access_flags =
mana_ib_verbs_to_gdma_access_flags(access_flags);
}
err = mana_ib_gd_create_mr(dev, mr, &mr_params);
if (err)
goto err_dma_region;
/*
* There is no need to keep track of dma_region_handle after MR is
* successfully created. The dma_region_handle is tracked in the PF
* as part of the lifecycle of this MR.
*/
return &mr->ibmr;
err_dma_region:
mana_gd_destroy_dma_region(mdev_to_gc(dev), dma_region_handle);
err_umem:
ib_umem_release(mr->umem);
err_free:
kfree(mr);
return ERR_PTR(err);
}
struct ib_mr *mana_ib_reg_user_mr_dmabuf(struct ib_pd *ibpd, u64 start, u64 length,
u64 iova, int fd, int access_flags,
struct ib_dmah *dmah,
struct uverbs_attr_bundle *attrs)
{
struct mana_ib_pd *pd = container_of(ibpd, struct mana_ib_pd, ibpd);
struct gdma_create_mr_params mr_params = {};
struct ib_device *ibdev = ibpd->device;
struct ib_umem_dmabuf *umem_dmabuf;
struct mana_ib_dev *dev;
struct mana_ib_mr *mr;
u64 dma_region_handle;
int err;
if (dmah)
return ERR_PTR(-EOPNOTSUPP);
dev = container_of(ibdev, struct mana_ib_dev, ib_dev);
access_flags &= ~IB_ACCESS_OPTIONAL;
if (access_flags & ~VALID_MR_FLAGS)
return ERR_PTR(-EOPNOTSUPP);
mr = kzalloc_obj(*mr);
if (!mr)
return ERR_PTR(-ENOMEM);
umem_dmabuf = ib_umem_dmabuf_get_pinned(ibdev, start, length, fd, access_flags);
if (IS_ERR(umem_dmabuf)) {
err = PTR_ERR(umem_dmabuf);
ibdev_dbg(ibdev, "Failed to get dmabuf umem, %pe\n",
umem_dmabuf);
goto err_free;
}
mr->umem = &umem_dmabuf->umem;
err = mana_ib_create_dma_region(dev, mr->umem, &dma_region_handle, iova);
if (err) {
ibdev_dbg(ibdev, "Failed create dma region for user-mr, %d\n",
err);
goto err_umem;
}
mr_params.pd_handle = pd->pd_handle;
mr_params.mr_type = GDMA_MR_TYPE_GVA;
mr_params.gva.dma_region_handle = dma_region_handle;
mr_params.gva.virtual_address = iova;
mr_params.gva.access_flags =
mana_ib_verbs_to_gdma_access_flags(access_flags);
err = mana_ib_gd_create_mr(dev, mr, &mr_params);
if (err)
goto err_dma_region;
/*
* There is no need to keep track of dma_region_handle after MR is
* successfully created. The dma_region_handle is tracked in the PF
* as part of the lifecycle of this MR.
*/
return &mr->ibmr;
err_dma_region:
mana_gd_destroy_dma_region(mdev_to_gc(dev), dma_region_handle);
err_umem:
ib_umem_release(mr->umem);
err_free:
kfree(mr);
return ERR_PTR(err);
}
struct ib_mr *mana_ib_get_dma_mr(struct ib_pd *ibpd, int access_flags)
{
struct mana_ib_pd *pd = container_of(ibpd, struct mana_ib_pd, ibpd);
struct gdma_create_mr_params mr_params = {};
struct ib_device *ibdev = ibpd->device;
struct mana_ib_dev *dev;
struct mana_ib_mr *mr;
int err;
dev = container_of(ibdev, struct mana_ib_dev, ib_dev);
if (access_flags & ~VALID_DMA_MR_FLAGS)
return ERR_PTR(-EINVAL);
mr = kzalloc_obj(*mr);
if (!mr)
return ERR_PTR(-ENOMEM);
mr_params.pd_handle = pd->pd_handle;
mr_params.mr_type = GDMA_MR_TYPE_GPA;
err = mana_ib_gd_create_mr(dev, mr, &mr_params);
if (err)
goto err_free;
return &mr->ibmr;
err_free:
kfree(mr);
return ERR_PTR(err);
}
static int mana_ib_gd_create_mw(struct mana_ib_dev *dev, struct mana_ib_pd *pd, struct ib_mw *ibmw)
{
struct mana_ib_mw *mw = container_of(ibmw, struct mana_ib_mw, ibmw);
struct gdma_context *gc = mdev_to_gc(dev);
struct gdma_create_mr_response resp = {};
struct gdma_create_mr_request req = {};
int err;
mana_gd_init_req_hdr(&req.hdr, GDMA_CREATE_MR, sizeof(req), sizeof(resp));
req.hdr.req.msg_version = GDMA_MESSAGE_V2;
req.pd_handle = pd->pd_handle;
switch (mw->ibmw.type) {
case IB_MW_TYPE_1:
req.mr_type = GDMA_MR_TYPE_MW1;
break;
case IB_MW_TYPE_2:
req.mr_type = GDMA_MR_TYPE_MW2;
break;
default:
return -EINVAL;
}
err = mana_gd_send_request(gc, sizeof(req), &req, sizeof(resp), &resp);
if (err)
return err;
mw->ibmw.rkey = resp.rkey;
mw->mw_handle = resp.mr_handle;
return 0;
}
int mana_ib_alloc_mw(struct ib_mw *ibmw, struct ib_udata *udata)
{
struct mana_ib_dev *mdev = container_of(ibmw->device, struct mana_ib_dev, ib_dev);
struct mana_ib_pd *pd = container_of(ibmw->pd, struct mana_ib_pd, ibpd);
return mana_ib_gd_create_mw(mdev, pd, ibmw);
}
int mana_ib_dealloc_mw(struct ib_mw *ibmw)
{
struct mana_ib_dev *dev = container_of(ibmw->device, struct mana_ib_dev, ib_dev);
struct mana_ib_mw *mw = container_of(ibmw, struct mana_ib_mw, ibmw);
return mana_ib_gd_destroy_mr(dev, mw->mw_handle);
}
int mana_ib_dereg_mr(struct ib_mr *ibmr, struct ib_udata *udata)
{
struct mana_ib_mr *mr = container_of(ibmr, struct mana_ib_mr, ibmr);
struct ib_device *ibdev = ibmr->device;
struct mana_ib_dev *dev;
int err;
dev = container_of(ibdev, struct mana_ib_dev, ib_dev);
err = mana_ib_gd_destroy_mr(dev, mr->mr_handle);
if (err)
return err;
if (mr->umem)
ib_umem_release(mr->umem);
kfree(mr);
return 0;
}
static int mana_ib_gd_alloc_dm(struct mana_ib_dev *mdev, struct mana_ib_dm *dm,
struct ib_dm_alloc_attr *attr)
{
struct gdma_context *gc = mdev_to_gc(mdev);
struct gdma_alloc_dm_resp resp = {};
struct gdma_alloc_dm_req req = {};
int err;
mana_gd_init_req_hdr(&req.hdr, GDMA_ALLOC_DM, sizeof(req), sizeof(resp));
req.length = attr->length;
req.alignment = attr->alignment;
req.flags = attr->flags;
err = mana_gd_send_request(gc, sizeof(req), &req, sizeof(resp), &resp);
if (err)
return err;
dm->dm_handle = resp.dm_handle;
return 0;
}
struct ib_dm *mana_ib_alloc_dm(struct ib_device *ibdev,
struct ib_ucontext *context,
struct ib_dm_alloc_attr *attr,
struct uverbs_attr_bundle *attrs)
{
struct mana_ib_dev *dev = container_of(ibdev, struct mana_ib_dev, ib_dev);
struct mana_ib_dm *dm;
int err;
dm = kzalloc_obj(*dm);
if (!dm)
return ERR_PTR(-ENOMEM);
err = mana_ib_gd_alloc_dm(dev, dm, attr);
if (err)
goto err_free;
return &dm->ibdm;
err_free:
kfree(dm);
return ERR_PTR(err);
}
static int mana_ib_gd_destroy_dm(struct mana_ib_dev *mdev, struct mana_ib_dm *dm)
{
struct gdma_context *gc = mdev_to_gc(mdev);
struct gdma_destroy_dm_resp resp = {};
struct gdma_destroy_dm_req req = {};
mana_gd_init_req_hdr(&req.hdr, GDMA_DESTROY_DM, sizeof(req), sizeof(resp));
req.dm_handle = dm->dm_handle;
return mana_gd_send_request(gc, sizeof(req), &req, sizeof(resp), &resp);
}
int mana_ib_dealloc_dm(struct ib_dm *ibdm, struct uverbs_attr_bundle *attrs)
{
struct mana_ib_dev *dev = container_of(ibdm->device, struct mana_ib_dev, ib_dev);
struct mana_ib_dm *dm = container_of(ibdm, struct mana_ib_dm, ibdm);
int err;
err = mana_ib_gd_destroy_dm(dev, dm);
if (err)
return err;
kfree(dm);
return 0;
}
struct ib_mr *mana_ib_reg_dm_mr(struct ib_pd *ibpd, struct ib_dm *ibdm,
struct ib_dm_mr_attr *attr,
struct uverbs_attr_bundle *attrs)
{
struct mana_ib_dev *dev = container_of(ibpd->device, struct mana_ib_dev, ib_dev);
struct mana_ib_dm *mana_dm = container_of(ibdm, struct mana_ib_dm, ibdm);
struct mana_ib_pd *pd = container_of(ibpd, struct mana_ib_pd, ibpd);
struct gdma_create_mr_params mr_params = {};
struct mana_ib_mr *mr;
int err;
attr->access_flags &= ~IB_ACCESS_OPTIONAL;
if (attr->access_flags & ~VALID_MR_FLAGS)
return ERR_PTR(-EOPNOTSUPP);
mr = kzalloc_obj(*mr);
if (!mr)
return ERR_PTR(-ENOMEM);
mr_params.pd_handle = pd->pd_handle;
mr_params.mr_type = GDMA_MR_TYPE_DM;
mr_params.da.dm_handle = mana_dm->dm_handle;
mr_params.da.offset = attr->offset;
mr_params.da.length = attr->length;
mr_params.da.access_flags =
mana_ib_verbs_to_gdma_access_flags(attr->access_flags);
err = mana_ib_gd_create_mr(dev, mr, &mr_params);
if (err)
goto err_free;
return &mr->ibmr;
err_free:
kfree(mr);
return ERR_PTR(err);
}
+951
View File
@@ -0,0 +1,951 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) 2022, Microsoft Corporation. All rights reserved.
*/
#include "mana_ib.h"
static int mana_ib_cfg_vport_steering(struct mana_ib_dev *dev,
struct net_device *ndev,
mana_handle_t default_rxobj,
mana_handle_t ind_table[],
u32 log_ind_tbl_size, u32 rx_hash_key_len,
u8 *rx_hash_key)
{
struct mana_port_context *mpc = netdev_priv(ndev);
struct mana_cfg_rx_steer_req_v2 *req;
struct mana_cfg_rx_steer_resp resp = {};
struct gdma_context *gc;
u32 req_buf_size;
int i, err;
gc = mdev_to_gc(dev);
if (rx_hash_key_len > sizeof(req->hashkey))
return -EINVAL;
req_buf_size = struct_size(req, indir_tab, MANA_INDIRECT_TABLE_DEF_SIZE);
req = kzalloc(req_buf_size, GFP_KERNEL);
if (!req)
return -ENOMEM;
mana_gd_init_req_hdr(&req->hdr, MANA_CONFIG_VPORT_RX, req_buf_size,
sizeof(resp));
req->hdr.req.msg_version = GDMA_MESSAGE_V2;
req->vport = mpc->port_handle;
req->rx_enable = 1;
req->update_default_rxobj = 1;
req->default_rxobj = default_rxobj;
req->hdr.dev_id = gc->mana.dev_id;
/* If there are more than 1 entries in indirection table, enable RSS */
if (log_ind_tbl_size)
req->rss_enable = true;
req->num_indir_entries = MANA_INDIRECT_TABLE_DEF_SIZE;
req->indir_tab_offset = offsetof(struct mana_cfg_rx_steer_req_v2,
indir_tab);
req->update_indir_tab = true;
req->cqe_coalescing_enable = 1;
/* The ind table passed to the hardware must have
* MANA_INDIRECT_TABLE_DEF_SIZE entries. Adjust the verb
* ind_table to MANA_INDIRECT_TABLE_SIZE if required
*/
ibdev_dbg(&dev->ib_dev, "ind table size %u\n", 1 << log_ind_tbl_size);
for (i = 0; i < MANA_INDIRECT_TABLE_DEF_SIZE; i++) {
req->indir_tab[i] = ind_table[i % (1 << log_ind_tbl_size)];
ibdev_dbg(&dev->ib_dev, "index %u handle 0x%llx\n", i,
req->indir_tab[i]);
}
req->update_hashkey = true;
if (rx_hash_key_len)
memcpy(req->hashkey, rx_hash_key, rx_hash_key_len);
else
netdev_rss_key_fill(req->hashkey, MANA_HASH_KEY_SIZE);
ibdev_dbg(&dev->ib_dev, "vport handle %llu default_rxobj 0x%llx\n",
req->vport, default_rxobj);
err = mana_gd_send_request(gc, req_buf_size, req, sizeof(resp), &resp);
kfree(req);
return err;
}
static int mana_ib_create_qp_rss(struct ib_qp *ibqp, struct ib_pd *pd,
struct ib_qp_init_attr *attr,
struct ib_udata *udata)
{
struct mana_ib_pd *mana_pd = container_of(pd, struct mana_ib_pd, ibpd);
struct mana_ib_qp *qp = container_of(ibqp, struct mana_ib_qp, ibqp);
struct mana_ib_dev *mdev =
container_of(pd->device, struct mana_ib_dev, ib_dev);
struct ib_rwq_ind_table *ind_tbl = attr->rwq_ind_tbl;
struct mana_ib_create_qp_rss_resp resp = {};
struct mana_ib_create_qp_rss ucmd;
mana_handle_t *mana_ind_table;
struct mana_port_context *mpc;
unsigned int ind_tbl_size;
struct net_device *ndev;
struct mana_ib_cq *cq;
struct mana_ib_wq *wq;
struct mana_eq *eq;
struct ib_cq *ibcq;
struct ib_wq *ibwq;
int i = 0;
u32 port;
int ret;
if (!udata)
return -EINVAL;
ret = ib_copy_validate_udata_in(udata, ucmd, port);
if (ret)
return ret;
if (attr->cap.max_recv_wr > mdev->adapter_caps.max_qp_wr) {
ibdev_dbg(&mdev->ib_dev,
"Requested max_recv_wr %d exceeding limit\n",
attr->cap.max_recv_wr);
return -EINVAL;
}
if (attr->cap.max_recv_sge > MAX_RX_WQE_SGL_ENTRIES) {
ibdev_dbg(&mdev->ib_dev,
"Requested max_recv_sge %d exceeding limit\n",
attr->cap.max_recv_sge);
return -EINVAL;
}
ind_tbl_size = 1 << ind_tbl->log_ind_tbl_size;
if (ind_tbl_size > MANA_INDIRECT_TABLE_DEF_SIZE) {
ibdev_dbg(&mdev->ib_dev,
"Indirect table size %d exceeding limit\n",
ind_tbl_size);
return -EINVAL;
}
if (ucmd.rx_hash_function != MANA_IB_RX_HASH_FUNC_TOEPLITZ) {
ibdev_dbg(&mdev->ib_dev,
"RX Hash function is not supported, %d\n",
ucmd.rx_hash_function);
return -EINVAL;
}
/* IB ports start with 1, MANA start with 0 */
port = ucmd.port;
ndev = mana_ib_get_netdev(pd->device, port);
if (!ndev) {
ibdev_dbg(&mdev->ib_dev, "Invalid port %u in creating qp\n",
port);
return -EINVAL;
}
mpc = netdev_priv(ndev);
ibdev_dbg(&mdev->ib_dev, "rx_hash_function %d port %d\n",
ucmd.rx_hash_function, port);
mana_ind_table = kzalloc_objs(mana_handle_t, ind_tbl_size);
if (!mana_ind_table) {
ret = -ENOMEM;
goto fail;
}
qp->port = port;
/* Take a reference on the vport to ensure EQs outlive this QP.
* The vport must already be configured by a raw QP on the
* same port — cross-port PD sharing is not supported.
* Only one RSS QP per vport is allowed because each one
* overwrites the steering config and destroy disables RX
* globally.
*/
mutex_lock(&mana_pd->vport_mutex);
if (!mana_pd->vport_use_count || mana_pd->vport_port != port) {
mutex_unlock(&mana_pd->vport_mutex);
ret = -EINVAL;
goto fail;
}
if (mana_pd->has_rss_qp) {
mutex_unlock(&mana_pd->vport_mutex);
ibdev_dbg(&mdev->ib_dev,
"Only one RSS QP per vport is supported\n");
ret = -EBUSY;
goto fail;
}
mana_pd->vport_use_count++;
mana_pd->has_rss_qp = true;
mutex_unlock(&mana_pd->vport_mutex);
for (i = 0; i < ind_tbl_size; i++) {
struct mana_obj_spec wq_spec = {};
struct mana_obj_spec cq_spec = {};
ibwq = ind_tbl->ind_tbl[i];
wq = container_of(ibwq, struct mana_ib_wq, ibwq);
ibcq = ibwq->cq;
cq = container_of(ibcq, struct mana_ib_cq, ibcq);
wq_spec.gdma_region = wq->queue.gdma_region;
wq_spec.queue_size = wq->wq_buf_size;
cq_spec.gdma_region = cq->queue.gdma_region;
cq_spec.queue_size = cq->cqe * COMP_ENTRY_SIZE;
cq_spec.modr_ctx_id = 0;
/* Map comp_vector to a per-vPort EQ. The modulo handles
* the case where the RDMA-advertised num_comp_vectors
* exceeds this port's num_queues (e.g. after ethtool -L
* reduces it), remapping to an available EQ rather than
* failing the QP creation.
*/
eq = &mpc->eqs[cq->comp_vector % mpc->num_queues];
cq_spec.attached_eq = eq->eq->id;
ret = mana_create_wq_obj(mpc, mpc->port_handle, GDMA_RQ,
&wq_spec, &cq_spec, &wq->rx_object);
if (ret)
goto free_vport;
/* The GDMA regions are now owned by the WQ object */
wq->queue.gdma_region = GDMA_INVALID_DMA_REGION;
cq->queue.gdma_region = GDMA_INVALID_DMA_REGION;
wq->queue.id = wq_spec.queue_index;
cq->queue.id = cq_spec.queue_index;
ibdev_dbg(&mdev->ib_dev,
"rx_object 0x%llx wq id %llu cq id %llu\n",
wq->rx_object, wq->queue.id, cq->queue.id);
resp.entries[i].cqid = cq->queue.id;
resp.entries[i].wqid = wq->queue.id;
mana_ind_table[i] = wq->rx_object;
/* Create CQ table entry */
ret = mana_ib_install_cq_cb(mdev, cq);
if (ret) {
mana_destroy_wq_obj(mpc, GDMA_RQ, wq->rx_object);
goto free_vport;
}
}
resp.num_entries = i;
ret = mana_ib_cfg_vport_steering(mdev, ndev, wq->rx_object,
mana_ind_table,
ind_tbl->log_ind_tbl_size,
ucmd.rx_hash_key_len,
ucmd.rx_hash_key);
if (ret)
goto free_vport;
ret = ib_respond_udata(udata, resp);
if (ret)
goto err_disable_vport_rx;
kfree(mana_ind_table);
return 0;
err_disable_vport_rx:
mana_disable_vport_rx(mpc);
free_vport:
while (i-- > 0) {
ibwq = ind_tbl->ind_tbl[i];
ibcq = ibwq->cq;
wq = container_of(ibwq, struct mana_ib_wq, ibwq);
cq = container_of(ibcq, struct mana_ib_cq, ibcq);
mana_ib_remove_cq_cb(mdev, cq);
mana_destroy_wq_obj(mpc, GDMA_RQ, wq->rx_object);
}
mutex_lock(&mana_pd->vport_mutex);
mana_pd->has_rss_qp = false;
mutex_unlock(&mana_pd->vport_mutex);
mana_ib_uncfg_vport(mdev, mana_pd, port);
fail:
kfree(mana_ind_table);
return ret;
}
static int mana_ib_create_qp_raw(struct ib_qp *ibqp, struct ib_pd *ibpd,
struct ib_qp_init_attr *attr,
struct ib_udata *udata)
{
struct mana_ib_pd *pd = container_of(ibpd, struct mana_ib_pd, ibpd);
struct mana_ib_qp *qp = container_of(ibqp, struct mana_ib_qp, ibqp);
struct mana_ib_dev *mdev =
container_of(ibpd->device, struct mana_ib_dev, ib_dev);
struct mana_ib_cq *send_cq =
container_of(attr->send_cq, struct mana_ib_cq, ibcq);
struct mana_ib_ucontext *mana_ucontext =
rdma_udata_to_drv_context(udata, struct mana_ib_ucontext,
ibucontext);
struct mana_ib_create_qp_resp resp = {};
struct mana_ib_create_qp ucmd = {};
struct mana_obj_spec wq_spec = {};
struct mana_obj_spec cq_spec = {};
struct mana_port_context *mpc;
struct net_device *ndev;
struct mana_eq *eq;
int eq_vec;
u32 port;
int err;
if (!mana_ucontext)
return -EINVAL;
err = ib_copy_validate_udata_in(udata, ucmd, port);
if (err)
return err;
if (attr->cap.max_send_wr > mdev->adapter_caps.max_qp_wr) {
ibdev_dbg(&mdev->ib_dev,
"Requested max_send_wr %d exceeding limit\n",
attr->cap.max_send_wr);
return -EINVAL;
}
if (attr->cap.max_send_sge > MAX_TX_WQE_SGL_ENTRIES) {
ibdev_dbg(&mdev->ib_dev,
"Requested max_send_sge %d exceeding limit\n",
attr->cap.max_send_sge);
return -EINVAL;
}
port = ucmd.port;
ndev = mana_ib_get_netdev(ibpd->device, port);
if (!ndev) {
ibdev_dbg(&mdev->ib_dev, "Invalid port %u in creating qp\n",
port);
return -EINVAL;
}
mpc = netdev_priv(ndev);
ibdev_dbg(&mdev->ib_dev, "port %u ndev %p mpc %p\n", port, ndev, mpc);
err = mana_ib_cfg_vport(mdev, port, pd, mana_ucontext->doorbell);
if (err)
return err;
qp->port = port;
ibdev_dbg(&mdev->ib_dev, "ucmd sq_buf_addr 0x%llx port %u\n",
ucmd.sq_buf_addr, ucmd.port);
err = mana_ib_create_queue(mdev, ucmd.sq_buf_addr, ucmd.sq_buf_size, &qp->raw_sq);
if (err) {
ibdev_dbg(&mdev->ib_dev,
"Failed to create queue for create qp-raw, err %d\n", err);
goto err_free_vport;
}
/* Create a WQ on the same port handle used by the Ethernet */
wq_spec.gdma_region = qp->raw_sq.gdma_region;
wq_spec.queue_size = ucmd.sq_buf_size;
cq_spec.gdma_region = send_cq->queue.gdma_region;
cq_spec.queue_size = send_cq->cqe * COMP_ENTRY_SIZE;
cq_spec.modr_ctx_id = 0;
eq_vec = send_cq->comp_vector;
if (!mpc->eqs) {
err = -EINVAL;
goto err_destroy_queue;
}
/* Map comp_vector to a per-vPort EQ. See comment in
* mana_ib_create_qp_rss() for the modulo rationale.
*/
eq = &mpc->eqs[eq_vec % mpc->num_queues];
cq_spec.attached_eq = eq->eq->id;
err = mana_create_wq_obj(mpc, mpc->port_handle, GDMA_SQ, &wq_spec,
&cq_spec, &qp->qp_handle);
if (err) {
ibdev_dbg(&mdev->ib_dev,
"Failed to create wq for create raw-qp, err %d\n",
err);
goto err_destroy_queue;
}
/* The GDMA regions are now owned by the WQ object */
qp->raw_sq.gdma_region = GDMA_INVALID_DMA_REGION;
send_cq->queue.gdma_region = GDMA_INVALID_DMA_REGION;
qp->raw_sq.id = wq_spec.queue_index;
send_cq->queue.id = cq_spec.queue_index;
/* Create CQ table entry */
err = mana_ib_install_cq_cb(mdev, send_cq);
if (err)
goto err_destroy_wq_obj;
ibdev_dbg(&mdev->ib_dev,
"qp->qp_handle 0x%llx sq id %llu cq id %llu\n",
qp->qp_handle, qp->raw_sq.id, send_cq->queue.id);
resp.sqid = qp->raw_sq.id;
resp.cqid = send_cq->queue.id;
resp.tx_vp_offset = pd->tx_vp_offset;
err = ib_respond_udata(udata, resp);
if (err)
goto err_remove_cq_cb;
return 0;
err_remove_cq_cb:
mana_ib_remove_cq_cb(mdev, send_cq);
err_destroy_wq_obj:
mana_destroy_wq_obj(mpc, GDMA_SQ, qp->qp_handle);
err_destroy_queue:
mana_ib_destroy_queue(mdev, &qp->raw_sq);
err_free_vport:
mana_ib_uncfg_vport(mdev, pd, port);
return err;
}
static u32 mana_ib_wqe_size(u32 sge, u32 oob_size)
{
u32 wqe_size = sge * sizeof(struct gdma_sge) + sizeof(struct gdma_wqe) + oob_size;
return ALIGN(wqe_size, GDMA_WQE_BU_SIZE);
}
static u32 mana_ib_queue_size(struct ib_qp_init_attr *attr, u32 queue_type)
{
u32 queue_size;
switch (attr->qp_type) {
case IB_QPT_UD:
case IB_QPT_GSI:
if (queue_type == MANA_UD_SEND_QUEUE)
queue_size = attr->cap.max_send_wr *
mana_ib_wqe_size(attr->cap.max_send_sge, INLINE_OOB_LARGE_SIZE);
else
queue_size = attr->cap.max_recv_wr *
mana_ib_wqe_size(attr->cap.max_recv_sge, INLINE_OOB_SMALL_SIZE);
break;
default:
return 0;
}
return MANA_PAGE_ALIGN(roundup_pow_of_two(queue_size));
}
static enum gdma_queue_type mana_ib_queue_type(struct ib_qp_init_attr *attr, u32 queue_type)
{
enum gdma_queue_type type;
switch (attr->qp_type) {
case IB_QPT_UD:
case IB_QPT_GSI:
if (queue_type == MANA_UD_SEND_QUEUE)
type = GDMA_SQ;
else
type = GDMA_RQ;
break;
default:
type = GDMA_INVALID_QUEUE;
}
return type;
}
static int mana_table_store_rc_qp(struct mana_ib_dev *mdev, struct mana_ib_qp *qp)
{
return xa_insert_irq(&mdev->qp_table_wq, qp->ibqp.qp_num, qp,
GFP_KERNEL);
}
static void mana_table_remove_rc_qp(struct mana_ib_dev *mdev, struct mana_ib_qp *qp)
{
xa_erase_irq(&mdev->qp_table_wq, qp->ibqp.qp_num);
}
static int mana_table_store_ud_qp(struct mana_ib_dev *mdev, struct mana_ib_qp *qp)
{
u32 qids = qp->ud_qp.queues[MANA_UD_SEND_QUEUE].id | MANA_SENDQ_MASK;
u32 qidr = qp->ud_qp.queues[MANA_UD_RECV_QUEUE].id;
int err;
err = xa_insert_irq(&mdev->qp_table_wq, qids, qp, GFP_KERNEL);
if (err)
return err;
err = xa_insert_irq(&mdev->qp_table_wq, qidr, qp, GFP_KERNEL);
if (err)
goto remove_sq;
return 0;
remove_sq:
xa_erase_irq(&mdev->qp_table_wq, qids);
return err;
}
static void mana_table_remove_ud_qp(struct mana_ib_dev *mdev, struct mana_ib_qp *qp)
{
u32 qids = qp->ud_qp.queues[MANA_UD_SEND_QUEUE].id | MANA_SENDQ_MASK;
u32 qidr = qp->ud_qp.queues[MANA_UD_RECV_QUEUE].id;
xa_erase_irq(&mdev->qp_table_wq, qids);
xa_erase_irq(&mdev->qp_table_wq, qidr);
}
static int mana_table_store_qp(struct mana_ib_dev *mdev, struct mana_ib_qp *qp)
{
refcount_set(&qp->refcount, 1);
init_completion(&qp->free);
switch (qp->ibqp.qp_type) {
case IB_QPT_RC:
return mana_table_store_rc_qp(mdev, qp);
case IB_QPT_UD:
case IB_QPT_GSI:
return mana_table_store_ud_qp(mdev, qp);
default:
ibdev_dbg(&mdev->ib_dev, "Unknown QP type for storing in mana table, %d\n",
qp->ibqp.qp_type);
}
return -EINVAL;
}
static void mana_table_remove_qp(struct mana_ib_dev *mdev,
struct mana_ib_qp *qp)
{
switch (qp->ibqp.qp_type) {
case IB_QPT_RC:
mana_table_remove_rc_qp(mdev, qp);
break;
case IB_QPT_UD:
case IB_QPT_GSI:
mana_table_remove_ud_qp(mdev, qp);
break;
default:
ibdev_dbg(&mdev->ib_dev, "Unknown QP type for removing from mana table, %d\n",
qp->ibqp.qp_type);
return;
}
mana_put_qp_ref(qp);
wait_for_completion(&qp->free);
}
static int mana_ib_create_rc_qp(struct ib_qp *ibqp, struct ib_pd *ibpd,
struct ib_qp_init_attr *attr, struct ib_udata *udata)
{
struct mana_ib_dev *mdev = container_of(ibpd->device, struct mana_ib_dev, ib_dev);
struct mana_ib_qp *qp = container_of(ibqp, struct mana_ib_qp, ibqp);
struct mana_ib_create_rc_qp_resp resp = {};
struct mana_ib_ucontext *mana_ucontext;
struct mana_ib_create_rc_qp ucmd = {};
int i, err, j;
u64 flags = 0;
u32 doorbell;
if (!udata)
return -EINVAL;
mana_ucontext = rdma_udata_to_drv_context(udata, struct mana_ib_ucontext, ibucontext);
doorbell = mana_ucontext->doorbell;
flags = MANA_RC_FLAG_NO_FMR;
err = ib_copy_validate_udata_in(udata, ucmd, queue_size);
if (err)
return err;
for (i = 0, j = 0; i < MANA_RC_QUEUE_TYPE_MAX; ++i) {
/* skip FMR for user-level RC QPs */
if (i == MANA_RC_SEND_QUEUE_FMR) {
qp->rc_qp.queues[i].id = INVALID_QUEUE_ID;
qp->rc_qp.queues[i].gdma_region = GDMA_INVALID_DMA_REGION;
continue;
}
err = mana_ib_create_queue(mdev, ucmd.queue_buf[j], ucmd.queue_size[j],
&qp->rc_qp.queues[i]);
if (err) {
ibdev_err(&mdev->ib_dev, "Failed to create queue %d, err %d\n", i, err);
goto destroy_queues;
}
j++;
}
err = mana_ib_gd_create_rc_qp(mdev, qp, attr, doorbell, flags);
if (err) {
ibdev_err(&mdev->ib_dev, "Failed to create rc qp %d\n", err);
goto destroy_queues;
}
qp->ibqp.qp_num = qp->rc_qp.queues[MANA_RC_RECV_QUEUE_RESPONDER].id;
qp->port = attr->port_num;
if (udata) {
for (i = 0, j = 0; i < MANA_RC_QUEUE_TYPE_MAX; ++i) {
if (i == MANA_RC_SEND_QUEUE_FMR)
continue;
resp.queue_id[j] = qp->rc_qp.queues[i].id;
j++;
}
err = ib_respond_udata(udata, resp);
if (err)
goto destroy_qp;
}
err = mana_table_store_qp(mdev, qp);
if (err)
goto destroy_qp;
return 0;
destroy_qp:
mana_ib_gd_destroy_rc_qp(mdev, qp);
destroy_queues:
while (i-- > 0)
mana_ib_destroy_queue(mdev, &qp->rc_qp.queues[i]);
return err;
}
static void mana_add_qp_to_cqs(struct mana_ib_qp *qp)
{
struct mana_ib_cq *send_cq = container_of(qp->ibqp.send_cq, struct mana_ib_cq, ibcq);
struct mana_ib_cq *recv_cq = container_of(qp->ibqp.recv_cq, struct mana_ib_cq, ibcq);
unsigned long flags;
spin_lock_irqsave(&send_cq->cq_lock, flags);
list_add_tail(&qp->cq_send_list, &send_cq->list_send_qp);
spin_unlock_irqrestore(&send_cq->cq_lock, flags);
spin_lock_irqsave(&recv_cq->cq_lock, flags);
list_add_tail(&qp->cq_recv_list, &recv_cq->list_recv_qp);
spin_unlock_irqrestore(&recv_cq->cq_lock, flags);
}
static void mana_remove_qp_from_cqs(struct mana_ib_qp *qp)
{
struct mana_ib_cq *send_cq = container_of(qp->ibqp.send_cq, struct mana_ib_cq, ibcq);
struct mana_ib_cq *recv_cq = container_of(qp->ibqp.recv_cq, struct mana_ib_cq, ibcq);
unsigned long flags;
spin_lock_irqsave(&send_cq->cq_lock, flags);
list_del(&qp->cq_send_list);
spin_unlock_irqrestore(&send_cq->cq_lock, flags);
spin_lock_irqsave(&recv_cq->cq_lock, flags);
list_del(&qp->cq_recv_list);
spin_unlock_irqrestore(&recv_cq->cq_lock, flags);
}
static int mana_ib_create_ud_qp(struct ib_qp *ibqp, struct ib_pd *ibpd,
struct ib_qp_init_attr *attr, struct ib_udata *udata)
{
struct mana_ib_dev *mdev = container_of(ibpd->device, struct mana_ib_dev, ib_dev);
struct mana_ib_qp *qp = container_of(ibqp, struct mana_ib_qp, ibqp);
u32 doorbell, queue_size;
int i, err;
if (udata) {
ibdev_dbg(&mdev->ib_dev, "User-level UD QPs are not supported\n");
return -EOPNOTSUPP;
}
for (i = 0; i < MANA_UD_QUEUE_TYPE_MAX; ++i) {
queue_size = mana_ib_queue_size(attr, i);
err = mana_ib_create_kernel_queue(mdev, queue_size, mana_ib_queue_type(attr, i),
&qp->ud_qp.queues[i]);
if (err) {
ibdev_err(&mdev->ib_dev, "Failed to create queue %d, err %d\n",
i, err);
goto destroy_queues;
}
}
doorbell = mdev->gdma_dev->doorbell;
err = create_shadow_queue(&qp->shadow_rq, attr->cap.max_recv_wr,
sizeof(struct ud_rq_shadow_wqe));
if (err) {
ibdev_err(&mdev->ib_dev, "Failed to create shadow rq err %d\n", err);
goto destroy_queues;
}
err = create_shadow_queue(&qp->shadow_sq, attr->cap.max_send_wr,
sizeof(struct ud_sq_shadow_wqe));
if (err) {
ibdev_err(&mdev->ib_dev, "Failed to create shadow sq err %d\n", err);
goto destroy_shadow_queues;
}
err = mana_ib_gd_create_ud_qp(mdev, qp, attr, doorbell, attr->qp_type);
if (err) {
ibdev_err(&mdev->ib_dev, "Failed to create ud qp %d\n", err);
goto destroy_shadow_queues;
}
qp->ibqp.qp_num = qp->ud_qp.queues[MANA_UD_RECV_QUEUE].id;
qp->port = attr->port_num;
for (i = 0; i < MANA_UD_QUEUE_TYPE_MAX; ++i)
qp->ud_qp.queues[i].kmem->id = qp->ud_qp.queues[i].id;
err = mana_table_store_qp(mdev, qp);
if (err)
goto destroy_qp;
mana_add_qp_to_cqs(qp);
return 0;
destroy_qp:
mana_ib_gd_destroy_ud_qp(mdev, qp);
destroy_shadow_queues:
destroy_shadow_queue(&qp->shadow_rq);
destroy_shadow_queue(&qp->shadow_sq);
destroy_queues:
while (i-- > 0)
mana_ib_destroy_queue(mdev, &qp->ud_qp.queues[i]);
return err;
}
int mana_ib_create_qp(struct ib_qp *ibqp, struct ib_qp_init_attr *attr,
struct ib_udata *udata)
{
switch (attr->qp_type) {
case IB_QPT_RAW_PACKET:
/* When rwq_ind_tbl is used, it's for creating WQs for RSS */
if (attr->rwq_ind_tbl)
return mana_ib_create_qp_rss(ibqp, ibqp->pd, attr,
udata);
return mana_ib_create_qp_raw(ibqp, ibqp->pd, attr, udata);
case IB_QPT_RC:
return mana_ib_create_rc_qp(ibqp, ibqp->pd, attr, udata);
case IB_QPT_UD:
case IB_QPT_GSI:
return mana_ib_create_ud_qp(ibqp, ibqp->pd, attr, udata);
default:
ibdev_dbg(ibqp->device, "Creating QP type %u not supported\n",
attr->qp_type);
}
return -EINVAL;
}
static int mana_ib_gd_modify_qp(struct ib_qp *ibqp, struct ib_qp_attr *attr,
int attr_mask, struct ib_udata *udata)
{
struct mana_ib_dev *mdev = container_of(ibqp->device, struct mana_ib_dev, ib_dev);
struct mana_ib_qp *qp = container_of(ibqp, struct mana_ib_qp, ibqp);
struct mana_rnic_set_qp_state_resp resp = {};
struct mana_rnic_set_qp_state_req req = {};
struct gdma_context *gc = mdev_to_gc(mdev);
struct mana_port_context *mpc;
struct net_device *ndev;
mana_gd_init_req_hdr(&req.hdr, MANA_IB_SET_QP_STATE, sizeof(req), sizeof(resp));
req.hdr.req.msg_version = GDMA_MESSAGE_V3;
req.hdr.dev_id = mdev->gdma_dev->dev_id;
req.adapter = mdev->adapter_handle;
req.qp_handle = qp->qp_handle;
req.qp_state = attr->qp_state;
req.attr_mask = attr_mask;
req.path_mtu = attr->path_mtu;
req.rq_psn = attr->rq_psn;
req.sq_psn = attr->sq_psn;
req.dest_qpn = attr->dest_qp_num;
req.max_dest_rd_atomic = attr->max_dest_rd_atomic;
req.retry_cnt = attr->retry_cnt;
req.rnr_retry = attr->rnr_retry;
req.min_rnr_timer = attr->min_rnr_timer;
req.rate_limit = attr->rate_limit;
req.qkey = attr->qkey;
req.local_ack_timeout = attr->timeout;
req.qp_access_flags = attr->qp_access_flags;
req.max_rd_atomic = attr->max_rd_atomic;
if (attr_mask & IB_QP_AV) {
ndev = mana_ib_get_netdev(&mdev->ib_dev, ibqp->port);
if (!ndev) {
ibdev_dbg(&mdev->ib_dev, "Invalid port %u in QP %u\n",
ibqp->port, ibqp->qp_num);
return -EINVAL;
}
mpc = netdev_priv(ndev);
copy_in_reverse(req.ah_attr.src_mac, mpc->mac_addr, ETH_ALEN);
copy_in_reverse(req.ah_attr.dest_mac, attr->ah_attr.roce.dmac, ETH_ALEN);
copy_in_reverse(req.ah_attr.src_addr, attr->ah_attr.grh.sgid_attr->gid.raw,
sizeof(union ib_gid));
copy_in_reverse(req.ah_attr.dest_addr, attr->ah_attr.grh.dgid.raw,
sizeof(union ib_gid));
if (rdma_gid_attr_network_type(attr->ah_attr.grh.sgid_attr) == RDMA_NETWORK_IPV4) {
req.ah_attr.src_addr_type = SGID_TYPE_IPV4;
req.ah_attr.dest_addr_type = SGID_TYPE_IPV4;
} else {
req.ah_attr.src_addr_type = SGID_TYPE_IPV6;
req.ah_attr.dest_addr_type = SGID_TYPE_IPV6;
}
req.ah_attr.dest_port = ROCE_V2_UDP_DPORT;
req.ah_attr.src_port = rdma_get_udp_sport(attr->ah_attr.grh.flow_label,
ibqp->qp_num, attr->dest_qp_num);
req.ah_attr.traffic_class = attr->ah_attr.grh.traffic_class >> 2;
req.ah_attr.hop_limit = attr->ah_attr.grh.hop_limit;
req.ah_attr.flow_label = attr->ah_attr.grh.flow_label;
}
return mana_gd_send_request(gc, sizeof(req), &req, sizeof(resp), &resp);
}
int mana_ib_modify_qp(struct ib_qp *ibqp, struct ib_qp_attr *attr,
int attr_mask, struct ib_udata *udata)
{
switch (ibqp->qp_type) {
case IB_QPT_RC:
case IB_QPT_UD:
case IB_QPT_GSI:
return mana_ib_gd_modify_qp(ibqp, attr, attr_mask, udata);
default:
ibdev_dbg(ibqp->device, "Modify QP type %u not supported", ibqp->qp_type);
return -EOPNOTSUPP;
}
}
static int mana_ib_destroy_qp_rss(struct mana_ib_qp *qp,
struct ib_rwq_ind_table *ind_tbl,
struct ib_udata *udata)
{
struct mana_ib_dev *mdev =
container_of(qp->ibqp.device, struct mana_ib_dev, ib_dev);
struct ib_pd *ibpd = qp->ibqp.pd;
struct mana_port_context *mpc;
struct net_device *ndev;
struct mana_ib_pd *pd;
struct mana_ib_wq *wq;
struct ib_wq *ibwq;
int i;
ndev = mana_ib_get_netdev(qp->ibqp.device, qp->port);
mpc = netdev_priv(ndev);
pd = container_of(ibpd, struct mana_ib_pd, ibpd);
/* Disable vPort RX steering before destroying RX WQ objects.
* Otherwise firmware still routes traffic to the destroyed queues,
* which can cause bogus completions on reused CQ IDs when the
* ethernet driver later creates new queues on mana_open().
*
* Unlike the ethernet teardown path, mana_fence_rqs() cannot be
* used here because the fence completion CQE is delivered on the
* CQ which is polled by userspace (e.g. DPDK), so there is no way
* for the kernel to wait for fence completion.
*
* This is best effort — if it fails there is not much we can do,
* and mana_cfg_vport_steering() already logs the error.
*/
mana_disable_vport_rx(mpc);
for (i = 0; i < (1 << ind_tbl->log_ind_tbl_size); i++) {
ibwq = ind_tbl->ind_tbl[i];
wq = container_of(ibwq, struct mana_ib_wq, ibwq);
ibdev_dbg(&mdev->ib_dev, "destroying wq->rx_object %llu\n",
wq->rx_object);
mana_destroy_wq_obj(mpc, GDMA_RQ, wq->rx_object);
}
mutex_lock(&pd->vport_mutex);
pd->has_rss_qp = false;
mutex_unlock(&pd->vport_mutex);
mana_ib_uncfg_vport(mdev, pd, qp->port);
return 0;
}
static int mana_ib_destroy_qp_raw(struct mana_ib_qp *qp, struct ib_udata *udata)
{
struct mana_ib_dev *mdev =
container_of(qp->ibqp.device, struct mana_ib_dev, ib_dev);
struct ib_pd *ibpd = qp->ibqp.pd;
struct mana_port_context *mpc;
struct net_device *ndev;
struct mana_ib_pd *pd;
ndev = mana_ib_get_netdev(qp->ibqp.device, qp->port);
mpc = netdev_priv(ndev);
pd = container_of(ibpd, struct mana_ib_pd, ibpd);
mana_destroy_wq_obj(mpc, GDMA_SQ, qp->qp_handle);
mana_ib_destroy_queue(mdev, &qp->raw_sq);
mana_ib_uncfg_vport(mdev, pd, qp->port);
return 0;
}
static int mana_ib_destroy_rc_qp(struct mana_ib_qp *qp, struct ib_udata *udata)
{
struct mana_ib_dev *mdev =
container_of(qp->ibqp.device, struct mana_ib_dev, ib_dev);
int i;
mana_table_remove_qp(mdev, qp);
/* Ignore return code as there is not much we can do about it.
* The error message is printed inside.
*/
mana_ib_gd_destroy_rc_qp(mdev, qp);
for (i = 0; i < MANA_RC_QUEUE_TYPE_MAX; ++i)
mana_ib_destroy_queue(mdev, &qp->rc_qp.queues[i]);
return 0;
}
static int mana_ib_destroy_ud_qp(struct mana_ib_qp *qp, struct ib_udata *udata)
{
struct mana_ib_dev *mdev =
container_of(qp->ibqp.device, struct mana_ib_dev, ib_dev);
int i;
mana_remove_qp_from_cqs(qp);
mana_table_remove_qp(mdev, qp);
destroy_shadow_queue(&qp->shadow_rq);
destroy_shadow_queue(&qp->shadow_sq);
/* Ignore return code as there is not much we can do about it.
* The error message is printed inside.
*/
mana_ib_gd_destroy_ud_qp(mdev, qp);
for (i = 0; i < MANA_UD_QUEUE_TYPE_MAX; ++i)
mana_ib_destroy_queue(mdev, &qp->ud_qp.queues[i]);
return 0;
}
int mana_ib_destroy_qp(struct ib_qp *ibqp, struct ib_udata *udata)
{
struct mana_ib_qp *qp = container_of(ibqp, struct mana_ib_qp, ibqp);
switch (ibqp->qp_type) {
case IB_QPT_RAW_PACKET:
if (ibqp->rwq_ind_tbl)
return mana_ib_destroy_qp_rss(qp, ibqp->rwq_ind_tbl,
udata);
return mana_ib_destroy_qp_raw(qp, udata);
case IB_QPT_RC:
return mana_ib_destroy_rc_qp(qp, udata);
case IB_QPT_UD:
case IB_QPT_GSI:
return mana_ib_destroy_ud_qp(qp, udata);
default:
ibdev_dbg(ibqp->device, "Unexpected QP type %u\n",
ibqp->qp_type);
}
return -ENOENT;
}
@@ -0,0 +1,115 @@
/* SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB */
/*
* Copyright (c) 2024, Microsoft Corporation. All rights reserved.
*/
#ifndef _MANA_SHADOW_QUEUE_H_
#define _MANA_SHADOW_QUEUE_H_
struct shadow_wqe_header {
u16 opcode;
u16 error_code;
u32 posted_wqe_size;
u64 wr_id;
};
struct ud_rq_shadow_wqe {
struct shadow_wqe_header header;
u32 byte_len;
u32 src_qpn;
};
struct ud_sq_shadow_wqe {
struct shadow_wqe_header header;
};
struct shadow_queue {
/* Unmasked producer index, Incremented on wqe posting */
u64 prod_idx;
/* Unmasked consumer index, Incremented on cq polling */
u64 cons_idx;
/* Unmasked index of next-to-complete (from HW) shadow WQE */
u64 next_to_complete_idx;
/* queue size in wqes */
u32 length;
/* distance between elements in bytes */
u32 stride;
/* ring buffer holding wqes */
void *buffer;
};
static inline int create_shadow_queue(struct shadow_queue *queue, uint32_t length, uint32_t stride)
{
queue->buffer = kvmalloc_array(length, stride, GFP_KERNEL);
if (!queue->buffer)
return -ENOMEM;
queue->length = length;
queue->stride = stride;
return 0;
}
static inline void destroy_shadow_queue(struct shadow_queue *queue)
{
kvfree(queue->buffer);
}
static inline bool shadow_queue_full(struct shadow_queue *queue)
{
return (queue->prod_idx - queue->cons_idx) >= queue->length;
}
static inline bool shadow_queue_empty(struct shadow_queue *queue)
{
return queue->prod_idx == queue->cons_idx;
}
static inline void *
shadow_queue_get_element(const struct shadow_queue *queue, u64 unmasked_index)
{
u32 index = unmasked_index % queue->length;
return ((u8 *)queue->buffer + index * queue->stride);
}
static inline void *
shadow_queue_producer_entry(struct shadow_queue *queue)
{
return shadow_queue_get_element(queue, queue->prod_idx);
}
static inline void *
shadow_queue_get_next_to_consume(const struct shadow_queue *queue)
{
if (queue->cons_idx == queue->next_to_complete_idx)
return NULL;
return shadow_queue_get_element(queue, queue->cons_idx);
}
static inline void *
shadow_queue_get_next_to_complete(struct shadow_queue *queue)
{
if (queue->next_to_complete_idx == queue->prod_idx)
return NULL;
return shadow_queue_get_element(queue, queue->next_to_complete_idx);
}
static inline void shadow_queue_advance_producer(struct shadow_queue *queue)
{
queue->prod_idx++;
}
static inline void shadow_queue_advance_consumer(struct shadow_queue *queue)
{
queue->cons_idx++;
}
static inline void shadow_queue_advance_next_to_complete(struct shadow_queue *queue)
{
queue->next_to_complete_idx++;
}
#endif
@@ -0,0 +1,86 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) 2022, Microsoft Corporation. All rights reserved.
*/
#include "mana_ib.h"
struct ib_wq *mana_ib_create_wq(struct ib_pd *pd,
struct ib_wq_init_attr *init_attr,
struct ib_udata *udata)
{
struct mana_ib_dev *mdev =
container_of(pd->device, struct mana_ib_dev, ib_dev);
struct mana_ib_create_wq ucmd;
struct mana_ib_wq *wq;
int err;
err = ib_copy_validate_udata_in(udata, ucmd, reserved);
if (err)
return ERR_PTR(err);
wq = kzalloc_obj(*wq);
if (!wq)
return ERR_PTR(-ENOMEM);
ibdev_dbg(&mdev->ib_dev, "ucmd wq_buf_addr 0x%llx\n", ucmd.wq_buf_addr);
err = mana_ib_create_queue(mdev, ucmd.wq_buf_addr, ucmd.wq_buf_size, &wq->queue);
if (err) {
ibdev_dbg(&mdev->ib_dev,
"Failed to create queue for create wq, %d\n", err);
goto err_free_wq;
}
wq->wqe = init_attr->max_wr;
wq->wq_buf_size = ucmd.wq_buf_size;
wq->rx_object = INVALID_MANA_HANDLE;
return &wq->ibwq;
err_free_wq:
kfree(wq);
return ERR_PTR(err);
}
int mana_ib_modify_wq(struct ib_wq *wq, struct ib_wq_attr *wq_attr,
u32 wq_attr_mask, struct ib_udata *udata)
{
/* modify_wq is not supported by this version of the driver */
return -EOPNOTSUPP;
}
int mana_ib_destroy_wq(struct ib_wq *ibwq, struct ib_udata *udata)
{
struct mana_ib_wq *wq = container_of(ibwq, struct mana_ib_wq, ibwq);
struct ib_device *ib_dev = ibwq->device;
struct mana_ib_dev *mdev;
mdev = container_of(ib_dev, struct mana_ib_dev, ib_dev);
mana_ib_destroy_queue(mdev, &wq->queue);
kfree(wq);
return 0;
}
int mana_ib_create_rwq_ind_table(struct ib_rwq_ind_table *ib_rwq_ind_table,
struct ib_rwq_ind_table_init_attr *init_attr,
struct ib_udata *udata)
{
/*
* There is no additional data in ind_table to be maintained by this
* driver, do nothing
*/
return 0;
}
int mana_ib_destroy_rwq_ind_table(struct ib_rwq_ind_table *ib_rwq_ind_tbl)
{
/*
* There is no additional data in ind_table to be maintained by this
* driver, do nothing
*/
return 0;
}
+168
View File
@@ -0,0 +1,168 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) 2024, Microsoft Corporation. All rights reserved.
*/
#include "mana_ib.h"
#define MAX_WR_SGL_NUM (2)
static int mana_ib_post_recv_ud(struct mana_ib_qp *qp, const struct ib_recv_wr *wr)
{
struct mana_ib_dev *mdev = container_of(qp->ibqp.device, struct mana_ib_dev, ib_dev);
struct gdma_queue *queue = qp->ud_qp.queues[MANA_UD_RECV_QUEUE].kmem;
struct gdma_posted_wqe_info wqe_info = {0};
struct gdma_sge gdma_sgl[MAX_WR_SGL_NUM];
struct gdma_wqe_request wqe_req = {0};
struct ud_rq_shadow_wqe *shadow_wqe;
int err, i;
if (shadow_queue_full(&qp->shadow_rq))
return -EINVAL;
if (wr->num_sge > MAX_WR_SGL_NUM)
return -EINVAL;
for (i = 0; i < wr->num_sge; ++i) {
gdma_sgl[i].address = wr->sg_list[i].addr;
gdma_sgl[i].mem_key = wr->sg_list[i].lkey;
gdma_sgl[i].size = wr->sg_list[i].length;
}
wqe_req.num_sge = wr->num_sge;
wqe_req.sgl = gdma_sgl;
err = mana_gd_post_work_request(queue, &wqe_req, &wqe_info);
if (err)
return err;
shadow_wqe = shadow_queue_producer_entry(&qp->shadow_rq);
memset(shadow_wqe, 0, sizeof(*shadow_wqe));
shadow_wqe->header.opcode = IB_WC_RECV;
shadow_wqe->header.wr_id = wr->wr_id;
shadow_wqe->header.posted_wqe_size = wqe_info.wqe_size_in_bu;
shadow_queue_advance_producer(&qp->shadow_rq);
mana_gd_wq_ring_doorbell(mdev_to_gc(mdev), queue);
return 0;
}
int mana_ib_post_recv(struct ib_qp *ibqp, const struct ib_recv_wr *wr,
const struct ib_recv_wr **bad_wr)
{
struct mana_ib_qp *qp = container_of(ibqp, struct mana_ib_qp, ibqp);
int err = 0;
for (; wr; wr = wr->next) {
switch (ibqp->qp_type) {
case IB_QPT_UD:
case IB_QPT_GSI:
err = mana_ib_post_recv_ud(qp, wr);
if (unlikely(err)) {
*bad_wr = wr;
return err;
}
break;
default:
ibdev_dbg(ibqp->device, "Posting recv wr on qp type %u is not supported\n",
ibqp->qp_type);
return -EINVAL;
}
}
return err;
}
static int mana_ib_post_send_ud(struct mana_ib_qp *qp, const struct ib_ud_wr *wr)
{
struct mana_ib_dev *mdev = container_of(qp->ibqp.device, struct mana_ib_dev, ib_dev);
struct mana_ib_ah *ah = container_of(wr->ah, struct mana_ib_ah, ibah);
struct net_device *ndev = mana_ib_get_netdev(&mdev->ib_dev, qp->port);
struct gdma_queue *queue = qp->ud_qp.queues[MANA_UD_SEND_QUEUE].kmem;
struct gdma_sge gdma_sgl[MAX_WR_SGL_NUM + 1];
struct gdma_posted_wqe_info wqe_info = {0};
struct gdma_wqe_request wqe_req = {0};
struct rdma_send_oob send_oob = {0};
struct ud_sq_shadow_wqe *shadow_wqe;
int err, i;
if (!ndev) {
ibdev_dbg(&mdev->ib_dev, "Invalid port %u in QP %u\n",
qp->port, qp->ibqp.qp_num);
return -EINVAL;
}
if (wr->wr.opcode != IB_WR_SEND)
return -EINVAL;
if (shadow_queue_full(&qp->shadow_sq))
return -EINVAL;
if (wr->wr.num_sge > MAX_WR_SGL_NUM)
return -EINVAL;
gdma_sgl[0].address = ah->dma_handle;
gdma_sgl[0].mem_key = qp->ibqp.pd->local_dma_lkey;
gdma_sgl[0].size = sizeof(struct mana_ib_av);
for (i = 0; i < wr->wr.num_sge; ++i) {
gdma_sgl[i + 1].address = wr->wr.sg_list[i].addr;
gdma_sgl[i + 1].mem_key = wr->wr.sg_list[i].lkey;
gdma_sgl[i + 1].size = wr->wr.sg_list[i].length;
}
wqe_req.num_sge = wr->wr.num_sge + 1;
wqe_req.sgl = gdma_sgl;
wqe_req.inline_oob_size = sizeof(struct rdma_send_oob);
wqe_req.inline_oob_data = &send_oob;
wqe_req.flags = GDMA_WR_OOB_IN_SGL;
wqe_req.client_data_unit = ib_mtu_enum_to_int(ib_mtu_int_to_enum(ndev->mtu));
send_oob.wqe_type = WQE_TYPE_UD_SEND;
send_oob.fence = !!(wr->wr.send_flags & IB_SEND_FENCE);
send_oob.signaled = !!(wr->wr.send_flags & IB_SEND_SIGNALED);
send_oob.solicited = !!(wr->wr.send_flags & IB_SEND_SOLICITED);
send_oob.psn = qp->ud_qp.sq_psn;
send_oob.ssn_or_rqpn = wr->remote_qpn;
send_oob.ud_send.remote_qkey =
qp->ibqp.qp_type == IB_QPT_GSI ? IB_QP1_QKEY : wr->remote_qkey;
err = mana_gd_post_work_request(queue, &wqe_req, &wqe_info);
if (err)
return err;
qp->ud_qp.sq_psn++;
shadow_wqe = shadow_queue_producer_entry(&qp->shadow_sq);
memset(shadow_wqe, 0, sizeof(*shadow_wqe));
shadow_wqe->header.opcode = IB_WC_SEND;
shadow_wqe->header.wr_id = wr->wr.wr_id;
shadow_wqe->header.posted_wqe_size = wqe_info.wqe_size_in_bu;
shadow_queue_advance_producer(&qp->shadow_sq);
mana_gd_wq_ring_doorbell(mdev_to_gc(mdev), queue);
return 0;
}
int mana_ib_post_send(struct ib_qp *ibqp, const struct ib_send_wr *wr,
const struct ib_send_wr **bad_wr)
{
int err = 0;
struct mana_ib_qp *qp = container_of(ibqp, struct mana_ib_qp, ibqp);
for (; wr; wr = wr->next) {
switch (ibqp->qp_type) {
case IB_QPT_UD:
case IB_QPT_GSI:
err = mana_ib_post_send_ud(qp, ud_wr(wr));
if (unlikely(err)) {
*bad_wr = wr;
return err;
}
break;
default:
ibdev_dbg(ibqp->device, "Posting send wr on qp type %u is not supported\n",
ibqp->qp_type);
return -EINVAL;
}
}
return err;
}