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
+4
View File
@@ -0,0 +1,4 @@
subdir-ccflags-y += -I$(srctree)/drivers/vdpa/mlx5/core
obj-$(CONFIG_MLX5_VDPA_NET) += mlx5_vdpa.o
mlx5_vdpa-$(CONFIG_MLX5_VDPA_NET) += net/mlx5_vnet.o core/resources.o core/mr.o net/debug.o
@@ -0,0 +1,189 @@
/* SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB */
/* Copyright (c) 2020 Mellanox Technologies Ltd. */
#ifndef __MLX5_VDPA_H__
#define __MLX5_VDPA_H__
#include <linux/etherdevice.h>
#include <linux/vringh.h>
#include <linux/vdpa.h>
#include <linux/mlx5/driver.h>
#define MLX5V_ETH_HARD_MTU (ETH_HLEN + VLAN_HLEN + ETH_FCS_LEN)
extern int mlx5_vdpa_max_iotlb_entries;
struct mlx5_vdpa_direct_mr {
u64 start;
u64 end;
u32 perm;
u32 mr;
struct sg_table sg_head;
int log_size;
int nsg;
int nent;
struct list_head list;
u64 offset;
};
struct mlx5_vdpa_mr {
u32 mkey;
/* list of direct MRs descendants of this indirect mr */
struct list_head head;
unsigned long num_directs;
unsigned long num_klms;
struct vhost_iotlb *iotlb;
bool user_mr;
refcount_t refcount;
struct list_head mr_list;
};
struct mlx5_vdpa_resources {
u32 pdn;
struct mlx5_uars_page *uar;
void __iomem *kick_addr;
u64 phys_kick_addr;
u16 uid;
u32 null_mkey;
bool valid;
};
struct mlx5_control_vq {
struct vhost_iotlb *iotlb;
/* spinlock to synchronize iommu table */
spinlock_t iommu_lock;
struct vringh vring;
bool ready;
u64 desc_addr;
u64 device_addr;
u64 driver_addr;
struct vdpa_callback event_cb;
struct vringh_kiov riov;
struct vringh_kiov wiov;
unsigned short head;
unsigned int received_desc;
unsigned int completed_desc;
};
struct mlx5_vdpa_wq_ent {
struct work_struct work;
struct mlx5_vdpa_dev *mvdev;
};
enum {
MLX5_VDPA_DATAVQ_GROUP,
MLX5_VDPA_CVQ_GROUP,
MLX5_VDPA_DATAVQ_DESC_GROUP,
MLX5_VDPA_NUMVQ_GROUPS
};
enum {
MLX5_VDPA_NUM_AS = 2
};
struct mlx5_vdpa_mr_resources {
struct mlx5_vdpa_mr *mr[MLX5_VDPA_NUM_AS];
unsigned int group2asid[MLX5_VDPA_NUMVQ_GROUPS];
/* Pre-deletion mr list */
struct list_head mr_list_head;
/* Deferred mr list */
struct list_head mr_gc_list_head;
struct workqueue_struct *wq_gc;
struct delayed_work gc_dwork_ent;
struct mutex lock;
atomic_t shutdown;
};
struct mlx5_vdpa_dev {
struct vdpa_device vdev;
struct mlx5_core_dev *mdev;
struct mlx5_vdpa_resources res;
struct mlx5_vdpa_mr_resources mres;
u64 mlx_features;
u64 actual_features;
u8 status;
u32 max_vqs;
u16 max_idx;
u32 generation;
struct mlx5_control_vq cvq;
struct workqueue_struct *wq;
bool suspended;
struct mlx5_async_ctx async_ctx;
};
struct mlx5_vdpa_async_cmd {
int err;
struct mlx5_async_work cb_work;
struct completion cmd_done;
void *in;
size_t inlen;
void *out;
size_t outlen;
};
int mlx5_vdpa_create_tis(struct mlx5_vdpa_dev *mvdev, void *in, u32 *tisn);
void mlx5_vdpa_destroy_tis(struct mlx5_vdpa_dev *mvdev, u32 tisn);
int mlx5_vdpa_create_rqt(struct mlx5_vdpa_dev *mvdev, void *in, int inlen, u32 *rqtn);
int mlx5_vdpa_modify_rqt(struct mlx5_vdpa_dev *mvdev, void *in, int inlen, u32 rqtn);
void mlx5_vdpa_destroy_rqt(struct mlx5_vdpa_dev *mvdev, u32 rqtn);
int mlx5_vdpa_create_tir(struct mlx5_vdpa_dev *mvdev, void *in, u32 *tirn);
void mlx5_vdpa_destroy_tir(struct mlx5_vdpa_dev *mvdev, u32 tirn);
int mlx5_vdpa_alloc_transport_domain(struct mlx5_vdpa_dev *mvdev, u32 *tdn);
void mlx5_vdpa_dealloc_transport_domain(struct mlx5_vdpa_dev *mvdev, u32 tdn);
int mlx5_vdpa_alloc_resources(struct mlx5_vdpa_dev *mvdev);
void mlx5_vdpa_free_resources(struct mlx5_vdpa_dev *mvdev);
int mlx5_vdpa_create_mkey(struct mlx5_vdpa_dev *mvdev, u32 *mkey, u32 *in,
int inlen);
int mlx5_vdpa_destroy_mkey(struct mlx5_vdpa_dev *mvdev, u32 mkey);
struct mlx5_vdpa_mr *mlx5_vdpa_create_mr(struct mlx5_vdpa_dev *mvdev,
struct vhost_iotlb *iotlb);
int mlx5_vdpa_init_mr_resources(struct mlx5_vdpa_dev *mvdev);
void mlx5_vdpa_destroy_mr_resources(struct mlx5_vdpa_dev *mvdev);
void mlx5_vdpa_clean_mrs(struct mlx5_vdpa_dev *mvdev);
void mlx5_vdpa_get_mr(struct mlx5_vdpa_dev *mvdev,
struct mlx5_vdpa_mr *mr);
void mlx5_vdpa_put_mr(struct mlx5_vdpa_dev *mvdev,
struct mlx5_vdpa_mr *mr);
void mlx5_vdpa_update_mr(struct mlx5_vdpa_dev *mvdev,
struct mlx5_vdpa_mr *mr,
unsigned int asid);
int mlx5_vdpa_update_cvq_iotlb(struct mlx5_vdpa_dev *mvdev,
struct vhost_iotlb *iotlb,
unsigned int asid);
int mlx5_vdpa_create_dma_mr(struct mlx5_vdpa_dev *mvdev);
int mlx5_vdpa_reset_mr(struct mlx5_vdpa_dev *mvdev, unsigned int asid);
int mlx5_vdpa_exec_async_cmds(struct mlx5_vdpa_dev *mvdev,
struct mlx5_vdpa_async_cmd *cmds,
int num_cmds);
#define mlx5_vdpa_err(__dev, format, ...) \
dev_err((__dev)->mdev->device, "%s:%d:(pid %d) error: " format, __func__, __LINE__, \
current->pid, ##__VA_ARGS__)
#define mlx5_vdpa_warn(__dev, format, ...) \
dev_warn((__dev)->mdev->device, "%s:%d:(pid %d) warning: " format, __func__, __LINE__, \
current->pid, ##__VA_ARGS__)
#define mlx5_vdpa_info(__dev, format, ...) \
dev_info((__dev)->mdev->device, "%s:%d:(pid %d): " format, __func__, __LINE__, \
current->pid, ##__VA_ARGS__)
#define mlx5_vdpa_dbg(__dev, format, ...) \
dev_debug((__dev)->mdev->device, "%s:%d:(pid %d): " format, __func__, __LINE__, \
current->pid, ##__VA_ARGS__)
#endif /* __MLX5_VDPA_H__ */
+923
View File
@@ -0,0 +1,923 @@
// SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB
/* Copyright (c) 2020 Mellanox Technologies Ltd. */
#include <linux/vhost_types.h>
#include <linux/vdpa.h>
#include <linux/gcd.h>
#include <linux/string.h>
#include <linux/mlx5/qp.h>
#include "mlx5_vdpa.h"
/* DIV_ROUND_UP where the divider is a power of 2 give by its log base 2 value */
#define MLX5_DIV_ROUND_UP_POW2(_n, _s) \
({ \
u64 __s = _s; \
u64 _res; \
_res = (((_n) + (1 << (__s)) - 1) >> (__s)); \
_res; \
})
static int get_octo_len(u64 len, int page_shift)
{
u64 page_size = 1ULL << page_shift;
int npages;
npages = ALIGN(len, page_size) >> page_shift;
return (npages + 1) / 2;
}
static void mlx5_set_access_mode(void *mkc, int mode)
{
MLX5_SET(mkc, mkc, access_mode_1_0, mode & 0x3);
MLX5_SET(mkc, mkc, access_mode_4_2, mode >> 2);
}
static void populate_mtts(struct mlx5_vdpa_direct_mr *mr, __be64 *mtt)
{
struct scatterlist *sg;
int nsg = mr->nsg;
u64 dma_addr;
u64 dma_len;
int j = 0;
int i;
for_each_sg(mr->sg_head.sgl, sg, mr->nent, i) {
for (dma_addr = sg_dma_address(sg), dma_len = sg_dma_len(sg);
nsg && dma_len;
nsg--, dma_addr += BIT(mr->log_size), dma_len -= BIT(mr->log_size))
mtt[j++] = cpu_to_be64(dma_addr);
}
}
struct mlx5_create_mkey_mem {
u8 out[MLX5_ST_SZ_BYTES(create_mkey_out)];
u8 in[MLX5_ST_SZ_BYTES(create_mkey_in)];
__be64 mtt[];
};
struct mlx5_destroy_mkey_mem {
u8 out[MLX5_ST_SZ_BYTES(destroy_mkey_out)];
u8 in[MLX5_ST_SZ_BYTES(destroy_mkey_in)];
};
static void fill_create_direct_mr(struct mlx5_vdpa_dev *mvdev,
struct mlx5_vdpa_direct_mr *mr,
struct mlx5_create_mkey_mem *mem)
{
void *in = &mem->in;
void *mkc;
MLX5_SET(create_mkey_in, in, uid, mvdev->res.uid);
mkc = MLX5_ADDR_OF(create_mkey_in, in, memory_key_mkey_entry);
MLX5_SET(mkc, mkc, lw, !!(mr->perm & VHOST_MAP_WO));
MLX5_SET(mkc, mkc, lr, !!(mr->perm & VHOST_MAP_RO));
mlx5_set_access_mode(mkc, MLX5_MKC_ACCESS_MODE_MTT);
MLX5_SET(mkc, mkc, qpn, 0xffffff);
MLX5_SET(mkc, mkc, pd, mvdev->res.pdn);
MLX5_SET64(mkc, mkc, start_addr, mr->offset);
MLX5_SET64(mkc, mkc, len, mr->end - mr->start);
MLX5_SET(mkc, mkc, log_page_size, mr->log_size);
MLX5_SET(mkc, mkc, translations_octword_size,
get_octo_len(mr->end - mr->start, mr->log_size));
MLX5_SET(create_mkey_in, in, translations_octword_actual_size,
get_octo_len(mr->end - mr->start, mr->log_size));
populate_mtts(mr, MLX5_ADDR_OF(create_mkey_in, in, klm_pas_mtt));
MLX5_SET(create_mkey_in, in, opcode, MLX5_CMD_OP_CREATE_MKEY);
MLX5_SET(create_mkey_in, in, uid, mvdev->res.uid);
}
static void create_direct_mr_end(struct mlx5_vdpa_dev *mvdev,
struct mlx5_vdpa_direct_mr *mr,
struct mlx5_create_mkey_mem *mem)
{
u32 mkey_index = MLX5_GET(create_mkey_out, mem->out, mkey_index);
mr->mr = mlx5_idx_to_mkey(mkey_index);
}
static void fill_destroy_direct_mr(struct mlx5_vdpa_dev *mvdev,
struct mlx5_vdpa_direct_mr *mr,
struct mlx5_destroy_mkey_mem *mem)
{
void *in = &mem->in;
MLX5_SET(destroy_mkey_in, in, uid, mvdev->res.uid);
MLX5_SET(destroy_mkey_in, in, opcode, MLX5_CMD_OP_DESTROY_MKEY);
MLX5_SET(destroy_mkey_in, in, mkey_index, mlx5_mkey_to_idx(mr->mr));
}
static void destroy_direct_mr(struct mlx5_vdpa_dev *mvdev, struct mlx5_vdpa_direct_mr *mr)
{
if (!mr->mr)
return;
mlx5_vdpa_destroy_mkey(mvdev, mr->mr);
}
static u64 map_start(struct vhost_iotlb_map *map, struct mlx5_vdpa_direct_mr *mr)
{
return max_t(u64, map->start, mr->start);
}
static u64 map_end(struct vhost_iotlb_map *map, struct mlx5_vdpa_direct_mr *mr)
{
return min_t(u64, map->last + 1, mr->end);
}
static u64 maplen(struct vhost_iotlb_map *map, struct mlx5_vdpa_direct_mr *mr)
{
return map_end(map, mr) - map_start(map, mr);
}
#define MLX5_VDPA_INVALID_START_ADDR ((u64)-1)
#define MLX5_VDPA_INVALID_LEN ((u64)-1)
static u64 indir_start_addr(struct mlx5_vdpa_mr *mkey)
{
struct mlx5_vdpa_direct_mr *s;
s = list_first_entry_or_null(&mkey->head, struct mlx5_vdpa_direct_mr, list);
if (!s)
return MLX5_VDPA_INVALID_START_ADDR;
return s->start;
}
static u64 indir_len(struct mlx5_vdpa_mr *mkey)
{
struct mlx5_vdpa_direct_mr *s;
struct mlx5_vdpa_direct_mr *e;
s = list_first_entry_or_null(&mkey->head, struct mlx5_vdpa_direct_mr, list);
if (!s)
return MLX5_VDPA_INVALID_LEN;
e = list_last_entry(&mkey->head, struct mlx5_vdpa_direct_mr, list);
return e->end - s->start;
}
#define LOG_MAX_KLM_SIZE 30
#define MAX_KLM_SIZE BIT(LOG_MAX_KLM_SIZE)
static u32 klm_bcount(u64 size)
{
return (u32)size;
}
static void fill_indir(struct mlx5_vdpa_dev *mvdev, struct mlx5_vdpa_mr *mkey, void *in)
{
struct mlx5_vdpa_direct_mr *dmr;
struct mlx5_klm *klmarr;
struct mlx5_klm *klm;
bool first = true;
u64 preve;
int i;
klmarr = MLX5_ADDR_OF(create_mkey_in, in, klm_pas_mtt);
i = 0;
list_for_each_entry(dmr, &mkey->head, list) {
again:
klm = &klmarr[i++];
if (first) {
preve = dmr->start;
first = false;
}
if (preve == dmr->start) {
klm->key = cpu_to_be32(dmr->mr);
klm->bcount = cpu_to_be32(klm_bcount(dmr->end - dmr->start));
preve = dmr->end;
} else {
u64 bcount = min_t(u64, dmr->start - preve, MAX_KLM_SIZE);
klm->key = cpu_to_be32(mvdev->res.null_mkey);
klm->bcount = cpu_to_be32(klm_bcount(bcount));
preve += bcount;
goto again;
}
}
}
static int klm_byte_size(int nklms)
{
return 16 * ALIGN(nklms, 4);
}
#define MLX5_VDPA_MTT_ALIGN 16
static int create_direct_keys(struct mlx5_vdpa_dev *mvdev, struct mlx5_vdpa_mr *mr)
{
struct mlx5_vdpa_async_cmd *cmds;
struct mlx5_vdpa_direct_mr *dmr;
int err = 0;
int i = 0;
cmds = kvzalloc_objs(*cmds, mr->num_directs);
if (!cmds)
return -ENOMEM;
list_for_each_entry(dmr, &mr->head, list) {
struct mlx5_create_mkey_mem *cmd_mem;
int mttcount;
mttcount = ALIGN(dmr->nsg, MLX5_VDPA_MTT_ALIGN / sizeof(cmd_mem->mtt[0]));
cmd_mem = kvzalloc_flex(*cmd_mem, mtt, mttcount);
if (!cmd_mem) {
err = -ENOMEM;
goto done;
}
cmds[i].out = cmd_mem->out;
cmds[i].outlen = sizeof(cmd_mem->out);
cmds[i].in = cmd_mem->in;
cmds[i].inlen = struct_size(cmd_mem, mtt, mttcount) -
offsetof(struct mlx5_create_mkey_mem, in);
fill_create_direct_mr(mvdev, dmr, cmd_mem);
i++;
}
err = mlx5_vdpa_exec_async_cmds(mvdev, cmds, mr->num_directs);
if (err) {
mlx5_vdpa_err(mvdev, "error issuing MTT mkey creation for direct mrs: %d\n", err);
goto done;
}
i = 0;
list_for_each_entry(dmr, &mr->head, list) {
struct mlx5_vdpa_async_cmd *cmd = &cmds[i++];
struct mlx5_create_mkey_mem *cmd_mem;
cmd_mem = container_of(cmd->out, struct mlx5_create_mkey_mem, out);
if (!cmd->err) {
create_direct_mr_end(mvdev, dmr, cmd_mem);
} else {
err = err ? err : cmd->err;
mlx5_vdpa_err(mvdev, "error creating MTT mkey [0x%llx, 0x%llx]: %d\n",
dmr->start, dmr->end, cmd->err);
}
}
done:
for (i = i-1; i >= 0; i--) {
struct mlx5_create_mkey_mem *cmd_mem;
cmd_mem = container_of(cmds[i].out, struct mlx5_create_mkey_mem, out);
kvfree(cmd_mem);
}
kvfree(cmds);
return err;
}
DEFINE_FREE(free_cmds, struct mlx5_vdpa_async_cmd *, kvfree(_T))
DEFINE_FREE(free_cmd_mem, struct mlx5_destroy_mkey_mem *, kvfree(_T))
static int destroy_direct_keys(struct mlx5_vdpa_dev *mvdev, struct mlx5_vdpa_mr *mr)
{
struct mlx5_destroy_mkey_mem *cmd_mem __free(free_cmd_mem) = NULL;
struct mlx5_vdpa_async_cmd *cmds __free(free_cmds) = NULL;
struct mlx5_vdpa_direct_mr *dmr;
int err = 0;
int i = 0;
cmds = kvzalloc_objs(*cmds, mr->num_directs);
cmd_mem = kvzalloc_objs(*cmd_mem, mr->num_directs);
if (!cmds || !cmd_mem)
return -ENOMEM;
list_for_each_entry(dmr, &mr->head, list) {
cmds[i].out = cmd_mem[i].out;
cmds[i].outlen = sizeof(cmd_mem[i].out);
cmds[i].in = cmd_mem[i].in;
cmds[i].inlen = sizeof(cmd_mem[i].in);
fill_destroy_direct_mr(mvdev, dmr, &cmd_mem[i]);
i++;
}
err = mlx5_vdpa_exec_async_cmds(mvdev, cmds, mr->num_directs);
if (err) {
mlx5_vdpa_err(mvdev, "error issuing MTT mkey deletion for direct mrs: %d\n", err);
return err;
}
i = 0;
list_for_each_entry(dmr, &mr->head, list) {
struct mlx5_vdpa_async_cmd *cmd = &cmds[i++];
dmr->mr = 0;
if (cmd->err) {
err = err ? err : cmd->err;
mlx5_vdpa_err(mvdev, "error deleting MTT mkey [0x%llx, 0x%llx]: %d\n",
dmr->start, dmr->end, cmd->err);
}
}
return err;
}
static int create_indirect_key(struct mlx5_vdpa_dev *mvdev, struct mlx5_vdpa_mr *mr)
{
int inlen;
void *mkc;
void *in;
int err;
u64 start;
u64 len;
start = indir_start_addr(mr);
len = indir_len(mr);
if (start == MLX5_VDPA_INVALID_START_ADDR || len == MLX5_VDPA_INVALID_LEN)
return -EINVAL;
inlen = MLX5_ST_SZ_BYTES(create_mkey_in) + klm_byte_size(mr->num_klms);
in = kzalloc(inlen, GFP_KERNEL);
if (!in)
return -ENOMEM;
MLX5_SET(create_mkey_in, in, uid, mvdev->res.uid);
mkc = MLX5_ADDR_OF(create_mkey_in, in, memory_key_mkey_entry);
MLX5_SET(mkc, mkc, lw, 1);
MLX5_SET(mkc, mkc, lr, 1);
mlx5_set_access_mode(mkc, MLX5_MKC_ACCESS_MODE_KLMS);
MLX5_SET(mkc, mkc, qpn, 0xffffff);
MLX5_SET(mkc, mkc, pd, mvdev->res.pdn);
MLX5_SET64(mkc, mkc, start_addr, start);
MLX5_SET64(mkc, mkc, len, len);
MLX5_SET(mkc, mkc, translations_octword_size, klm_byte_size(mr->num_klms) / 16);
MLX5_SET(create_mkey_in, in, translations_octword_actual_size, mr->num_klms);
fill_indir(mvdev, mr, in);
err = mlx5_vdpa_create_mkey(mvdev, &mr->mkey, in, inlen);
kfree(in);
return err;
}
static void destroy_indirect_key(struct mlx5_vdpa_dev *mvdev, struct mlx5_vdpa_mr *mkey)
{
mlx5_vdpa_destroy_mkey(mvdev, mkey->mkey);
}
static int map_direct_mr(struct mlx5_vdpa_dev *mvdev, struct mlx5_vdpa_direct_mr *mr,
struct vhost_iotlb *iotlb)
{
struct vhost_iotlb_map *map;
unsigned long lgcd = 0;
int log_entity_size;
unsigned long size;
int err;
struct page *pg;
unsigned int nsg;
int sglen;
u64 pa, offset;
u64 paend;
struct scatterlist *sg;
struct device *dma = mvdev->vdev.vmap.dma_dev;
for (map = vhost_iotlb_itree_first(iotlb, mr->start, mr->end - 1);
map; map = vhost_iotlb_itree_next(map, mr->start, mr->end - 1)) {
size = maplen(map, mr);
lgcd = gcd(lgcd, size);
}
log_entity_size = ilog2(lgcd);
sglen = 1 << log_entity_size;
nsg = MLX5_DIV_ROUND_UP_POW2(mr->end - mr->start, log_entity_size);
err = sg_alloc_table(&mr->sg_head, nsg, GFP_KERNEL);
if (err)
return err;
sg = mr->sg_head.sgl;
for (map = vhost_iotlb_itree_first(iotlb, mr->start, mr->end - 1);
map; map = vhost_iotlb_itree_next(map, mr->start, mr->end - 1)) {
offset = mr->start > map->start ? mr->start - map->start : 0;
pa = map->addr + offset;
paend = map->addr + offset + maplen(map, mr);
for (; pa < paend; pa += sglen) {
pg = pfn_to_page(__phys_to_pfn(pa));
if (!sg) {
mlx5_vdpa_warn(mvdev, "sg null. start 0x%llx, end 0x%llx\n",
map->start, map->last + 1);
err = -ENOMEM;
goto err_map;
}
sg_set_page(sg, pg, sglen, 0);
sg = sg_next(sg);
if (!sg)
goto done;
}
}
done:
mr->log_size = log_entity_size;
mr->nsg = nsg;
mr->nent = dma_map_sg_attrs(dma, mr->sg_head.sgl, mr->nsg, DMA_BIDIRECTIONAL, 0);
if (!mr->nent) {
err = -ENOMEM;
goto err_map;
}
return 0;
err_map:
sg_free_table(&mr->sg_head);
return err;
}
static void unmap_direct_mr(struct mlx5_vdpa_dev *mvdev, struct mlx5_vdpa_direct_mr *mr)
{
struct device *dma = mvdev->vdev.vmap.dma_dev;
destroy_direct_mr(mvdev, mr);
dma_unmap_sg_attrs(dma, mr->sg_head.sgl, mr->nsg, DMA_BIDIRECTIONAL, 0);
sg_free_table(&mr->sg_head);
}
static int add_direct_chain(struct mlx5_vdpa_dev *mvdev,
struct mlx5_vdpa_mr *mr,
u64 start,
u64 size,
u8 perm,
struct vhost_iotlb *iotlb)
{
struct mlx5_vdpa_direct_mr *dmr;
struct mlx5_vdpa_direct_mr *n;
LIST_HEAD(tmp);
u64 st;
u64 sz;
int err;
st = start;
while (size) {
sz = (u32)min_t(u64, MAX_KLM_SIZE, size);
dmr = kzalloc_obj(*dmr);
if (!dmr) {
err = -ENOMEM;
goto err_alloc;
}
dmr->start = st;
dmr->end = st + sz;
dmr->perm = perm;
err = map_direct_mr(mvdev, dmr, iotlb);
if (err) {
kfree(dmr);
goto err_alloc;
}
list_add_tail(&dmr->list, &tmp);
size -= sz;
mr->num_directs++;
mr->num_klms++;
st += sz;
}
list_splice_tail(&tmp, &mr->head);
return 0;
err_alloc:
list_for_each_entry_safe(dmr, n, &mr->head, list) {
list_del_init(&dmr->list);
unmap_direct_mr(mvdev, dmr);
kfree(dmr);
}
return err;
}
/* The iotlb pointer contains a list of maps. Go over the maps, possibly
* merging mergeable maps, and create direct memory keys that provide the
* device access to memory. The direct mkeys are then referred to by the
* indirect memory key that provides access to the enitre address space given
* by iotlb.
*/
static int create_user_mr(struct mlx5_vdpa_dev *mvdev,
struct mlx5_vdpa_mr *mr,
struct vhost_iotlb *iotlb)
{
struct mlx5_vdpa_direct_mr *dmr;
struct mlx5_vdpa_direct_mr *n;
struct vhost_iotlb_map *map;
u32 pperm = U16_MAX;
u64 last = U64_MAX;
u64 ps = U64_MAX;
u64 pe = U64_MAX;
u64 start = 0;
int err = 0;
int nnuls;
INIT_LIST_HEAD(&mr->head);
for (map = vhost_iotlb_itree_first(iotlb, start, last); map;
map = vhost_iotlb_itree_next(map, start, last)) {
start = map->start;
if (pe == map->start && pperm == map->perm) {
pe = map->last + 1;
} else {
if (ps != U64_MAX) {
if (pe < map->start) {
/* We have a hole in the map. Check how
* many null keys are required to fill it.
*/
nnuls = MLX5_DIV_ROUND_UP_POW2(map->start - pe,
LOG_MAX_KLM_SIZE);
mr->num_klms += nnuls;
}
err = add_direct_chain(mvdev, mr, ps, pe - ps, pperm, iotlb);
if (err)
goto err_chain;
}
ps = map->start;
pe = map->last + 1;
pperm = map->perm;
}
}
err = add_direct_chain(mvdev, mr, ps, pe - ps, pperm, iotlb);
if (err)
goto err_chain;
err = create_direct_keys(mvdev, mr);
if (err)
goto err_chain;
/* Create the memory key that defines the guests's address space. This
* memory key refers to the direct keys that contain the MTT
* translations
*/
err = create_indirect_key(mvdev, mr);
if (err)
goto err_chain;
mr->user_mr = true;
return 0;
err_chain:
list_for_each_entry_safe_reverse(dmr, n, &mr->head, list) {
list_del_init(&dmr->list);
unmap_direct_mr(mvdev, dmr);
kfree(dmr);
}
return err;
}
static int create_dma_mr(struct mlx5_vdpa_dev *mvdev, struct mlx5_vdpa_mr *mr)
{
int inlen = MLX5_ST_SZ_BYTES(create_mkey_in);
void *mkc;
u32 *in;
int err;
in = kzalloc(inlen, GFP_KERNEL);
if (!in)
return -ENOMEM;
mkc = MLX5_ADDR_OF(create_mkey_in, in, memory_key_mkey_entry);
MLX5_SET(mkc, mkc, access_mode_1_0, MLX5_MKC_ACCESS_MODE_PA);
MLX5_SET(mkc, mkc, length64, 1);
MLX5_SET(mkc, mkc, lw, 1);
MLX5_SET(mkc, mkc, lr, 1);
MLX5_SET(mkc, mkc, pd, mvdev->res.pdn);
MLX5_SET(mkc, mkc, qpn, 0xffffff);
err = mlx5_vdpa_create_mkey(mvdev, &mr->mkey, in, inlen);
if (!err)
mr->user_mr = false;
kfree(in);
return err;
}
static void destroy_dma_mr(struct mlx5_vdpa_dev *mvdev, struct mlx5_vdpa_mr *mr)
{
mlx5_vdpa_destroy_mkey(mvdev, mr->mkey);
}
static int dup_iotlb(struct vhost_iotlb *dst, struct vhost_iotlb *src)
{
struct vhost_iotlb_map *map;
u64 start = 0, last = ULLONG_MAX;
int err;
if (dst == src)
return -EINVAL;
if (!src) {
err = vhost_iotlb_add_range(dst, start, last, start, VHOST_ACCESS_RW);
return err;
}
for (map = vhost_iotlb_itree_first(src, start, last); map;
map = vhost_iotlb_itree_next(map, start, last)) {
err = vhost_iotlb_add_range(dst, map->start, map->last,
map->addr, map->perm);
if (err)
return err;
}
return 0;
}
static void prune_iotlb(struct vhost_iotlb *iotlb)
{
vhost_iotlb_del_range(iotlb, 0, ULLONG_MAX);
}
static void destroy_user_mr(struct mlx5_vdpa_dev *mvdev, struct mlx5_vdpa_mr *mr)
{
struct mlx5_vdpa_direct_mr *dmr;
struct mlx5_vdpa_direct_mr *n;
destroy_indirect_key(mvdev, mr);
destroy_direct_keys(mvdev, mr);
list_for_each_entry_safe_reverse(dmr, n, &mr->head, list) {
list_del_init(&dmr->list);
unmap_direct_mr(mvdev, dmr);
kfree(dmr);
}
}
static void _mlx5_vdpa_destroy_mr(struct mlx5_vdpa_dev *mvdev, struct mlx5_vdpa_mr *mr)
{
if (WARN_ON(!mr))
return;
if (mr->user_mr)
destroy_user_mr(mvdev, mr);
else
destroy_dma_mr(mvdev, mr);
vhost_iotlb_free(mr->iotlb);
list_del(&mr->mr_list);
kfree(mr);
}
/* There can be multiple .set_map() operations in quick succession.
* This large delay is a simple way to prevent the MR cleanup from blocking
* .set_map() MR creation in this scenario.
*/
#define MLX5_VDPA_MR_GC_TRIGGER_MS 2000
static void mlx5_vdpa_mr_gc_handler(struct work_struct *work)
{
struct mlx5_vdpa_mr_resources *mres;
struct mlx5_vdpa_mr *mr, *tmp;
struct mlx5_vdpa_dev *mvdev;
mres = container_of(work, struct mlx5_vdpa_mr_resources, gc_dwork_ent.work);
if (atomic_read(&mres->shutdown)) {
mutex_lock(&mres->lock);
} else if (!mutex_trylock(&mres->lock)) {
queue_delayed_work(mres->wq_gc, &mres->gc_dwork_ent,
msecs_to_jiffies(MLX5_VDPA_MR_GC_TRIGGER_MS));
return;
}
mvdev = container_of(mres, struct mlx5_vdpa_dev, mres);
list_for_each_entry_safe(mr, tmp, &mres->mr_gc_list_head, mr_list) {
_mlx5_vdpa_destroy_mr(mvdev, mr);
}
mutex_unlock(&mres->lock);
}
static void _mlx5_vdpa_put_mr(struct mlx5_vdpa_dev *mvdev,
struct mlx5_vdpa_mr *mr)
{
struct mlx5_vdpa_mr_resources *mres = &mvdev->mres;
if (!mr)
return;
if (refcount_dec_and_test(&mr->refcount)) {
list_move_tail(&mr->mr_list, &mres->mr_gc_list_head);
queue_delayed_work(mres->wq_gc, &mres->gc_dwork_ent,
msecs_to_jiffies(MLX5_VDPA_MR_GC_TRIGGER_MS));
}
}
void mlx5_vdpa_put_mr(struct mlx5_vdpa_dev *mvdev,
struct mlx5_vdpa_mr *mr)
{
mutex_lock(&mvdev->mres.lock);
_mlx5_vdpa_put_mr(mvdev, mr);
mutex_unlock(&mvdev->mres.lock);
}
static void _mlx5_vdpa_get_mr(struct mlx5_vdpa_dev *mvdev,
struct mlx5_vdpa_mr *mr)
{
if (!mr)
return;
refcount_inc(&mr->refcount);
}
void mlx5_vdpa_get_mr(struct mlx5_vdpa_dev *mvdev,
struct mlx5_vdpa_mr *mr)
{
mutex_lock(&mvdev->mres.lock);
_mlx5_vdpa_get_mr(mvdev, mr);
mutex_unlock(&mvdev->mres.lock);
}
void mlx5_vdpa_update_mr(struct mlx5_vdpa_dev *mvdev,
struct mlx5_vdpa_mr *new_mr,
unsigned int asid)
{
struct mlx5_vdpa_mr *old_mr = mvdev->mres.mr[asid];
mutex_lock(&mvdev->mres.lock);
_mlx5_vdpa_put_mr(mvdev, old_mr);
mvdev->mres.mr[asid] = new_mr;
mutex_unlock(&mvdev->mres.lock);
}
static void mlx5_vdpa_show_mr_leaks(struct mlx5_vdpa_dev *mvdev)
{
struct mlx5_vdpa_mr *mr;
mutex_lock(&mvdev->mres.lock);
list_for_each_entry(mr, &mvdev->mres.mr_list_head, mr_list) {
mlx5_vdpa_warn(mvdev, "mkey still alive after resource delete: "
"mr: %p, mkey: 0x%x, refcount: %u\n",
mr, mr->mkey, refcount_read(&mr->refcount));
}
mutex_unlock(&mvdev->mres.lock);
}
void mlx5_vdpa_clean_mrs(struct mlx5_vdpa_dev *mvdev)
{
if (!mvdev->res.valid)
return;
for (int i = 0; i < MLX5_VDPA_NUM_AS; i++)
mlx5_vdpa_update_mr(mvdev, NULL, i);
prune_iotlb(mvdev->cvq.iotlb);
mlx5_vdpa_show_mr_leaks(mvdev);
}
static int _mlx5_vdpa_create_mr(struct mlx5_vdpa_dev *mvdev,
struct mlx5_vdpa_mr *mr,
struct vhost_iotlb *iotlb)
{
int err;
if (mlx5_vdpa_max_iotlb_entries < 2)
return -EINVAL;
if (iotlb)
err = create_user_mr(mvdev, mr, iotlb);
else
err = create_dma_mr(mvdev, mr);
if (err)
return err;
mr->iotlb = vhost_iotlb_alloc(mlx5_vdpa_max_iotlb_entries, 0);
if (!mr->iotlb) {
err = -ENOMEM;
goto err_mr;
}
err = dup_iotlb(mr->iotlb, iotlb);
if (err)
goto err_iotlb;
list_add_tail(&mr->mr_list, &mvdev->mres.mr_list_head);
return 0;
err_iotlb:
vhost_iotlb_free(mr->iotlb);
err_mr:
if (iotlb)
destroy_user_mr(mvdev, mr);
else
destroy_dma_mr(mvdev, mr);
return err;
}
struct mlx5_vdpa_mr *mlx5_vdpa_create_mr(struct mlx5_vdpa_dev *mvdev,
struct vhost_iotlb *iotlb)
{
struct mlx5_vdpa_mr *mr;
int err;
mr = kzalloc_obj(*mr);
if (!mr)
return ERR_PTR(-ENOMEM);
mutex_lock(&mvdev->mres.lock);
err = _mlx5_vdpa_create_mr(mvdev, mr, iotlb);
mutex_unlock(&mvdev->mres.lock);
if (err)
goto out_err;
refcount_set(&mr->refcount, 1);
return mr;
out_err:
kfree(mr);
return ERR_PTR(err);
}
int mlx5_vdpa_update_cvq_iotlb(struct mlx5_vdpa_dev *mvdev,
struct vhost_iotlb *iotlb,
unsigned int asid)
{
int err;
if (mvdev->mres.group2asid[MLX5_VDPA_CVQ_GROUP] != asid)
return 0;
spin_lock(&mvdev->cvq.iommu_lock);
prune_iotlb(mvdev->cvq.iotlb);
err = dup_iotlb(mvdev->cvq.iotlb, iotlb);
spin_unlock(&mvdev->cvq.iommu_lock);
return err;
}
int mlx5_vdpa_create_dma_mr(struct mlx5_vdpa_dev *mvdev)
{
struct mlx5_vdpa_mr *mr;
mr = mlx5_vdpa_create_mr(mvdev, NULL);
if (IS_ERR(mr))
return PTR_ERR(mr);
mlx5_vdpa_update_mr(mvdev, mr, 0);
return mlx5_vdpa_update_cvq_iotlb(mvdev, NULL, 0);
}
int mlx5_vdpa_reset_mr(struct mlx5_vdpa_dev *mvdev, unsigned int asid)
{
if (asid >= MLX5_VDPA_NUM_AS)
return -EINVAL;
mlx5_vdpa_update_mr(mvdev, NULL, asid);
if (asid == 0 && MLX5_CAP_GEN(mvdev->mdev, umem_uid_0)) {
if (mlx5_vdpa_create_dma_mr(mvdev))
mlx5_vdpa_warn(mvdev, "create DMA MR failed\n");
} else {
mlx5_vdpa_update_cvq_iotlb(mvdev, NULL, asid);
}
return 0;
}
int mlx5_vdpa_init_mr_resources(struct mlx5_vdpa_dev *mvdev)
{
struct mlx5_vdpa_mr_resources *mres = &mvdev->mres;
mres->wq_gc = create_singlethread_workqueue("mlx5_vdpa_mr_gc");
if (!mres->wq_gc)
return -ENOMEM;
INIT_DELAYED_WORK(&mres->gc_dwork_ent, mlx5_vdpa_mr_gc_handler);
mutex_init(&mres->lock);
INIT_LIST_HEAD(&mres->mr_list_head);
INIT_LIST_HEAD(&mres->mr_gc_list_head);
return 0;
}
void mlx5_vdpa_destroy_mr_resources(struct mlx5_vdpa_dev *mvdev)
{
struct mlx5_vdpa_mr_resources *mres = &mvdev->mres;
if (!mres->wq_gc)
return;
atomic_set(&mres->shutdown, 1);
flush_delayed_work(&mres->gc_dwork_ent);
destroy_workqueue(mres->wq_gc);
mres->wq_gc = NULL;
mutex_destroy(&mres->lock);
}
@@ -0,0 +1,402 @@
// SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB
/* Copyright (c) 2020 Mellanox Technologies Ltd. */
#include <linux/iova.h>
#include <linux/mlx5/driver.h>
#include <linux/moduleparam.h>
#include "mlx5_vdpa.h"
int mlx5_vdpa_max_iotlb_entries = 2048;
module_param_named(max_iotlb_entries, mlx5_vdpa_max_iotlb_entries, int, 0444);
MODULE_PARM_DESC(max_iotlb_entries,
"Maximum number of iotlb entries. (default: 2048)");
static int alloc_pd(struct mlx5_vdpa_dev *dev, u32 *pdn, u16 uid)
{
struct mlx5_core_dev *mdev = dev->mdev;
u32 out[MLX5_ST_SZ_DW(alloc_pd_out)] = {};
u32 in[MLX5_ST_SZ_DW(alloc_pd_in)] = {};
int err;
MLX5_SET(alloc_pd_in, in, opcode, MLX5_CMD_OP_ALLOC_PD);
MLX5_SET(alloc_pd_in, in, uid, uid);
err = mlx5_cmd_exec_inout(mdev, alloc_pd, in, out);
if (!err)
*pdn = MLX5_GET(alloc_pd_out, out, pd);
return err;
}
static int dealloc_pd(struct mlx5_vdpa_dev *dev, u32 pdn, u16 uid)
{
u32 in[MLX5_ST_SZ_DW(dealloc_pd_in)] = {};
struct mlx5_core_dev *mdev = dev->mdev;
MLX5_SET(dealloc_pd_in, in, opcode, MLX5_CMD_OP_DEALLOC_PD);
MLX5_SET(dealloc_pd_in, in, pd, pdn);
MLX5_SET(dealloc_pd_in, in, uid, uid);
return mlx5_cmd_exec_in(mdev, dealloc_pd, in);
}
static int get_null_mkey(struct mlx5_vdpa_dev *dev, u32 *null_mkey)
{
u32 out[MLX5_ST_SZ_DW(query_special_contexts_out)] = {};
u32 in[MLX5_ST_SZ_DW(query_special_contexts_in)] = {};
struct mlx5_core_dev *mdev = dev->mdev;
int err;
MLX5_SET(query_special_contexts_in, in, opcode, MLX5_CMD_OP_QUERY_SPECIAL_CONTEXTS);
err = mlx5_cmd_exec_inout(mdev, query_special_contexts, in, out);
if (!err)
*null_mkey = MLX5_GET(query_special_contexts_out, out, null_mkey);
return err;
}
static int create_uctx(struct mlx5_vdpa_dev *mvdev, u16 *uid)
{
u32 out[MLX5_ST_SZ_DW(create_uctx_out)] = {};
int inlen;
void *in;
int err;
if (MLX5_CAP_GEN(mvdev->mdev, umem_uid_0))
return 0;
/* 0 means not supported */
if (!MLX5_CAP_GEN(mvdev->mdev, log_max_uctx))
return -EOPNOTSUPP;
inlen = MLX5_ST_SZ_BYTES(create_uctx_in);
in = kzalloc(inlen, GFP_KERNEL);
if (!in)
return -ENOMEM;
MLX5_SET(create_uctx_in, in, opcode, MLX5_CMD_OP_CREATE_UCTX);
MLX5_SET(create_uctx_in, in, uctx.cap, MLX5_UCTX_CAP_RAW_TX);
err = mlx5_cmd_exec(mvdev->mdev, in, inlen, out, sizeof(out));
kfree(in);
if (!err)
*uid = MLX5_GET(create_uctx_out, out, uid);
return err;
}
static void destroy_uctx(struct mlx5_vdpa_dev *mvdev, u32 uid)
{
u32 out[MLX5_ST_SZ_DW(destroy_uctx_out)] = {};
u32 in[MLX5_ST_SZ_DW(destroy_uctx_in)] = {};
if (!uid)
return;
MLX5_SET(destroy_uctx_in, in, opcode, MLX5_CMD_OP_DESTROY_UCTX);
MLX5_SET(destroy_uctx_in, in, uid, uid);
mlx5_cmd_exec(mvdev->mdev, in, sizeof(in), out, sizeof(out));
}
int mlx5_vdpa_create_tis(struct mlx5_vdpa_dev *mvdev, void *in, u32 *tisn)
{
u32 out[MLX5_ST_SZ_DW(create_tis_out)] = {};
int err;
MLX5_SET(create_tis_in, in, opcode, MLX5_CMD_OP_CREATE_TIS);
MLX5_SET(create_tis_in, in, uid, mvdev->res.uid);
err = mlx5_cmd_exec_inout(mvdev->mdev, create_tis, in, out);
if (!err)
*tisn = MLX5_GET(create_tis_out, out, tisn);
return err;
}
void mlx5_vdpa_destroy_tis(struct mlx5_vdpa_dev *mvdev, u32 tisn)
{
u32 in[MLX5_ST_SZ_DW(destroy_tis_in)] = {};
MLX5_SET(destroy_tis_in, in, opcode, MLX5_CMD_OP_DESTROY_TIS);
MLX5_SET(destroy_tis_in, in, uid, mvdev->res.uid);
MLX5_SET(destroy_tis_in, in, tisn, tisn);
mlx5_cmd_exec_in(mvdev->mdev, destroy_tis, in);
}
int mlx5_vdpa_create_rqt(struct mlx5_vdpa_dev *mvdev, void *in, int inlen, u32 *rqtn)
{
u32 out[MLX5_ST_SZ_DW(create_rqt_out)] = {};
int err;
MLX5_SET(create_rqt_in, in, opcode, MLX5_CMD_OP_CREATE_RQT);
err = mlx5_cmd_exec(mvdev->mdev, in, inlen, out, sizeof(out));
if (!err)
*rqtn = MLX5_GET(create_rqt_out, out, rqtn);
return err;
}
int mlx5_vdpa_modify_rqt(struct mlx5_vdpa_dev *mvdev, void *in, int inlen, u32 rqtn)
{
u32 out[MLX5_ST_SZ_DW(create_rqt_out)] = {};
MLX5_SET(modify_rqt_in, in, uid, mvdev->res.uid);
MLX5_SET(modify_rqt_in, in, rqtn, rqtn);
MLX5_SET(modify_rqt_in, in, opcode, MLX5_CMD_OP_MODIFY_RQT);
return mlx5_cmd_exec(mvdev->mdev, in, inlen, out, sizeof(out));
}
void mlx5_vdpa_destroy_rqt(struct mlx5_vdpa_dev *mvdev, u32 rqtn)
{
u32 in[MLX5_ST_SZ_DW(destroy_rqt_in)] = {};
MLX5_SET(destroy_rqt_in, in, opcode, MLX5_CMD_OP_DESTROY_RQT);
MLX5_SET(destroy_rqt_in, in, uid, mvdev->res.uid);
MLX5_SET(destroy_rqt_in, in, rqtn, rqtn);
mlx5_cmd_exec_in(mvdev->mdev, destroy_rqt, in);
}
int mlx5_vdpa_create_tir(struct mlx5_vdpa_dev *mvdev, void *in, u32 *tirn)
{
u32 out[MLX5_ST_SZ_DW(create_tir_out)] = {};
int err;
MLX5_SET(create_tir_in, in, opcode, MLX5_CMD_OP_CREATE_TIR);
err = mlx5_cmd_exec_inout(mvdev->mdev, create_tir, in, out);
if (!err)
*tirn = MLX5_GET(create_tir_out, out, tirn);
return err;
}
void mlx5_vdpa_destroy_tir(struct mlx5_vdpa_dev *mvdev, u32 tirn)
{
u32 in[MLX5_ST_SZ_DW(destroy_tir_in)] = {};
MLX5_SET(destroy_tir_in, in, opcode, MLX5_CMD_OP_DESTROY_TIR);
MLX5_SET(destroy_tir_in, in, uid, mvdev->res.uid);
MLX5_SET(destroy_tir_in, in, tirn, tirn);
mlx5_cmd_exec_in(mvdev->mdev, destroy_tir, in);
}
int mlx5_vdpa_alloc_transport_domain(struct mlx5_vdpa_dev *mvdev, u32 *tdn)
{
u32 out[MLX5_ST_SZ_DW(alloc_transport_domain_out)] = {};
u32 in[MLX5_ST_SZ_DW(alloc_transport_domain_in)] = {};
int err;
MLX5_SET(alloc_transport_domain_in, in, opcode, MLX5_CMD_OP_ALLOC_TRANSPORT_DOMAIN);
MLX5_SET(alloc_transport_domain_in, in, uid, mvdev->res.uid);
err = mlx5_cmd_exec_inout(mvdev->mdev, alloc_transport_domain, in, out);
if (!err)
*tdn = MLX5_GET(alloc_transport_domain_out, out, transport_domain);
return err;
}
void mlx5_vdpa_dealloc_transport_domain(struct mlx5_vdpa_dev *mvdev, u32 tdn)
{
u32 in[MLX5_ST_SZ_DW(dealloc_transport_domain_in)] = {};
MLX5_SET(dealloc_transport_domain_in, in, opcode, MLX5_CMD_OP_DEALLOC_TRANSPORT_DOMAIN);
MLX5_SET(dealloc_transport_domain_in, in, uid, mvdev->res.uid);
MLX5_SET(dealloc_transport_domain_in, in, transport_domain, tdn);
mlx5_cmd_exec_in(mvdev->mdev, dealloc_transport_domain, in);
}
int mlx5_vdpa_create_mkey(struct mlx5_vdpa_dev *mvdev, u32 *mkey, u32 *in,
int inlen)
{
u32 lout[MLX5_ST_SZ_DW(create_mkey_out)] = {};
u32 mkey_index;
int err;
MLX5_SET(create_mkey_in, in, opcode, MLX5_CMD_OP_CREATE_MKEY);
MLX5_SET(create_mkey_in, in, uid, mvdev->res.uid);
err = mlx5_cmd_exec(mvdev->mdev, in, inlen, lout, sizeof(lout));
if (err)
return err;
mkey_index = MLX5_GET(create_mkey_out, lout, mkey_index);
*mkey = mlx5_idx_to_mkey(mkey_index);
return 0;
}
int mlx5_vdpa_destroy_mkey(struct mlx5_vdpa_dev *mvdev, u32 mkey)
{
u32 in[MLX5_ST_SZ_DW(destroy_mkey_in)] = {};
MLX5_SET(destroy_mkey_in, in, uid, mvdev->res.uid);
MLX5_SET(destroy_mkey_in, in, opcode, MLX5_CMD_OP_DESTROY_MKEY);
MLX5_SET(destroy_mkey_in, in, mkey_index, mlx5_mkey_to_idx(mkey));
return mlx5_cmd_exec_in(mvdev->mdev, destroy_mkey, in);
}
static int init_ctrl_vq(struct mlx5_vdpa_dev *mvdev)
{
if (mlx5_vdpa_max_iotlb_entries < 2)
return -EINVAL;
mvdev->cvq.iotlb = vhost_iotlb_alloc(mlx5_vdpa_max_iotlb_entries, 0);
if (!mvdev->cvq.iotlb)
return -ENOMEM;
spin_lock_init(&mvdev->cvq.iommu_lock);
vringh_set_iotlb(&mvdev->cvq.vring, mvdev->cvq.iotlb, &mvdev->cvq.iommu_lock);
return 0;
}
static void cleanup_ctrl_vq(struct mlx5_vdpa_dev *mvdev)
{
vhost_iotlb_free(mvdev->cvq.iotlb);
}
int mlx5_vdpa_alloc_resources(struct mlx5_vdpa_dev *mvdev)
{
u64 offset = MLX5_CAP64_DEV_VDPA_EMULATION(mvdev->mdev, doorbell_bar_offset);
struct mlx5_vdpa_resources *res = &mvdev->res;
struct mlx5_core_dev *mdev = mvdev->mdev;
u64 kick_addr;
int err;
if (res->valid) {
mlx5_vdpa_warn(mvdev, "resources already allocated\n");
return -EINVAL;
}
res->uar = mlx5_get_uars_page(mdev);
if (IS_ERR(res->uar)) {
err = PTR_ERR(res->uar);
goto err_uars;
}
err = create_uctx(mvdev, &res->uid);
if (err)
goto err_uctx;
err = alloc_pd(mvdev, &res->pdn, res->uid);
if (err)
goto err_pd;
err = get_null_mkey(mvdev, &res->null_mkey);
if (err)
goto err_key;
kick_addr = mdev->bar_addr + offset;
res->phys_kick_addr = kick_addr;
res->kick_addr = ioremap(kick_addr, PAGE_SIZE);
if (!res->kick_addr) {
err = -ENOMEM;
goto err_key;
}
err = init_ctrl_vq(mvdev);
if (err)
goto err_ctrl;
res->valid = true;
return 0;
err_ctrl:
iounmap(res->kick_addr);
err_key:
dealloc_pd(mvdev, res->pdn, res->uid);
err_pd:
destroy_uctx(mvdev, res->uid);
err_uctx:
mlx5_put_uars_page(mdev, res->uar);
err_uars:
return err;
}
void mlx5_vdpa_free_resources(struct mlx5_vdpa_dev *mvdev)
{
struct mlx5_vdpa_resources *res = &mvdev->res;
if (!res->valid)
return;
cleanup_ctrl_vq(mvdev);
iounmap(res->kick_addr);
res->kick_addr = NULL;
dealloc_pd(mvdev, res->pdn, res->uid);
destroy_uctx(mvdev, res->uid);
mlx5_put_uars_page(mvdev->mdev, res->uar);
res->valid = false;
}
static void virtqueue_cmd_callback(int status, struct mlx5_async_work *context)
{
struct mlx5_vdpa_async_cmd *cmd =
container_of(context, struct mlx5_vdpa_async_cmd, cb_work);
cmd->err = mlx5_cmd_check(context->ctx->dev, status, cmd->in, cmd->out);
complete(&cmd->cmd_done);
}
static int issue_async_cmd(struct mlx5_vdpa_dev *mvdev,
struct mlx5_vdpa_async_cmd *cmds,
int issued,
int *completed)
{
struct mlx5_vdpa_async_cmd *cmd = &cmds[issued];
int err;
retry:
err = mlx5_cmd_exec_cb(&mvdev->async_ctx,
cmd->in, cmd->inlen,
cmd->out, cmd->outlen,
virtqueue_cmd_callback,
&cmd->cb_work);
if (err == -EBUSY) {
if (*completed < issued) {
/* Throttled by own commands: wait for oldest completion. */
wait_for_completion(&cmds[*completed].cmd_done);
(*completed)++;
goto retry;
} else {
/* Throttled by external commands: switch to sync api. */
err = mlx5_cmd_exec(mvdev->mdev,
cmd->in, cmd->inlen,
cmd->out, cmd->outlen);
if (!err)
(*completed)++;
}
}
return err;
}
int mlx5_vdpa_exec_async_cmds(struct mlx5_vdpa_dev *mvdev,
struct mlx5_vdpa_async_cmd *cmds,
int num_cmds)
{
int completed = 0;
int issued = 0;
int err = 0;
for (int i = 0; i < num_cmds; i++)
init_completion(&cmds[i].cmd_done);
while (issued < num_cmds) {
err = issue_async_cmd(mvdev, cmds, issued, &completed);
if (err) {
mlx5_vdpa_err(mvdev, "error issuing command %d of %d: %d\n",
issued, num_cmds, err);
break;
}
issued++;
}
while (completed < issued)
wait_for_completion(&cmds[completed++].cmd_done);
return err;
}
+153
View File
@@ -0,0 +1,153 @@
// SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB
/* Copyright (c) 2022, NVIDIA CORPORATION & AFFILIATES. All rights reserved. */
#include <linux/debugfs.h>
#include <linux/mlx5/fs.h>
#include "mlx5_vnet.h"
static int tirn_show(struct seq_file *file, void *priv)
{
struct mlx5_vdpa_net *ndev = file->private;
seq_printf(file, "0x%x\n", ndev->res.tirn);
return 0;
}
DEFINE_SHOW_ATTRIBUTE(tirn);
void mlx5_vdpa_remove_tirn(struct mlx5_vdpa_net *ndev)
{
if (ndev->debugfs)
debugfs_remove(ndev->res.tirn_dent);
}
void mlx5_vdpa_add_tirn(struct mlx5_vdpa_net *ndev)
{
ndev->res.tirn_dent = debugfs_create_file("tirn", 0444, ndev->rx_dent,
ndev, &tirn_fops);
}
static int rx_flow_table_show(struct seq_file *file, void *priv)
{
struct mlx5_vdpa_net *ndev = file->private;
seq_printf(file, "0x%x\n", mlx5_flow_table_id(ndev->rxft));
return 0;
}
DEFINE_SHOW_ATTRIBUTE(rx_flow_table);
void mlx5_vdpa_remove_rx_flow_table(struct mlx5_vdpa_net *ndev)
{
if (ndev->debugfs)
debugfs_remove(ndev->rx_table_dent);
}
void mlx5_vdpa_add_rx_flow_table(struct mlx5_vdpa_net *ndev)
{
ndev->rx_table_dent = debugfs_create_file("table_id", 0444, ndev->rx_dent,
ndev, &rx_flow_table_fops);
}
#if defined(CONFIG_MLX5_VDPA_STEERING_DEBUG)
static int packets_show(struct seq_file *file, void *priv)
{
struct mlx5_vdpa_counter *counter = file->private;
u64 packets;
u64 bytes;
int err;
err = mlx5_fc_query(counter->mdev, counter->counter, &packets, &bytes);
if (err)
return err;
seq_printf(file, "0x%llx\n", packets);
return 0;
}
static int bytes_show(struct seq_file *file, void *priv)
{
struct mlx5_vdpa_counter *counter = file->private;
u64 packets;
u64 bytes;
int err;
err = mlx5_fc_query(counter->mdev, counter->counter, &packets, &bytes);
if (err)
return err;
seq_printf(file, "0x%llx\n", bytes);
return 0;
}
DEFINE_SHOW_ATTRIBUTE(packets);
DEFINE_SHOW_ATTRIBUTE(bytes);
static void add_counter_node(struct mlx5_vdpa_counter *counter,
struct dentry *parent)
{
debugfs_create_file("packets", 0444, parent, counter,
&packets_fops);
debugfs_create_file("bytes", 0444, parent, counter,
&bytes_fops);
}
void mlx5_vdpa_add_rx_counters(struct mlx5_vdpa_net *ndev,
struct macvlan_node *node)
{
static const char *ut = "untagged";
char vidstr[9];
u16 vid;
node->ucast_counter.mdev = ndev->mvdev.mdev;
node->mcast_counter.mdev = ndev->mvdev.mdev;
if (node->tagged) {
vid = key2vid(node->macvlan);
snprintf(vidstr, sizeof(vidstr), "0x%x", vid);
} else {
strcpy(vidstr, ut);
}
node->dent = debugfs_create_dir(vidstr, ndev->rx_dent);
if (IS_ERR(node->dent)) {
node->dent = NULL;
return;
}
node->ucast_counter.dent = debugfs_create_dir("ucast", node->dent);
if (IS_ERR(node->ucast_counter.dent))
return;
add_counter_node(&node->ucast_counter, node->ucast_counter.dent);
node->mcast_counter.dent = debugfs_create_dir("mcast", node->dent);
if (IS_ERR(node->mcast_counter.dent))
return;
add_counter_node(&node->mcast_counter, node->mcast_counter.dent);
}
void mlx5_vdpa_remove_rx_counters(struct mlx5_vdpa_net *ndev,
struct macvlan_node *node)
{
if (node->dent && ndev->debugfs)
debugfs_remove_recursive(node->dent);
}
#endif
void mlx5_vdpa_add_debugfs(struct mlx5_vdpa_net *ndev)
{
struct mlx5_core_dev *mdev;
mdev = ndev->mvdev.mdev;
ndev->debugfs = debugfs_create_dir(dev_name(&ndev->mvdev.vdev.dev),
mlx5_debugfs_get_dev_root(mdev));
if (!IS_ERR(ndev->debugfs))
ndev->rx_dent = debugfs_create_dir("rx", ndev->debugfs);
}
void mlx5_vdpa_remove_debugfs(struct mlx5_vdpa_net *ndev)
{
debugfs_remove_recursive(ndev->debugfs);
ndev->debugfs = NULL;
}
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,119 @@
/* SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB */
/* Copyright (c) 2022, NVIDIA CORPORATION & AFFILIATES. All rights reserved. */
#ifndef __MLX5_VNET_H__
#define __MLX5_VNET_H__
#include "mlx5_vdpa.h"
#define to_mlx5_vdpa_ndev(__mvdev) \
container_of(__mvdev, struct mlx5_vdpa_net, mvdev)
#define to_mvdev(__vdev) container_of((__vdev), struct mlx5_vdpa_dev, vdev)
struct mlx5_vdpa_net_resources {
u32 tisn;
u32 tdn;
u32 tirn;
u32 rqtn;
bool valid;
struct dentry *tirn_dent;
};
#define MLX5V_MACVLAN_SIZE 256
static inline u16 key2vid(u64 key)
{
return (u16)(key >> 48) & 0xfff;
}
#define MLX5_VDPA_IRQ_NAME_LEN 32
struct mlx5_vdpa_irq_pool_entry {
struct msi_map map;
bool used;
char name[MLX5_VDPA_IRQ_NAME_LEN];
void *dev_id;
};
struct mlx5_vdpa_irq_pool {
int num_ent;
struct mlx5_vdpa_irq_pool_entry *entries;
};
struct mlx5_vdpa_net {
struct mlx5_vdpa_dev mvdev;
struct mlx5_vdpa_net_resources res;
struct virtio_net_config config;
struct mlx5_vdpa_virtqueue *vqs;
struct vdpa_callback *event_cbs;
/* Serialize vq resources creation and destruction. This is required
* since memory map might change and we need to destroy and create
* resources while driver in operational.
*/
struct rw_semaphore reslock;
struct mlx5_flow_table *rxft;
struct dentry *rx_dent;
struct dentry *rx_table_dent;
bool setup;
bool needs_teardown;
u32 cur_num_vqs;
u32 rqt_size;
bool nb_registered;
struct notifier_block nb;
struct vdpa_callback config_cb;
struct mlx5_vdpa_wq_ent cvq_ent;
struct hlist_head macvlan_hash[MLX5V_MACVLAN_SIZE];
struct mlx5_vdpa_irq_pool irqp;
struct dentry *debugfs;
u32 umem_1_buffer_param_a;
u32 umem_1_buffer_param_b;
u32 umem_2_buffer_param_a;
u32 umem_2_buffer_param_b;
u32 umem_3_buffer_param_a;
u32 umem_3_buffer_param_b;
};
struct mlx5_vdpa_counter {
struct mlx5_fc *counter;
struct dentry *dent;
struct mlx5_core_dev *mdev;
};
struct macvlan_node {
struct hlist_node hlist;
struct mlx5_flow_handle *ucast_rule;
struct mlx5_flow_handle *mcast_rule;
u64 macvlan;
struct mlx5_vdpa_net *ndev;
bool tagged;
#if defined(CONFIG_MLX5_VDPA_STEERING_DEBUG)
struct dentry *dent;
struct mlx5_vdpa_counter ucast_counter;
struct mlx5_vdpa_counter mcast_counter;
#endif
};
void mlx5_vdpa_add_debugfs(struct mlx5_vdpa_net *ndev);
void mlx5_vdpa_remove_debugfs(struct mlx5_vdpa_net *ndev);
void mlx5_vdpa_add_rx_flow_table(struct mlx5_vdpa_net *ndev);
void mlx5_vdpa_remove_rx_flow_table(struct mlx5_vdpa_net *ndev);
void mlx5_vdpa_add_tirn(struct mlx5_vdpa_net *ndev);
void mlx5_vdpa_remove_tirn(struct mlx5_vdpa_net *ndev);
#if defined(CONFIG_MLX5_VDPA_STEERING_DEBUG)
void mlx5_vdpa_add_rx_counters(struct mlx5_vdpa_net *ndev,
struct macvlan_node *node);
void mlx5_vdpa_remove_rx_counters(struct mlx5_vdpa_net *ndev,
struct macvlan_node *node);
#else
static inline void mlx5_vdpa_add_rx_counters(struct mlx5_vdpa_net *ndev,
struct macvlan_node *node) {}
static inline void mlx5_vdpa_remove_rx_counters(struct mlx5_vdpa_net *ndev,
struct macvlan_node *node) {}
#endif
#endif /* __MLX5_VNET_H__ */