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,11 @@
# SPDX-License-Identifier: GPL-2.0-only
config INFINIBAND_HNS_HIP08
tristate "Hisilicon Hip08 Family RoCE support"
depends on ARM64 || (COMPILE_TEST && 64BIT)
depends on PCI && HNS3
help
RoCE driver support for Hisilicon RoCE engine in Hisilicon Hip08 SoC.
The RoCE engine is a PCI device.
To compile this driver, choose M here. This module will be called
hns-roce-hw-v2.
@@ -0,0 +1,16 @@
# SPDX-License-Identifier: GPL-2.0-only
#
# Makefile for the Hisilicon RoCE drivers.
#
ccflags-y := -I $(srctree)/drivers/net/ethernet/hisilicon/hns3
ccflags-y += -I $(srctree)/drivers/net/ethernet/hisilicon/hns3/hns3pf
ccflags-y += -I $(srctree)/drivers/net/ethernet/hisilicon/hns3/hns3_common
ccflags-y += -I $(src)
hns-roce-hw-v2-objs := hns_roce_main.o hns_roce_cmd.o hns_roce_pd.o \
hns_roce_ah.o hns_roce_hem.o hns_roce_mr.o hns_roce_qp.o \
hns_roce_cq.o hns_roce_alloc.o hns_roce_db.o hns_roce_srq.o hns_roce_restrack.o \
hns_roce_debugfs.o hns_roce_hw_v2.o hns_roce_bond.o
obj-$(CONFIG_INFINIBAND_HNS_HIP08) += hns-roce-hw-v2.o
@@ -0,0 +1,140 @@
/*
* Copyright (c) 2016 Hisilicon Limited.
*
* This software is available to you under a choice of one of two
* licenses. You may choose to be licensed under the terms of the GNU
* General Public License (GPL) Version 2, available from the file
* COPYING in the main directory of this source tree, or the
* OpenIB.org BSD license below:
*
* Redistribution and use in source and binary forms, with or
* without modification, are permitted provided that the following
* conditions are met:
*
* - Redistributions of source code must retain the above
* copyright notice, this list of conditions and the following
* disclaimer.
*
* - Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following
* disclaimer in the documentation and/or other materials
* provided with the distribution.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#include <rdma/ib_addr.h>
#include <rdma/ib_cache.h>
#include <rdma/uverbs_ioctl.h>
#include "hns_roce_device.h"
#include "hns_roce_hw_v2.h"
static inline u16 get_ah_udp_sport(const struct rdma_ah_attr *ah_attr)
{
u32 fl = ah_attr->grh.flow_label;
u16 sport;
if (!fl)
sport = get_random_u32_inclusive(IB_ROCE_UDP_ENCAP_VALID_PORT_MIN,
IB_ROCE_UDP_ENCAP_VALID_PORT_MAX);
else
sport = rdma_flow_label_to_udp_sport(fl);
return sport;
}
int hns_roce_create_ah(struct ib_ah *ibah, struct rdma_ah_init_attr *init_attr,
struct ib_udata *udata)
{
struct rdma_ah_attr *ah_attr = init_attr->ah_attr;
const struct ib_global_route *grh = rdma_ah_read_grh(ah_attr);
struct hns_roce_dev *hr_dev = to_hr_dev(ibah->device);
struct hns_roce_ib_create_ah_resp resp = {};
struct hns_roce_ah *ah = to_hr_ah(ibah);
u8 tclass = get_tclass(grh);
u8 priority = 0;
u8 tc_mode = 0;
int ret = 0;
if (hr_dev->pci_dev->revision == PCI_REVISION_ID_HIP08 && udata) {
ret = -EOPNOTSUPP;
goto err_out;
}
ah->av.port = rdma_ah_get_port_num(ah_attr);
ah->av.gid_index = grh->sgid_index;
if (rdma_ah_get_static_rate(ah_attr))
ah->av.stat_rate = IB_RATE_10_GBPS;
ah->av.hop_limit = grh->hop_limit;
ah->av.flowlabel = grh->flow_label;
ah->av.udp_sport = get_ah_udp_sport(ah_attr);
ah->av.tclass = tclass;
ah->av.sl = rdma_ah_get_sl(ah_attr);
if (grh->sgid_attr->gid_type == IB_GID_TYPE_ROCE_UDP_ENCAP) {
ret = hr_dev->hw->get_dscp(hr_dev, tclass, &tc_mode, &priority);
if (ret == -EOPNOTSUPP)
ret = 0;
else if (ret)
goto err_out;
if (tc_mode == HNAE3_TC_MAP_MODE_DSCP)
ah->av.sl = priority;
}
if (!check_sl_valid(hr_dev, ah->av.sl)) {
ret = -EINVAL;
goto err_out;
}
memcpy(ah->av.dgid, grh->dgid.raw, HNS_ROCE_GID_SIZE);
memcpy(ah->av.mac, ah_attr->roce.dmac, ETH_ALEN);
/* HIP08 needs to record vlan info in Address Vector */
if (hr_dev->pci_dev->revision == PCI_REVISION_ID_HIP08) {
ret = rdma_read_gid_l2_fields(ah_attr->grh.sgid_attr,
&ah->av.vlan_id, NULL);
if (ret)
goto err_out;
ah->av.vlan_en = ah->av.vlan_id < VLAN_N_VID;
}
if (udata) {
resp.priority = ah->av.sl;
resp.tc_mode = tc_mode;
memcpy(resp.dmac, ah_attr->roce.dmac, ETH_ALEN);
ret = ib_respond_udata(udata, resp);
}
err_out:
if (ret)
atomic64_inc(&hr_dev->dfx_cnt[HNS_ROCE_DFX_AH_CREATE_ERR_CNT]);
return ret;
}
int hns_roce_query_ah(struct ib_ah *ibah, struct rdma_ah_attr *ah_attr)
{
struct hns_roce_ah *ah = to_hr_ah(ibah);
memset(ah_attr, 0, sizeof(*ah_attr));
rdma_ah_set_sl(ah_attr, ah->av.sl);
rdma_ah_set_port_num(ah_attr, ah->av.port);
rdma_ah_set_static_rate(ah_attr, ah->av.stat_rate);
rdma_ah_set_grh(ah_attr, NULL, ah->av.flowlabel,
ah->av.gid_index, ah->av.hop_limit, ah->av.tclass);
rdma_ah_set_dgid_raw(ah_attr, ah->av.dgid);
return 0;
}
@@ -0,0 +1,187 @@
/*
* Copyright (c) 2016 Hisilicon Limited.
* Copyright (c) 2007, 2008 Mellanox Technologies. All rights reserved.
*
* This software is available to you under a choice of one of two
* licenses. You may choose to be licensed under the terms of the GNU
* General Public License (GPL) Version 2, available from the file
* COPYING in the main directory of this source tree, or the
* OpenIB.org BSD license below:
*
* Redistribution and use in source and binary forms, with or
* without modification, are permitted provided that the following
* conditions are met:
*
* - Redistributions of source code must retain the above
* copyright notice, this list of conditions and the following
* disclaimer.
*
* - Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following
* disclaimer in the documentation and/or other materials
* provided with the distribution.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#include <linux/vmalloc.h>
#include <rdma/iter.h>
#include "hns_roce_device.h"
void hns_roce_buf_free(struct hns_roce_dev *hr_dev, struct hns_roce_buf *buf)
{
struct hns_roce_buf_list *trunks;
u32 i;
if (!buf)
return;
trunks = buf->trunk_list;
if (trunks) {
buf->trunk_list = NULL;
for (i = 0; i < buf->ntrunks; i++)
dma_free_coherent(hr_dev->dev, 1 << buf->trunk_shift,
trunks[i].buf, trunks[i].map);
kfree(trunks);
}
kfree(buf);
}
/*
* Allocate the dma buffer for storing ROCEE table entries
*
* @size: required size
* @page_shift: the unit size in a continuous dma address range
* @flags: HNS_ROCE_BUF_ flags to control the allocation flow.
*/
struct hns_roce_buf *hns_roce_buf_alloc(struct hns_roce_dev *hr_dev, u32 size,
u32 page_shift, u32 flags)
{
u32 trunk_size, page_size, alloced_size;
struct hns_roce_buf_list *trunks;
struct hns_roce_buf *buf;
gfp_t gfp_flags;
u32 ntrunk, i;
/* The minimum shift of the page accessed by hw is HNS_HW_PAGE_SHIFT */
if (WARN_ON(page_shift < HNS_HW_PAGE_SHIFT))
return ERR_PTR(-EINVAL);
gfp_flags = (flags & HNS_ROCE_BUF_NOSLEEP) ? GFP_ATOMIC : GFP_KERNEL;
buf = kzalloc_obj(*buf, gfp_flags);
if (!buf)
return ERR_PTR(-ENOMEM);
buf->page_shift = page_shift;
page_size = 1 << buf->page_shift;
/* Calc the trunk size and num by required size and page_shift */
if (flags & HNS_ROCE_BUF_DIRECT) {
buf->trunk_shift = order_base_2(ALIGN(size, PAGE_SIZE));
ntrunk = 1;
} else {
buf->trunk_shift = order_base_2(ALIGN(page_size, PAGE_SIZE));
ntrunk = DIV_ROUND_UP(size, 1 << buf->trunk_shift);
}
trunks = kzalloc_objs(*trunks, ntrunk, gfp_flags);
if (!trunks) {
kfree(buf);
return ERR_PTR(-ENOMEM);
}
trunk_size = 1 << buf->trunk_shift;
alloced_size = 0;
for (i = 0; i < ntrunk; i++) {
trunks[i].buf = dma_alloc_coherent(hr_dev->dev, trunk_size,
&trunks[i].map, gfp_flags);
if (!trunks[i].buf)
break;
alloced_size += trunk_size;
}
buf->ntrunks = i;
/* In nofail mode, it's only failed when the alloced size is 0 */
if ((flags & HNS_ROCE_BUF_NOFAIL) ? i == 0 : i != ntrunk) {
for (i = 0; i < buf->ntrunks; i++)
dma_free_coherent(hr_dev->dev, trunk_size,
trunks[i].buf, trunks[i].map);
kfree(trunks);
kfree(buf);
return ERR_PTR(-ENOMEM);
}
buf->npages = DIV_ROUND_UP(alloced_size, page_size);
buf->trunk_list = trunks;
return buf;
}
int hns_roce_get_kmem_bufs(struct hns_roce_dev *hr_dev, dma_addr_t *bufs,
int buf_cnt, struct hns_roce_buf *buf,
unsigned int page_shift)
{
unsigned int offset, max_size;
int total = 0;
int i;
if (page_shift > buf->trunk_shift) {
dev_err(hr_dev->dev, "failed to check kmem buf shift %u > %u\n",
page_shift, buf->trunk_shift);
return -EINVAL;
}
offset = 0;
max_size = buf->ntrunks << buf->trunk_shift;
for (i = 0; i < buf_cnt && offset < max_size; i++) {
bufs[total++] = hns_roce_buf_dma_addr(buf, offset);
offset += (1 << page_shift);
}
return total;
}
int hns_roce_get_umem_bufs(dma_addr_t *bufs, int buf_cnt, struct ib_umem *umem,
unsigned int page_shift)
{
struct ib_block_iter biter;
int total = 0;
/* convert system page cnt to hw page cnt */
rdma_umem_for_each_dma_block(umem, &biter, 1 << page_shift) {
bufs[total++] = rdma_block_iter_dma_address(&biter);
if (total >= buf_cnt)
goto done;
}
done:
return total;
}
void hns_roce_cleanup_bitmap(struct hns_roce_dev *hr_dev)
{
if (hr_dev->caps.flags & HNS_ROCE_CAP_FLAG_XRC)
ida_destroy(&hr_dev->xrcd_ida.ida);
if (hr_dev->caps.flags & HNS_ROCE_CAP_FLAG_SRQ) {
ida_destroy(&hr_dev->srq_table.srq_ida.ida);
xa_destroy(&hr_dev->srq_table.xa);
}
hns_roce_cleanup_qp_table(hr_dev);
hns_roce_cleanup_cq_table(hr_dev);
ida_destroy(&hr_dev->mr_table.mtpt_ida.ida);
ida_destroy(&hr_dev->pd_ida.ida);
ida_destroy(&hr_dev->uar_ida.ida);
}
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,95 @@
/* SPDX-License-Identifier: GPL-2.0+ */
/*
* Copyright (c) 2025 Hisilicon Limited.
*/
#ifndef _HNS_ROCE_BOND_H
#define _HNS_ROCE_BOND_H
#include <linux/netdevice.h>
#include <net/bonding.h>
#define ROCE_BOND_FUNC_MAX 4
#define ROCE_BOND_NUM_MAX 2
#define BOND_ID(id) BIT(id)
#define BOND_ERR_LOG(fmt, ...) \
pr_err("HNS RoCE Bonding: " fmt, ##__VA_ARGS__)
enum {
BOND_MODE_1,
BOND_MODE_2_4,
};
enum hns_roce_bond_hashtype {
BOND_HASH_L2,
BOND_HASH_L34,
BOND_HASH_L23,
};
enum bond_support_type {
BOND_NOT_SUPPORT,
/*
* bond_grp already exists, but in the current
* conditions it's no longer supported
*/
BOND_EXISTING_NOT_SUPPORT,
BOND_SUPPORT,
};
enum hns_roce_bond_state {
HNS_ROCE_BOND_NOT_ATTACHED,
HNS_ROCE_BOND_NOT_BONDED,
HNS_ROCE_BOND_IS_BONDED,
HNS_ROCE_BOND_SLAVE_CHANGE_NUM,
HNS_ROCE_BOND_SLAVE_CHANGESTATE,
};
enum hns_roce_bond_cmd_type {
HNS_ROCE_SET_BOND,
HNS_ROCE_CHANGE_BOND,
HNS_ROCE_CLEAR_BOND,
};
struct hns_roce_func_info {
struct net_device *net_dev;
struct hnae3_handle *handle;
};
struct hns_roce_bond_group {
struct net_device *upper_dev;
struct hns_roce_dev *main_hr_dev;
u8 active_slave_num;
u32 slave_map;
u32 active_slave_map;
u8 bond_id;
u8 bus_num;
struct hns_roce_func_info bond_func_info[ROCE_BOND_FUNC_MAX];
bool bond_ready;
enum hns_roce_bond_state bond_state;
enum netdev_lag_tx_type tx_type;
enum netdev_lag_hash hash_type;
struct mutex bond_mutex;
struct notifier_block bond_nb;
struct delayed_work bond_work;
};
struct hns_roce_die_info {
u8 bond_id_mask;
struct hns_roce_bond_group *bgrps[ROCE_BOND_NUM_MAX];
struct mutex die_mutex;
u8 suspend_cnt;
};
struct hns_roce_bond_group *hns_roce_get_bond_grp(struct net_device *net_dev,
u8 bus_num);
int hns_roce_alloc_bond_grp(struct hns_roce_dev *hr_dev);
void hns_roce_dealloc_bond_grp(void);
void hns_roce_cleanup_bond(struct hns_roce_bond_group *bond_grp);
bool hns_roce_bond_is_active(struct hns_roce_dev *hr_dev);
int hns_roce_bond_init(struct hns_roce_dev *hr_dev);
void hns_roce_bond_suspend(struct hnae3_handle *handle);
void hns_roce_bond_resume(struct hnae3_handle *handle);
#endif
@@ -0,0 +1,291 @@
/*
* Copyright (c) 2016 Hisilicon Limited.
*
* This software is available to you under a choice of one of two
* licenses. You may choose to be licensed under the terms of the GNU
* General Public License (GPL) Version 2, available from the file
* COPYING in the main directory of this source tree, or the
* OpenIB.org BSD license below:
*
* Redistribution and use in source and binary forms, with or
* without modification, are permitted provided that the following
* conditions are met:
*
* - Redistributions of source code must retain the above
* copyright notice, this list of conditions and the following
* disclaimer.
*
* - Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following
* disclaimer in the documentation and/or other materials
* provided with the distribution.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#include <linux/dmapool.h>
#include "hns_roce_common.h"
#include "hns_roce_device.h"
#include "hns_roce_cmd.h"
#define CMD_POLL_TOKEN 0xffff
#define CMD_MAX_NUM 32
static int hns_roce_cmd_mbox_post_hw(struct hns_roce_dev *hr_dev,
struct hns_roce_mbox_msg *mbox_msg)
{
int ret;
ret = hr_dev->hw->post_mbox(hr_dev, mbox_msg);
if (ret)
return ret;
atomic64_inc(&hr_dev->dfx_cnt[HNS_ROCE_DFX_MBX_POSTED_CNT]);
return 0;
}
/* this should be called with "poll_sem" */
static int __hns_roce_cmd_mbox_poll(struct hns_roce_dev *hr_dev,
struct hns_roce_mbox_msg *mbox_msg)
{
int ret;
ret = hns_roce_cmd_mbox_post_hw(hr_dev, mbox_msg);
if (ret) {
dev_err_ratelimited(hr_dev->dev,
"failed to post mailbox 0x%x in poll mode, ret = %d.\n",
mbox_msg->cmd, ret);
return ret;
}
ret = hr_dev->hw->poll_mbox_done(hr_dev);
if (ret)
return ret;
atomic64_inc(&hr_dev->dfx_cnt[HNS_ROCE_DFX_MBX_POLLED_CNT]);
return 0;
}
static int hns_roce_cmd_mbox_poll(struct hns_roce_dev *hr_dev,
struct hns_roce_mbox_msg *mbox_msg)
{
int ret;
down(&hr_dev->cmd.poll_sem);
ret = __hns_roce_cmd_mbox_poll(hr_dev, mbox_msg);
up(&hr_dev->cmd.poll_sem);
return ret;
}
void hns_roce_cmd_event(struct hns_roce_dev *hr_dev, u16 token, u8 status,
u64 out_param)
{
struct hns_roce_cmd_context *context =
&hr_dev->cmd.context[token % hr_dev->cmd.max_cmds];
if (unlikely(token != context->token)) {
dev_err_ratelimited(hr_dev->dev,
"[cmd] invalid ae token 0x%x, context token is 0x%x.\n",
token, context->token);
return;
}
context->result = (status == HNS_ROCE_CMD_SUCCESS) ? 0 : (-EIO);
context->out_param = out_param;
complete(&context->done);
atomic64_inc(&hr_dev->dfx_cnt[HNS_ROCE_DFX_MBX_EVENT_CNT]);
}
static int __hns_roce_cmd_mbox_wait(struct hns_roce_dev *hr_dev,
struct hns_roce_mbox_msg *mbox_msg)
{
struct hns_roce_cmdq *cmd = &hr_dev->cmd;
struct hns_roce_cmd_context *context;
struct device *dev = hr_dev->dev;
int ret;
spin_lock(&cmd->context_lock);
do {
context = &cmd->context[cmd->free_head];
cmd->free_head = context->next;
} while (context->busy);
context->busy = 1;
context->token += cmd->max_cmds;
spin_unlock(&cmd->context_lock);
reinit_completion(&context->done);
mbox_msg->token = context->token;
ret = hns_roce_cmd_mbox_post_hw(hr_dev, mbox_msg);
if (ret) {
dev_err_ratelimited(dev,
"failed to post mailbox 0x%x in event mode, ret = %d.\n",
mbox_msg->cmd, ret);
goto out;
}
if (!wait_for_completion_timeout(&context->done,
msecs_to_jiffies(HNS_ROCE_CMD_TIMEOUT_MSECS))) {
dev_err_ratelimited(dev, "[cmd] token 0x%x mailbox 0x%x timeout.\n",
context->token, mbox_msg->cmd);
ret = -EBUSY;
goto out;
}
ret = context->result;
if (ret)
dev_err_ratelimited(dev, "[cmd] token 0x%x mailbox 0x%x error %d.\n",
context->token, mbox_msg->cmd, ret);
out:
context->busy = 0;
return ret;
}
static int hns_roce_cmd_mbox_wait(struct hns_roce_dev *hr_dev,
struct hns_roce_mbox_msg *mbox_msg)
{
int ret;
down(&hr_dev->cmd.event_sem);
ret = __hns_roce_cmd_mbox_wait(hr_dev, mbox_msg);
up(&hr_dev->cmd.event_sem);
return ret;
}
int hns_roce_cmd_mbox(struct hns_roce_dev *hr_dev, u64 in_param, u64 out_param,
u8 cmd, unsigned long tag)
{
struct hns_roce_mbox_msg mbox_msg = {};
bool is_busy;
if (hr_dev->hw->chk_mbox_avail)
if (!hr_dev->hw->chk_mbox_avail(hr_dev, &is_busy))
return is_busy ? -EBUSY : 0;
mbox_msg.in_param = in_param;
mbox_msg.out_param = out_param;
mbox_msg.cmd = cmd;
mbox_msg.tag = tag;
if (hr_dev->cmd.use_events) {
mbox_msg.event_en = 1;
return hns_roce_cmd_mbox_wait(hr_dev, &mbox_msg);
} else {
mbox_msg.event_en = 0;
mbox_msg.token = CMD_POLL_TOKEN;
return hns_roce_cmd_mbox_poll(hr_dev, &mbox_msg);
}
}
int hns_roce_cmd_init(struct hns_roce_dev *hr_dev)
{
sema_init(&hr_dev->cmd.poll_sem, 1);
hr_dev->cmd.use_events = 0;
hr_dev->cmd.max_cmds = CMD_MAX_NUM;
hr_dev->cmd.pool = dma_pool_create("hns_roce_cmd", hr_dev->dev,
HNS_ROCE_MAILBOX_SIZE,
HNS_ROCE_MAILBOX_SIZE, 0);
if (!hr_dev->cmd.pool)
return -ENOMEM;
return 0;
}
void hns_roce_cmd_cleanup(struct hns_roce_dev *hr_dev)
{
dma_pool_destroy(hr_dev->cmd.pool);
}
int hns_roce_cmd_use_events(struct hns_roce_dev *hr_dev)
{
struct hns_roce_cmdq *hr_cmd = &hr_dev->cmd;
int i;
hr_cmd->context =
kzalloc_objs(*hr_cmd->context, hr_cmd->max_cmds);
if (!hr_cmd->context) {
hr_dev->cmd_mod = 0;
return -ENOMEM;
}
for (i = 0; i < hr_cmd->max_cmds; ++i) {
hr_cmd->context[i].token = i;
hr_cmd->context[i].next = i + 1;
init_completion(&hr_cmd->context[i].done);
}
hr_cmd->context[hr_cmd->max_cmds - 1].next = 0;
hr_cmd->free_head = 0;
sema_init(&hr_cmd->event_sem, hr_cmd->max_cmds);
spin_lock_init(&hr_cmd->context_lock);
hr_cmd->use_events = 1;
return 0;
}
void hns_roce_cmd_use_polling(struct hns_roce_dev *hr_dev)
{
struct hns_roce_cmdq *hr_cmd = &hr_dev->cmd;
kfree(hr_cmd->context);
hr_cmd->use_events = 0;
}
struct hns_roce_cmd_mailbox *
hns_roce_alloc_cmd_mailbox(struct hns_roce_dev *hr_dev)
{
struct hns_roce_cmd_mailbox *mailbox;
mailbox = kmalloc_obj(*mailbox);
if (!mailbox)
return ERR_PTR(-ENOMEM);
mailbox->buf =
dma_pool_alloc(hr_dev->cmd.pool, GFP_KERNEL, &mailbox->dma);
if (!mailbox->buf) {
kfree(mailbox);
return ERR_PTR(-ENOMEM);
}
return mailbox;
}
void hns_roce_free_cmd_mailbox(struct hns_roce_dev *hr_dev,
struct hns_roce_cmd_mailbox *mailbox)
{
if (!mailbox)
return;
dma_pool_free(hr_dev->cmd.pool, mailbox->buf, mailbox->dma);
kfree(mailbox);
}
int hns_roce_create_hw_ctx(struct hns_roce_dev *dev,
struct hns_roce_cmd_mailbox *mailbox,
u8 cmd, unsigned long idx)
{
return hns_roce_cmd_mbox(dev, mailbox->dma, 0, cmd, idx);
}
int hns_roce_destroy_hw_ctx(struct hns_roce_dev *dev, u8 cmd, unsigned long idx)
{
return hns_roce_cmd_mbox(dev, 0, 0, cmd, idx);
}
@@ -0,0 +1,158 @@
/*
* Copyright (c) 2016 Hisilicon Limited.
*
* This software is available to you under a choice of one of two
* licenses. You may choose to be licensed under the terms of the GNU
* General Public License (GPL) Version 2, available from the file
* COPYING in the main directory of this source tree, or the
* OpenIB.org BSD license below:
*
* Redistribution and use in source and binary forms, with or
* without modification, are permitted provided that the following
* conditions are met:
*
* - Redistributions of source code must retain the above
* copyright notice, this list of conditions and the following
* disclaimer.
*
* - Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following
* disclaimer in the documentation and/or other materials
* provided with the distribution.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#ifndef _HNS_ROCE_CMD_H
#define _HNS_ROCE_CMD_H
#define HNS_ROCE_MAILBOX_SIZE 4096
#define HNS_ROCE_CMD_TIMEOUT_MSECS 10000
enum {
/* QPC BT commands */
HNS_ROCE_CMD_WRITE_QPC_BT0 = 0x0,
HNS_ROCE_CMD_WRITE_QPC_BT1 = 0x1,
HNS_ROCE_CMD_WRITE_QPC_BT2 = 0x2,
HNS_ROCE_CMD_READ_QPC_BT0 = 0x4,
HNS_ROCE_CMD_READ_QPC_BT1 = 0x5,
HNS_ROCE_CMD_READ_QPC_BT2 = 0x6,
HNS_ROCE_CMD_DESTROY_QPC_BT0 = 0x8,
HNS_ROCE_CMD_DESTROY_QPC_BT1 = 0x9,
HNS_ROCE_CMD_DESTROY_QPC_BT2 = 0xa,
/* QPC operation */
HNS_ROCE_CMD_MODIFY_QPC = 0x41,
HNS_ROCE_CMD_QUERY_QPC = 0x42,
HNS_ROCE_CMD_MODIFY_CQC = 0x52,
HNS_ROCE_CMD_QUERY_CQC = 0x53,
/* CQC BT commands */
HNS_ROCE_CMD_WRITE_CQC_BT0 = 0x10,
HNS_ROCE_CMD_WRITE_CQC_BT1 = 0x11,
HNS_ROCE_CMD_WRITE_CQC_BT2 = 0x12,
HNS_ROCE_CMD_READ_CQC_BT0 = 0x14,
HNS_ROCE_CMD_READ_CQC_BT1 = 0x15,
HNS_ROCE_CMD_READ_CQC_BT2 = 0x1b,
HNS_ROCE_CMD_DESTROY_CQC_BT0 = 0x18,
HNS_ROCE_CMD_DESTROY_CQC_BT1 = 0x19,
HNS_ROCE_CMD_DESTROY_CQC_BT2 = 0x1a,
/* MPT BT commands */
HNS_ROCE_CMD_WRITE_MPT_BT0 = 0x20,
HNS_ROCE_CMD_WRITE_MPT_BT1 = 0x21,
HNS_ROCE_CMD_WRITE_MPT_BT2 = 0x22,
HNS_ROCE_CMD_READ_MPT_BT0 = 0x24,
HNS_ROCE_CMD_READ_MPT_BT1 = 0x25,
HNS_ROCE_CMD_READ_MPT_BT2 = 0x26,
HNS_ROCE_CMD_DESTROY_MPT_BT0 = 0x28,
HNS_ROCE_CMD_DESTROY_MPT_BT1 = 0x29,
HNS_ROCE_CMD_DESTROY_MPT_BT2 = 0x2a,
/* CQC TIMER commands */
HNS_ROCE_CMD_WRITE_CQC_TIMER_BT0 = 0x23,
HNS_ROCE_CMD_READ_CQC_TIMER_BT0 = 0x27,
/* MPT commands */
HNS_ROCE_CMD_QUERY_MPT = 0x62,
/* SRQC BT commands */
HNS_ROCE_CMD_WRITE_SRQC_BT0 = 0x30,
HNS_ROCE_CMD_WRITE_SRQC_BT1 = 0x31,
HNS_ROCE_CMD_WRITE_SRQC_BT2 = 0x32,
HNS_ROCE_CMD_READ_SRQC_BT0 = 0x34,
HNS_ROCE_CMD_READ_SRQC_BT1 = 0x35,
HNS_ROCE_CMD_READ_SRQC_BT2 = 0x36,
HNS_ROCE_CMD_DESTROY_SRQC_BT0 = 0x38,
HNS_ROCE_CMD_DESTROY_SRQC_BT1 = 0x39,
HNS_ROCE_CMD_DESTROY_SRQC_BT2 = 0x3a,
/* QPC TIMER commands */
HNS_ROCE_CMD_WRITE_QPC_TIMER_BT0 = 0x33,
HNS_ROCE_CMD_READ_QPC_TIMER_BT0 = 0x37,
/* EQC commands */
HNS_ROCE_CMD_CREATE_AEQC = 0x80,
HNS_ROCE_CMD_MODIFY_AEQC = 0x81,
HNS_ROCE_CMD_QUERY_AEQC = 0x82,
HNS_ROCE_CMD_DESTROY_AEQC = 0x83,
HNS_ROCE_CMD_CREATE_CEQC = 0x90,
HNS_ROCE_CMD_MODIFY_CEQC = 0x91,
HNS_ROCE_CMD_QUERY_CEQC = 0x92,
HNS_ROCE_CMD_DESTROY_CEQC = 0x93,
/* SCC CTX commands */
HNS_ROCE_CMD_QUERY_SCCC = 0xa2,
/* SCC CTX BT commands */
HNS_ROCE_CMD_READ_SCCC_BT0 = 0xa4,
HNS_ROCE_CMD_WRITE_SCCC_BT0 = 0xa5,
};
enum {
/* TPT commands */
HNS_ROCE_CMD_CREATE_MPT = 0xd,
HNS_ROCE_CMD_DESTROY_MPT = 0xf,
/* CQ commands */
HNS_ROCE_CMD_CREATE_CQC = 0x16,
HNS_ROCE_CMD_DESTROY_CQC = 0x17,
/* QP/EE commands */
HNS_ROCE_CMD_RST2INIT_QP = 0x19,
HNS_ROCE_CMD_INIT2RTR_QP = 0x1a,
HNS_ROCE_CMD_RTR2RTS_QP = 0x1b,
HNS_ROCE_CMD_RTS2RTS_QP = 0x1c,
HNS_ROCE_CMD_2ERR_QP = 0x1e,
HNS_ROCE_CMD_RTS2SQD_QP = 0x1f,
HNS_ROCE_CMD_SQD2RTS_QP = 0x20,
HNS_ROCE_CMD_2RST_QP = 0x21,
HNS_ROCE_CMD_QUERY_QP = 0x22,
HNS_ROCE_CMD_SQD2SQD_QP = 0x38,
HNS_ROCE_CMD_CREATE_SRQ = 0x70,
HNS_ROCE_CMD_MODIFY_SRQC = 0x72,
HNS_ROCE_CMD_QUERY_SRQC = 0x73,
HNS_ROCE_CMD_DESTROY_SRQ = 0x74,
};
int hns_roce_cmd_mbox(struct hns_roce_dev *hr_dev, u64 in_param, u64 out_param,
u8 cmd, unsigned long tag);
struct hns_roce_cmd_mailbox *
hns_roce_alloc_cmd_mailbox(struct hns_roce_dev *hr_dev);
void hns_roce_free_cmd_mailbox(struct hns_roce_dev *hr_dev,
struct hns_roce_cmd_mailbox *mailbox);
int hns_roce_create_hw_ctx(struct hns_roce_dev *dev,
struct hns_roce_cmd_mailbox *mailbox,
u8 cmd, unsigned long idx);
int hns_roce_destroy_hw_ctx(struct hns_roce_dev *dev, u8 cmd,
unsigned long idx);
#endif /* _HNS_ROCE_CMD_H */
@@ -0,0 +1,199 @@
/*
* Copyright (c) 2016 Hisilicon Limited.
*
* This software is available to you under a choice of one of two
* licenses. You may choose to be licensed under the terms of the GNU
* General Public License (GPL) Version 2, available from the file
* COPYING in the main directory of this source tree, or the
* OpenIB.org BSD license below:
*
* Redistribution and use in source and binary forms, with or
* without modification, are permitted provided that the following
* conditions are met:
*
* - Redistributions of source code must retain the above
* copyright notice, this list of conditions and the following
* disclaimer.
*
* - Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following
* disclaimer in the documentation and/or other materials
* provided with the distribution.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#ifndef _HNS_ROCE_COMMON_H
#define _HNS_ROCE_COMMON_H
#include <linux/bitfield.h>
#define roce_write(dev, reg, val) writel((val), (dev)->reg_base + (reg))
#define roce_read(dev, reg) readl((dev)->reg_base + (reg))
#define roce_raw_write(value, addr) \
__raw_writel((__force u32)cpu_to_le32(value), (addr))
#define roce_get_field(origin, mask, shift) \
((le32_to_cpu(origin) & (mask)) >> (u32)(shift))
#define roce_get_bit(origin, shift) \
roce_get_field((origin), (1ul << (shift)), (shift))
#define roce_set_field(origin, mask, shift, val) \
do { \
(origin) &= ~cpu_to_le32(mask); \
(origin) |= \
cpu_to_le32(((u32)(val) << (u32)(shift)) & (mask)); \
} while (0)
#define roce_set_bit(origin, shift, val) \
roce_set_field((origin), (1ul << (shift)), (shift), (val))
#define FIELD_LOC(field_type, field_h, field_l) field_type, field_h, field_l
#define _hr_reg_enable(ptr, field_type, field_h, field_l) \
({ \
const field_type *_ptr = ptr; \
*((__le32 *)_ptr + (field_h) / 32) |= cpu_to_le32( \
BIT((field_l) % 32) + \
BUILD_BUG_ON_ZERO((field_h) != (field_l))); \
})
#define hr_reg_enable(ptr, field) _hr_reg_enable(ptr, field)
#define _hr_reg_clear(ptr, field_type, field_h, field_l) \
({ \
const field_type *_ptr = ptr; \
BUILD_BUG_ON(((field_h) / 32) != ((field_l) / 32)); \
*((__le32 *)_ptr + (field_h) / 32) &= \
~cpu_to_le32(GENMASK((field_h) % 32, (field_l) % 32)); \
})
#define hr_reg_clear(ptr, field) _hr_reg_clear(ptr, field)
#define _hr_reg_write_bool(ptr, field_type, field_h, field_l, val) \
({ \
(val) ? _hr_reg_enable(ptr, field_type, field_h, field_l) : \
_hr_reg_clear(ptr, field_type, field_h, field_l); \
})
#define hr_reg_write_bool(ptr, field, val) _hr_reg_write_bool(ptr, field, val)
#define _hr_reg_write(ptr, field_type, field_h, field_l, val) \
({ \
_hr_reg_clear(ptr, field_type, field_h, field_l); \
*((__le32 *)ptr + (field_h) / 32) |= cpu_to_le32(FIELD_PREP( \
GENMASK((field_h) % 32, (field_l) % 32), val)); \
})
#define hr_reg_write(ptr, field, val) _hr_reg_write(ptr, field, val)
#define _hr_reg_read(ptr, field_type, field_h, field_l) \
({ \
const field_type *_ptr = ptr; \
BUILD_BUG_ON(((field_h) / 32) != ((field_l) / 32)); \
FIELD_GET(GENMASK((field_h) % 32, (field_l) % 32), \
le32_to_cpu(*((__le32 *)_ptr + (field_h) / 32))); \
})
#define hr_reg_read(ptr, field) _hr_reg_read(ptr, field)
/*************ROCEE_REG DEFINITION****************/
#define ROCEE_VENDOR_ID_REG 0x0
#define ROCEE_VENDOR_PART_ID_REG 0x4
#define ROCEE_SYS_IMAGE_GUID_L_REG 0xC
#define ROCEE_SYS_IMAGE_GUID_H_REG 0x10
#define ROCEE_PORT_GID_L_0_REG 0x50
#define ROCEE_PORT_GID_ML_0_REG 0x54
#define ROCEE_PORT_GID_MH_0_REG 0x58
#define ROCEE_PORT_GID_H_0_REG 0x5C
#define ROCEE_BT_CMD_H_REG 0x204
#define ROCEE_SMAC_L_0_REG 0x240
#define ROCEE_SMAC_H_0_REG 0x244
#define ROCEE_QP1C_CFG3_0_REG 0x27C
#define ROCEE_CAEP_AEQE_CONS_IDX_REG 0x3AC
#define ROCEE_CAEP_CEQC_CONS_IDX_0_REG 0x3BC
#define ROCEE_ECC_UCERR_ALM1_REG 0xB38
#define ROCEE_ECC_UCERR_ALM2_REG 0xB3C
#define ROCEE_ECC_CERR_ALM1_REG 0xB44
#define ROCEE_ECC_CERR_ALM2_REG 0xB48
#define ROCEE_ACK_DELAY_REG 0x14
#define ROCEE_GLB_CFG_REG 0x18
#define ROCEE_DMAE_USER_CFG1_REG 0x40
#define ROCEE_DMAE_USER_CFG2_REG 0x44
#define ROCEE_DB_SQ_WL_REG 0x154
#define ROCEE_DB_OTHERS_WL_REG 0x158
#define ROCEE_RAQ_WL_REG 0x15C
#define ROCEE_WRMS_POL_TIME_INTERVAL_REG 0x160
#define ROCEE_EXT_DB_SQ_REG 0x164
#define ROCEE_EXT_DB_SQ_H_REG 0x168
#define ROCEE_EXT_DB_OTH_REG 0x16C
#define ROCEE_EXT_DB_OTH_H_REG 0x170
#define ROCEE_EXT_DB_SQ_WL_EMPTY_REG 0x174
#define ROCEE_EXT_DB_SQ_WL_REG 0x178
#define ROCEE_EXT_DB_OTHERS_WL_EMPTY_REG 0x17C
#define ROCEE_EXT_DB_OTHERS_WL_REG 0x180
#define ROCEE_EXT_RAQ_REG 0x184
#define ROCEE_EXT_RAQ_H_REG 0x188
#define ROCEE_CAEP_CE_INTERVAL_CFG_REG 0x190
#define ROCEE_CAEP_CE_BURST_NUM_CFG_REG 0x194
#define ROCEE_BT_CMD_L_REG 0x200
#define ROCEE_MB1_REG 0x210
#define ROCEE_MB6_REG 0x224
#define ROCEE_DB_SQ_L_0_REG 0x230
#define ROCEE_DB_OTHERS_L_0_REG 0x238
#define ROCEE_QP1C_CFG0_0_REG 0x270
#define ROCEE_CAEP_AEQC_AEQE_SHIFT_REG 0x3A0
#define ROCEE_CAEP_CEQC_SHIFT_0_REG 0x3B0
#define ROCEE_CAEP_CE_IRQ_MASK_0_REG 0x3C0
#define ROCEE_CAEP_CEQ_ALM_OVF_0_REG 0x3C4
#define ROCEE_CAEP_AE_MASK_REG 0x6C8
#define ROCEE_CAEP_AE_ST_REG 0x6CC
#define ROCEE_CAEP_CQE_WCMD_EMPTY 0x850
#define ROCEE_SCAEP_WR_CQE_CNT 0x8D0
#define ROCEE_ECC_UCERR_ALM0_REG 0xB34
#define ROCEE_ECC_CERR_ALM0_REG 0xB40
/* V2 ROCEE REG */
#define ROCEE_TX_CMQ_BASEADDR_L_REG 0x07000
#define ROCEE_TX_CMQ_BASEADDR_H_REG 0x07004
#define ROCEE_TX_CMQ_DEPTH_REG 0x07008
#define ROCEE_TX_CMQ_PI_REG 0x07010
#define ROCEE_TX_CMQ_CI_REG 0x07014
#define ROCEE_RX_CMQ_BASEADDR_L_REG 0x07018
#define ROCEE_RX_CMQ_BASEADDR_H_REG 0x0701c
#define ROCEE_RX_CMQ_DEPTH_REG 0x07020
#define ROCEE_RX_CMQ_TAIL_REG 0x07024
#define ROCEE_RX_CMQ_HEAD_REG 0x07028
#define ROCEE_VF_EQ_DB_CFG0_REG 0x238
#define ROCEE_VF_EQ_DB_CFG1_REG 0x23C
#define ROCEE_VF_ABN_INT_CFG_REG 0x13000
#define ROCEE_VF_ABN_INT_ST_REG 0x13004
#define ROCEE_VF_ABN_INT_EN_REG 0x13008
#define ROCEE_VF_EVENT_INT_EN_REG 0x1300c
#endif /* _HNS_ROCE_COMMON_H */
@@ -0,0 +1,587 @@
/*
* Copyright (c) 2016 Hisilicon Limited.
*
* This software is available to you under a choice of one of two
* licenses. You may choose to be licensed under the terms of the GNU
* General Public License (GPL) Version 2, available from the file
* COPYING in the main directory of this source tree, or the
* OpenIB.org BSD license below:
*
* Redistribution and use in source and binary forms, with or
* without modification, are permitted provided that the following
* conditions are met:
*
* - Redistributions of source code must retain the above
* copyright notice, this list of conditions and the following
* disclaimer.
*
* - Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following
* disclaimer in the documentation and/or other materials
* provided with the distribution.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#include <linux/pci.h>
#include <rdma/ib_umem.h>
#include <rdma/uverbs_ioctl.h>
#include "hns_roce_device.h"
#include "hns_roce_cmd.h"
#include "hns_roce_hem.h"
#include "hns_roce_common.h"
void hns_roce_put_cq_bankid_for_uctx(struct hns_roce_ucontext *uctx)
{
struct hns_roce_dev *hr_dev = to_hr_dev(uctx->ibucontext.device);
struct hns_roce_cq_table *cq_table = &hr_dev->cq_table;
if (hr_dev->pci_dev->revision < PCI_REVISION_ID_HIP09)
return;
mutex_lock(&cq_table->bank_mutex);
cq_table->ctx_num[uctx->cq_bank_id]--;
mutex_unlock(&cq_table->bank_mutex);
}
void hns_roce_get_cq_bankid_for_uctx(struct hns_roce_ucontext *uctx)
{
struct hns_roce_dev *hr_dev = to_hr_dev(uctx->ibucontext.device);
struct hns_roce_cq_table *cq_table = &hr_dev->cq_table;
u32 least_load = U32_MAX;
u8 bankid = 0;
u8 i;
if (hr_dev->pci_dev->revision < PCI_REVISION_ID_HIP09)
return;
mutex_lock(&cq_table->bank_mutex);
for (i = 0; i < HNS_ROCE_CQ_BANK_NUM; i++) {
if (!(cq_table->valid_cq_bank_mask & BIT(i)))
continue;
if (cq_table->ctx_num[i] < least_load) {
least_load = cq_table->ctx_num[i];
bankid = i;
}
}
cq_table->ctx_num[bankid]++;
mutex_unlock(&cq_table->bank_mutex);
uctx->cq_bank_id = bankid;
}
static u8 get_least_load_bankid_for_cq(struct hns_roce_bank *bank)
{
u32 least_load = bank[0].inuse;
u8 bankid = 0;
u32 bankcnt;
u8 i;
for (i = 1; i < HNS_ROCE_CQ_BANK_NUM; i++) {
bankcnt = bank[i].inuse;
if (bankcnt < least_load) {
least_load = bankcnt;
bankid = i;
}
}
return bankid;
}
static u8 select_cq_bankid(struct hns_roce_dev *hr_dev,
struct hns_roce_bank *bank, struct ib_udata *udata)
{
struct hns_roce_ucontext *uctx = udata ?
rdma_udata_to_drv_context(udata, struct hns_roce_ucontext,
ibucontext) : NULL;
if (hr_dev->pci_dev->revision >= PCI_REVISION_ID_HIP09)
return uctx ? uctx->cq_bank_id : 0;
return get_least_load_bankid_for_cq(bank);
}
static int alloc_cqn(struct hns_roce_dev *hr_dev, struct hns_roce_cq *hr_cq,
struct ib_udata *udata)
{
struct hns_roce_cq_table *cq_table = &hr_dev->cq_table;
struct hns_roce_bank *bank;
u8 bankid;
int id;
mutex_lock(&cq_table->bank_mutex);
bankid = select_cq_bankid(hr_dev, cq_table->bank, udata);
bank = &cq_table->bank[bankid];
id = ida_alloc_range(&bank->ida, bank->min, bank->max, GFP_KERNEL);
if (id < 0) {
mutex_unlock(&cq_table->bank_mutex);
return id;
}
/* the lower 2 bits is bankid */
hr_cq->cqn = (id << CQ_BANKID_SHIFT) | bankid;
bank->inuse++;
mutex_unlock(&cq_table->bank_mutex);
return 0;
}
static inline u8 get_cq_bankid(unsigned long cqn)
{
/* The lower 2 bits of CQN are used to hash to different banks */
return (u8)(cqn & GENMASK(1, 0));
}
static void free_cqn(struct hns_roce_dev *hr_dev, unsigned long cqn)
{
struct hns_roce_cq_table *cq_table = &hr_dev->cq_table;
struct hns_roce_bank *bank;
bank = &cq_table->bank[get_cq_bankid(cqn)];
ida_free(&bank->ida, cqn >> CQ_BANKID_SHIFT);
mutex_lock(&cq_table->bank_mutex);
bank->inuse--;
mutex_unlock(&cq_table->bank_mutex);
}
static int hns_roce_create_cqc(struct hns_roce_dev *hr_dev,
struct hns_roce_cq *hr_cq,
u64 *mtts, dma_addr_t dma_handle)
{
struct ib_device *ibdev = &hr_dev->ib_dev;
struct hns_roce_cmd_mailbox *mailbox;
int ret;
mailbox = hns_roce_alloc_cmd_mailbox(hr_dev);
if (IS_ERR(mailbox)) {
ibdev_err(ibdev, "failed to alloc mailbox for CQC.\n");
return PTR_ERR(mailbox);
}
hr_dev->hw->write_cqc(hr_dev, hr_cq, mailbox->buf, mtts, dma_handle);
ret = hns_roce_create_hw_ctx(hr_dev, mailbox, HNS_ROCE_CMD_CREATE_CQC,
hr_cq->cqn);
if (ret)
ibdev_err_ratelimited(ibdev,
"failed to send create cmd for CQ(0x%lx), ret = %d.\n",
hr_cq->cqn, ret);
hns_roce_free_cmd_mailbox(hr_dev, mailbox);
return ret;
}
static int alloc_cqc(struct hns_roce_dev *hr_dev, struct hns_roce_cq *hr_cq)
{
struct hns_roce_cq_table *cq_table = &hr_dev->cq_table;
struct ib_device *ibdev = &hr_dev->ib_dev;
u64 mtts[MTT_MIN_COUNT] = {};
int ret;
ret = hns_roce_mtr_find(hr_dev, &hr_cq->mtr, 0, mtts, ARRAY_SIZE(mtts));
if (ret) {
ibdev_err(ibdev, "failed to find CQ mtr, ret = %d.\n", ret);
return ret;
}
/* Get CQC memory HEM(Hardware Entry Memory) table */
ret = hns_roce_table_get(hr_dev, &cq_table->table, hr_cq->cqn);
if (ret) {
ibdev_err(ibdev, "failed to get CQ(0x%lx) context, ret = %d.\n",
hr_cq->cqn, ret);
return ret;
}
ret = xa_err(xa_store_irq(&cq_table->array, hr_cq->cqn, hr_cq, GFP_KERNEL));
if (ret) {
ibdev_err(ibdev, "failed to xa_store CQ, ret = %d.\n", ret);
goto err_put;
}
ret = hns_roce_create_cqc(hr_dev, hr_cq, mtts,
hns_roce_get_mtr_ba(&hr_cq->mtr));
if (ret)
goto err_xa;
return 0;
err_xa:
xa_erase_irq(&cq_table->array, hr_cq->cqn);
err_put:
hns_roce_table_put(hr_dev, &cq_table->table, hr_cq->cqn);
return ret;
}
static void free_cqc(struct hns_roce_dev *hr_dev, struct hns_roce_cq *hr_cq)
{
struct hns_roce_cq_table *cq_table = &hr_dev->cq_table;
struct device *dev = hr_dev->dev;
int ret;
ret = hns_roce_destroy_hw_ctx(hr_dev, HNS_ROCE_CMD_DESTROY_CQC,
hr_cq->cqn);
if (ret)
dev_err_ratelimited(dev, "DESTROY_CQ failed (%d) for CQN %06lx\n",
ret, hr_cq->cqn);
xa_erase_irq(&cq_table->array, hr_cq->cqn);
/* Waiting interrupt process procedure carried out */
synchronize_irq(hr_dev->eq_table.eq[hr_cq->vector].irq);
/* wait for all interrupt processed */
if (refcount_dec_and_test(&hr_cq->refcount))
complete(&hr_cq->free);
wait_for_completion(&hr_cq->free);
hns_roce_table_put(hr_dev, &cq_table->table, hr_cq->cqn);
}
static int alloc_cq_buf(struct hns_roce_dev *hr_dev, struct hns_roce_cq *hr_cq,
struct ib_udata *udata, unsigned long addr)
{
struct ib_device *ibdev = &hr_dev->ib_dev;
struct hns_roce_buf_attr buf_attr = {};
int ret;
buf_attr.page_shift = hr_dev->caps.cqe_buf_pg_sz + PAGE_SHIFT;
buf_attr.region[0].size = hr_cq->cq_depth * hr_cq->cqe_size;
buf_attr.region[0].hopnum = hr_dev->caps.cqe_hop_num;
buf_attr.region_count = 1;
ret = hns_roce_mtr_create(hr_dev, &hr_cq->mtr, &buf_attr,
hr_dev->caps.cqe_ba_pg_sz + PAGE_SHIFT,
udata, addr);
if (ret)
ibdev_err(ibdev, "failed to alloc CQ mtr, ret = %d.\n", ret);
return ret;
}
static void free_cq_buf(struct hns_roce_dev *hr_dev, struct hns_roce_cq *hr_cq)
{
hns_roce_mtr_destroy(hr_dev, &hr_cq->mtr);
}
static int alloc_cq_db(struct hns_roce_dev *hr_dev, struct hns_roce_cq *hr_cq,
struct ib_udata *udata, unsigned long addr,
struct hns_roce_ib_create_cq_resp *resp)
{
bool has_db = hr_dev->caps.flags & HNS_ROCE_CAP_FLAG_CQ_RECORD_DB;
struct hns_roce_ucontext *uctx;
int err;
if (udata) {
if (has_db &&
udata->outlen >= offsetofend(typeof(*resp), cap_flags)) {
uctx = rdma_udata_to_drv_context(udata,
struct hns_roce_ucontext, ibucontext);
err = hns_roce_db_map_user(uctx, addr, &hr_cq->db);
if (err)
return err;
hr_cq->flags |= HNS_ROCE_CQ_FLAG_RECORD_DB;
resp->cap_flags |= HNS_ROCE_CQ_FLAG_RECORD_DB;
}
} else {
if (has_db) {
err = hns_roce_alloc_db(hr_dev, &hr_cq->db, 1);
if (err)
return err;
hr_cq->set_ci_db = hr_cq->db.db_record;
*hr_cq->set_ci_db = 0;
hr_cq->flags |= HNS_ROCE_CQ_FLAG_RECORD_DB;
}
hr_cq->db_reg = hr_dev->reg_base + hr_dev->odb_offset +
DB_REG_OFFSET * hr_dev->priv_uar.index;
}
return 0;
}
static void free_cq_db(struct hns_roce_dev *hr_dev, struct hns_roce_cq *hr_cq,
struct ib_udata *udata)
{
struct hns_roce_ucontext *uctx;
if (!(hr_cq->flags & HNS_ROCE_CQ_FLAG_RECORD_DB))
return;
hr_cq->flags &= ~HNS_ROCE_CQ_FLAG_RECORD_DB;
if (udata) {
uctx = rdma_udata_to_drv_context(udata,
struct hns_roce_ucontext,
ibucontext);
hns_roce_db_unmap_user(uctx, &hr_cq->db);
} else {
hns_roce_free_db(hr_dev, &hr_cq->db);
}
}
static int verify_cq_create_attr(struct hns_roce_dev *hr_dev,
const struct ib_cq_init_attr *attr)
{
struct ib_device *ibdev = &hr_dev->ib_dev;
if (!attr->cqe || attr->cqe > hr_dev->caps.max_cqes) {
ibdev_err(ibdev, "failed to check CQ count %u, max = %u.\n",
attr->cqe, hr_dev->caps.max_cqes);
return -EINVAL;
}
if (attr->comp_vector >= hr_dev->caps.num_comp_vectors) {
ibdev_err(ibdev, "failed to check CQ vector = %u, max = %d.\n",
attr->comp_vector, hr_dev->caps.num_comp_vectors);
return -EINVAL;
}
return 0;
}
static void set_cq_param(struct hns_roce_cq *hr_cq, u32 cq_entries, int vector,
struct hns_roce_ib_create_cq *ucmd)
{
struct hns_roce_dev *hr_dev = to_hr_dev(hr_cq->ib_cq.device);
cq_entries = max(cq_entries, hr_dev->caps.min_cqes);
cq_entries = roundup_pow_of_two(cq_entries);
hr_cq->ib_cq.cqe = cq_entries - 1; /* used as cqe index */
hr_cq->cq_depth = cq_entries;
hr_cq->vector = vector;
spin_lock_init(&hr_cq->lock);
INIT_LIST_HEAD(&hr_cq->sq_list);
INIT_LIST_HEAD(&hr_cq->rq_list);
}
static int set_cqe_size(struct hns_roce_cq *hr_cq, struct ib_udata *udata,
struct hns_roce_ib_create_cq *ucmd)
{
struct hns_roce_dev *hr_dev = to_hr_dev(hr_cq->ib_cq.device);
if (!udata) {
hr_cq->cqe_size = hr_dev->caps.cqe_sz;
return 0;
}
if (udata->inlen >= offsetofend(typeof(*ucmd), cqe_size)) {
if (ucmd->cqe_size != HNS_ROCE_V2_CQE_SIZE &&
ucmd->cqe_size != HNS_ROCE_V3_CQE_SIZE) {
ibdev_err(&hr_dev->ib_dev,
"invalid cqe size %u.\n", ucmd->cqe_size);
return -EINVAL;
}
hr_cq->cqe_size = ucmd->cqe_size;
} else {
hr_cq->cqe_size = HNS_ROCE_V2_CQE_SIZE;
}
return 0;
}
int hns_roce_create_cq(struct ib_cq *ib_cq, const struct ib_cq_init_attr *attr,
struct uverbs_attr_bundle *attrs)
{
struct hns_roce_dev *hr_dev = to_hr_dev(ib_cq->device);
struct ib_udata *udata = &attrs->driver_udata;
struct hns_roce_ib_create_cq_resp resp = {};
struct hns_roce_cq *hr_cq = to_hr_cq(ib_cq);
struct ib_device *ibdev = &hr_dev->ib_dev;
struct hns_roce_ib_create_cq ucmd = {};
int ret;
if (attr->flags) {
ret = -EOPNOTSUPP;
goto err_out;
}
ret = verify_cq_create_attr(hr_dev, attr);
if (ret)
goto err_out;
if (udata) {
ret = ib_copy_validate_udata_in(udata, ucmd, db_addr);
if (ret)
goto err_out;
}
set_cq_param(hr_cq, attr->cqe, attr->comp_vector, &ucmd);
ret = set_cqe_size(hr_cq, udata, &ucmd);
if (ret)
goto err_out;
ret = alloc_cq_buf(hr_dev, hr_cq, udata, ucmd.buf_addr);
if (ret) {
ibdev_err(ibdev, "failed to alloc CQ buf, ret = %d.\n", ret);
goto err_out;
}
ret = alloc_cq_db(hr_dev, hr_cq, udata, ucmd.db_addr, &resp);
if (ret) {
ibdev_err(ibdev, "failed to alloc CQ db, ret = %d.\n", ret);
goto err_cq_buf;
}
ret = alloc_cqn(hr_dev, hr_cq, udata);
if (ret) {
ibdev_err(ibdev, "failed to alloc CQN, ret = %d.\n", ret);
goto err_cq_db;
}
ret = alloc_cqc(hr_dev, hr_cq);
if (ret) {
ibdev_err(ibdev,
"failed to alloc CQ context, ret = %d.\n", ret);
goto err_cqn;
}
if (udata) {
resp.cqn = hr_cq->cqn;
ret = ib_respond_udata(udata, resp);
if (ret)
goto err_cqc;
}
hr_cq->cons_index = 0;
hr_cq->arm_sn = 1;
refcount_set(&hr_cq->refcount, 1);
init_completion(&hr_cq->free);
return 0;
err_cqc:
free_cqc(hr_dev, hr_cq);
err_cqn:
free_cqn(hr_dev, hr_cq->cqn);
err_cq_db:
free_cq_db(hr_dev, hr_cq, udata);
err_cq_buf:
free_cq_buf(hr_dev, hr_cq);
err_out:
atomic64_inc(&hr_dev->dfx_cnt[HNS_ROCE_DFX_CQ_CREATE_ERR_CNT]);
return ret;
}
int hns_roce_destroy_cq(struct ib_cq *ib_cq, struct ib_udata *udata)
{
struct hns_roce_dev *hr_dev = to_hr_dev(ib_cq->device);
struct hns_roce_cq *hr_cq = to_hr_cq(ib_cq);
free_cqc(hr_dev, hr_cq);
free_cqn(hr_dev, hr_cq->cqn);
free_cq_db(hr_dev, hr_cq, udata);
free_cq_buf(hr_dev, hr_cq);
return 0;
}
void hns_roce_cq_completion(struct hns_roce_dev *hr_dev, u32 cqn)
{
struct hns_roce_cq *hr_cq;
struct ib_cq *ibcq;
hr_cq = xa_load(&hr_dev->cq_table.array,
cqn & (hr_dev->caps.num_cqs - 1));
if (!hr_cq) {
dev_warn(hr_dev->dev, "completion event for bogus CQ 0x%06x\n",
cqn);
return;
}
++hr_cq->arm_sn;
ibcq = &hr_cq->ib_cq;
if (ibcq->comp_handler)
ibcq->comp_handler(ibcq, ibcq->cq_context);
}
void hns_roce_cq_event(struct hns_roce_dev *hr_dev, u32 cqn, int event_type)
{
struct device *dev = hr_dev->dev;
struct hns_roce_cq *hr_cq;
struct ib_event event;
struct ib_cq *ibcq;
if (event_type != HNS_ROCE_EVENT_TYPE_CQ_ID_INVALID &&
event_type != HNS_ROCE_EVENT_TYPE_CQ_ACCESS_ERROR &&
event_type != HNS_ROCE_EVENT_TYPE_CQ_OVERFLOW) {
dev_err(dev, "unexpected event type 0x%x on CQ 0x%06x\n",
event_type, cqn);
return;
}
xa_lock(&hr_dev->cq_table.array);
hr_cq = xa_load(&hr_dev->cq_table.array,
cqn & (hr_dev->caps.num_cqs - 1));
if (hr_cq)
refcount_inc(&hr_cq->refcount);
xa_unlock(&hr_dev->cq_table.array);
if (!hr_cq) {
dev_warn(dev, "async event for bogus CQ 0x%06x\n", cqn);
return;
}
ibcq = &hr_cq->ib_cq;
if (ibcq->event_handler) {
event.device = ibcq->device;
event.element.cq = ibcq;
event.event = IB_EVENT_CQ_ERR;
ibcq->event_handler(&event, ibcq->cq_context);
}
if (refcount_dec_and_test(&hr_cq->refcount))
complete(&hr_cq->free);
}
void hns_roce_init_cq_table(struct hns_roce_dev *hr_dev)
{
struct hns_roce_cq_table *cq_table = &hr_dev->cq_table;
unsigned int reserved_from_bot;
unsigned int i;
mutex_init(&cq_table->bank_mutex);
xa_init(&cq_table->array);
reserved_from_bot = hr_dev->caps.reserved_cqs;
for (i = 0; i < reserved_from_bot; i++) {
cq_table->bank[get_cq_bankid(i)].inuse++;
cq_table->bank[get_cq_bankid(i)].min++;
}
for (i = 0; i < HNS_ROCE_CQ_BANK_NUM; i++) {
ida_init(&cq_table->bank[i].ida);
cq_table->bank[i].max = hr_dev->caps.num_cqs /
HNS_ROCE_CQ_BANK_NUM - 1;
}
if (hr_dev->caps.flags & HNS_ROCE_CAP_FLAG_LIMIT_BANK)
cq_table->valid_cq_bank_mask = VALID_CQ_BANK_MASK_LIMIT;
else
cq_table->valid_cq_bank_mask = VALID_CQ_BANK_MASK_DEFAULT;
}
void hns_roce_cleanup_cq_table(struct hns_roce_dev *hr_dev)
{
int i;
for (i = 0; i < HNS_ROCE_CQ_BANK_NUM; i++)
ida_destroy(&hr_dev->cq_table.bank[i].ida);
xa_destroy(&hr_dev->cq_table.array);
mutex_destroy(&hr_dev->cq_table.bank_mutex);
}
@@ -0,0 +1,180 @@
/* SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) */
/*
* Copyright (c) 2017 Hisilicon Limited.
* Copyright (c) 2007, 2008 Mellanox Technologies. All rights reserved.
*/
#include <rdma/ib_umem.h>
#include "hns_roce_device.h"
int hns_roce_db_map_user(struct hns_roce_ucontext *context, unsigned long virt,
struct hns_roce_db *db)
{
unsigned long page_addr = virt & PAGE_MASK;
struct hns_roce_user_db_page *page;
unsigned int offset;
int ret = 0;
mutex_lock(&context->page_mutex);
list_for_each_entry(page, &context->page_list, list)
if (page->user_virt == page_addr)
goto found;
page = kmalloc_obj(*page);
if (!page) {
ret = -ENOMEM;
goto out;
}
refcount_set(&page->refcount, 1);
page->user_virt = page_addr;
page->umem = ib_umem_get_va(context->ibucontext.device, page_addr,
PAGE_SIZE, 0);
if (IS_ERR(page->umem)) {
ret = PTR_ERR(page->umem);
kfree(page);
goto out;
}
list_add(&page->list, &context->page_list);
found:
offset = virt - page_addr;
db->dma = sg_dma_address(page->umem->sgt_append.sgt.sgl) + offset;
db->virt_addr = sg_virt(page->umem->sgt_append.sgt.sgl) + offset;
db->u.user_page = page;
refcount_inc(&page->refcount);
out:
mutex_unlock(&context->page_mutex);
return ret;
}
void hns_roce_db_unmap_user(struct hns_roce_ucontext *context,
struct hns_roce_db *db)
{
mutex_lock(&context->page_mutex);
refcount_dec(&db->u.user_page->refcount);
if (refcount_dec_if_one(&db->u.user_page->refcount)) {
list_del(&db->u.user_page->list);
ib_umem_release(db->u.user_page->umem);
kfree(db->u.user_page);
}
mutex_unlock(&context->page_mutex);
}
static struct hns_roce_db_pgdir *hns_roce_alloc_db_pgdir(
struct device *dma_device)
{
struct hns_roce_db_pgdir *pgdir;
pgdir = kzalloc_obj(*pgdir);
if (!pgdir)
return NULL;
bitmap_fill(pgdir->order1,
HNS_ROCE_DB_PER_PAGE / HNS_ROCE_DB_TYPE_COUNT);
pgdir->bits[0] = pgdir->order0;
pgdir->bits[1] = pgdir->order1;
pgdir->page = dma_alloc_coherent(dma_device, PAGE_SIZE,
&pgdir->db_dma, GFP_KERNEL);
if (!pgdir->page) {
kfree(pgdir);
return NULL;
}
return pgdir;
}
static int hns_roce_alloc_db_from_pgdir(struct hns_roce_db_pgdir *pgdir,
struct hns_roce_db *db, int order)
{
unsigned long o;
unsigned long i;
for (o = order; o <= 1; ++o) {
i = find_first_bit(pgdir->bits[o], HNS_ROCE_DB_PER_PAGE >> o);
if (i < HNS_ROCE_DB_PER_PAGE >> o)
goto found;
}
return -ENOMEM;
found:
clear_bit(i, pgdir->bits[o]);
i <<= o;
if (o > order)
set_bit(i ^ 1, pgdir->bits[order]);
db->u.pgdir = pgdir;
db->index = i;
db->db_record = pgdir->page + db->index;
db->dma = pgdir->db_dma + db->index * HNS_ROCE_DB_UNIT_SIZE;
db->order = order;
return 0;
}
int hns_roce_alloc_db(struct hns_roce_dev *hr_dev, struct hns_roce_db *db,
int order)
{
struct hns_roce_db_pgdir *pgdir;
int ret = 0;
mutex_lock(&hr_dev->pgdir_mutex);
list_for_each_entry(pgdir, &hr_dev->pgdir_list, list)
if (!hns_roce_alloc_db_from_pgdir(pgdir, db, order))
goto out;
pgdir = hns_roce_alloc_db_pgdir(hr_dev->dev);
if (!pgdir) {
ret = -ENOMEM;
goto out;
}
list_add(&pgdir->list, &hr_dev->pgdir_list);
/* This should never fail -- we just allocated an empty page: */
WARN_ON(hns_roce_alloc_db_from_pgdir(pgdir, db, order));
out:
mutex_unlock(&hr_dev->pgdir_mutex);
return ret;
}
void hns_roce_free_db(struct hns_roce_dev *hr_dev, struct hns_roce_db *db)
{
unsigned long o;
unsigned long i;
mutex_lock(&hr_dev->pgdir_mutex);
o = db->order;
i = db->index;
if (db->order == 0 && test_bit(i ^ 1, db->u.pgdir->order0)) {
clear_bit(i ^ 1, db->u.pgdir->order0);
++o;
}
i >>= o;
set_bit(i, db->u.pgdir->bits[o]);
if (bitmap_full(db->u.pgdir->order1,
HNS_ROCE_DB_PER_PAGE / HNS_ROCE_DB_TYPE_COUNT)) {
dma_free_coherent(hr_dev->dev, PAGE_SIZE, db->u.pgdir->page,
db->u.pgdir->db_dma);
list_del(&db->u.pgdir->list);
kfree(db->u.pgdir);
}
mutex_unlock(&hr_dev->pgdir_mutex);
}
@@ -0,0 +1,370 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* Copyright (c) 2023 Hisilicon Limited.
*/
#include <linux/cleanup.h>
#include <linux/debugfs.h>
#include <linux/device.h>
#include <linux/pci.h>
#include "hns_roce_device.h"
#include "hns_roce_hw_v2.h"
static struct dentry *hns_roce_dbgfs_root;
static int hns_debugfs_seqfile_open(struct inode *inode, struct file *f)
{
struct hns_debugfs_seqfile *seqfile = inode->i_private;
return single_open(f, seqfile->read, seqfile->data);
}
static ssize_t hns_debugfs_seqfile_write(struct file *file,
const char __user *buffer,
size_t count, loff_t *ppos)
{
struct hns_debugfs_seqfile *seqfile = file_inode(file)->i_private;
char buf[16] = {};
if (!seqfile->write)
return -EOPNOTSUPP;
if (count >= sizeof(buf))
return -EINVAL;
if (copy_from_user(buf, buffer, count))
return -EFAULT;
return seqfile->write(buf, count, seqfile->data);
}
static const struct file_operations hns_debugfs_seqfile_fops = {
.owner = THIS_MODULE,
.open = hns_debugfs_seqfile_open,
.release = single_release,
.read = seq_read,
.write = hns_debugfs_seqfile_write,
.llseek = seq_lseek
};
static const char * const sw_stat_info[] = {
[HNS_ROCE_DFX_AEQE_CNT] = "aeqe",
[HNS_ROCE_DFX_CEQE_CNT] = "ceqe",
[HNS_ROCE_DFX_CMDS_CNT] = "cmds",
[HNS_ROCE_DFX_CMDS_ERR_CNT] = "cmds_err",
[HNS_ROCE_DFX_MBX_POSTED_CNT] = "posted_mbx",
[HNS_ROCE_DFX_MBX_POLLED_CNT] = "polled_mbx",
[HNS_ROCE_DFX_MBX_EVENT_CNT] = "mbx_event",
[HNS_ROCE_DFX_QP_CREATE_ERR_CNT] = "qp_create_err",
[HNS_ROCE_DFX_QP_MODIFY_ERR_CNT] = "qp_modify_err",
[HNS_ROCE_DFX_CQ_CREATE_ERR_CNT] = "cq_create_err",
[HNS_ROCE_DFX_CQ_MODIFY_ERR_CNT] = "cq_modify_err",
[HNS_ROCE_DFX_SRQ_CREATE_ERR_CNT] = "srq_create_err",
[HNS_ROCE_DFX_SRQ_MODIFY_ERR_CNT] = "srq_modify_err",
[HNS_ROCE_DFX_XRCD_ALLOC_ERR_CNT] = "xrcd_alloc_err",
[HNS_ROCE_DFX_MR_REG_ERR_CNT] = "mr_reg_err",
[HNS_ROCE_DFX_MR_REREG_ERR_CNT] = "mr_rereg_err",
[HNS_ROCE_DFX_AH_CREATE_ERR_CNT] = "ah_create_err",
[HNS_ROCE_DFX_MMAP_ERR_CNT] = "mmap_err",
[HNS_ROCE_DFX_UCTX_ALLOC_ERR_CNT] = "uctx_alloc_err",
};
static int sw_stat_debugfs_show(struct seq_file *file, void *offset)
{
struct hns_roce_dev *hr_dev = file->private;
int i;
for (i = 0; i < HNS_ROCE_DFX_CNT_TOTAL; i++)
seq_printf(file, "%-20s --- %lld\n", sw_stat_info[i],
atomic64_read(&hr_dev->dfx_cnt[i]));
return 0;
}
static void create_sw_stat_debugfs(struct hns_roce_dev *hr_dev,
struct dentry *parent)
{
struct hns_sw_stat_debugfs *dbgfs = &hr_dev->dbgfs.sw_stat_root;
dbgfs->sw_stat.read = sw_stat_debugfs_show;
dbgfs->sw_stat.data = hr_dev;
dbgfs->root = debugfs_create_dir("sw_stat", parent);
debugfs_create_file("sw_stat", 0400, dbgfs->root, &dbgfs->sw_stat,
&hns_debugfs_seqfile_fops);
}
#define __HNS_SCC_ATTR(_name, _type, _offset, _size, _min, _max) { \
.name = _name, \
.algo_type = _type, \
.offset = _offset, \
.size = _size, \
.min = _min, \
.max = _max, \
}
#define HNS_DCQCN_CC_ATTR_RW(_name, NAME) \
__HNS_SCC_ATTR(_name, HNS_ROCE_SCC_ALGO_DCQCN, \
HNS_ROCE_DCQCN_##NAME##_OFS, \
HNS_ROCE_DCQCN_##NAME##_SZ, \
0, HNS_ROCE_DCQCN_##NAME##_MAX)
#define HNS_LDCP_CC_ATTR_RW(_name, NAME) \
__HNS_SCC_ATTR(_name, HNS_ROCE_SCC_ALGO_LDCP, \
HNS_ROCE_LDCP_##NAME##_OFS, \
HNS_ROCE_LDCP_##NAME##_SZ, \
0, HNS_ROCE_LDCP_##NAME##_MAX)
#define HNS_HC3_CC_ATTR_RW(_name, NAME) \
__HNS_SCC_ATTR(_name, HNS_ROCE_SCC_ALGO_HC3, \
HNS_ROCE_HC3_##NAME##_OFS, \
HNS_ROCE_HC3_##NAME##_SZ, \
HNS_ROCE_HC3_##NAME##_MIN, \
HNS_ROCE_HC3_##NAME##_MAX)
#define HNS_DIP_CC_ATTR_RW(_name, NAME) \
__HNS_SCC_ATTR(_name, HNS_ROCE_SCC_ALGO_DIP, \
HNS_ROCE_DIP_##NAME##_OFS, \
HNS_ROCE_DIP_##NAME##_SZ, \
0, HNS_ROCE_DIP_##NAME##_MAX)
static const struct hns_roce_cong_attr {
enum hns_roce_cong_type cong_type;
const char *name;
struct hns_roce_cc_param_attr params[HNS_ROCE_CC_PARAM_MAX_NUM];
} cong_attrs[] = {
{ CONG_TYPE_DCQCN, "dcqcn_cc_param",
{
HNS_DCQCN_CC_ATTR_RW("ai", AI),
HNS_DCQCN_CC_ATTR_RW("f", F),
HNS_DCQCN_CC_ATTR_RW("tkp", TKP),
HNS_DCQCN_CC_ATTR_RW("tmp", TMP),
HNS_DCQCN_CC_ATTR_RW("alp", ALP),
HNS_DCQCN_CC_ATTR_RW("max_speed", MAX_SPEED),
HNS_DCQCN_CC_ATTR_RW("g", G),
HNS_DCQCN_CC_ATTR_RW("al", AL),
HNS_DCQCN_CC_ATTR_RW("cnp_time", CNP_TIME),
HNS_DCQCN_CC_ATTR_RW("ashift", ASHIFT),
}
},
{ CONG_TYPE_LDCP, "ldcp_cc_param",
{
HNS_LDCP_CC_ATTR_RW("cwd0", CWD0),
HNS_LDCP_CC_ATTR_RW("alpha", ALPHA),
HNS_LDCP_CC_ATTR_RW("gamma", GAMMA),
HNS_LDCP_CC_ATTR_RW("beta", BETA),
HNS_LDCP_CC_ATTR_RW("eta", ETA),
}
},
{ CONG_TYPE_HC3, "hc3_cc_param",
{
HNS_HC3_CC_ATTR_RW("initial_window", INITIAL_WINDOW),
HNS_HC3_CC_ATTR_RW("bandwidth", BANDWIDTH),
HNS_HC3_CC_ATTR_RW("qlen_shift", QLEN_SHIFT),
HNS_HC3_CC_ATTR_RW("port_usage_shift", PORT_USAGE_SHIFT),
HNS_HC3_CC_ATTR_RW("over_period", OVER_PERIOD),
HNS_HC3_CC_ATTR_RW("max_stage", MAX_STAGE),
HNS_HC3_CC_ATTR_RW("gamma_shift", GAMMA_SHIFT),
}
},
{ CONG_TYPE_DIP, "dip_cc_param",
{
HNS_DIP_CC_ATTR_RW("ai", AI),
HNS_DIP_CC_ATTR_RW("f", F),
HNS_DIP_CC_ATTR_RW("tkp", TKP),
HNS_DIP_CC_ATTR_RW("tmp", TMP),
HNS_DIP_CC_ATTR_RW("alp", ALP),
HNS_DIP_CC_ATTR_RW("max_speed", MAX_SPEED),
HNS_DIP_CC_ATTR_RW("g", G),
HNS_DIP_CC_ATTR_RW("al", AL),
HNS_DIP_CC_ATTR_RW("cnp_time", CNP_TIME),
HNS_DIP_CC_ATTR_RW("ashift", ASHIFT),
}
}
};
static int cc_param_debugfs_show(struct seq_file *file, void *offset)
{
struct hns_cc_param_seqfile *param_seqfile = file->private;
const struct hns_roce_cc_param_attr *param_attr = param_seqfile->param_attr;
int algo_type = param_attr->algo_type;
int index = param_seqfile->index;
struct hns_roce_dev *hr_dev =
container_of(param_seqfile, struct hns_roce_dev,
dbgfs.cc_param_root[algo_type].params[index]);
struct hns_roce_scc_param *scc_param;
__le32 val = 0;
scc_param = &hr_dev->scc_param[algo_type];
scoped_guard(mutex, &scc_param->scc_mutex) {
memcpy(&val,
(void *)scc_param->param + param_attr->offset,
param_attr->size);
}
seq_printf(file, "%u\n", le32_to_cpu(val));
return 0;
}
static ssize_t cc_param_debugfs_store(char *buf, size_t count, void *data)
{
struct hns_cc_param_seqfile *param_seqfile = data;
const struct hns_roce_cc_param_attr *param_attr = param_seqfile->param_attr;
int algo_type = param_attr->algo_type;
int index = param_seqfile->index;
struct hns_roce_dev *hr_dev =
container_of(param_seqfile, struct hns_roce_dev,
dbgfs.cc_param_root[algo_type].params[index]);
struct hns_roce_scc_param *scc_param;
__le32 attr_val, old_val = 0;
u32 val;
int ret;
if (kstrtou32(buf, 0, &val))
return -EINVAL;
if (val > param_attr->max || val < param_attr->min)
return -EINVAL;
attr_val = cpu_to_le32(val);
scc_param = &hr_dev->scc_param[algo_type];
guard(mutex)(&scc_param->scc_mutex);
memcpy(&old_val, (void *)scc_param->param + param_attr->offset,
param_attr->size);
memcpy((void *)scc_param->param + param_attr->offset, &attr_val,
param_attr->size);
ret = hr_dev->hw->config_scc_param(hr_dev, algo_type);
if (ret) {
memcpy((void *)scc_param->param + param_attr->offset, &old_val,
param_attr->size);
return ret;
}
return count;
}
static void get_default_scc_param(struct hns_roce_dev *hr_dev)
{
int ret;
int i;
for (i = 0; i < HNS_ROCE_SCC_ALGO_TOTAL; i++) {
ret = hr_dev->hw->query_scc_param(hr_dev, i);
if (ret && ret != -EOPNOTSUPP)
ibdev_warn_ratelimited(&hr_dev->ib_dev,
"failed to get default parameters of scc algo %d, ret = %d.\n",
i, ret);
}
}
static int hns_roce_alloc_scc_param(struct hns_roce_dev *hr_dev)
{
struct hns_roce_scc_param *scc_param;
int i;
scc_param = kvcalloc(HNS_ROCE_SCC_ALGO_TOTAL, sizeof(*scc_param),
GFP_KERNEL);
if (!scc_param)
return -ENOMEM;
for (i = 0; i < HNS_ROCE_SCC_ALGO_TOTAL; i++) {
scc_param[i].algo_type = i;
scc_param[i].hr_dev = hr_dev;
mutex_init(&scc_param[i].scc_mutex);
}
hr_dev->scc_param = scc_param;
get_default_scc_param(hr_dev);
return 0;
}
static void hns_roce_dealloc_scc_param(struct hns_roce_dev *hr_dev)
{
int i;
if (!hr_dev->scc_param)
return;
for (i = 0; i < HNS_ROCE_SCC_ALGO_TOTAL; i++)
mutex_destroy(&hr_dev->scc_param[i].scc_mutex);
kvfree(hr_dev->scc_param);
}
static void create_cc_param_debugfs(struct hns_roce_dev *hr_dev,
struct dentry *parent)
{
const struct hns_roce_cong_attr *cong_attr;
struct hns_cc_param_debugfs *dbgfs;
int i, j;
int ret;
if (hr_dev->pci_dev->revision <= PCI_REVISION_ID_HIP08 ||
!(hr_dev->caps.flags & HNS_ROCE_CAP_FLAG_QP_FLOW_CTRL) ||
hr_dev->is_vf)
return;
ret = hns_roce_alloc_scc_param(hr_dev);
if (ret)
return;
for (i = 0; i < CONG_TYPE_MAX_NUM; i++) {
cong_attr = &cong_attrs[i];
if (!test_bit(cong_attr->cong_type,
(unsigned long *)&hr_dev->caps.cong_cap))
continue;
dbgfs = &hr_dev->dbgfs.cc_param_root[i];
dbgfs->root = debugfs_create_dir(cong_attr->name, parent);
for (j = 0; j < HNS_ROCE_CC_PARAM_MAX_NUM; j++) {
if (!cong_attr->params[j].name)
break;
dbgfs->params[j].param_attr = &cong_attr->params[j];
dbgfs->params[j].index = j;
dbgfs->params[j].seqfile.read = cc_param_debugfs_show;
dbgfs->params[j].seqfile.write = cc_param_debugfs_store;
dbgfs->params[j].seqfile.data = &dbgfs->params[j];
debugfs_create_file(cong_attr->params[j].name, 0600,
dbgfs->root,
&dbgfs->params[j].seqfile,
&hns_debugfs_seqfile_fops);
}
}
}
/* debugfs for device */
void hns_roce_register_debugfs(struct hns_roce_dev *hr_dev)
{
struct hns_roce_dev_debugfs *dbgfs = &hr_dev->dbgfs;
dbgfs->root = debugfs_create_dir(pci_name(hr_dev->pci_dev),
hns_roce_dbgfs_root);
create_sw_stat_debugfs(hr_dev, dbgfs->root);
create_cc_param_debugfs(hr_dev, dbgfs->root);
}
void hns_roce_unregister_debugfs(struct hns_roce_dev *hr_dev)
{
debugfs_remove_recursive(hr_dev->dbgfs.root);
hns_roce_dealloc_scc_param(hr_dev);
}
/* debugfs for hns module */
void hns_roce_init_debugfs(void)
{
hns_roce_dbgfs_root = debugfs_create_dir("hns_roce", NULL);
}
void hns_roce_cleanup_debugfs(void)
{
debugfs_remove_recursive(hns_roce_dbgfs_root);
hns_roce_dbgfs_root = NULL;
}
@@ -0,0 +1,59 @@
/* SPDX-License-Identifier: GPL-2.0+ */
/*
* Copyright (c) 2023 Hisilicon Limited.
*/
#ifndef __HNS_ROCE_DEBUGFS_H
#define __HNS_ROCE_DEBUGFS_H
/* debugfs seqfile */
struct hns_debugfs_seqfile {
int (*read)(struct seq_file *seq, void *data);
ssize_t (*write)(char *buf, size_t count, void *data);
void *data;
};
struct hns_sw_stat_debugfs {
struct dentry *root;
struct hns_debugfs_seqfile sw_stat;
};
struct hns_roce_cc_param_attr {
const char *name;
int algo_type;
u32 offset;
u32 size;
u32 max;
u32 min;
};
struct hns_cc_param_seqfile {
struct hns_debugfs_seqfile seqfile;
const struct hns_roce_cc_param_attr *param_attr;
int index;
};
#define HNS_ROCE_CC_PARAM_MAX_NUM 11
struct hns_cc_param_debugfs {
struct dentry *root;
struct hns_cc_param_seqfile params[HNS_ROCE_CC_PARAM_MAX_NUM];
};
#define CONG_TYPE_MAX_NUM 4
/* Debugfs for device */
struct hns_roce_dev_debugfs {
struct dentry *root;
struct hns_sw_stat_debugfs sw_stat_root;
struct hns_cc_param_debugfs cc_param_root[CONG_TYPE_MAX_NUM];
};
struct hns_roce_dev;
void hns_roce_init_debugfs(void);
void hns_roce_cleanup_debugfs(void);
void hns_roce_register_debugfs(struct hns_roce_dev *hr_dev);
void hns_roce_unregister_debugfs(struct hns_roce_dev *hr_dev);
#endif
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,120 @@
/*
* Copyright (c) 2016 Hisilicon Limited.
* Copyright (c) 2007, 2008 Mellanox Technologies. All rights reserved.
*
* This software is available to you under a choice of one of two
* licenses. You may choose to be licensed under the terms of the GNU
* General Public License (GPL) Version 2, available from the file
* COPYING in the main directory of this source tree, or the
* OpenIB.org BSD license below:
*
* Redistribution and use in source and binary forms, with or
* without modification, are permitted provided that the following
* conditions are met:
*
* - Redistributions of source code must retain the above
* copyright notice, this list of conditions and the following
* disclaimer.
*
* - Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following
* disclaimer in the documentation and/or other materials
* provided with the distribution.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#ifndef _HNS_ROCE_HEM_H
#define _HNS_ROCE_HEM_H
#define HEM_HOP_STEP_DIRECT 0xff
enum {
/* MAP HEM(Hardware Entry Memory) */
HEM_TYPE_QPC = 0,
HEM_TYPE_MTPT,
HEM_TYPE_CQC,
HEM_TYPE_SRQC,
HEM_TYPE_SCCC,
HEM_TYPE_QPC_TIMER,
HEM_TYPE_CQC_TIMER,
HEM_TYPE_GMV,
/* UNMAP HEM */
HEM_TYPE_MTT,
HEM_TYPE_CQE,
HEM_TYPE_SRQWQE,
HEM_TYPE_IDX,
HEM_TYPE_IRRL,
HEM_TYPE_TRRL,
};
#define check_whether_bt_num_3(type, hop_num) \
((type) < HEM_TYPE_MTT && (hop_num) == 2)
#define check_whether_bt_num_2(type, hop_num) \
(((type) < HEM_TYPE_MTT && (hop_num) == 1) || \
((type) >= HEM_TYPE_MTT && (hop_num) == 2))
#define check_whether_bt_num_1(type, hop_num) \
(((type) < HEM_TYPE_MTT && (hop_num) == HNS_ROCE_HOP_NUM_0) || \
((type) >= HEM_TYPE_MTT && (hop_num) == 1) || \
((type) >= HEM_TYPE_MTT && (hop_num) == HNS_ROCE_HOP_NUM_0))
struct hns_roce_hem {
void *buf;
dma_addr_t dma;
unsigned long size;
refcount_t refcount;
};
struct hns_roce_hem_mhop {
u32 hop_num;
u32 buf_chunk_size;
u32 bt_chunk_size;
u32 ba_l0_num;
u32 l0_idx; /* level 0 base address table index */
u32 l1_idx; /* level 1 base address table index */
u32 l2_idx; /* level 2 base address table index */
};
void hns_roce_free_hem(struct hns_roce_dev *hr_dev, struct hns_roce_hem *hem);
int hns_roce_table_get(struct hns_roce_dev *hr_dev,
struct hns_roce_hem_table *table, unsigned long obj);
void hns_roce_table_put(struct hns_roce_dev *hr_dev,
struct hns_roce_hem_table *table, unsigned long obj);
void *hns_roce_table_find(struct hns_roce_dev *hr_dev,
struct hns_roce_hem_table *table, unsigned long obj,
dma_addr_t *dma_handle);
int hns_roce_init_hem_table(struct hns_roce_dev *hr_dev,
struct hns_roce_hem_table *table, u32 type,
unsigned long obj_size, unsigned long nobj);
void hns_roce_cleanup_hem_table(struct hns_roce_dev *hr_dev,
struct hns_roce_hem_table *table);
void hns_roce_cleanup_hem(struct hns_roce_dev *hr_dev);
int hns_roce_calc_hem_mhop(struct hns_roce_dev *hr_dev,
struct hns_roce_hem_table *table, unsigned long *obj,
struct hns_roce_hem_mhop *mhop);
bool hns_roce_check_whether_mhop(struct hns_roce_dev *hr_dev, u32 type);
void hns_roce_hem_list_init(struct hns_roce_hem_list *hem_list);
int hns_roce_hem_list_calc_root_ba(const struct hns_roce_buf_region *regions,
int region_cnt, int unit);
int hns_roce_hem_list_request(struct hns_roce_dev *hr_dev,
struct hns_roce_hem_list *hem_list,
const struct hns_roce_buf_region *regions,
int region_cnt, unsigned int bt_pg_shift);
void hns_roce_hem_list_release(struct hns_roce_dev *hr_dev,
struct hns_roce_hem_list *hem_list);
void *hns_roce_hem_list_find_mtt(struct hns_roce_dev *hr_dev,
struct hns_roce_hem_list *hem_list,
int offset, int *mtt_cnt);
#endif /* _HNS_ROCE_HEM_H */
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,171 @@
/*
* Copyright (c) 2016 Hisilicon Limited.
*
* This software is available to you under a choice of one of two
* licenses. You may choose to be licensed under the terms of the GNU
* General Public License (GPL) Version 2, available from the file
* COPYING in the main directory of this source tree, or the
* OpenIB.org BSD license below:
*
* Redistribution and use in source and binary forms, with or
* without modification, are permitted provided that the following
* conditions are met:
*
* - Redistributions of source code must retain the above
* copyright notice, this list of conditions and the following
* disclaimer.
*
* - Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following
* disclaimer in the documentation and/or other materials
* provided with the distribution.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#include <rdma/uverbs_ioctl.h>
#include "hns_roce_device.h"
void hns_roce_init_pd_table(struct hns_roce_dev *hr_dev)
{
struct hns_roce_ida *pd_ida = &hr_dev->pd_ida;
ida_init(&pd_ida->ida);
pd_ida->max = hr_dev->caps.num_pds - 1;
pd_ida->min = hr_dev->caps.reserved_pds;
}
int hns_roce_alloc_pd(struct ib_pd *ibpd, struct ib_udata *udata)
{
struct ib_device *ib_dev = ibpd->device;
struct hns_roce_dev *hr_dev = to_hr_dev(ib_dev);
struct hns_roce_ida *pd_ida = &hr_dev->pd_ida;
struct hns_roce_pd *pd = to_hr_pd(ibpd);
int ret = 0;
int id;
id = ida_alloc_range(&pd_ida->ida, pd_ida->min, pd_ida->max,
GFP_KERNEL);
if (id < 0) {
ibdev_err(ib_dev, "failed to alloc pd, id = %d.\n", id);
return -ENOMEM;
}
pd->pdn = (unsigned long)id;
if (udata) {
struct hns_roce_ib_alloc_pd_resp resp = {.pdn = pd->pdn};
ret = ib_respond_udata(udata, resp);
if (ret)
ida_free(&pd_ida->ida, id);
}
return ret;
}
int hns_roce_dealloc_pd(struct ib_pd *pd, struct ib_udata *udata)
{
struct hns_roce_dev *hr_dev = to_hr_dev(pd->device);
ida_free(&hr_dev->pd_ida.ida, (int)to_hr_pd(pd)->pdn);
return 0;
}
int hns_roce_uar_alloc(struct hns_roce_dev *hr_dev, struct hns_roce_uar *uar)
{
struct hns_roce_ida *uar_ida = &hr_dev->uar_ida;
int id;
/* Using bitmap to manager UAR index */
id = ida_alloc_range(&uar_ida->ida, uar_ida->min, uar_ida->max,
GFP_KERNEL);
if (id < 0) {
ibdev_err(&hr_dev->ib_dev, "failed to alloc uar id(%d).\n", id);
return -ENOMEM;
}
uar->logic_idx = (unsigned long)id;
if (uar->logic_idx > 0 && hr_dev->caps.phy_num_uars > 1)
uar->index = (uar->logic_idx - 1) %
(hr_dev->caps.phy_num_uars - 1) + 1;
else
uar->index = 0;
uar->pfn = ((pci_resource_start(hr_dev->pci_dev, 2)) >> PAGE_SHIFT);
if (hr_dev->caps.flags & HNS_ROCE_CAP_FLAG_DIRECT_WQE)
hr_dev->dwqe_page = pci_resource_start(hr_dev->pci_dev, 4);
return 0;
}
void hns_roce_init_uar_table(struct hns_roce_dev *hr_dev)
{
struct hns_roce_ida *uar_ida = &hr_dev->uar_ida;
ida_init(&uar_ida->ida);
uar_ida->max = hr_dev->caps.num_uars - 1;
uar_ida->min = hr_dev->caps.reserved_uars;
}
static int hns_roce_xrcd_alloc(struct hns_roce_dev *hr_dev, u32 *xrcdn)
{
struct hns_roce_ida *xrcd_ida = &hr_dev->xrcd_ida;
int id;
id = ida_alloc_range(&xrcd_ida->ida, xrcd_ida->min, xrcd_ida->max,
GFP_KERNEL);
if (id < 0) {
ibdev_err(&hr_dev->ib_dev, "failed to alloc xrcdn(%d).\n", id);
return -ENOMEM;
}
*xrcdn = (u32)id;
return 0;
}
void hns_roce_init_xrcd_table(struct hns_roce_dev *hr_dev)
{
struct hns_roce_ida *xrcd_ida = &hr_dev->xrcd_ida;
ida_init(&xrcd_ida->ida);
xrcd_ida->max = hr_dev->caps.num_xrcds - 1;
xrcd_ida->min = hr_dev->caps.reserved_xrcds;
}
int hns_roce_alloc_xrcd(struct ib_xrcd *ib_xrcd, struct ib_udata *udata)
{
struct hns_roce_dev *hr_dev = to_hr_dev(ib_xrcd->device);
struct hns_roce_xrcd *xrcd = to_hr_xrcd(ib_xrcd);
int ret;
if (!(hr_dev->caps.flags & HNS_ROCE_CAP_FLAG_XRC)) {
ret = -EOPNOTSUPP;
goto err_out;
}
ret = hns_roce_xrcd_alloc(hr_dev, &xrcd->xrcdn);
err_out:
if (ret)
atomic64_inc(&hr_dev->dfx_cnt[HNS_ROCE_DFX_XRCD_ALLOC_ERR_CNT]);
return ret;
}
int hns_roce_dealloc_xrcd(struct ib_xrcd *ib_xrcd, struct ib_udata *udata)
{
struct hns_roce_dev *hr_dev = to_hr_dev(ib_xrcd->device);
u32 xrcdn = to_hr_xrcd(ib_xrcd)->xrcdn;
ida_free(&hr_dev->xrcd_ida.ida, (int)xrcdn);
return 0;
}
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,234 @@
// SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
// Copyright (c) 2019 Hisilicon Limited.
#include <rdma/rdma_cm.h>
#include <rdma/restrack.h>
#include <uapi/rdma/rdma_netlink.h>
#include "hns_roce_common.h"
#include "hns_roce_device.h"
#include "hns_roce_hw_v2.h"
int hns_roce_fill_res_cq_entry(struct sk_buff *msg, struct ib_cq *ib_cq)
{
struct hns_roce_cq *hr_cq = to_hr_cq(ib_cq);
struct nlattr *table_attr;
table_attr = nla_nest_start(msg, RDMA_NLDEV_ATTR_DRIVER);
if (!table_attr)
return -EMSGSIZE;
if (rdma_nl_put_driver_u32(msg, "cq_depth", hr_cq->cq_depth))
goto err;
if (rdma_nl_put_driver_u32(msg, "cons_index", hr_cq->cons_index))
goto err;
if (rdma_nl_put_driver_u32(msg, "cqe_size", hr_cq->cqe_size))
goto err;
if (rdma_nl_put_driver_u32(msg, "arm_sn", hr_cq->arm_sn))
goto err;
nla_nest_end(msg, table_attr);
return 0;
err:
nla_nest_cancel(msg, table_attr);
return -EMSGSIZE;
}
int hns_roce_fill_res_cq_entry_raw(struct sk_buff *msg, struct ib_cq *ib_cq)
{
struct hns_roce_dev *hr_dev = to_hr_dev(ib_cq->device);
struct hns_roce_cq *hr_cq = to_hr_cq(ib_cq);
struct hns_roce_v2_cq_context context;
int ret;
if (!hr_dev->hw->query_cqc)
return -EINVAL;
ret = hr_dev->hw->query_cqc(hr_dev, hr_cq->cqn, &context);
if (ret)
return ret;
ret = nla_put(msg, RDMA_NLDEV_ATTR_RES_RAW, sizeof(context), &context);
return ret;
}
int hns_roce_fill_res_qp_entry(struct sk_buff *msg, struct ib_qp *ib_qp)
{
struct hns_roce_qp *hr_qp = to_hr_qp(ib_qp);
struct nlattr *table_attr;
table_attr = nla_nest_start(msg, RDMA_NLDEV_ATTR_DRIVER);
if (!table_attr)
return -EMSGSIZE;
if (rdma_nl_put_driver_u32_hex(msg, "sq_wqe_cnt", hr_qp->sq.wqe_cnt))
goto err;
if (rdma_nl_put_driver_u32_hex(msg, "sq_max_gs", hr_qp->sq.max_gs))
goto err;
if (rdma_nl_put_driver_u32_hex(msg, "rq_wqe_cnt", hr_qp->rq.wqe_cnt))
goto err;
if (rdma_nl_put_driver_u32_hex(msg, "rq_max_gs", hr_qp->rq.max_gs))
goto err;
if (rdma_nl_put_driver_u32_hex(msg, "ext_sge_sge_cnt", hr_qp->sge.sge_cnt))
goto err;
nla_nest_end(msg, table_attr);
return 0;
err:
nla_nest_cancel(msg, table_attr);
return -EMSGSIZE;
}
int hns_roce_fill_res_qp_entry_raw(struct sk_buff *msg, struct ib_qp *ib_qp)
{
struct hns_roce_dev *hr_dev = to_hr_dev(ib_qp->device);
struct hns_roce_qp *hr_qp = to_hr_qp(ib_qp);
struct hns_roce_full_qp_ctx {
struct hns_roce_v2_qp_context qpc;
struct hns_roce_v2_scc_context sccc;
} context = {};
u32 sccn = hr_qp->qpn;
int ret;
if (!hr_dev->hw->query_qpc)
return -EINVAL;
ret = hr_dev->hw->query_qpc(hr_dev, hr_qp->qpn, &context.qpc);
if (ret)
return ret;
/* If SCC is disabled or the query fails, the queried SCCC will
* be all 0.
*/
if (!(hr_dev->caps.flags & HNS_ROCE_CAP_FLAG_QP_FLOW_CTRL) ||
!hr_dev->hw->query_sccc)
goto out;
if (hr_qp->cong_type == CONG_TYPE_DIP) {
if (!hr_qp->dip)
goto out;
sccn = hr_qp->dip->dip_idx;
}
ret = hr_dev->hw->query_sccc(hr_dev, sccn, &context.sccc);
if (ret)
ibdev_warn_ratelimited(&hr_dev->ib_dev,
"failed to query SCCC, ret = %d.\n",
ret);
out:
ret = nla_put(msg, RDMA_NLDEV_ATTR_RES_RAW, sizeof(context), &context);
return ret;
}
int hns_roce_fill_res_mr_entry(struct sk_buff *msg, struct ib_mr *ib_mr)
{
struct hns_roce_mr *hr_mr = to_hr_mr(ib_mr);
struct nlattr *table_attr;
table_attr = nla_nest_start(msg, RDMA_NLDEV_ATTR_DRIVER);
if (!table_attr)
return -EMSGSIZE;
if (rdma_nl_put_driver_u32_hex(msg, "pbl_hop_num", hr_mr->pbl_hop_num))
goto err;
if (rdma_nl_put_driver_u32_hex(msg, "ba_pg_shift",
hr_mr->pbl_mtr.hem_cfg.ba_pg_shift))
goto err;
if (rdma_nl_put_driver_u32_hex(msg, "buf_pg_shift",
hr_mr->pbl_mtr.hem_cfg.buf_pg_shift))
goto err;
nla_nest_end(msg, table_attr);
return 0;
err:
nla_nest_cancel(msg, table_attr);
return -EMSGSIZE;
}
int hns_roce_fill_res_mr_entry_raw(struct sk_buff *msg, struct ib_mr *ib_mr)
{
struct hns_roce_dev *hr_dev = to_hr_dev(ib_mr->device);
struct hns_roce_mr *hr_mr = to_hr_mr(ib_mr);
struct hns_roce_v2_mpt_entry context;
int ret;
if (!hr_dev->hw->query_mpt)
return -EINVAL;
ret = hr_dev->hw->query_mpt(hr_dev, hr_mr->key, &context);
if (ret)
return ret;
ret = nla_put(msg, RDMA_NLDEV_ATTR_RES_RAW, sizeof(context), &context);
return ret;
}
int hns_roce_fill_res_srq_entry(struct sk_buff *msg, struct ib_srq *ib_srq)
{
struct hns_roce_srq *hr_srq = to_hr_srq(ib_srq);
struct nlattr *table_attr;
table_attr = nla_nest_start(msg, RDMA_NLDEV_ATTR_DRIVER);
if (!table_attr)
return -EMSGSIZE;
if (rdma_nl_put_driver_u32_hex(msg, "srqn", hr_srq->srqn))
goto err;
if (rdma_nl_put_driver_u32_hex(msg, "wqe_cnt", hr_srq->wqe_cnt))
goto err;
if (rdma_nl_put_driver_u32_hex(msg, "max_gs", hr_srq->max_gs))
goto err;
if (rdma_nl_put_driver_u32_hex(msg, "xrcdn", hr_srq->xrcdn))
goto err;
nla_nest_end(msg, table_attr);
return 0;
err:
nla_nest_cancel(msg, table_attr);
return -EMSGSIZE;
}
int hns_roce_fill_res_srq_entry_raw(struct sk_buff *msg, struct ib_srq *ib_srq)
{
struct hns_roce_dev *hr_dev = to_hr_dev(ib_srq->device);
struct hns_roce_srq *hr_srq = to_hr_srq(ib_srq);
struct hns_roce_srq_context context;
int ret;
if (!hr_dev->hw->query_srqc)
return -EINVAL;
ret = hr_dev->hw->query_srqc(hr_dev, hr_srq->srqn, &context);
if (ret)
return ret;
ret = nla_put(msg, RDMA_NLDEV_ATTR_RES_RAW, sizeof(context), &context);
return ret;
}
@@ -0,0 +1,525 @@
// SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB
/*
* Copyright (c) 2018 Hisilicon Limited.
*/
#include <rdma/ib_umem.h>
#include <rdma/uverbs_ioctl.h>
#include "hns_roce_device.h"
#include "hns_roce_cmd.h"
#include "hns_roce_hem.h"
void hns_roce_srq_event(struct hns_roce_dev *hr_dev, u32 srqn, int event_type)
{
struct hns_roce_srq_table *srq_table = &hr_dev->srq_table;
struct hns_roce_srq *srq;
xa_lock(&srq_table->xa);
srq = xa_load(&srq_table->xa, srqn & (hr_dev->caps.num_srqs - 1));
if (srq && !refcount_inc_not_zero(&srq->refcount))
srq = NULL;
xa_unlock(&srq_table->xa);
if (!srq) {
dev_warn(hr_dev->dev, "Async event for bogus SRQ %08x\n", srqn);
return;
}
srq->event(srq, event_type);
if (refcount_dec_and_test(&srq->refcount))
complete(&srq->free);
}
static void hns_roce_ib_srq_event(struct hns_roce_srq *srq,
enum hns_roce_event event_type)
{
struct hns_roce_dev *hr_dev = to_hr_dev(srq->ibsrq.device);
struct ib_srq *ibsrq = &srq->ibsrq;
struct ib_event event;
if (ibsrq->event_handler) {
event.device = ibsrq->device;
event.element.srq = ibsrq;
switch (event_type) {
case HNS_ROCE_EVENT_TYPE_SRQ_LIMIT_REACH:
event.event = IB_EVENT_SRQ_LIMIT_REACHED;
break;
case HNS_ROCE_EVENT_TYPE_SRQ_CATAS_ERROR:
event.event = IB_EVENT_SRQ_ERR;
break;
default:
dev_err(hr_dev->dev,
"hns_roce:Unexpected event type %d on SRQ %06lx\n",
event_type, srq->srqn);
return;
}
ibsrq->event_handler(&event, ibsrq->srq_context);
}
}
static int alloc_srqn(struct hns_roce_dev *hr_dev, struct hns_roce_srq *srq)
{
struct hns_roce_ida *srq_ida = &hr_dev->srq_table.srq_ida;
int id;
id = ida_alloc_range(&srq_ida->ida, srq_ida->min, srq_ida->max,
GFP_KERNEL);
if (id < 0) {
ibdev_err(&hr_dev->ib_dev, "failed to alloc srq(%d).\n", id);
return -ENOMEM;
}
srq->srqn = id;
return 0;
}
static void free_srqn(struct hns_roce_dev *hr_dev, struct hns_roce_srq *srq)
{
ida_free(&hr_dev->srq_table.srq_ida.ida, (int)srq->srqn);
}
static int hns_roce_create_srqc(struct hns_roce_dev *hr_dev,
struct hns_roce_srq *srq)
{
struct ib_device *ibdev = &hr_dev->ib_dev;
struct hns_roce_cmd_mailbox *mailbox;
int ret;
mailbox = hns_roce_alloc_cmd_mailbox(hr_dev);
if (IS_ERR(mailbox)) {
ibdev_err(ibdev, "failed to alloc mailbox for SRQC.\n");
return PTR_ERR(mailbox);
}
ret = hr_dev->hw->write_srqc(srq, mailbox->buf);
if (ret) {
ibdev_err(ibdev, "failed to write SRQC.\n");
goto err_mbox;
}
ret = hns_roce_create_hw_ctx(hr_dev, mailbox, HNS_ROCE_CMD_CREATE_SRQ,
srq->srqn);
if (ret)
ibdev_err_ratelimited(ibdev, "failed to config SRQC, ret = %d.\n", ret);
err_mbox:
hns_roce_free_cmd_mailbox(hr_dev, mailbox);
return ret;
}
static int alloc_srqc(struct hns_roce_dev *hr_dev, struct hns_roce_srq *srq)
{
struct hns_roce_srq_table *srq_table = &hr_dev->srq_table;
struct ib_device *ibdev = &hr_dev->ib_dev;
int ret;
ret = hns_roce_table_get(hr_dev, &srq_table->table, srq->srqn);
if (ret) {
ibdev_err(ibdev, "failed to get SRQC table, ret = %d.\n", ret);
return ret;
}
ret = xa_err(xa_store_irq(&srq_table->xa, srq->srqn, srq, GFP_KERNEL));
if (ret) {
ibdev_err(ibdev, "failed to store SRQC, ret = %d.\n", ret);
goto err_put;
}
ret = hns_roce_create_srqc(hr_dev, srq);
if (ret)
goto err_xa;
return 0;
err_xa:
xa_erase_irq(&srq_table->xa, srq->srqn);
err_put:
hns_roce_table_put(hr_dev, &srq_table->table, srq->srqn);
return ret;
}
static void free_srqc(struct hns_roce_dev *hr_dev, struct hns_roce_srq *srq)
{
struct hns_roce_srq_table *srq_table = &hr_dev->srq_table;
int ret;
ret = hns_roce_destroy_hw_ctx(hr_dev, HNS_ROCE_CMD_DESTROY_SRQ,
srq->srqn);
if (ret)
dev_err_ratelimited(hr_dev->dev, "DESTROY_SRQ failed (%d) for SRQN %06lx\n",
ret, srq->srqn);
xa_erase_irq(&srq_table->xa, srq->srqn);
if (refcount_dec_and_test(&srq->refcount))
complete(&srq->free);
wait_for_completion(&srq->free);
hns_roce_table_put(hr_dev, &srq_table->table, srq->srqn);
}
static int alloc_srq_idx(struct hns_roce_dev *hr_dev, struct hns_roce_srq *srq,
struct ib_udata *udata, unsigned long addr)
{
struct hns_roce_idx_que *idx_que = &srq->idx_que;
struct ib_device *ibdev = &hr_dev->ib_dev;
struct hns_roce_buf_attr buf_attr = {};
int ret;
srq->idx_que.entry_shift = ilog2(HNS_ROCE_IDX_QUE_ENTRY_SZ);
buf_attr.page_shift = hr_dev->caps.idx_buf_pg_sz + PAGE_SHIFT;
buf_attr.region[0].size = to_hr_hem_entries_size(srq->wqe_cnt,
srq->idx_que.entry_shift);
buf_attr.region[0].hopnum = hr_dev->caps.idx_hop_num;
buf_attr.region_count = 1;
ret = hns_roce_mtr_create(hr_dev, &idx_que->mtr, &buf_attr,
hr_dev->caps.idx_ba_pg_sz + PAGE_SHIFT,
udata, addr);
if (ret) {
ibdev_err(ibdev,
"failed to alloc SRQ idx mtr, ret = %d.\n", ret);
return ret;
}
if (!udata) {
idx_que->bitmap = bitmap_zalloc(srq->wqe_cnt, GFP_KERNEL);
if (!idx_que->bitmap) {
ibdev_err(ibdev, "failed to alloc SRQ idx bitmap.\n");
ret = -ENOMEM;
goto err_idx_mtr;
}
}
idx_que->head = 0;
idx_que->tail = 0;
return 0;
err_idx_mtr:
hns_roce_mtr_destroy(hr_dev, &idx_que->mtr);
return ret;
}
static void free_srq_idx(struct hns_roce_dev *hr_dev, struct hns_roce_srq *srq)
{
struct hns_roce_idx_que *idx_que = &srq->idx_que;
bitmap_free(idx_que->bitmap);
idx_que->bitmap = NULL;
hns_roce_mtr_destroy(hr_dev, &idx_que->mtr);
}
static int alloc_srq_wqe_buf(struct hns_roce_dev *hr_dev,
struct hns_roce_srq *srq,
struct ib_udata *udata, unsigned long addr)
{
struct ib_device *ibdev = &hr_dev->ib_dev;
struct hns_roce_buf_attr buf_attr = {};
int ret;
srq->wqe_shift = ilog2(roundup_pow_of_two(max(HNS_ROCE_SGE_SIZE,
HNS_ROCE_SGE_SIZE *
srq->max_gs)));
buf_attr.page_shift = hr_dev->caps.srqwqe_buf_pg_sz + PAGE_SHIFT;
buf_attr.region[0].size = to_hr_hem_entries_size(srq->wqe_cnt,
srq->wqe_shift);
buf_attr.region[0].hopnum = hr_dev->caps.srqwqe_hop_num;
buf_attr.region_count = 1;
ret = hns_roce_mtr_create(hr_dev, &srq->buf_mtr, &buf_attr,
hr_dev->caps.srqwqe_ba_pg_sz + PAGE_SHIFT,
udata, addr);
if (ret)
ibdev_err(ibdev,
"failed to alloc SRQ buf mtr, ret = %d.\n", ret);
return ret;
}
static void free_srq_wqe_buf(struct hns_roce_dev *hr_dev,
struct hns_roce_srq *srq)
{
hns_roce_mtr_destroy(hr_dev, &srq->buf_mtr);
}
static int alloc_srq_wrid(struct hns_roce_srq *srq)
{
srq->wrid = kvmalloc_array(srq->wqe_cnt, sizeof(u64), GFP_KERNEL);
if (!srq->wrid)
return -ENOMEM;
return 0;
}
static void free_srq_wrid(struct hns_roce_srq *srq)
{
kvfree(srq->wrid);
srq->wrid = NULL;
}
static u32 proc_srq_sge(struct hns_roce_dev *dev, struct hns_roce_srq *hr_srq,
bool user)
{
u32 max_sge = dev->caps.max_srq_sges;
if (dev->pci_dev->revision >= PCI_REVISION_ID_HIP09)
return max_sge;
/* Reserve SGEs only for HIP08 in kernel; The userspace driver will
* calculate number of max_sge with reserved SGEs when allocating wqe
* buf, so there is no need to do this again in kernel. But the number
* may exceed the capacity of SGEs recorded in the firmware, so the
* kernel driver should just adapt the value accordingly.
*/
if (user)
max_sge = roundup_pow_of_two(max_sge + 1);
else
hr_srq->rsv_sge = 1;
return max_sge;
}
static int set_srq_basic_param(struct hns_roce_srq *srq,
struct ib_srq_init_attr *init_attr,
struct ib_udata *udata)
{
struct hns_roce_dev *hr_dev = to_hr_dev(srq->ibsrq.device);
struct ib_srq_attr *attr = &init_attr->attr;
u32 max_sge;
max_sge = proc_srq_sge(hr_dev, srq, !!udata);
if (attr->max_wr > hr_dev->caps.max_srq_wrs ||
attr->max_sge > max_sge || !attr->max_sge) {
ibdev_err(&hr_dev->ib_dev,
"invalid SRQ attr, depth = %u, sge = %u.\n",
attr->max_wr, attr->max_sge);
return -EINVAL;
}
attr->max_wr = max_t(u32, attr->max_wr, HNS_ROCE_MIN_SRQ_WQE_NUM);
srq->wqe_cnt = roundup_pow_of_two(attr->max_wr);
srq->max_gs = roundup_pow_of_two(attr->max_sge + srq->rsv_sge);
attr->max_wr = srq->wqe_cnt;
attr->max_sge = srq->max_gs - srq->rsv_sge;
attr->srq_limit = 0;
return 0;
}
static void set_srq_ext_param(struct hns_roce_srq *srq,
struct ib_srq_init_attr *init_attr)
{
srq->cqn = ib_srq_has_cq(init_attr->srq_type) ?
to_hr_cq(init_attr->ext.cq)->cqn : 0;
srq->xrcdn = (init_attr->srq_type == IB_SRQT_XRC) ?
to_hr_xrcd(init_attr->ext.xrc.xrcd)->xrcdn : 0;
}
static int set_srq_param(struct hns_roce_srq *srq,
struct ib_srq_init_attr *init_attr,
struct ib_udata *udata)
{
int ret;
ret = set_srq_basic_param(srq, init_attr, udata);
if (ret)
return ret;
set_srq_ext_param(srq, init_attr);
return 0;
}
static int alloc_srq_buf(struct hns_roce_dev *hr_dev, struct hns_roce_srq *srq,
struct ib_udata *udata,
struct hns_roce_ib_create_srq *ucmd)
{
int ret;
ret = alloc_srq_idx(hr_dev, srq, udata, ucmd->que_addr);
if (ret)
return ret;
ret = alloc_srq_wqe_buf(hr_dev, srq, udata, ucmd->buf_addr);
if (ret)
goto err_idx;
if (!udata) {
ret = alloc_srq_wrid(srq);
if (ret)
goto err_wqe_buf;
}
return 0;
err_wqe_buf:
free_srq_wqe_buf(hr_dev, srq);
err_idx:
free_srq_idx(hr_dev, srq);
return ret;
}
static void free_srq_buf(struct hns_roce_dev *hr_dev, struct hns_roce_srq *srq)
{
free_srq_wrid(srq);
free_srq_wqe_buf(hr_dev, srq);
free_srq_idx(hr_dev, srq);
}
static void free_srq_db(struct hns_roce_dev *hr_dev, struct hns_roce_srq *srq,
struct ib_udata *udata)
{
struct hns_roce_ucontext *uctx;
if (!(srq->cap_flags & HNS_ROCE_SRQ_CAP_RECORD_DB))
return;
srq->cap_flags &= ~HNS_ROCE_SRQ_CAP_RECORD_DB;
if (udata) {
uctx = rdma_udata_to_drv_context(udata,
struct hns_roce_ucontext,
ibucontext);
hns_roce_db_unmap_user(uctx, &srq->rdb);
} else {
hns_roce_free_db(hr_dev, &srq->rdb);
}
}
static int alloc_srq_db(struct hns_roce_dev *hr_dev, struct hns_roce_srq *srq,
struct ib_udata *udata,
struct hns_roce_ib_create_srq *ucmd,
struct hns_roce_ib_create_srq_resp *resp)
{
struct hns_roce_ucontext *uctx;
int ret;
if (udata) {
if ((hr_dev->caps.flags & HNS_ROCE_CAP_FLAG_SRQ_RECORD_DB) &&
(ucmd->req_cap_flags & HNS_ROCE_SRQ_CAP_RECORD_DB)) {
uctx = rdma_udata_to_drv_context(udata,
struct hns_roce_ucontext, ibucontext);
ret = hns_roce_db_map_user(uctx, ucmd->db_addr,
&srq->rdb);
if (ret)
return ret;
srq->cap_flags |= HNS_ROCE_RSP_SRQ_CAP_RECORD_DB;
}
} else {
if (hr_dev->caps.flags & HNS_ROCE_CAP_FLAG_SRQ_RECORD_DB) {
ret = hns_roce_alloc_db(hr_dev, &srq->rdb, 1);
if (ret)
return ret;
*srq->rdb.db_record = 0;
srq->cap_flags |= HNS_ROCE_RSP_SRQ_CAP_RECORD_DB;
}
srq->db_reg = hr_dev->reg_base + SRQ_DB_REG;
}
return 0;
}
int hns_roce_create_srq(struct ib_srq *ib_srq,
struct ib_srq_init_attr *init_attr,
struct ib_udata *udata)
{
struct hns_roce_dev *hr_dev = to_hr_dev(ib_srq->device);
struct hns_roce_ib_create_srq_resp resp = {};
struct hns_roce_srq *srq = to_hr_srq(ib_srq);
struct hns_roce_ib_create_srq ucmd = {};
int ret;
mutex_init(&srq->mutex);
spin_lock_init(&srq->lock);
ret = set_srq_param(srq, init_attr, udata);
if (ret)
goto err_out;
if (udata) {
ret = ib_copy_validate_udata_in(udata, ucmd, que_addr);
if (ret)
goto err_out;
}
ret = alloc_srq_buf(hr_dev, srq, udata, &ucmd);
if (ret)
goto err_out;
ret = alloc_srq_db(hr_dev, srq, udata, &ucmd, &resp);
if (ret)
goto err_srq_buf;
ret = alloc_srqn(hr_dev, srq);
if (ret)
goto err_srq_db;
ret = alloc_srqc(hr_dev, srq);
if (ret)
goto err_srqn;
srq->event = hns_roce_ib_srq_event;
init_completion(&srq->free);
refcount_set_release(&srq->refcount, 1);
if (udata) {
resp.cap_flags = srq->cap_flags;
resp.srqn = srq->srqn;
ret = ib_respond_udata(udata, resp);
if (ret)
goto err_srqc;
}
return 0;
err_srqc:
free_srqc(hr_dev, srq);
err_srqn:
free_srqn(hr_dev, srq);
err_srq_db:
free_srq_db(hr_dev, srq, udata);
err_srq_buf:
free_srq_buf(hr_dev, srq);
err_out:
mutex_destroy(&srq->mutex);
atomic64_inc(&hr_dev->dfx_cnt[HNS_ROCE_DFX_SRQ_CREATE_ERR_CNT]);
return ret;
}
int hns_roce_destroy_srq(struct ib_srq *ibsrq, struct ib_udata *udata)
{
struct hns_roce_dev *hr_dev = to_hr_dev(ibsrq->device);
struct hns_roce_srq *srq = to_hr_srq(ibsrq);
free_srqc(hr_dev, srq);
free_srqn(hr_dev, srq);
free_srq_db(hr_dev, srq, udata);
free_srq_buf(hr_dev, srq);
mutex_destroy(&srq->mutex);
return 0;
}
void hns_roce_init_srq_table(struct hns_roce_dev *hr_dev)
{
struct hns_roce_srq_table *srq_table = &hr_dev->srq_table;
struct hns_roce_ida *srq_ida = &srq_table->srq_ida;
xa_init(&srq_table->xa);
ida_init(&srq_ida->ida);
srq_ida->max = hr_dev->caps.num_srqs - 1;
srq_ida->min = hr_dev->caps.reserved_srqs;
}
@@ -0,0 +1,216 @@
/* SPDX-License-Identifier: GPL-2.0+ */
/*
* Copyright (c) 2025 Hisilicon Limited.
*/
#undef TRACE_SYSTEM
#define TRACE_SYSTEM hns_roce
#if !defined(__HNS_ROCE_TRACE_H) || defined(TRACE_HEADER_MULTI_READ)
#define __HNS_ROCE_TRACE_H
#include <linux/tracepoint.h>
#include <linux/string_choices.h>
#include "hns_roce_device.h"
#include "hns_roce_hw_v2.h"
DECLARE_EVENT_CLASS(flush_head_template,
TP_PROTO(unsigned long qpn, u32 pi,
enum hns_roce_trace_type type),
TP_ARGS(qpn, pi, type),
TP_STRUCT__entry(__field(unsigned long, qpn)
__field(u32, pi)
__field(enum hns_roce_trace_type, type)
),
TP_fast_assign(__entry->qpn = qpn;
__entry->pi = pi;
__entry->type = type;
),
TP_printk("%s 0x%lx flush head 0x%x.",
trace_type_to_str(__entry->type),
__entry->qpn, __entry->pi)
);
DEFINE_EVENT(flush_head_template, hns_sq_flush_cqe,
TP_PROTO(unsigned long qpn, u32 pi,
enum hns_roce_trace_type type),
TP_ARGS(qpn, pi, type));
DEFINE_EVENT(flush_head_template, hns_rq_flush_cqe,
TP_PROTO(unsigned long qpn, u32 pi,
enum hns_roce_trace_type type),
TP_ARGS(qpn, pi, type));
#define MAX_SGE_PER_WQE 64
#define MAX_WQE_SIZE (MAX_SGE_PER_WQE * HNS_ROCE_SGE_SIZE)
DECLARE_EVENT_CLASS(wqe_template,
TP_PROTO(unsigned long qpn, u32 idx, void *wqe, u32 len,
u64 id, enum hns_roce_trace_type type),
TP_ARGS(qpn, idx, wqe, len, id, type),
TP_STRUCT__entry(__field(unsigned long, qpn)
__field(u32, idx)
__array(u32, wqe,
MAX_WQE_SIZE / sizeof(__le32))
__field(u32, len)
__field(u64, id)
__field(enum hns_roce_trace_type, type)
),
TP_fast_assign(__entry->qpn = qpn;
__entry->idx = idx;
__entry->id = id;
__entry->len = len / sizeof(__le32);
__entry->type = type;
for (int i = 0; i < __entry->len; i++)
__entry->wqe[i] = le32_to_cpu(((__le32 *)wqe)[i]);
),
TP_printk("%s 0x%lx wqe(0x%x/0x%llx): %s",
trace_type_to_str(__entry->type),
__entry->qpn, __entry->idx, __entry->id,
__print_array(__entry->wqe, __entry->len,
sizeof(__le32)))
);
DEFINE_EVENT(wqe_template, hns_sq_wqe,
TP_PROTO(unsigned long qpn, u32 idx, void *wqe, u32 len, u64 id,
enum hns_roce_trace_type type),
TP_ARGS(qpn, idx, wqe, len, id, type));
DEFINE_EVENT(wqe_template, hns_rq_wqe,
TP_PROTO(unsigned long qpn, u32 idx, void *wqe, u32 len, u64 id,
enum hns_roce_trace_type type),
TP_ARGS(qpn, idx, wqe, len, id, type));
DEFINE_EVENT(wqe_template, hns_srq_wqe,
TP_PROTO(unsigned long qpn, u32 idx, void *wqe, u32 len, u64 id,
enum hns_roce_trace_type type),
TP_ARGS(qpn, idx, wqe, len, id, type));
TRACE_EVENT(hns_ae_info,
TP_PROTO(int event_type, void *aeqe, unsigned int len),
TP_ARGS(event_type, aeqe, len),
TP_STRUCT__entry(__field(int, event_type)
__array(u32, aeqe,
HNS_ROCE_V3_EQE_SIZE / sizeof(__le32))
__field(u32, len)
),
TP_fast_assign(__entry->event_type = event_type;
__entry->len = len / sizeof(__le32);
for (int i = 0; i < __entry->len; i++)
__entry->aeqe[i] = le32_to_cpu(((__le32 *)aeqe)[i]);
),
TP_printk("event %2d aeqe: %s", __entry->event_type,
__print_array(__entry->aeqe, __entry->len, sizeof(__le32)))
);
TRACE_EVENT(hns_mr,
TP_PROTO(struct hns_roce_mr *mr),
TP_ARGS(mr),
TP_STRUCT__entry(__field(u64, iova)
__field(u64, size)
__field(u32, key)
__field(u32, pd)
__field(u32, pbl_hop_num)
__field(u32, npages)
__field(int, type)
__field(int, enabled)
),
TP_fast_assign(__entry->iova = mr->iova;
__entry->size = mr->size;
__entry->key = mr->key;
__entry->pd = mr->pd;
__entry->pbl_hop_num = mr->pbl_hop_num;
__entry->npages = mr->npages;
__entry->type = mr->type;
__entry->enabled = mr->enabled;
),
TP_printk("iova:0x%llx, size:%llu, key:%u, pd:%u, pbl_hop:%u, npages:%u, type:%d, status:%d",
__entry->iova, __entry->size, __entry->key,
__entry->pd, __entry->pbl_hop_num, __entry->npages,
__entry->type, __entry->enabled)
);
TRACE_EVENT(hns_buf_attr,
TP_PROTO(struct hns_roce_buf_attr *attr),
TP_ARGS(attr),
TP_STRUCT__entry(__field(unsigned int, region_count)
__field(unsigned int, region0_size)
__field(int, region0_hopnum)
__field(unsigned int, region1_size)
__field(int, region1_hopnum)
__field(unsigned int, region2_size)
__field(int, region2_hopnum)
__field(unsigned int, page_shift)
__field(bool, mtt_only)
),
TP_fast_assign(__entry->region_count = attr->region_count;
__entry->region0_size = attr->region[0].size;
__entry->region0_hopnum = attr->region[0].hopnum;
__entry->region1_size = attr->region[1].size;
__entry->region1_hopnum = attr->region[1].hopnum;
__entry->region2_size = attr->region[2].size;
__entry->region2_hopnum = attr->region[2].hopnum;
__entry->page_shift = attr->page_shift;
__entry->mtt_only = attr->mtt_only;
),
TP_printk("rg cnt:%u, pg_sft:0x%x, mtt_only:%s, rg 0 (sz:%u, hop:%u), rg 1 (sz:%u, hop:%u), rg 2 (sz:%u, hop:%u)\n",
__entry->region_count, __entry->page_shift,
str_yes_no(__entry->mtt_only),
__entry->region0_size, __entry->region0_hopnum,
__entry->region1_size, __entry->region1_hopnum,
__entry->region2_size, __entry->region2_hopnum)
);
DECLARE_EVENT_CLASS(cmdq,
TP_PROTO(struct hns_roce_dev *hr_dev,
struct hns_roce_cmq_desc *desc),
TP_ARGS(hr_dev, desc),
TP_STRUCT__entry(__string(dev_name, dev_name(hr_dev->dev))
__field(u16, opcode)
__field(u16, flag)
__field(u16, retval)
__array(u32, data, 6)
),
TP_fast_assign(__assign_str(dev_name);
__entry->opcode = le16_to_cpu(desc->opcode);
__entry->flag = le16_to_cpu(desc->flag);
__entry->retval = le16_to_cpu(desc->retval);
for (int i = 0; i < 6; i++)
__entry->data[i] = le32_to_cpu(desc->data[i]);
),
TP_printk("%s cmdq opcode:0x%x, flag:0x%x, retval:0x%x, data:%s\n",
__get_str(dev_name), __entry->opcode,
__entry->flag, __entry->retval,
__print_array(__entry->data, 6, sizeof(__le32)))
);
DEFINE_EVENT(cmdq, hns_cmdq_req,
TP_PROTO(struct hns_roce_dev *hr_dev,
struct hns_roce_cmq_desc *desc),
TP_ARGS(hr_dev, desc));
DEFINE_EVENT(cmdq, hns_cmdq_resp,
TP_PROTO(struct hns_roce_dev *hr_dev,
struct hns_roce_cmq_desc *desc),
TP_ARGS(hr_dev, desc));
#endif /* __HNS_ROCE_TRACE_H */
#undef TRACE_INCLUDE_FILE
#define TRACE_INCLUDE_FILE hns_roce_trace
#undef TRACE_INCLUDE_PATH
#define TRACE_INCLUDE_PATH .
#include <trace/define_trace.h>