mirror of
git://git.yoctoproject.org/linux-yocto.git
synced 2025-07-05 13:25:20 +02:00

Introduce the 'data direct' driver for a ConnectX-8 Data Direct device. The 'data direct' driver functions as the affiliated DMA device for one or more capable mlx5_ib devices. This DMA device, as the name suggests, is used exclusively for DMA operations. It can be considered a DMA engine managed by a PF/VF, lacking network capabilities and having minimal overall capabilities. Consequently, the DMA NIC PF will not be exposed to or directly used by software applications. The driver will not have any direct interface or interaction with the firmware (no command interface, no capabilities, etc.). It will operate solely over PCI to enable its DMA functionality. Registration and un-registration of the driver are handled as part of the mlx5_ib initialization and exit processes, as the mlx5_ib devices will effectively be its clients. The driver will serve as the DMA device for accessing another PCI device to achieve optimal performance (both on the same NUMA node, P2P access, etc.). Upon probing, it will read its VUID over PCI to handle mlx5_ib device registrations with the same VUID. Upon removal, it will notify its clients to allow them to clean up the resources that were mmaped with its DMA device. Signed-off-by: Yishai Hadas <yishaih@nvidia.com> Link: https://patch.msgid.link/b77edecfd476c3f445da96ab6aef499ae47b2829.1722512548.git.leon@kernel.org Signed-off-by: Leon Romanovsky <leon@kernel.org>
228 lines
5.4 KiB
C
228 lines
5.4 KiB
C
// SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB
|
|
/*
|
|
* Copyright (c) 2024, NVIDIA CORPORATION & AFFILIATES. All rights reserved
|
|
*/
|
|
|
|
#include "mlx5_ib.h"
|
|
#include "data_direct.h"
|
|
|
|
static LIST_HEAD(mlx5_data_direct_dev_list);
|
|
static LIST_HEAD(mlx5_data_direct_reg_list);
|
|
|
|
/*
|
|
* This mutex should be held when accessing either of the above lists
|
|
*/
|
|
static DEFINE_MUTEX(mlx5_data_direct_mutex);
|
|
|
|
struct mlx5_data_direct_registration {
|
|
struct mlx5_ib_dev *ibdev;
|
|
char vuid[MLX5_ST_SZ_BYTES(array1024_auto) + 1];
|
|
struct list_head list;
|
|
};
|
|
|
|
static const struct pci_device_id mlx5_data_direct_pci_table[] = {
|
|
{ PCI_VDEVICE(MELLANOX, 0x2100) }, /* ConnectX-8 Data Direct */
|
|
{ 0, }
|
|
};
|
|
|
|
static int mlx5_data_direct_vpd_get_vuid(struct mlx5_data_direct_dev *dev)
|
|
{
|
|
struct pci_dev *pdev = dev->pdev;
|
|
unsigned int vpd_size, kw_len;
|
|
u8 *vpd_data;
|
|
int start;
|
|
int ret;
|
|
|
|
vpd_data = pci_vpd_alloc(pdev, &vpd_size);
|
|
if (IS_ERR(vpd_data)) {
|
|
pci_err(pdev, "Unable to read VPD, err=%ld\n", PTR_ERR(vpd_data));
|
|
return PTR_ERR(vpd_data);
|
|
}
|
|
|
|
start = pci_vpd_find_ro_info_keyword(vpd_data, vpd_size, "VU", &kw_len);
|
|
if (start < 0) {
|
|
ret = start;
|
|
pci_err(pdev, "VU keyword not found, err=%d\n", ret);
|
|
goto end;
|
|
}
|
|
|
|
dev->vuid = kmemdup_nul(vpd_data + start, kw_len, GFP_KERNEL);
|
|
ret = dev->vuid ? 0 : -ENOMEM;
|
|
|
|
end:
|
|
kfree(vpd_data);
|
|
return ret;
|
|
}
|
|
|
|
static void mlx5_data_direct_shutdown(struct pci_dev *pdev)
|
|
{
|
|
pci_disable_device(pdev);
|
|
}
|
|
|
|
static int mlx5_data_direct_set_dma_caps(struct pci_dev *pdev)
|
|
{
|
|
int err;
|
|
|
|
err = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(64));
|
|
if (err) {
|
|
dev_warn(&pdev->dev,
|
|
"Warning: couldn't set 64-bit PCI DMA mask, err=%d\n", err);
|
|
err = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(32));
|
|
if (err) {
|
|
dev_err(&pdev->dev, "Can't set PCI DMA mask, err=%d\n", err);
|
|
return err;
|
|
}
|
|
}
|
|
|
|
dma_set_max_seg_size(&pdev->dev, SZ_2G);
|
|
return 0;
|
|
}
|
|
|
|
int mlx5_data_direct_ib_reg(struct mlx5_ib_dev *ibdev, char *vuid)
|
|
{
|
|
struct mlx5_data_direct_registration *reg;
|
|
struct mlx5_data_direct_dev *dev;
|
|
|
|
reg = kzalloc(sizeof(*reg), GFP_KERNEL);
|
|
if (!reg)
|
|
return -ENOMEM;
|
|
|
|
reg->ibdev = ibdev;
|
|
strcpy(reg->vuid, vuid);
|
|
|
|
mutex_lock(&mlx5_data_direct_mutex);
|
|
list_for_each_entry(dev, &mlx5_data_direct_dev_list, list) {
|
|
if (strcmp(dev->vuid, vuid) == 0) {
|
|
mlx5_ib_data_direct_bind(ibdev, dev);
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* Add the registration to its global list, to be used upon bind/unbind
|
|
* of its affiliated data direct device
|
|
*/
|
|
list_add_tail(®->list, &mlx5_data_direct_reg_list);
|
|
mutex_unlock(&mlx5_data_direct_mutex);
|
|
return 0;
|
|
}
|
|
|
|
void mlx5_data_direct_ib_unreg(struct mlx5_ib_dev *ibdev)
|
|
{
|
|
struct mlx5_data_direct_registration *reg;
|
|
|
|
mutex_lock(&mlx5_data_direct_mutex);
|
|
list_for_each_entry(reg, &mlx5_data_direct_reg_list, list) {
|
|
if (reg->ibdev == ibdev) {
|
|
list_del(®->list);
|
|
kfree(reg);
|
|
goto end;
|
|
}
|
|
}
|
|
|
|
WARN_ON(true);
|
|
end:
|
|
mutex_unlock(&mlx5_data_direct_mutex);
|
|
}
|
|
|
|
static void mlx5_data_direct_dev_reg(struct mlx5_data_direct_dev *dev)
|
|
{
|
|
struct mlx5_data_direct_registration *reg;
|
|
|
|
mutex_lock(&mlx5_data_direct_mutex);
|
|
list_for_each_entry(reg, &mlx5_data_direct_reg_list, list) {
|
|
if (strcmp(dev->vuid, reg->vuid) == 0)
|
|
mlx5_ib_data_direct_bind(reg->ibdev, dev);
|
|
}
|
|
|
|
/* Add the data direct device to the global list, further IB devices may
|
|
* use it later as well
|
|
*/
|
|
list_add_tail(&dev->list, &mlx5_data_direct_dev_list);
|
|
mutex_unlock(&mlx5_data_direct_mutex);
|
|
}
|
|
|
|
static void mlx5_data_direct_dev_unreg(struct mlx5_data_direct_dev *dev)
|
|
{
|
|
struct mlx5_data_direct_registration *reg;
|
|
|
|
mutex_lock(&mlx5_data_direct_mutex);
|
|
/* Prevent any further affiliations */
|
|
list_del(&dev->list);
|
|
list_for_each_entry(reg, &mlx5_data_direct_reg_list, list) {
|
|
if (strcmp(dev->vuid, reg->vuid) == 0)
|
|
mlx5_ib_data_direct_unbind(reg->ibdev);
|
|
}
|
|
mutex_unlock(&mlx5_data_direct_mutex);
|
|
}
|
|
|
|
static int mlx5_data_direct_probe(struct pci_dev *pdev, const struct pci_device_id *id)
|
|
{
|
|
struct mlx5_data_direct_dev *dev;
|
|
int err;
|
|
|
|
dev = kzalloc(sizeof(*dev), GFP_KERNEL);
|
|
if (!dev)
|
|
return -ENOMEM;
|
|
|
|
dev->device = &pdev->dev;
|
|
dev->pdev = pdev;
|
|
|
|
pci_set_drvdata(dev->pdev, dev);
|
|
err = pci_enable_device(pdev);
|
|
if (err) {
|
|
dev_err(dev->device, "Cannot enable PCI device, err=%d\n", err);
|
|
goto err;
|
|
}
|
|
|
|
pci_set_master(pdev);
|
|
err = mlx5_data_direct_set_dma_caps(pdev);
|
|
if (err)
|
|
goto err_disable;
|
|
|
|
if (pci_enable_atomic_ops_to_root(pdev, PCI_EXP_DEVCAP2_ATOMIC_COMP32) &&
|
|
pci_enable_atomic_ops_to_root(pdev, PCI_EXP_DEVCAP2_ATOMIC_COMP64) &&
|
|
pci_enable_atomic_ops_to_root(pdev, PCI_EXP_DEVCAP2_ATOMIC_COMP128))
|
|
dev_dbg(dev->device, "Enabling pci atomics failed\n");
|
|
|
|
err = mlx5_data_direct_vpd_get_vuid(dev);
|
|
if (err)
|
|
goto err_disable;
|
|
|
|
mlx5_data_direct_dev_reg(dev);
|
|
return 0;
|
|
|
|
err_disable:
|
|
pci_disable_device(pdev);
|
|
err:
|
|
kfree(dev);
|
|
return err;
|
|
}
|
|
|
|
static void mlx5_data_direct_remove(struct pci_dev *pdev)
|
|
{
|
|
struct mlx5_data_direct_dev *dev = pci_get_drvdata(pdev);
|
|
|
|
mlx5_data_direct_dev_unreg(dev);
|
|
pci_disable_device(pdev);
|
|
kfree(dev->vuid);
|
|
kfree(dev);
|
|
}
|
|
|
|
static struct pci_driver mlx5_data_direct_driver = {
|
|
.name = KBUILD_MODNAME,
|
|
.id_table = mlx5_data_direct_pci_table,
|
|
.probe = mlx5_data_direct_probe,
|
|
.remove = mlx5_data_direct_remove,
|
|
.shutdown = mlx5_data_direct_shutdown,
|
|
};
|
|
|
|
int mlx5_data_direct_driver_register(void)
|
|
{
|
|
return pci_register_driver(&mlx5_data_direct_driver);
|
|
}
|
|
|
|
void mlx5_data_direct_driver_unregister(void)
|
|
{
|
|
pci_unregister_driver(&mlx5_data_direct_driver);
|
|
}
|