mirror of
https://github.com/linux-msm/laptops-kernel.git
synced 2026-08-13 14:19:53 -07:00
Merge tag 'for_linus' of git://git.kernel.org/pub/scm/linux/kernel/git/mst/vhost
Pull virtio updates from Michael Tsirkin: - device feature provisioning in ifcvf, mlx5 - new SolidNET driver - support for zoned block device in virtio blk - numa support in virtio pmem - VIRTIO_F_RING_RESET support in vhost-net - more debugfs entries in mlx5 - resume support in vdpa - completion batching in virtio blk - cleanup of dma api use in vdpa - now simulating more features in vdpa-sim - documentation, features, fixes all over the place * tag 'for_linus' of git://git.kernel.org/pub/scm/linux/kernel/git/mst/vhost: (64 commits) vdpa/mlx5: support device features provisioning vdpa/mlx5: make MTU/STATUS presence conditional on feature bits vdpa: validate device feature provisioning against supported class vdpa: validate provisioned device features against specified attribute vdpa: conditionally read STATUS in config space vdpa: fix improper error message when adding vdpa dev vdpa/mlx5: Initialize CVQ iotlb spinlock vdpa/mlx5: Don't clear mr struct on destroy MR vdpa/mlx5: Directly assign memory key tools/virtio: enable to build with retpoline vringh: fix a typo in comments for vringh_kiov vhost-vdpa: print warning when vhost_vdpa_alloc_domain fails scsi: virtio_scsi: fix handling of kmalloc failure vdpa: Fix a couple of spelling mistakes in some messages vhost-net: support VIRTIO_F_RING_RESET vhost-scsi: convert sysfs snprintf and sprintf to sysfs_emit vdpa: mlx5: support per virtqueue dma device vdpa: set dma mask for vDPA device virtio-vdpa: support per vq dma device vdpa: introduce get_vq_dma_device() ...
This commit is contained in:
@@ -108,6 +108,7 @@ available subsections can be seen below.
|
||||
vfio-mediated-device
|
||||
vfio
|
||||
vfio-pci-device-specific-driver-acceptance
|
||||
virtio/index
|
||||
xilinx/index
|
||||
xillybus
|
||||
zorro
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
.. SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
======
|
||||
Virtio
|
||||
======
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 1
|
||||
|
||||
virtio
|
||||
writing_virtio_drivers
|
||||
@@ -0,0 +1,145 @@
|
||||
.. SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
.. _virtio:
|
||||
|
||||
===============
|
||||
Virtio on Linux
|
||||
===============
|
||||
|
||||
Introduction
|
||||
============
|
||||
|
||||
Virtio is an open standard that defines a protocol for communication
|
||||
between drivers and devices of different types, see Chapter 5 ("Device
|
||||
Types") of the virtio spec (`[1]`_). Originally developed as a standard
|
||||
for paravirtualized devices implemented by a hypervisor, it can be used
|
||||
to interface any compliant device (real or emulated) with a driver.
|
||||
|
||||
For illustrative purposes, this document will focus on the common case
|
||||
of a Linux kernel running in a virtual machine and using paravirtualized
|
||||
devices provided by the hypervisor, which exposes them as virtio devices
|
||||
via standard mechanisms such as PCI.
|
||||
|
||||
|
||||
Device - Driver communication: virtqueues
|
||||
=========================================
|
||||
|
||||
Although the virtio devices are really an abstraction layer in the
|
||||
hypervisor, they're exposed to the guest as if they are physical devices
|
||||
using a specific transport method -- PCI, MMIO or CCW -- that is
|
||||
orthogonal to the device itself. The virtio spec defines these transport
|
||||
methods in detail, including device discovery, capabilities and
|
||||
interrupt handling.
|
||||
|
||||
The communication between the driver in the guest OS and the device in
|
||||
the hypervisor is done through shared memory (that's what makes virtio
|
||||
devices so efficient) using specialized data structures called
|
||||
virtqueues, which are actually ring buffers [#f1]_ of buffer descriptors
|
||||
similar to the ones used in a network device:
|
||||
|
||||
.. kernel-doc:: include/uapi/linux/virtio_ring.h
|
||||
:identifiers: struct vring_desc
|
||||
|
||||
All the buffers the descriptors point to are allocated by the guest and
|
||||
used by the host either for reading or for writing but not for both.
|
||||
|
||||
Refer to Chapter 2.5 ("Virtqueues") of the virtio spec (`[1]`_) for the
|
||||
reference definitions of virtqueues and "Virtqueues and virtio ring: How
|
||||
the data travels" blog post (`[2]`_) for an illustrated overview of how
|
||||
the host device and the guest driver communicate.
|
||||
|
||||
The :c:type:`vring_virtqueue` struct models a virtqueue, including the
|
||||
ring buffers and management data. Embedded in this struct is the
|
||||
:c:type:`virtqueue` struct, which is the data structure that's
|
||||
ultimately used by virtio drivers:
|
||||
|
||||
.. kernel-doc:: include/linux/virtio.h
|
||||
:identifiers: struct virtqueue
|
||||
|
||||
The callback function pointed by this struct is triggered when the
|
||||
device has consumed the buffers provided by the driver. More
|
||||
specifically, the trigger will be an interrupt issued by the hypervisor
|
||||
(see vring_interrupt()). Interrupt request handlers are registered for
|
||||
a virtqueue during the virtqueue setup process (transport-specific).
|
||||
|
||||
.. kernel-doc:: drivers/virtio/virtio_ring.c
|
||||
:identifiers: vring_interrupt
|
||||
|
||||
|
||||
Device discovery and probing
|
||||
============================
|
||||
|
||||
In the kernel, the virtio core contains the virtio bus driver and
|
||||
transport-specific drivers like `virtio-pci` and `virtio-mmio`. Then
|
||||
there are individual virtio drivers for specific device types that are
|
||||
registered to the virtio bus driver.
|
||||
|
||||
How a virtio device is found and configured by the kernel depends on how
|
||||
the hypervisor defines it. Taking the `QEMU virtio-console
|
||||
<https://gitlab.com/qemu-project/qemu/-/blob/master/hw/char/virtio-console.c>`__
|
||||
device as an example. When using PCI as a transport method, the device
|
||||
will present itself on the PCI bus with vendor 0x1af4 (Red Hat, Inc.)
|
||||
and device id 0x1003 (virtio console), as defined in the spec, so the
|
||||
kernel will detect it as it would do with any other PCI device.
|
||||
|
||||
During the PCI enumeration process, if a device is found to match the
|
||||
virtio-pci driver (according to the virtio-pci device table, any PCI
|
||||
device with vendor id = 0x1af4)::
|
||||
|
||||
/* Qumranet donated their vendor ID for devices 0x1000 thru 0x10FF. */
|
||||
static const struct pci_device_id virtio_pci_id_table[] = {
|
||||
{ PCI_DEVICE(PCI_VENDOR_ID_REDHAT_QUMRANET, PCI_ANY_ID) },
|
||||
{ 0 }
|
||||
};
|
||||
|
||||
then the virtio-pci driver is probed and, if the probing goes well, the
|
||||
device is registered to the virtio bus::
|
||||
|
||||
static int virtio_pci_probe(struct pci_dev *pci_dev,
|
||||
const struct pci_device_id *id)
|
||||
{
|
||||
...
|
||||
|
||||
if (force_legacy) {
|
||||
rc = virtio_pci_legacy_probe(vp_dev);
|
||||
/* Also try modern mode if we can't map BAR0 (no IO space). */
|
||||
if (rc == -ENODEV || rc == -ENOMEM)
|
||||
rc = virtio_pci_modern_probe(vp_dev);
|
||||
if (rc)
|
||||
goto err_probe;
|
||||
} else {
|
||||
rc = virtio_pci_modern_probe(vp_dev);
|
||||
if (rc == -ENODEV)
|
||||
rc = virtio_pci_legacy_probe(vp_dev);
|
||||
if (rc)
|
||||
goto err_probe;
|
||||
}
|
||||
|
||||
...
|
||||
|
||||
rc = register_virtio_device(&vp_dev->vdev);
|
||||
|
||||
When the device is registered to the virtio bus the kernel will look
|
||||
for a driver in the bus that can handle the device and call that
|
||||
driver's ``probe`` method.
|
||||
|
||||
At this point, the virtqueues will be allocated and configured by
|
||||
calling the appropriate ``virtio_find`` helper function, such as
|
||||
virtio_find_single_vq() or virtio_find_vqs(), which will end up calling
|
||||
a transport-specific ``find_vqs`` method.
|
||||
|
||||
|
||||
References
|
||||
==========
|
||||
|
||||
_`[1]` Virtio Spec v1.2:
|
||||
https://docs.oasis-open.org/virtio/virtio/v1.2/virtio-v1.2.html
|
||||
|
||||
.. Check for later versions of the spec as well.
|
||||
|
||||
_`[2]` Virtqueues and virtio ring: How the data travels
|
||||
https://www.redhat.com/en/blog/virtqueues-and-virtio-ring-how-data-travels
|
||||
|
||||
.. rubric:: Footnotes
|
||||
|
||||
.. [#f1] that's why they may be also referred to as virtrings.
|
||||
@@ -0,0 +1,197 @@
|
||||
.. SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
.. _writing_virtio_drivers:
|
||||
|
||||
======================
|
||||
Writing Virtio Drivers
|
||||
======================
|
||||
|
||||
Introduction
|
||||
============
|
||||
|
||||
This document serves as a basic guideline for driver programmers that
|
||||
need to hack a new virtio driver or understand the essentials of the
|
||||
existing ones. See :ref:`Virtio on Linux <virtio>` for a general
|
||||
overview of virtio.
|
||||
|
||||
|
||||
Driver boilerplate
|
||||
==================
|
||||
|
||||
As a bare minimum, a virtio driver needs to register in the virtio bus
|
||||
and configure the virtqueues for the device according to its spec, the
|
||||
configuration of the virtqueues in the driver side must match the
|
||||
virtqueue definitions in the device. A basic driver skeleton could look
|
||||
like this::
|
||||
|
||||
#include <linux/virtio.h>
|
||||
#include <linux/virtio_ids.h>
|
||||
#include <linux/virtio_config.h>
|
||||
#include <linux/module.h>
|
||||
|
||||
/* device private data (one per device) */
|
||||
struct virtio_dummy_dev {
|
||||
struct virtqueue *vq;
|
||||
};
|
||||
|
||||
static void virtio_dummy_recv_cb(struct virtqueue *vq)
|
||||
{
|
||||
struct virtio_dummy_dev *dev = vq->vdev->priv;
|
||||
char *buf;
|
||||
unsigned int len;
|
||||
|
||||
while ((buf = virtqueue_get_buf(dev->vq, &len)) != NULL) {
|
||||
/* process the received data */
|
||||
}
|
||||
}
|
||||
|
||||
static int virtio_dummy_probe(struct virtio_device *vdev)
|
||||
{
|
||||
struct virtio_dummy_dev *dev = NULL;
|
||||
|
||||
/* initialize device data */
|
||||
dev = kzalloc(sizeof(struct virtio_dummy_dev), GFP_KERNEL);
|
||||
if (!dev)
|
||||
return -ENOMEM;
|
||||
|
||||
/* the device has a single virtqueue */
|
||||
dev->vq = virtio_find_single_vq(vdev, virtio_dummy_recv_cb, "input");
|
||||
if (IS_ERR(dev->vq)) {
|
||||
kfree(dev);
|
||||
return PTR_ERR(dev->vq);
|
||||
|
||||
}
|
||||
vdev->priv = dev;
|
||||
|
||||
/* from this point on, the device can notify and get callbacks */
|
||||
virtio_device_ready(vdev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void virtio_dummy_remove(struct virtio_device *vdev)
|
||||
{
|
||||
struct virtio_dummy_dev *dev = vdev->priv;
|
||||
|
||||
/*
|
||||
* disable vq interrupts: equivalent to
|
||||
* vdev->config->reset(vdev)
|
||||
*/
|
||||
virtio_reset_device(vdev);
|
||||
|
||||
/* detach unused buffers */
|
||||
while ((buf = virtqueue_detach_unused_buf(dev->vq)) != NULL) {
|
||||
kfree(buf);
|
||||
}
|
||||
|
||||
/* remove virtqueues */
|
||||
vdev->config->del_vqs(vdev);
|
||||
|
||||
kfree(dev);
|
||||
}
|
||||
|
||||
static const struct virtio_device_id id_table[] = {
|
||||
{ VIRTIO_ID_DUMMY, VIRTIO_DEV_ANY_ID },
|
||||
{ 0 },
|
||||
};
|
||||
|
||||
static struct virtio_driver virtio_dummy_driver = {
|
||||
.driver.name = KBUILD_MODNAME,
|
||||
.driver.owner = THIS_MODULE,
|
||||
.id_table = id_table,
|
||||
.probe = virtio_dummy_probe,
|
||||
.remove = virtio_dummy_remove,
|
||||
};
|
||||
|
||||
module_virtio_driver(virtio_dummy_driver);
|
||||
MODULE_DEVICE_TABLE(virtio, id_table);
|
||||
MODULE_DESCRIPTION("Dummy virtio driver");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
||||
The device id ``VIRTIO_ID_DUMMY`` here is a placeholder, virtio drivers
|
||||
should be added only for devices that are defined in the spec, see
|
||||
include/uapi/linux/virtio_ids.h. Device ids need to be at least reserved
|
||||
in the virtio spec before being added to that file.
|
||||
|
||||
If your driver doesn't have to do anything special in its ``init`` and
|
||||
``exit`` methods, you can use the module_virtio_driver() helper to
|
||||
reduce the amount of boilerplate code.
|
||||
|
||||
The ``probe`` method does the minimum driver setup in this case
|
||||
(memory allocation for the device data) and initializes the
|
||||
virtqueue. virtio_device_ready() is used to enable the virtqueue and to
|
||||
notify the device that the driver is ready to manage the device
|
||||
("DRIVER_OK"). The virtqueues are anyway enabled automatically by the
|
||||
core after ``probe`` returns.
|
||||
|
||||
.. kernel-doc:: include/linux/virtio_config.h
|
||||
:identifiers: virtio_device_ready
|
||||
|
||||
In any case, the virtqueues need to be enabled before adding buffers to
|
||||
them.
|
||||
|
||||
Sending and receiving data
|
||||
==========================
|
||||
|
||||
The virtio_dummy_recv_cb() callback in the code above will be triggered
|
||||
when the device notifies the driver after it finishes processing a
|
||||
descriptor or descriptor chain, either for reading or writing. However,
|
||||
that's only the second half of the virtio device-driver communication
|
||||
process, as the communication is always started by the driver regardless
|
||||
of the direction of the data transfer.
|
||||
|
||||
To configure a buffer transfer from the driver to the device, first you
|
||||
have to add the buffers -- packed as `scatterlists` -- to the
|
||||
appropriate virtqueue using any of the virtqueue_add_inbuf(),
|
||||
virtqueue_add_outbuf() or virtqueue_add_sgs(), depending on whether you
|
||||
need to add one input `scatterlist` (for the device to fill in), one
|
||||
output `scatterlist` (for the device to consume) or multiple
|
||||
`scatterlists`, respectively. Then, once the virtqueue is set up, a call
|
||||
to virtqueue_kick() sends a notification that will be serviced by the
|
||||
hypervisor that implements the device::
|
||||
|
||||
struct scatterlist sg[1];
|
||||
sg_init_one(sg, buffer, BUFLEN);
|
||||
virtqueue_add_inbuf(dev->vq, sg, 1, buffer, GFP_ATOMIC);
|
||||
virtqueue_kick(dev->vq);
|
||||
|
||||
.. kernel-doc:: drivers/virtio/virtio_ring.c
|
||||
:identifiers: virtqueue_add_inbuf
|
||||
|
||||
.. kernel-doc:: drivers/virtio/virtio_ring.c
|
||||
:identifiers: virtqueue_add_outbuf
|
||||
|
||||
.. kernel-doc:: drivers/virtio/virtio_ring.c
|
||||
:identifiers: virtqueue_add_sgs
|
||||
|
||||
Then, after the device has read or written the buffers prepared by the
|
||||
driver and notifies it back, the driver can call virtqueue_get_buf() to
|
||||
read the data produced by the device (if the virtqueue was set up with
|
||||
input buffers) or simply to reclaim the buffers if they were already
|
||||
consumed by the device:
|
||||
|
||||
.. kernel-doc:: drivers/virtio/virtio_ring.c
|
||||
:identifiers: virtqueue_get_buf_ctx
|
||||
|
||||
The virtqueue callbacks can be disabled and re-enabled using the
|
||||
virtqueue_disable_cb() and the family of virtqueue_enable_cb() functions
|
||||
respectively. See drivers/virtio/virtio_ring.c for more details:
|
||||
|
||||
.. kernel-doc:: drivers/virtio/virtio_ring.c
|
||||
:identifiers: virtqueue_disable_cb
|
||||
|
||||
.. kernel-doc:: drivers/virtio/virtio_ring.c
|
||||
:identifiers: virtqueue_enable_cb
|
||||
|
||||
But note that some spurious callbacks can still be triggered under
|
||||
certain scenarios. The way to disable callbacks reliably is to reset the
|
||||
device or the virtqueue (virtio_reset_device()).
|
||||
|
||||
|
||||
References
|
||||
==========
|
||||
|
||||
_`[1]` Virtio Spec v1.2:
|
||||
https://docs.oasis-open.org/virtio/virtio/v1.2/virtio-v1.2.html
|
||||
|
||||
Check for later versions of the spec as well.
|
||||
@@ -22057,6 +22057,7 @@ S: Maintained
|
||||
F: Documentation/ABI/testing/sysfs-bus-vdpa
|
||||
F: Documentation/ABI/testing/sysfs-class-vduse
|
||||
F: Documentation/devicetree/bindings/virtio/
|
||||
F: Documentation/driver-api/virtio/
|
||||
F: drivers/block/virtio_blk.c
|
||||
F: drivers/crypto/virtio/
|
||||
F: drivers/net/virtio_net.c
|
||||
@@ -22077,6 +22078,10 @@ IFCVF VIRTIO DATA PATH ACCELERATOR
|
||||
R: Zhu Lingshan <lingshan.zhu@intel.com>
|
||||
F: drivers/vdpa/ifcvf/
|
||||
|
||||
SNET DPU VIRTIO DATA PATH ACCELERATOR
|
||||
R: Alvaro Karsz <alvaro.karsz@solid-run.com>
|
||||
F: drivers/vdpa/solidrun/
|
||||
|
||||
VIRTIO BALLOON
|
||||
M: "Michael S. Tsirkin" <mst@redhat.com>
|
||||
M: David Hildenbrand <david@redhat.com>
|
||||
|
||||
+414
-54
File diff suppressed because it is too large
Load Diff
@@ -32,7 +32,6 @@ static int init_vq(struct virtio_pmem *vpmem)
|
||||
static int virtio_pmem_probe(struct virtio_device *vdev)
|
||||
{
|
||||
struct nd_region_desc ndr_desc = {};
|
||||
int nid = dev_to_node(&vdev->dev);
|
||||
struct nd_region *nd_region;
|
||||
struct virtio_pmem *vpmem;
|
||||
struct resource res;
|
||||
@@ -79,7 +78,15 @@ static int virtio_pmem_probe(struct virtio_device *vdev)
|
||||
dev_set_drvdata(&vdev->dev, vpmem->nvdimm_bus);
|
||||
|
||||
ndr_desc.res = &res;
|
||||
ndr_desc.numa_node = nid;
|
||||
|
||||
ndr_desc.numa_node = memory_add_physaddr_to_nid(res.start);
|
||||
ndr_desc.target_node = phys_to_target_node(res.start);
|
||||
if (ndr_desc.target_node == NUMA_NO_NODE) {
|
||||
ndr_desc.target_node = ndr_desc.numa_node;
|
||||
dev_dbg(&vdev->dev, "changing target node from %d to %d",
|
||||
NUMA_NO_NODE, ndr_desc.target_node);
|
||||
}
|
||||
|
||||
ndr_desc.flush = async_pmem_flush;
|
||||
ndr_desc.provider_data = vdev;
|
||||
set_bit(ND_REGION_PAGEMAP, &ndr_desc.flags);
|
||||
|
||||
@@ -5366,6 +5366,14 @@ DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_AMD, 0x7901, quirk_no_flr);
|
||||
DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_INTEL, 0x1502, quirk_no_flr);
|
||||
DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_INTEL, 0x1503, quirk_no_flr);
|
||||
|
||||
/* FLR may cause the SolidRun SNET DPU (rev 0x1) to hang */
|
||||
static void quirk_no_flr_snet(struct pci_dev *dev)
|
||||
{
|
||||
if (dev->revision == 0x1)
|
||||
quirk_no_flr(dev);
|
||||
}
|
||||
DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_SOLIDRUN, 0x1000, quirk_no_flr_snet);
|
||||
|
||||
static void quirk_no_ext_tags(struct pci_dev *pdev)
|
||||
{
|
||||
struct pci_host_bridge *bridge = pci_find_host_bridge(pdev->bus);
|
||||
|
||||
@@ -330,7 +330,7 @@ static void virtscsi_handle_param_change(struct virtio_scsi *vscsi,
|
||||
scsi_device_put(sdev);
|
||||
}
|
||||
|
||||
static void virtscsi_rescan_hotunplug(struct virtio_scsi *vscsi)
|
||||
static int virtscsi_rescan_hotunplug(struct virtio_scsi *vscsi)
|
||||
{
|
||||
struct scsi_device *sdev;
|
||||
struct Scsi_Host *shost = virtio_scsi_host(vscsi->vdev);
|
||||
@@ -338,6 +338,11 @@ static void virtscsi_rescan_hotunplug(struct virtio_scsi *vscsi)
|
||||
int result, inquiry_len, inq_result_len = 256;
|
||||
char *inq_result = kmalloc(inq_result_len, GFP_KERNEL);
|
||||
|
||||
if (!inq_result) {
|
||||
kfree(inq_result);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
shost_for_each_device(sdev, shost) {
|
||||
inquiry_len = sdev->inquiry_len ? sdev->inquiry_len : 36;
|
||||
|
||||
@@ -366,6 +371,7 @@ static void virtscsi_rescan_hotunplug(struct virtio_scsi *vscsi)
|
||||
}
|
||||
|
||||
kfree(inq_result);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void virtscsi_handle_event(struct work_struct *work)
|
||||
@@ -377,9 +383,13 @@ static void virtscsi_handle_event(struct work_struct *work)
|
||||
|
||||
if (event->event &
|
||||
cpu_to_virtio32(vscsi->vdev, VIRTIO_SCSI_T_EVENTS_MISSED)) {
|
||||
int ret;
|
||||
|
||||
event->event &= ~cpu_to_virtio32(vscsi->vdev,
|
||||
VIRTIO_SCSI_T_EVENTS_MISSED);
|
||||
virtscsi_rescan_hotunplug(vscsi);
|
||||
ret = virtscsi_rescan_hotunplug(vscsi);
|
||||
if (ret)
|
||||
return;
|
||||
scsi_scan_host(virtio_scsi_host(vscsi->vdev));
|
||||
}
|
||||
|
||||
|
||||
@@ -71,6 +71,18 @@ config MLX5_VDPA_NET
|
||||
be executed by the hardware. It also supports a variety of stateless
|
||||
offloads depending on the actual device used and firmware version.
|
||||
|
||||
config MLX5_VDPA_STEERING_DEBUG
|
||||
bool "expose steering counters on debugfs"
|
||||
select MLX5_VDPA
|
||||
help
|
||||
Expose RX steering counters in debugfs to aid in debugging. For each VLAN
|
||||
or non VLAN interface, two hardware counters are added to the RX flow
|
||||
table: one for unicast and one for multicast.
|
||||
The counters counts the number of packets and bytes and exposes them in
|
||||
debugfs. Once can read the counters using, e.g.:
|
||||
cat /sys/kernel/debug/mlx5/mlx5_core.sf.1/vdpa-0/rx/untagged/ucast/packets
|
||||
cat /sys/kernel/debug/mlx5/mlx5_core.sf.1/vdpa-0/rx/untagged/mcast/bytes
|
||||
|
||||
config VP_VDPA
|
||||
tristate "Virtio PCI bridge vDPA driver"
|
||||
select VIRTIO_PCI_LIB
|
||||
@@ -86,4 +98,22 @@ config ALIBABA_ENI_VDPA
|
||||
VDPA driver for Alibaba ENI (Elastic Network Interface) which is built upon
|
||||
virtio 0.9.5 specification.
|
||||
|
||||
config SNET_VDPA
|
||||
tristate "SolidRun's vDPA driver for SolidNET"
|
||||
depends on PCI_MSI && PCI_IOV && (HWMON || HWMON=n)
|
||||
|
||||
# This driver MAY create a HWMON device.
|
||||
# Depending on (HWMON || HWMON=n) ensures that:
|
||||
# If HWMON=n the driver can be compiled either as a module or built-in.
|
||||
# If HWMON=y the driver can be compiled either as a module or built-in.
|
||||
# If HWMON=m the driver is forced to be compiled as a module.
|
||||
# By doing so, IS_ENABLED can be used instead of IS_REACHABLE
|
||||
|
||||
help
|
||||
vDPA driver for SolidNET DPU.
|
||||
With this driver, the VirtIO dataplane can be
|
||||
offloaded to a SolidNET DPU.
|
||||
This driver includes a HW monitor device that
|
||||
reads health values from the DPU.
|
||||
|
||||
endif # VDPA
|
||||
|
||||
@@ -6,3 +6,4 @@ obj-$(CONFIG_IFCVF) += ifcvf/
|
||||
obj-$(CONFIG_MLX5_VDPA) += mlx5/
|
||||
obj-$(CONFIG_VP_VDPA) += virtio_pci/
|
||||
obj-$(CONFIG_ALIBABA_ENI_VDPA) += alibaba/
|
||||
obj-$(CONFIG_SNET_VDPA) += solidrun/
|
||||
|
||||
@@ -10,11 +10,6 @@
|
||||
|
||||
#include "ifcvf_base.h"
|
||||
|
||||
struct ifcvf_adapter *vf_to_adapter(struct ifcvf_hw *hw)
|
||||
{
|
||||
return container_of(hw, struct ifcvf_adapter, vf);
|
||||
}
|
||||
|
||||
u16 ifcvf_set_vq_vector(struct ifcvf_hw *hw, u16 qid, int vector)
|
||||
{
|
||||
struct virtio_pci_common_cfg __iomem *cfg = hw->common_cfg;
|
||||
@@ -37,8 +32,6 @@ u16 ifcvf_set_config_vector(struct ifcvf_hw *hw, int vector)
|
||||
static void __iomem *get_cap_addr(struct ifcvf_hw *hw,
|
||||
struct virtio_pci_cap *cap)
|
||||
{
|
||||
struct ifcvf_adapter *ifcvf;
|
||||
struct pci_dev *pdev;
|
||||
u32 length, offset;
|
||||
u8 bar;
|
||||
|
||||
@@ -46,17 +39,14 @@ static void __iomem *get_cap_addr(struct ifcvf_hw *hw,
|
||||
offset = le32_to_cpu(cap->offset);
|
||||
bar = cap->bar;
|
||||
|
||||
ifcvf= vf_to_adapter(hw);
|
||||
pdev = ifcvf->pdev;
|
||||
|
||||
if (bar >= IFCVF_PCI_MAX_RESOURCE) {
|
||||
IFCVF_DBG(pdev,
|
||||
IFCVF_DBG(hw->pdev,
|
||||
"Invalid bar number %u to get capabilities\n", bar);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (offset + length > pci_resource_len(pdev, bar)) {
|
||||
IFCVF_DBG(pdev,
|
||||
if (offset + length > pci_resource_len(hw->pdev, bar)) {
|
||||
IFCVF_DBG(hw->pdev,
|
||||
"offset(%u) + len(%u) overflows bar%u's capability\n",
|
||||
offset, length, bar);
|
||||
return NULL;
|
||||
@@ -92,6 +82,7 @@ int ifcvf_init_hw(struct ifcvf_hw *hw, struct pci_dev *pdev)
|
||||
IFCVF_ERR(pdev, "Failed to read PCI capability list\n");
|
||||
return -EIO;
|
||||
}
|
||||
hw->pdev = pdev;
|
||||
|
||||
while (pos) {
|
||||
ret = ifcvf_read_config_range(pdev, (u32 *)&cap,
|
||||
@@ -215,15 +206,13 @@ u64 ifcvf_get_hw_features(struct ifcvf_hw *hw)
|
||||
|
||||
u64 ifcvf_get_features(struct ifcvf_hw *hw)
|
||||
{
|
||||
return hw->hw_features;
|
||||
return hw->dev_features;
|
||||
}
|
||||
|
||||
int ifcvf_verify_min_features(struct ifcvf_hw *hw, u64 features)
|
||||
{
|
||||
struct ifcvf_adapter *ifcvf = vf_to_adapter(hw);
|
||||
|
||||
if (!(features & BIT_ULL(VIRTIO_F_ACCESS_PLATFORM)) && features) {
|
||||
IFCVF_ERR(ifcvf->pdev, "VIRTIO_F_ACCESS_PLATFORM is not negotiated\n");
|
||||
IFCVF_ERR(hw->pdev, "VIRTIO_F_ACCESS_PLATFORM is not negotiated\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
@@ -232,13 +221,11 @@ int ifcvf_verify_min_features(struct ifcvf_hw *hw, u64 features)
|
||||
|
||||
u32 ifcvf_get_config_size(struct ifcvf_hw *hw)
|
||||
{
|
||||
struct ifcvf_adapter *adapter;
|
||||
u32 net_config_size = sizeof(struct virtio_net_config);
|
||||
u32 blk_config_size = sizeof(struct virtio_blk_config);
|
||||
u32 cap_size = hw->cap_dev_config_size;
|
||||
u32 config_size;
|
||||
|
||||
adapter = vf_to_adapter(hw);
|
||||
/* If the onboard device config space size is greater than
|
||||
* the size of struct virtio_net/blk_config, only the spec
|
||||
* implementing contents size is returned, this is very
|
||||
@@ -253,7 +240,7 @@ u32 ifcvf_get_config_size(struct ifcvf_hw *hw)
|
||||
break;
|
||||
default:
|
||||
config_size = 0;
|
||||
IFCVF_ERR(adapter->pdev, "VIRTIO ID %u not supported\n", hw->dev_type);
|
||||
IFCVF_ERR(hw->pdev, "VIRTIO ID %u not supported\n", hw->dev_type);
|
||||
}
|
||||
|
||||
return config_size;
|
||||
@@ -301,14 +288,11 @@ static void ifcvf_set_features(struct ifcvf_hw *hw, u64 features)
|
||||
|
||||
static int ifcvf_config_features(struct ifcvf_hw *hw)
|
||||
{
|
||||
struct ifcvf_adapter *ifcvf;
|
||||
|
||||
ifcvf = vf_to_adapter(hw);
|
||||
ifcvf_set_features(hw, hw->req_features);
|
||||
ifcvf_add_status(hw, VIRTIO_CONFIG_S_FEATURES_OK);
|
||||
|
||||
if (!(ifcvf_get_status(hw) & VIRTIO_CONFIG_S_FEATURES_OK)) {
|
||||
IFCVF_ERR(ifcvf->pdev, "Failed to set FEATURES_OK status\n");
|
||||
IFCVF_ERR(hw->pdev, "Failed to set FEATURES_OK status\n");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
#include <uapi/linux/virtio_blk.h>
|
||||
#include <uapi/linux/virtio_config.h>
|
||||
#include <uapi/linux/virtio_pci.h>
|
||||
#include <uapi/linux/vdpa.h>
|
||||
|
||||
#define N3000_DEVICE_ID 0x1041
|
||||
#define N3000_SUBSYS_DEVICE_ID 0x001A
|
||||
@@ -38,9 +39,6 @@
|
||||
#define IFCVF_DBG(pdev, fmt, ...) dev_dbg(&pdev->dev, fmt, ##__VA_ARGS__)
|
||||
#define IFCVF_INFO(pdev, fmt, ...) dev_info(&pdev->dev, fmt, ##__VA_ARGS__)
|
||||
|
||||
#define ifcvf_private_to_vf(adapter) \
|
||||
(&((struct ifcvf_adapter *)adapter)->vf)
|
||||
|
||||
/* all vqs and config interrupt has its own vector */
|
||||
#define MSIX_VECTOR_PER_VQ_AND_CONFIG 1
|
||||
/* all vqs share a vector, and config interrupt has a separate vector */
|
||||
@@ -78,6 +76,8 @@ struct ifcvf_hw {
|
||||
u32 dev_type;
|
||||
u64 req_features;
|
||||
u64 hw_features;
|
||||
/* provisioned device features */
|
||||
u64 dev_features;
|
||||
struct virtio_pci_common_cfg __iomem *common_cfg;
|
||||
void __iomem *dev_cfg;
|
||||
struct vring_info vring[IFCVF_MAX_QUEUES];
|
||||
@@ -89,12 +89,13 @@ struct ifcvf_hw {
|
||||
u16 nr_vring;
|
||||
/* VIRTIO_PCI_CAP_DEVICE_CFG size */
|
||||
u32 cap_dev_config_size;
|
||||
struct pci_dev *pdev;
|
||||
};
|
||||
|
||||
struct ifcvf_adapter {
|
||||
struct vdpa_device vdpa;
|
||||
struct pci_dev *pdev;
|
||||
struct ifcvf_hw vf;
|
||||
struct ifcvf_hw *vf;
|
||||
};
|
||||
|
||||
struct ifcvf_vring_lm_cfg {
|
||||
@@ -109,6 +110,7 @@ struct ifcvf_lm_cfg {
|
||||
|
||||
struct ifcvf_vdpa_mgmt_dev {
|
||||
struct vdpa_mgmt_dev mdev;
|
||||
struct ifcvf_hw vf;
|
||||
struct ifcvf_adapter *adapter;
|
||||
struct pci_dev *pdev;
|
||||
};
|
||||
|
||||
@@ -69,10 +69,9 @@ static void ifcvf_free_irq_vectors(void *data)
|
||||
pci_free_irq_vectors(data);
|
||||
}
|
||||
|
||||
static void ifcvf_free_per_vq_irq(struct ifcvf_adapter *adapter)
|
||||
static void ifcvf_free_per_vq_irq(struct ifcvf_hw *vf)
|
||||
{
|
||||
struct pci_dev *pdev = adapter->pdev;
|
||||
struct ifcvf_hw *vf = &adapter->vf;
|
||||
struct pci_dev *pdev = vf->pdev;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < vf->nr_vring; i++) {
|
||||
@@ -83,10 +82,9 @@ static void ifcvf_free_per_vq_irq(struct ifcvf_adapter *adapter)
|
||||
}
|
||||
}
|
||||
|
||||
static void ifcvf_free_vqs_reused_irq(struct ifcvf_adapter *adapter)
|
||||
static void ifcvf_free_vqs_reused_irq(struct ifcvf_hw *vf)
|
||||
{
|
||||
struct pci_dev *pdev = adapter->pdev;
|
||||
struct ifcvf_hw *vf = &adapter->vf;
|
||||
struct pci_dev *pdev = vf->pdev;
|
||||
|
||||
if (vf->vqs_reused_irq != -EINVAL) {
|
||||
devm_free_irq(&pdev->dev, vf->vqs_reused_irq, vf);
|
||||
@@ -95,20 +93,17 @@ static void ifcvf_free_vqs_reused_irq(struct ifcvf_adapter *adapter)
|
||||
|
||||
}
|
||||
|
||||
static void ifcvf_free_vq_irq(struct ifcvf_adapter *adapter)
|
||||
static void ifcvf_free_vq_irq(struct ifcvf_hw *vf)
|
||||
{
|
||||
struct ifcvf_hw *vf = &adapter->vf;
|
||||
|
||||
if (vf->msix_vector_status == MSIX_VECTOR_PER_VQ_AND_CONFIG)
|
||||
ifcvf_free_per_vq_irq(adapter);
|
||||
ifcvf_free_per_vq_irq(vf);
|
||||
else
|
||||
ifcvf_free_vqs_reused_irq(adapter);
|
||||
ifcvf_free_vqs_reused_irq(vf);
|
||||
}
|
||||
|
||||
static void ifcvf_free_config_irq(struct ifcvf_adapter *adapter)
|
||||
static void ifcvf_free_config_irq(struct ifcvf_hw *vf)
|
||||
{
|
||||
struct pci_dev *pdev = adapter->pdev;
|
||||
struct ifcvf_hw *vf = &adapter->vf;
|
||||
struct pci_dev *pdev = vf->pdev;
|
||||
|
||||
if (vf->config_irq == -EINVAL)
|
||||
return;
|
||||
@@ -123,12 +118,12 @@ static void ifcvf_free_config_irq(struct ifcvf_adapter *adapter)
|
||||
}
|
||||
}
|
||||
|
||||
static void ifcvf_free_irq(struct ifcvf_adapter *adapter)
|
||||
static void ifcvf_free_irq(struct ifcvf_hw *vf)
|
||||
{
|
||||
struct pci_dev *pdev = adapter->pdev;
|
||||
struct pci_dev *pdev = vf->pdev;
|
||||
|
||||
ifcvf_free_vq_irq(adapter);
|
||||
ifcvf_free_config_irq(adapter);
|
||||
ifcvf_free_vq_irq(vf);
|
||||
ifcvf_free_config_irq(vf);
|
||||
ifcvf_free_irq_vectors(pdev);
|
||||
}
|
||||
|
||||
@@ -137,10 +132,9 @@ static void ifcvf_free_irq(struct ifcvf_adapter *adapter)
|
||||
* It returns the number of allocated vectors, negative
|
||||
* return value when fails.
|
||||
*/
|
||||
static int ifcvf_alloc_vectors(struct ifcvf_adapter *adapter)
|
||||
static int ifcvf_alloc_vectors(struct ifcvf_hw *vf)
|
||||
{
|
||||
struct pci_dev *pdev = adapter->pdev;
|
||||
struct ifcvf_hw *vf = &adapter->vf;
|
||||
struct pci_dev *pdev = vf->pdev;
|
||||
int max_intr, ret;
|
||||
|
||||
/* all queues and config interrupt */
|
||||
@@ -160,10 +154,9 @@ static int ifcvf_alloc_vectors(struct ifcvf_adapter *adapter)
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int ifcvf_request_per_vq_irq(struct ifcvf_adapter *adapter)
|
||||
static int ifcvf_request_per_vq_irq(struct ifcvf_hw *vf)
|
||||
{
|
||||
struct pci_dev *pdev = adapter->pdev;
|
||||
struct ifcvf_hw *vf = &adapter->vf;
|
||||
struct pci_dev *pdev = vf->pdev;
|
||||
int i, vector, ret, irq;
|
||||
|
||||
vf->vqs_reused_irq = -EINVAL;
|
||||
@@ -190,15 +183,14 @@ static int ifcvf_request_per_vq_irq(struct ifcvf_adapter *adapter)
|
||||
|
||||
return 0;
|
||||
err:
|
||||
ifcvf_free_irq(adapter);
|
||||
ifcvf_free_irq(vf);
|
||||
|
||||
return -EFAULT;
|
||||
}
|
||||
|
||||
static int ifcvf_request_vqs_reused_irq(struct ifcvf_adapter *adapter)
|
||||
static int ifcvf_request_vqs_reused_irq(struct ifcvf_hw *vf)
|
||||
{
|
||||
struct pci_dev *pdev = adapter->pdev;
|
||||
struct ifcvf_hw *vf = &adapter->vf;
|
||||
struct pci_dev *pdev = vf->pdev;
|
||||
int i, vector, ret, irq;
|
||||
|
||||
vector = 0;
|
||||
@@ -224,15 +216,14 @@ static int ifcvf_request_vqs_reused_irq(struct ifcvf_adapter *adapter)
|
||||
|
||||
return 0;
|
||||
err:
|
||||
ifcvf_free_irq(adapter);
|
||||
ifcvf_free_irq(vf);
|
||||
|
||||
return -EFAULT;
|
||||
}
|
||||
|
||||
static int ifcvf_request_dev_irq(struct ifcvf_adapter *adapter)
|
||||
static int ifcvf_request_dev_irq(struct ifcvf_hw *vf)
|
||||
{
|
||||
struct pci_dev *pdev = adapter->pdev;
|
||||
struct ifcvf_hw *vf = &adapter->vf;
|
||||
struct pci_dev *pdev = vf->pdev;
|
||||
int i, vector, ret, irq;
|
||||
|
||||
vector = 0;
|
||||
@@ -265,29 +256,27 @@ static int ifcvf_request_dev_irq(struct ifcvf_adapter *adapter)
|
||||
|
||||
return 0;
|
||||
err:
|
||||
ifcvf_free_irq(adapter);
|
||||
ifcvf_free_irq(vf);
|
||||
|
||||
return -EFAULT;
|
||||
|
||||
}
|
||||
|
||||
static int ifcvf_request_vq_irq(struct ifcvf_adapter *adapter)
|
||||
static int ifcvf_request_vq_irq(struct ifcvf_hw *vf)
|
||||
{
|
||||
struct ifcvf_hw *vf = &adapter->vf;
|
||||
int ret;
|
||||
|
||||
if (vf->msix_vector_status == MSIX_VECTOR_PER_VQ_AND_CONFIG)
|
||||
ret = ifcvf_request_per_vq_irq(adapter);
|
||||
ret = ifcvf_request_per_vq_irq(vf);
|
||||
else
|
||||
ret = ifcvf_request_vqs_reused_irq(adapter);
|
||||
ret = ifcvf_request_vqs_reused_irq(vf);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int ifcvf_request_config_irq(struct ifcvf_adapter *adapter)
|
||||
static int ifcvf_request_config_irq(struct ifcvf_hw *vf)
|
||||
{
|
||||
struct pci_dev *pdev = adapter->pdev;
|
||||
struct ifcvf_hw *vf = &adapter->vf;
|
||||
struct pci_dev *pdev = vf->pdev;
|
||||
int config_vector, ret;
|
||||
|
||||
if (vf->msix_vector_status == MSIX_VECTOR_PER_VQ_AND_CONFIG)
|
||||
@@ -320,17 +309,16 @@ static int ifcvf_request_config_irq(struct ifcvf_adapter *adapter)
|
||||
|
||||
return 0;
|
||||
err:
|
||||
ifcvf_free_irq(adapter);
|
||||
ifcvf_free_irq(vf);
|
||||
|
||||
return -EFAULT;
|
||||
}
|
||||
|
||||
static int ifcvf_request_irq(struct ifcvf_adapter *adapter)
|
||||
static int ifcvf_request_irq(struct ifcvf_hw *vf)
|
||||
{
|
||||
struct ifcvf_hw *vf = &adapter->vf;
|
||||
int nvectors, ret, max_intr;
|
||||
|
||||
nvectors = ifcvf_alloc_vectors(adapter);
|
||||
nvectors = ifcvf_alloc_vectors(vf);
|
||||
if (nvectors <= 0)
|
||||
return -EFAULT;
|
||||
|
||||
@@ -341,16 +329,16 @@ static int ifcvf_request_irq(struct ifcvf_adapter *adapter)
|
||||
|
||||
if (nvectors == 1) {
|
||||
vf->msix_vector_status = MSIX_VECTOR_DEV_SHARED;
|
||||
ret = ifcvf_request_dev_irq(adapter);
|
||||
ret = ifcvf_request_dev_irq(vf);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = ifcvf_request_vq_irq(adapter);
|
||||
ret = ifcvf_request_vq_irq(vf);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = ifcvf_request_config_irq(adapter);
|
||||
ret = ifcvf_request_config_irq(vf);
|
||||
|
||||
if (ret)
|
||||
return ret;
|
||||
@@ -358,9 +346,9 @@ static int ifcvf_request_irq(struct ifcvf_adapter *adapter)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ifcvf_start_datapath(void *private)
|
||||
static int ifcvf_start_datapath(struct ifcvf_adapter *adapter)
|
||||
{
|
||||
struct ifcvf_hw *vf = ifcvf_private_to_vf(private);
|
||||
struct ifcvf_hw *vf = adapter->vf;
|
||||
u8 status;
|
||||
int ret;
|
||||
|
||||
@@ -374,9 +362,9 @@ static int ifcvf_start_datapath(void *private)
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int ifcvf_stop_datapath(void *private)
|
||||
static int ifcvf_stop_datapath(struct ifcvf_adapter *adapter)
|
||||
{
|
||||
struct ifcvf_hw *vf = ifcvf_private_to_vf(private);
|
||||
struct ifcvf_hw *vf = adapter->vf;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < vf->nr_vring; i++)
|
||||
@@ -389,7 +377,7 @@ static int ifcvf_stop_datapath(void *private)
|
||||
|
||||
static void ifcvf_reset_vring(struct ifcvf_adapter *adapter)
|
||||
{
|
||||
struct ifcvf_hw *vf = ifcvf_private_to_vf(adapter);
|
||||
struct ifcvf_hw *vf = adapter->vf;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < vf->nr_vring; i++) {
|
||||
@@ -414,7 +402,7 @@ static struct ifcvf_hw *vdpa_to_vf(struct vdpa_device *vdpa_dev)
|
||||
{
|
||||
struct ifcvf_adapter *adapter = vdpa_to_adapter(vdpa_dev);
|
||||
|
||||
return &adapter->vf;
|
||||
return adapter->vf;
|
||||
}
|
||||
|
||||
static u64 ifcvf_vdpa_get_device_features(struct vdpa_device *vdpa_dev)
|
||||
@@ -479,7 +467,7 @@ static void ifcvf_vdpa_set_status(struct vdpa_device *vdpa_dev, u8 status)
|
||||
|
||||
if ((status & VIRTIO_CONFIG_S_DRIVER_OK) &&
|
||||
!(status_old & VIRTIO_CONFIG_S_DRIVER_OK)) {
|
||||
ret = ifcvf_request_irq(adapter);
|
||||
ret = ifcvf_request_irq(vf);
|
||||
if (ret) {
|
||||
status = ifcvf_get_status(vf);
|
||||
status |= VIRTIO_CONFIG_S_FAILED;
|
||||
@@ -511,7 +499,7 @@ static int ifcvf_vdpa_reset(struct vdpa_device *vdpa_dev)
|
||||
|
||||
if (status_old & VIRTIO_CONFIG_S_DRIVER_OK) {
|
||||
ifcvf_stop_datapath(adapter);
|
||||
ifcvf_free_irq(adapter);
|
||||
ifcvf_free_irq(vf);
|
||||
}
|
||||
|
||||
ifcvf_reset_vring(adapter);
|
||||
@@ -755,17 +743,37 @@ static int ifcvf_vdpa_dev_add(struct vdpa_mgmt_dev *mdev, const char *name,
|
||||
struct vdpa_device *vdpa_dev;
|
||||
struct pci_dev *pdev;
|
||||
struct ifcvf_hw *vf;
|
||||
u64 device_features;
|
||||
int ret;
|
||||
|
||||
ifcvf_mgmt_dev = container_of(mdev, struct ifcvf_vdpa_mgmt_dev, mdev);
|
||||
if (!ifcvf_mgmt_dev->adapter)
|
||||
return -EOPNOTSUPP;
|
||||
vf = &ifcvf_mgmt_dev->vf;
|
||||
pdev = vf->pdev;
|
||||
adapter = vdpa_alloc_device(struct ifcvf_adapter, vdpa,
|
||||
&pdev->dev, &ifc_vdpa_ops, 1, 1, NULL, false);
|
||||
if (IS_ERR(adapter)) {
|
||||
IFCVF_ERR(pdev, "Failed to allocate vDPA structure");
|
||||
return PTR_ERR(adapter);
|
||||
}
|
||||
|
||||
adapter = ifcvf_mgmt_dev->adapter;
|
||||
vf = &adapter->vf;
|
||||
pdev = adapter->pdev;
|
||||
ifcvf_mgmt_dev->adapter = adapter;
|
||||
adapter->pdev = pdev;
|
||||
adapter->vdpa.dma_dev = &pdev->dev;
|
||||
adapter->vdpa.mdev = mdev;
|
||||
adapter->vf = vf;
|
||||
vdpa_dev = &adapter->vdpa;
|
||||
|
||||
device_features = vf->hw_features;
|
||||
if (config->mask & BIT_ULL(VDPA_ATTR_DEV_FEATURES)) {
|
||||
if (config->device_features & ~device_features) {
|
||||
IFCVF_ERR(pdev, "The provisioned features 0x%llx are not supported by this device with features 0x%llx\n",
|
||||
config->device_features, device_features);
|
||||
return -EINVAL;
|
||||
}
|
||||
device_features &= config->device_features;
|
||||
}
|
||||
vf->dev_features = device_features;
|
||||
|
||||
if (name)
|
||||
ret = dev_set_name(&vdpa_dev->dev, "%s", name);
|
||||
else
|
||||
@@ -781,7 +789,6 @@ static int ifcvf_vdpa_dev_add(struct vdpa_mgmt_dev *mdev, const char *name,
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static void ifcvf_vdpa_dev_del(struct vdpa_mgmt_dev *mdev, struct vdpa_device *dev)
|
||||
{
|
||||
struct ifcvf_vdpa_mgmt_dev *ifcvf_mgmt_dev;
|
||||
@@ -800,7 +807,6 @@ static int ifcvf_probe(struct pci_dev *pdev, const struct pci_device_id *id)
|
||||
{
|
||||
struct ifcvf_vdpa_mgmt_dev *ifcvf_mgmt_dev;
|
||||
struct device *dev = &pdev->dev;
|
||||
struct ifcvf_adapter *adapter;
|
||||
struct ifcvf_hw *vf;
|
||||
u32 dev_type;
|
||||
int ret, i;
|
||||
@@ -831,20 +837,16 @@ static int ifcvf_probe(struct pci_dev *pdev, const struct pci_device_id *id)
|
||||
}
|
||||
|
||||
pci_set_master(pdev);
|
||||
|
||||
adapter = vdpa_alloc_device(struct ifcvf_adapter, vdpa,
|
||||
dev, &ifc_vdpa_ops, 1, 1, NULL, false);
|
||||
if (IS_ERR(adapter)) {
|
||||
IFCVF_ERR(pdev, "Failed to allocate vDPA structure");
|
||||
return PTR_ERR(adapter);
|
||||
ifcvf_mgmt_dev = kzalloc(sizeof(struct ifcvf_vdpa_mgmt_dev), GFP_KERNEL);
|
||||
if (!ifcvf_mgmt_dev) {
|
||||
IFCVF_ERR(pdev, "Failed to alloc memory for the vDPA management device\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
vf = &adapter->vf;
|
||||
vf = &ifcvf_mgmt_dev->vf;
|
||||
vf->dev_type = get_dev_type(pdev);
|
||||
vf->base = pcim_iomap_table(pdev);
|
||||
|
||||
adapter->pdev = pdev;
|
||||
adapter->vdpa.dma_dev = &pdev->dev;
|
||||
vf->pdev = pdev;
|
||||
|
||||
ret = ifcvf_init_hw(vf, pdev);
|
||||
if (ret) {
|
||||
@@ -858,16 +860,6 @@ static int ifcvf_probe(struct pci_dev *pdev, const struct pci_device_id *id)
|
||||
vf->hw_features = ifcvf_get_hw_features(vf);
|
||||
vf->config_size = ifcvf_get_config_size(vf);
|
||||
|
||||
ifcvf_mgmt_dev = kzalloc(sizeof(struct ifcvf_vdpa_mgmt_dev), GFP_KERNEL);
|
||||
if (!ifcvf_mgmt_dev) {
|
||||
IFCVF_ERR(pdev, "Failed to alloc memory for the vDPA management device\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
ifcvf_mgmt_dev->mdev.ops = &ifcvf_vdpa_mgmt_dev_ops;
|
||||
ifcvf_mgmt_dev->mdev.device = dev;
|
||||
ifcvf_mgmt_dev->adapter = adapter;
|
||||
|
||||
dev_type = get_dev_type(pdev);
|
||||
switch (dev_type) {
|
||||
case VIRTIO_ID_NET:
|
||||
@@ -882,11 +874,11 @@ static int ifcvf_probe(struct pci_dev *pdev, const struct pci_device_id *id)
|
||||
goto err;
|
||||
}
|
||||
|
||||
ifcvf_mgmt_dev->mdev.ops = &ifcvf_vdpa_mgmt_dev_ops;
|
||||
ifcvf_mgmt_dev->mdev.device = dev;
|
||||
ifcvf_mgmt_dev->mdev.max_supported_vqs = vf->nr_vring;
|
||||
ifcvf_mgmt_dev->mdev.supported_features = vf->hw_features;
|
||||
|
||||
adapter->vdpa.mdev = &ifcvf_mgmt_dev->mdev;
|
||||
|
||||
ifcvf_mgmt_dev->mdev.config_attr_mask = (1 << VDPA_ATTR_DEV_FEATURES);
|
||||
|
||||
ret = vdpa_mgmtdev_register(&ifcvf_mgmt_dev->mdev);
|
||||
if (ret) {
|
||||
|
||||
@@ -1,4 +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
|
||||
mlx5_vdpa-$(CONFIG_MLX5_VDPA_NET) += net/mlx5_vnet.o core/resources.o core/mr.o net/debug.o
|
||||
|
||||
@@ -503,7 +503,6 @@ void mlx5_vdpa_destroy_mr(struct mlx5_vdpa_dev *mvdev)
|
||||
else
|
||||
destroy_dma_mr(mvdev, mr);
|
||||
|
||||
memset(mr, 0, sizeof(*mr));
|
||||
mr->initialized = false;
|
||||
out:
|
||||
mutex_unlock(&mr->mkey_mtx);
|
||||
|
||||
@@ -213,7 +213,7 @@ int mlx5_vdpa_create_mkey(struct mlx5_vdpa_dev *mvdev, u32 *mkey, u32 *in,
|
||||
return err;
|
||||
|
||||
mkey_index = MLX5_GET(create_mkey_out, lout, mkey_index);
|
||||
*mkey |= mlx5_idx_to_mkey(mkey_index);
|
||||
*mkey = mlx5_idx_to_mkey(mkey_index);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -233,6 +233,7 @@ static int init_ctrl_vq(struct mlx5_vdpa_dev *mvdev)
|
||||
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;
|
||||
|
||||
@@ -0,0 +1,152 @@
|
||||
// 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 dentry *dbg)
|
||||
{
|
||||
debugfs_remove_recursive(dbg);
|
||||
}
|
||||
@@ -18,15 +18,12 @@
|
||||
#include <linux/mlx5/mlx5_ifc_vdpa.h>
|
||||
#include <linux/mlx5/mpfs.h>
|
||||
#include "mlx5_vdpa.h"
|
||||
#include "mlx5_vnet.h"
|
||||
|
||||
MODULE_AUTHOR("Eli Cohen <eli@mellanox.com>");
|
||||
MODULE_DESCRIPTION("Mellanox VDPA driver");
|
||||
MODULE_LICENSE("Dual BSD/GPL");
|
||||
|
||||
#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)
|
||||
|
||||
#define VALID_FEATURES_MASK \
|
||||
(BIT_ULL(VIRTIO_NET_F_CSUM) | BIT_ULL(VIRTIO_NET_F_GUEST_CSUM) | \
|
||||
BIT_ULL(VIRTIO_NET_F_CTRL_GUEST_OFFLOADS) | BIT_ULL(VIRTIO_NET_F_MTU) | BIT_ULL(VIRTIO_NET_F_MAC) | \
|
||||
@@ -50,14 +47,6 @@ MODULE_LICENSE("Dual BSD/GPL");
|
||||
|
||||
#define MLX5V_UNTAGGED 0x1000
|
||||
|
||||
struct mlx5_vdpa_net_resources {
|
||||
u32 tisn;
|
||||
u32 tdn;
|
||||
u32 tirn;
|
||||
u32 rqtn;
|
||||
bool valid;
|
||||
};
|
||||
|
||||
struct mlx5_vdpa_cq_buf {
|
||||
struct mlx5_frag_buf_ctrl fbc;
|
||||
struct mlx5_frag_buf frag_buf;
|
||||
@@ -146,38 +135,6 @@ static bool is_index_valid(struct mlx5_vdpa_dev *mvdev, u16 idx)
|
||||
return idx <= mvdev->max_idx;
|
||||
}
|
||||
|
||||
#define MLX5V_MACVLAN_SIZE 256
|
||||
|
||||
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;
|
||||
bool setup;
|
||||
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 macvlan_node {
|
||||
struct hlist_node hlist;
|
||||
struct mlx5_flow_handle *ucast_rule;
|
||||
struct mlx5_flow_handle *mcast_rule;
|
||||
u64 macvlan;
|
||||
};
|
||||
|
||||
static void free_resources(struct mlx5_vdpa_net *ndev);
|
||||
static void init_mvqs(struct mlx5_vdpa_net *ndev);
|
||||
static int setup_driver(struct mlx5_vdpa_dev *mvdev);
|
||||
@@ -1431,36 +1388,85 @@ static int create_tir(struct mlx5_vdpa_net *ndev)
|
||||
|
||||
err = mlx5_vdpa_create_tir(&ndev->mvdev, in, &ndev->res.tirn);
|
||||
kfree(in);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
mlx5_vdpa_add_tirn(ndev);
|
||||
return err;
|
||||
}
|
||||
|
||||
static void destroy_tir(struct mlx5_vdpa_net *ndev)
|
||||
{
|
||||
mlx5_vdpa_remove_tirn(ndev);
|
||||
mlx5_vdpa_destroy_tir(&ndev->mvdev, ndev->res.tirn);
|
||||
}
|
||||
|
||||
#define MAX_STEERING_ENT 0x8000
|
||||
#define MAX_STEERING_GROUPS 2
|
||||
|
||||
static int mlx5_vdpa_add_mac_vlan_rules(struct mlx5_vdpa_net *ndev, u8 *mac,
|
||||
u16 vid, bool tagged,
|
||||
struct mlx5_flow_handle **ucast,
|
||||
struct mlx5_flow_handle **mcast)
|
||||
#if defined(CONFIG_MLX5_VDPA_STEERING_DEBUG)
|
||||
#define NUM_DESTS 2
|
||||
#else
|
||||
#define NUM_DESTS 1
|
||||
#endif
|
||||
|
||||
static int add_steering_counters(struct mlx5_vdpa_net *ndev,
|
||||
struct macvlan_node *node,
|
||||
struct mlx5_flow_act *flow_act,
|
||||
struct mlx5_flow_destination *dests)
|
||||
{
|
||||
struct mlx5_flow_destination dest = {};
|
||||
#if defined(CONFIG_MLX5_VDPA_STEERING_DEBUG)
|
||||
int err;
|
||||
|
||||
node->ucast_counter.counter = mlx5_fc_create(ndev->mvdev.mdev, false);
|
||||
if (IS_ERR(node->ucast_counter.counter))
|
||||
return PTR_ERR(node->ucast_counter.counter);
|
||||
|
||||
node->mcast_counter.counter = mlx5_fc_create(ndev->mvdev.mdev, false);
|
||||
if (IS_ERR(node->mcast_counter.counter)) {
|
||||
err = PTR_ERR(node->mcast_counter.counter);
|
||||
goto err_mcast_counter;
|
||||
}
|
||||
|
||||
dests[1].type = MLX5_FLOW_DESTINATION_TYPE_COUNTER;
|
||||
flow_act->action |= MLX5_FLOW_CONTEXT_ACTION_COUNT;
|
||||
return 0;
|
||||
|
||||
err_mcast_counter:
|
||||
mlx5_fc_destroy(ndev->mvdev.mdev, node->ucast_counter.counter);
|
||||
return err;
|
||||
#else
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
static void remove_steering_counters(struct mlx5_vdpa_net *ndev,
|
||||
struct macvlan_node *node)
|
||||
{
|
||||
#if defined(CONFIG_MLX5_VDPA_STEERING_DEBUG)
|
||||
mlx5_fc_destroy(ndev->mvdev.mdev, node->mcast_counter.counter);
|
||||
mlx5_fc_destroy(ndev->mvdev.mdev, node->ucast_counter.counter);
|
||||
#endif
|
||||
}
|
||||
|
||||
static int mlx5_vdpa_add_mac_vlan_rules(struct mlx5_vdpa_net *ndev, u8 *mac,
|
||||
struct macvlan_node *node)
|
||||
{
|
||||
struct mlx5_flow_destination dests[NUM_DESTS] = {};
|
||||
struct mlx5_flow_act flow_act = {};
|
||||
struct mlx5_flow_handle *rule;
|
||||
struct mlx5_flow_spec *spec;
|
||||
void *headers_c;
|
||||
void *headers_v;
|
||||
u8 *dmac_c;
|
||||
u8 *dmac_v;
|
||||
int err;
|
||||
u16 vid;
|
||||
|
||||
spec = kvzalloc(sizeof(*spec), GFP_KERNEL);
|
||||
if (!spec)
|
||||
return -ENOMEM;
|
||||
|
||||
vid = key2vid(node->macvlan);
|
||||
spec->match_criteria_enable = MLX5_MATCH_OUTER_HEADERS;
|
||||
headers_c = MLX5_ADDR_OF(fte_match_param, spec->match_criteria, outer_headers);
|
||||
headers_v = MLX5_ADDR_OF(fte_match_param, spec->match_value, outer_headers);
|
||||
@@ -1472,44 +1478,58 @@ static int mlx5_vdpa_add_mac_vlan_rules(struct mlx5_vdpa_net *ndev, u8 *mac,
|
||||
MLX5_SET(fte_match_set_lyr_2_4, headers_c, cvlan_tag, 1);
|
||||
MLX5_SET_TO_ONES(fte_match_set_lyr_2_4, headers_c, first_vid);
|
||||
}
|
||||
if (tagged) {
|
||||
if (node->tagged) {
|
||||
MLX5_SET(fte_match_set_lyr_2_4, headers_v, cvlan_tag, 1);
|
||||
MLX5_SET(fte_match_set_lyr_2_4, headers_v, first_vid, vid);
|
||||
}
|
||||
flow_act.action = MLX5_FLOW_CONTEXT_ACTION_FWD_DEST;
|
||||
dest.type = MLX5_FLOW_DESTINATION_TYPE_TIR;
|
||||
dest.tir_num = ndev->res.tirn;
|
||||
rule = mlx5_add_flow_rules(ndev->rxft, spec, &flow_act, &dest, 1);
|
||||
if (IS_ERR(rule))
|
||||
return PTR_ERR(rule);
|
||||
dests[0].type = MLX5_FLOW_DESTINATION_TYPE_TIR;
|
||||
dests[0].tir_num = ndev->res.tirn;
|
||||
err = add_steering_counters(ndev, node, &flow_act, dests);
|
||||
if (err)
|
||||
goto out_free;
|
||||
|
||||
*ucast = rule;
|
||||
#if defined(CONFIG_MLX5_VDPA_STEERING_DEBUG)
|
||||
dests[1].counter_id = mlx5_fc_id(node->ucast_counter.counter);
|
||||
#endif
|
||||
node->ucast_rule = mlx5_add_flow_rules(ndev->rxft, spec, &flow_act, dests, NUM_DESTS);
|
||||
if (IS_ERR(node->ucast_rule)) {
|
||||
err = PTR_ERR(node->ucast_rule);
|
||||
goto err_ucast;
|
||||
}
|
||||
|
||||
#if defined(CONFIG_MLX5_VDPA_STEERING_DEBUG)
|
||||
dests[1].counter_id = mlx5_fc_id(node->mcast_counter.counter);
|
||||
#endif
|
||||
|
||||
memset(dmac_c, 0, ETH_ALEN);
|
||||
memset(dmac_v, 0, ETH_ALEN);
|
||||
dmac_c[0] = 1;
|
||||
dmac_v[0] = 1;
|
||||
rule = mlx5_add_flow_rules(ndev->rxft, spec, &flow_act, &dest, 1);
|
||||
kvfree(spec);
|
||||
if (IS_ERR(rule)) {
|
||||
err = PTR_ERR(rule);
|
||||
node->mcast_rule = mlx5_add_flow_rules(ndev->rxft, spec, &flow_act, dests, NUM_DESTS);
|
||||
if (IS_ERR(node->mcast_rule)) {
|
||||
err = PTR_ERR(node->mcast_rule);
|
||||
goto err_mcast;
|
||||
}
|
||||
|
||||
*mcast = rule;
|
||||
kvfree(spec);
|
||||
mlx5_vdpa_add_rx_counters(ndev, node);
|
||||
return 0;
|
||||
|
||||
err_mcast:
|
||||
mlx5_del_flow_rules(*ucast);
|
||||
mlx5_del_flow_rules(node->ucast_rule);
|
||||
err_ucast:
|
||||
remove_steering_counters(ndev, node);
|
||||
out_free:
|
||||
kvfree(spec);
|
||||
return err;
|
||||
}
|
||||
|
||||
static void mlx5_vdpa_del_mac_vlan_rules(struct mlx5_vdpa_net *ndev,
|
||||
struct mlx5_flow_handle *ucast,
|
||||
struct mlx5_flow_handle *mcast)
|
||||
struct macvlan_node *node)
|
||||
{
|
||||
mlx5_del_flow_rules(ucast);
|
||||
mlx5_del_flow_rules(mcast);
|
||||
mlx5_vdpa_remove_rx_counters(ndev, node);
|
||||
mlx5_del_flow_rules(node->ucast_rule);
|
||||
mlx5_del_flow_rules(node->mcast_rule);
|
||||
}
|
||||
|
||||
static u64 search_val(u8 *mac, u16 vlan, bool tagged)
|
||||
@@ -1543,14 +1563,14 @@ static struct macvlan_node *mac_vlan_lookup(struct mlx5_vdpa_net *ndev, u64 valu
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int mac_vlan_add(struct mlx5_vdpa_net *ndev, u8 *mac, u16 vlan, bool tagged) // vlan -> vid
|
||||
static int mac_vlan_add(struct mlx5_vdpa_net *ndev, u8 *mac, u16 vid, bool tagged)
|
||||
{
|
||||
struct macvlan_node *ptr;
|
||||
u64 val;
|
||||
u32 idx;
|
||||
int err;
|
||||
|
||||
val = search_val(mac, vlan, tagged);
|
||||
val = search_val(mac, vid, tagged);
|
||||
if (mac_vlan_lookup(ndev, val))
|
||||
return -EEXIST;
|
||||
|
||||
@@ -1558,12 +1578,13 @@ static int mac_vlan_add(struct mlx5_vdpa_net *ndev, u8 *mac, u16 vlan, bool tagg
|
||||
if (!ptr)
|
||||
return -ENOMEM;
|
||||
|
||||
err = mlx5_vdpa_add_mac_vlan_rules(ndev, ndev->config.mac, vlan, tagged,
|
||||
&ptr->ucast_rule, &ptr->mcast_rule);
|
||||
ptr->tagged = tagged;
|
||||
ptr->macvlan = val;
|
||||
ptr->ndev = ndev;
|
||||
err = mlx5_vdpa_add_mac_vlan_rules(ndev, ndev->config.mac, ptr);
|
||||
if (err)
|
||||
goto err_add;
|
||||
|
||||
ptr->macvlan = val;
|
||||
idx = hash_64(val, 8);
|
||||
hlist_add_head(&ptr->hlist, &ndev->macvlan_hash[idx]);
|
||||
return 0;
|
||||
@@ -1582,7 +1603,8 @@ static void mac_vlan_del(struct mlx5_vdpa_net *ndev, u8 *mac, u16 vlan, bool tag
|
||||
return;
|
||||
|
||||
hlist_del(&ptr->hlist);
|
||||
mlx5_vdpa_del_mac_vlan_rules(ndev, ptr->ucast_rule, ptr->mcast_rule);
|
||||
mlx5_vdpa_del_mac_vlan_rules(ndev, ptr);
|
||||
remove_steering_counters(ndev, ptr);
|
||||
kfree(ptr);
|
||||
}
|
||||
|
||||
@@ -1595,7 +1617,8 @@ static void clear_mac_vlan_table(struct mlx5_vdpa_net *ndev)
|
||||
for (i = 0; i < MLX5V_MACVLAN_SIZE; i++) {
|
||||
hlist_for_each_entry_safe(pos, n, &ndev->macvlan_hash[i], hlist) {
|
||||
hlist_del(&pos->hlist);
|
||||
mlx5_vdpa_del_mac_vlan_rules(ndev, pos->ucast_rule, pos->mcast_rule);
|
||||
mlx5_vdpa_del_mac_vlan_rules(ndev, pos);
|
||||
remove_steering_counters(ndev, pos);
|
||||
kfree(pos);
|
||||
}
|
||||
}
|
||||
@@ -1621,6 +1644,7 @@ static int setup_steering(struct mlx5_vdpa_net *ndev)
|
||||
mlx5_vdpa_warn(&ndev->mvdev, "failed to create flow table\n");
|
||||
return PTR_ERR(ndev->rxft);
|
||||
}
|
||||
mlx5_vdpa_add_rx_flow_table(ndev);
|
||||
|
||||
err = mac_vlan_add(ndev, ndev->config.mac, 0, false);
|
||||
if (err)
|
||||
@@ -1629,6 +1653,7 @@ static int setup_steering(struct mlx5_vdpa_net *ndev)
|
||||
return 0;
|
||||
|
||||
err_add:
|
||||
mlx5_vdpa_remove_rx_flow_table(ndev);
|
||||
mlx5_destroy_flow_table(ndev->rxft);
|
||||
return err;
|
||||
}
|
||||
@@ -1636,6 +1661,7 @@ err_add:
|
||||
static void teardown_steering(struct mlx5_vdpa_net *ndev)
|
||||
{
|
||||
clear_mac_vlan_table(ndev);
|
||||
mlx5_vdpa_remove_rx_flow_table(ndev);
|
||||
mlx5_destroy_flow_table(ndev->rxft);
|
||||
}
|
||||
|
||||
@@ -2183,6 +2209,7 @@ static u64 get_supported_features(struct mlx5_core_dev *mdev)
|
||||
mlx_vdpa_features |= BIT_ULL(VIRTIO_NET_F_STATUS);
|
||||
mlx_vdpa_features |= BIT_ULL(VIRTIO_NET_F_MTU);
|
||||
mlx_vdpa_features |= BIT_ULL(VIRTIO_NET_F_CTRL_VLAN);
|
||||
mlx_vdpa_features |= BIT_ULL(VIRTIO_NET_F_MAC);
|
||||
|
||||
return mlx_vdpa_features;
|
||||
}
|
||||
@@ -2655,6 +2682,16 @@ static int mlx5_vdpa_set_map(struct vdpa_device *vdev, unsigned int asid,
|
||||
return err;
|
||||
}
|
||||
|
||||
static struct device *mlx5_get_vq_dma_dev(struct vdpa_device *vdev, u16 idx)
|
||||
{
|
||||
struct mlx5_vdpa_dev *mvdev = to_mvdev(vdev);
|
||||
|
||||
if (is_ctrl_vq_idx(mvdev, idx))
|
||||
return &vdev->dev;
|
||||
|
||||
return mvdev->vdev.dma_dev;
|
||||
}
|
||||
|
||||
static void mlx5_vdpa_free(struct vdpa_device *vdev)
|
||||
{
|
||||
struct mlx5_vdpa_dev *mvdev = to_mvdev(vdev);
|
||||
@@ -2870,6 +2907,7 @@ static const struct vdpa_config_ops mlx5_vdpa_ops = {
|
||||
.get_generation = mlx5_vdpa_get_generation,
|
||||
.set_map = mlx5_vdpa_set_map,
|
||||
.set_group_asid = mlx5_set_group_asid,
|
||||
.get_vq_dma_dev = mlx5_get_vq_dma_dev,
|
||||
.free = mlx5_vdpa_free,
|
||||
.suspend = mlx5_vdpa_suspend,
|
||||
};
|
||||
@@ -3009,6 +3047,8 @@ static int event_handler(struct notifier_block *nb, unsigned long event, void *p
|
||||
struct mlx5_vdpa_wq_ent *wqent;
|
||||
|
||||
if (event == MLX5_EVENT_TYPE_PORT_CHANGE) {
|
||||
if (!(ndev->mvdev.actual_features & BIT_ULL(VIRTIO_NET_F_STATUS)))
|
||||
return NOTIFY_DONE;
|
||||
switch (eqe->sub_type) {
|
||||
case MLX5_PORT_CHANGE_SUBTYPE_DOWN:
|
||||
case MLX5_PORT_CHANGE_SUBTYPE_ACTIVE:
|
||||
@@ -3060,6 +3100,7 @@ static int mlx5_vdpa_dev_add(struct vdpa_mgmt_dev *v_mdev, const char *name,
|
||||
struct mlx5_vdpa_dev *mvdev;
|
||||
struct mlx5_vdpa_net *ndev;
|
||||
struct mlx5_core_dev *mdev;
|
||||
u64 device_features;
|
||||
u32 max_vqs;
|
||||
u16 mtu;
|
||||
int err;
|
||||
@@ -3068,6 +3109,24 @@ static int mlx5_vdpa_dev_add(struct vdpa_mgmt_dev *v_mdev, const char *name,
|
||||
return -ENOSPC;
|
||||
|
||||
mdev = mgtdev->madev->mdev;
|
||||
device_features = mgtdev->mgtdev.supported_features;
|
||||
if (add_config->mask & BIT_ULL(VDPA_ATTR_DEV_FEATURES)) {
|
||||
if (add_config->device_features & ~device_features) {
|
||||
dev_warn(mdev->device,
|
||||
"The provisioned features 0x%llx are not supported by this device with features 0x%llx\n",
|
||||
add_config->device_features, device_features);
|
||||
return -EINVAL;
|
||||
}
|
||||
device_features &= add_config->device_features;
|
||||
}
|
||||
if (!(device_features & BIT_ULL(VIRTIO_F_VERSION_1) &&
|
||||
device_features & BIT_ULL(VIRTIO_F_ACCESS_PLATFORM))) {
|
||||
dev_warn(mdev->device,
|
||||
"Must provision minimum features 0x%llx for this device",
|
||||
BIT_ULL(VIRTIO_F_VERSION_1) | BIT_ULL(VIRTIO_F_ACCESS_PLATFORM));
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
if (!(MLX5_CAP_DEV_VDPA_EMULATION(mdev, virtio_queue_type) &
|
||||
MLX5_VIRTIO_EMULATION_CAP_VIRTIO_QUEUE_TYPE_SPLIT)) {
|
||||
dev_warn(mdev->device, "missing support for split virtqueues\n");
|
||||
@@ -3096,7 +3155,6 @@ static int mlx5_vdpa_dev_add(struct vdpa_mgmt_dev *v_mdev, const char *name,
|
||||
if (IS_ERR(ndev))
|
||||
return PTR_ERR(ndev);
|
||||
|
||||
ndev->mvdev.mlx_features = mgtdev->mgtdev.supported_features;
|
||||
ndev->mvdev.max_vqs = max_vqs;
|
||||
mvdev = &ndev->mvdev;
|
||||
mvdev->mdev = mdev;
|
||||
@@ -3118,20 +3176,26 @@ static int mlx5_vdpa_dev_add(struct vdpa_mgmt_dev *v_mdev, const char *name,
|
||||
goto err_alloc;
|
||||
}
|
||||
|
||||
err = query_mtu(mdev, &mtu);
|
||||
if (err)
|
||||
goto err_alloc;
|
||||
if (device_features & BIT_ULL(VIRTIO_NET_F_MTU)) {
|
||||
err = query_mtu(mdev, &mtu);
|
||||
if (err)
|
||||
goto err_alloc;
|
||||
|
||||
ndev->config.mtu = cpu_to_mlx5vdpa16(mvdev, mtu);
|
||||
ndev->config.mtu = cpu_to_mlx5vdpa16(mvdev, mtu);
|
||||
}
|
||||
|
||||
if (get_link_state(mvdev))
|
||||
ndev->config.status |= cpu_to_mlx5vdpa16(mvdev, VIRTIO_NET_S_LINK_UP);
|
||||
else
|
||||
ndev->config.status &= cpu_to_mlx5vdpa16(mvdev, ~VIRTIO_NET_S_LINK_UP);
|
||||
if (device_features & BIT_ULL(VIRTIO_NET_F_STATUS)) {
|
||||
if (get_link_state(mvdev))
|
||||
ndev->config.status |= cpu_to_mlx5vdpa16(mvdev, VIRTIO_NET_S_LINK_UP);
|
||||
else
|
||||
ndev->config.status &= cpu_to_mlx5vdpa16(mvdev, ~VIRTIO_NET_S_LINK_UP);
|
||||
}
|
||||
|
||||
if (add_config->mask & (1 << VDPA_ATTR_DEV_NET_CFG_MACADDR)) {
|
||||
memcpy(ndev->config.mac, add_config->net.mac, ETH_ALEN);
|
||||
} else {
|
||||
/* No bother setting mac address in config if not going to provision _F_MAC */
|
||||
} else if ((add_config->mask & BIT_ULL(VDPA_ATTR_DEV_FEATURES)) == 0 ||
|
||||
device_features & BIT_ULL(VIRTIO_NET_F_MAC)) {
|
||||
err = mlx5_query_nic_vport_mac_address(mdev, 0, 0, config->mac);
|
||||
if (err)
|
||||
goto err_alloc;
|
||||
@@ -3142,11 +3206,26 @@ static int mlx5_vdpa_dev_add(struct vdpa_mgmt_dev *v_mdev, const char *name,
|
||||
err = mlx5_mpfs_add_mac(pfmdev, config->mac);
|
||||
if (err)
|
||||
goto err_alloc;
|
||||
|
||||
ndev->mvdev.mlx_features |= BIT_ULL(VIRTIO_NET_F_MAC);
|
||||
} else if ((add_config->mask & BIT_ULL(VDPA_ATTR_DEV_FEATURES)) == 0) {
|
||||
/*
|
||||
* We used to clear _F_MAC feature bit if seeing
|
||||
* zero mac address when device features are not
|
||||
* specifically provisioned. Keep the behaviour
|
||||
* so old scripts do not break.
|
||||
*/
|
||||
device_features &= ~BIT_ULL(VIRTIO_NET_F_MAC);
|
||||
} else if (device_features & BIT_ULL(VIRTIO_NET_F_MAC)) {
|
||||
/* Don't provision zero mac address for _F_MAC */
|
||||
mlx5_vdpa_warn(&ndev->mvdev,
|
||||
"No mac address provisioned?\n");
|
||||
err = -EINVAL;
|
||||
goto err_alloc;
|
||||
}
|
||||
|
||||
config->max_virtqueue_pairs = cpu_to_mlx5vdpa16(mvdev, max_vqs / 2);
|
||||
if (device_features & BIT_ULL(VIRTIO_NET_F_MQ))
|
||||
config->max_virtqueue_pairs = cpu_to_mlx5vdpa16(mvdev, max_vqs / 2);
|
||||
|
||||
ndev->mvdev.mlx_features = device_features;
|
||||
mvdev->vdev.dma_dev = &mdev->pdev->dev;
|
||||
err = mlx5_vdpa_alloc_resources(&ndev->mvdev);
|
||||
if (err)
|
||||
@@ -3178,6 +3257,7 @@ static int mlx5_vdpa_dev_add(struct vdpa_mgmt_dev *v_mdev, const char *name,
|
||||
if (err)
|
||||
goto err_reg;
|
||||
|
||||
mlx5_vdpa_add_debugfs(ndev);
|
||||
mgtdev->ndev = ndev;
|
||||
return 0;
|
||||
|
||||
@@ -3204,6 +3284,8 @@ static void mlx5_vdpa_dev_del(struct vdpa_mgmt_dev *v_mdev, struct vdpa_device *
|
||||
struct mlx5_vdpa_net *ndev = to_mlx5_vdpa_ndev(mvdev);
|
||||
struct workqueue_struct *wq;
|
||||
|
||||
mlx5_vdpa_remove_debugfs(ndev->debugfs);
|
||||
ndev->debugfs = NULL;
|
||||
if (ndev->nb_registered) {
|
||||
ndev->nb_registered = false;
|
||||
mlx5_notifier_unregister(mvdev->mdev, &ndev->nb);
|
||||
@@ -3243,7 +3325,8 @@ static int mlx5v_probe(struct auxiliary_device *adev,
|
||||
mgtdev->mgtdev.id_table = id_table;
|
||||
mgtdev->mgtdev.config_attr_mask = BIT_ULL(VDPA_ATTR_DEV_NET_CFG_MACADDR) |
|
||||
BIT_ULL(VDPA_ATTR_DEV_NET_CFG_MAX_VQP) |
|
||||
BIT_ULL(VDPA_ATTR_DEV_NET_CFG_MTU);
|
||||
BIT_ULL(VDPA_ATTR_DEV_NET_CFG_MTU) |
|
||||
BIT_ULL(VDPA_ATTR_DEV_FEATURES);
|
||||
mgtdev->mgtdev.max_supported_vqs =
|
||||
MLX5_CAP_DEV_VDPA_EMULATION(mdev, max_num_virtio_queues) + 1;
|
||||
mgtdev->mgtdev.supported_features = get_supported_features(mdev);
|
||||
|
||||
@@ -0,0 +1,94 @@
|
||||
/* 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;
|
||||
}
|
||||
|
||||
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;
|
||||
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 dentry *debugfs;
|
||||
};
|
||||
|
||||
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 dentry *dbg);
|
||||
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__ */
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user