mirror of
https://github.com/linux-msm/laptops-kernel.git
synced 2026-08-13 14:19:53 -07:00
Merge tag 'vfio-v6.19-rc1' of https://github.com/awilliam/linux-vfio
Pull VFIO updates from Alex Williamson: - Move libvfio selftest artifacts in preparation of more tightly coupled integration with KVM selftests (David Matlack) - Fix comment typo in mtty driver (Chu Guangqing) - Support for new hardware revision in the hisi_acc vfio-pci variant driver where the migration registers can now be accessed via the PF. When enabled for this support, the full BAR can be exposed to the user (Longfang Liu) - Fix vfio cdev support for VF token passing, using the correct size for the kernel structure, thereby actually allowing userspace to provide a non-zero UUID token. Also set the match token callback for the hisi_acc, fixing VF token support for this this vfio-pci variant driver (Raghavendra Rao Ananta) - Introduce internal callbacks on vfio devices to simplify and consolidate duplicate code for generating VFIO_DEVICE_GET_REGION_INFO data, removing various ioctl intercepts with a more structured solution (Jason Gunthorpe) - Introduce dma-buf support for vfio-pci devices, allowing MMIO regions to be exposed through dma-buf objects with lifecycle managed through move operations. This enables low-level interactions such as a vfio-pci based SPDK drivers interacting directly with dma-buf capable RDMA devices to enable peer-to-peer operations. IOMMUFD is also now able to build upon this support to fill a long standing feature gap versus the legacy vfio type1 IOMMU backend with an implementation of P2P support for VM use cases that better manages the lifecycle of the P2P mapping (Leon Romanovsky, Jason Gunthorpe, Vivek Kasireddy) - Convert eventfd triggering for error and request signals to use RCU mechanisms in order to avoid a 3-way lockdep reported deadlock issue (Alex Williamson) - Fix a 32-bit overflow introduced via dma-buf support manifesting with large DMA buffers (Alex Mastro) - Convert nvgrace-gpu vfio-pci variant driver to insert mappings on fault rather than at mmap time. This conversion serves both to make use of huge PFNMAPs but also to both avoid corrected RAS events during reset by now being subject to vfio-pci-core's use of unmap_mapping_range(), and to enable a device readiness test after reset (Ankit Agrawal) - Refactoring of vfio selftests to support multi-device tests and split code to provide better separation between IOMMU and device objects. This work also enables a new test suite addition to measure parallel device initialization latency (David Matlack) * tag 'vfio-v6.19-rc1' of https://github.com/awilliam/linux-vfio: (65 commits) vfio: selftests: Add vfio_pci_device_init_perf_test vfio: selftests: Eliminate INVALID_IOVA vfio: selftests: Split libvfio.h into separate header files vfio: selftests: Move vfio_selftests_*() helpers into libvfio.c vfio: selftests: Rename vfio_util.h to libvfio.h vfio: selftests: Stop passing device for IOMMU operations vfio: selftests: Move IOVA allocator into iova_allocator.c vfio: selftests: Move IOMMU library code into iommu.c vfio: selftests: Rename struct vfio_dma_region to dma_region vfio: selftests: Upgrade driver logging to dev_err() vfio: selftests: Prefix logs with device BDF where relevant vfio: selftests: Eliminate overly chatty logging vfio: selftests: Support multiple devices in the same container/iommufd vfio: selftests: Introduce struct iommu vfio: selftests: Rename struct vfio_iommu_mode to iommu_mode vfio: selftests: Allow passing multiple BDFs on the command line vfio: selftests: Split run.sh into separate scripts vfio: selftests: Move run.sh into scripts directory vfio/nvgrace-gpu: wait for the GPU mem to be ready vfio/nvgrace-gpu: Inform devmem unmapped after reset ...
This commit is contained in:
@@ -9,22 +9,48 @@ between two devices on the bus. This type of transaction is henceforth
|
||||
called Peer-to-Peer (or P2P). However, there are a number of issues that
|
||||
make P2P transactions tricky to do in a perfectly safe way.
|
||||
|
||||
One of the biggest issues is that PCI doesn't require forwarding
|
||||
transactions between hierarchy domains, and in PCIe, each Root Port
|
||||
defines a separate hierarchy domain. To make things worse, there is no
|
||||
simple way to determine if a given Root Complex supports this or not.
|
||||
(See PCIe r4.0, sec 1.3.1). Therefore, as of this writing, the kernel
|
||||
only supports doing P2P when the endpoints involved are all behind the
|
||||
same PCI bridge, as such devices are all in the same PCI hierarchy
|
||||
domain, and the spec guarantees that all transactions within the
|
||||
hierarchy will be routable, but it does not require routing
|
||||
between hierarchies.
|
||||
For PCIe the routing of Transaction Layer Packets (TLPs) is well-defined up
|
||||
until they reach a host bridge or root port. If the path includes PCIe switches
|
||||
then based on the ACS settings the transaction can route entirely within
|
||||
the PCIe hierarchy and never reach the root port. The kernel will evaluate
|
||||
the PCIe topology and always permit P2P in these well-defined cases.
|
||||
|
||||
The second issue is that to make use of existing interfaces in Linux,
|
||||
memory that is used for P2P transactions needs to be backed by struct
|
||||
pages. However, PCI BARs are not typically cache coherent so there are
|
||||
a few corner case gotchas with these pages so developers need to
|
||||
be careful about what they do with them.
|
||||
However, if the P2P transaction reaches the host bridge then it might have to
|
||||
hairpin back out the same root port, be routed inside the CPU SOC to another
|
||||
PCIe root port, or routed internally to the SOC.
|
||||
|
||||
The PCIe specification doesn't define the forwarding of transactions between
|
||||
hierarchy domains and kernel defaults to blocking such routing. There is an
|
||||
allow list to allow detecting known-good HW, in which case P2P between any
|
||||
two PCIe devices will be permitted.
|
||||
|
||||
Since P2P inherently is doing transactions between two devices it requires two
|
||||
drivers to be co-operating inside the kernel. The providing driver has to convey
|
||||
its MMIO to the consuming driver. To meet the driver model lifecycle rules the
|
||||
MMIO must have all DMA mapping removed, all CPU accesses prevented, all page
|
||||
table mappings undone before the providing driver completes remove().
|
||||
|
||||
This requires the providing and consuming driver to actively work together to
|
||||
guarantee that the consuming driver has stopped using the MMIO during a removal
|
||||
cycle. This is done by either a synchronous invalidation shutdown or waiting
|
||||
for all usage refcounts to reach zero.
|
||||
|
||||
At the lowest level the P2P subsystem offers a naked struct p2p_provider that
|
||||
delegates lifecycle management to the providing driver. It is expected that
|
||||
drivers using this option will wrap their MMIO memory in DMABUF and use DMABUF
|
||||
to provide an invalidation shutdown. These MMIO addresess have no struct page, and
|
||||
if used with mmap() must create special PTEs. As such there are very few
|
||||
kernel uAPIs that can accept pointers to them; in particular they cannot be used
|
||||
with read()/write(), including O_DIRECT.
|
||||
|
||||
Building on this, the subsystem offers a layer to wrap the MMIO in a ZONE_DEVICE
|
||||
pgmap of MEMORY_DEVICE_PCI_P2PDMA to create struct pages. The lifecycle of
|
||||
pgmap ensures that when the pgmap is destroyed all other drivers have stopped
|
||||
using the MMIO. This option works with O_DIRECT flows, in some cases, if the
|
||||
underlying subsystem supports handling MEMORY_DEVICE_PCI_P2PDMA through
|
||||
FOLL_PCI_P2PDMA. The use of FOLL_LONGTERM is prevented. As this relies on pgmap
|
||||
it also relies on architecture support along with alignment and minimum size
|
||||
limitations.
|
||||
|
||||
|
||||
Driver Writer's Guide
|
||||
@@ -114,14 +140,39 @@ allocating scatter-gather lists with P2P memory.
|
||||
Struct Page Caveats
|
||||
-------------------
|
||||
|
||||
Driver writers should be very careful about not passing these special
|
||||
struct pages to code that isn't prepared for it. At this time, the kernel
|
||||
interfaces do not have any checks for ensuring this. This obviously
|
||||
precludes passing these pages to userspace.
|
||||
While the MEMORY_DEVICE_PCI_P2PDMA pages can be installed in VMAs,
|
||||
pin_user_pages() and related will not return them unless FOLL_PCI_P2PDMA is set.
|
||||
|
||||
P2P memory is also technically IO memory but should never have any side
|
||||
effects behind it. Thus, the order of loads and stores should not be important
|
||||
and ioreadX(), iowriteX() and friends should not be necessary.
|
||||
The MEMORY_DEVICE_PCI_P2PDMA pages require care to support in the kernel. The
|
||||
KVA is still MMIO and must still be accessed through the normal
|
||||
readX()/writeX()/etc helpers. Direct CPU access (e.g. memcpy) is forbidden, just
|
||||
like any other MMIO mapping. While this will actually work on some
|
||||
architectures, others will experience corruption or just crash in the kernel.
|
||||
Supporting FOLL_PCI_P2PDMA in a subsystem requires scrubbing it to ensure no CPU
|
||||
access happens.
|
||||
|
||||
|
||||
Usage With DMABUF
|
||||
=================
|
||||
|
||||
DMABUF provides an alternative to the above struct page-based
|
||||
client/provider/orchestrator system and should be used when struct page
|
||||
doesn't exist. In this mode the exporting driver will wrap
|
||||
some of its MMIO in a DMABUF and give the DMABUF FD to userspace.
|
||||
|
||||
Userspace can then pass the FD to an importing driver which will ask the
|
||||
exporting driver to map it to the importer.
|
||||
|
||||
In this case the initiator and target pci_devices are known and the P2P subsystem
|
||||
is used to determine the mapping type. The phys_addr_t-based DMA API is used to
|
||||
establish the dma_addr_t.
|
||||
|
||||
Lifecycle is controlled by DMABUF move_notify(). When the exporting driver wants
|
||||
to remove() it must deliver an invalidation shutdown to all DMABUF importing
|
||||
drivers through move_notify() and synchronously DMA unmap all the MMIO.
|
||||
|
||||
No importing driver can continue to have a DMA map to the MMIO after the
|
||||
exporting driver has destroyed its p2p_provider.
|
||||
|
||||
|
||||
P2P DMA Support Library
|
||||
|
||||
+1
-1
@@ -84,7 +84,7 @@ static inline bool blk_can_dma_map_iova(struct request *req,
|
||||
|
||||
static bool blk_dma_map_bus(struct blk_dma_iter *iter, struct phys_vec *vec)
|
||||
{
|
||||
iter->addr = pci_p2pdma_bus_addr_map(&iter->p2pdma, vec->paddr);
|
||||
iter->addr = pci_p2pdma_bus_addr_map(iter->p2pdma.mem, vec->paddr);
|
||||
iter->len = vec->len;
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -3032,11 +3032,36 @@ static void qm_put_pci_res(struct hisi_qm *qm)
|
||||
pci_release_mem_regions(pdev);
|
||||
}
|
||||
|
||||
static void hisi_mig_region_clear(struct hisi_qm *qm)
|
||||
{
|
||||
u32 val;
|
||||
|
||||
/* Clear migration region set of PF */
|
||||
if (qm->fun_type == QM_HW_PF && qm->ver > QM_HW_V3) {
|
||||
val = readl(qm->io_base + QM_MIG_REGION_SEL);
|
||||
val &= ~QM_MIG_REGION_EN;
|
||||
writel(val, qm->io_base + QM_MIG_REGION_SEL);
|
||||
}
|
||||
}
|
||||
|
||||
static void hisi_mig_region_enable(struct hisi_qm *qm)
|
||||
{
|
||||
u32 val;
|
||||
|
||||
/* Select migration region of PF */
|
||||
if (qm->fun_type == QM_HW_PF && qm->ver > QM_HW_V3) {
|
||||
val = readl(qm->io_base + QM_MIG_REGION_SEL);
|
||||
val |= QM_MIG_REGION_EN;
|
||||
writel(val, qm->io_base + QM_MIG_REGION_SEL);
|
||||
}
|
||||
}
|
||||
|
||||
static void hisi_qm_pci_uninit(struct hisi_qm *qm)
|
||||
{
|
||||
struct pci_dev *pdev = qm->pdev;
|
||||
|
||||
pci_free_irq_vectors(pdev);
|
||||
hisi_mig_region_clear(qm);
|
||||
qm_put_pci_res(qm);
|
||||
pci_disable_device(pdev);
|
||||
}
|
||||
@@ -5752,6 +5777,7 @@ int hisi_qm_init(struct hisi_qm *qm)
|
||||
goto err_free_qm_memory;
|
||||
|
||||
qm_cmd_init(qm);
|
||||
hisi_mig_region_enable(qm);
|
||||
|
||||
return 0;
|
||||
|
||||
@@ -5890,6 +5916,7 @@ static int qm_rebuild_for_resume(struct hisi_qm *qm)
|
||||
}
|
||||
|
||||
qm_cmd_init(qm);
|
||||
hisi_mig_region_enable(qm);
|
||||
hisi_qm_dev_err_init(qm);
|
||||
/* Set the doorbell timeout to QM_DB_TIMEOUT_CFG ns. */
|
||||
writel(QM_DB_TIMEOUT_SET, qm->io_base + QM_DB_TIMEOUT_CFG);
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# SPDX-License-Identifier: GPL-2.0-only
|
||||
obj-y := dma-buf.o dma-fence.o dma-fence-array.o dma-fence-chain.o \
|
||||
dma-fence-unwrap.o dma-resv.o
|
||||
dma-fence-unwrap.o dma-resv.o dma-buf-mapping.o
|
||||
obj-$(CONFIG_DMABUF_HEAPS) += dma-heap.o
|
||||
obj-$(CONFIG_DMABUF_HEAPS) += heaps/
|
||||
obj-$(CONFIG_SYNC_FILE) += sync_file.o
|
||||
|
||||
@@ -0,0 +1,248 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* DMA BUF Mapping Helpers
|
||||
*
|
||||
*/
|
||||
#include <linux/dma-buf-mapping.h>
|
||||
#include <linux/dma-resv.h>
|
||||
|
||||
static struct scatterlist *fill_sg_entry(struct scatterlist *sgl, size_t length,
|
||||
dma_addr_t addr)
|
||||
{
|
||||
unsigned int len, nents;
|
||||
int i;
|
||||
|
||||
nents = DIV_ROUND_UP(length, UINT_MAX);
|
||||
for (i = 0; i < nents; i++) {
|
||||
len = min_t(size_t, length, UINT_MAX);
|
||||
length -= len;
|
||||
/*
|
||||
* DMABUF abuses scatterlist to create a scatterlist
|
||||
* that does not have any CPU list, only the DMA list.
|
||||
* Always set the page related values to NULL to ensure
|
||||
* importers can't use it. The phys_addr based DMA API
|
||||
* does not require the CPU list for mapping or unmapping.
|
||||
*/
|
||||
sg_set_page(sgl, NULL, 0, 0);
|
||||
sg_dma_address(sgl) = addr + (dma_addr_t)i * UINT_MAX;
|
||||
sg_dma_len(sgl) = len;
|
||||
sgl = sg_next(sgl);
|
||||
}
|
||||
|
||||
return sgl;
|
||||
}
|
||||
|
||||
static unsigned int calc_sg_nents(struct dma_iova_state *state,
|
||||
struct dma_buf_phys_vec *phys_vec,
|
||||
size_t nr_ranges, size_t size)
|
||||
{
|
||||
unsigned int nents = 0;
|
||||
size_t i;
|
||||
|
||||
if (!state || !dma_use_iova(state)) {
|
||||
for (i = 0; i < nr_ranges; i++)
|
||||
nents += DIV_ROUND_UP(phys_vec[i].len, UINT_MAX);
|
||||
} else {
|
||||
/*
|
||||
* In IOVA case, there is only one SG entry which spans
|
||||
* for whole IOVA address space, but we need to make sure
|
||||
* that it fits sg->length, maybe we need more.
|
||||
*/
|
||||
nents = DIV_ROUND_UP(size, UINT_MAX);
|
||||
}
|
||||
|
||||
return nents;
|
||||
}
|
||||
|
||||
/**
|
||||
* struct dma_buf_dma - holds DMA mapping information
|
||||
* @sgt: Scatter-gather table
|
||||
* @state: DMA IOVA state relevant in IOMMU-based DMA
|
||||
* @size: Total size of DMA transfer
|
||||
*/
|
||||
struct dma_buf_dma {
|
||||
struct sg_table sgt;
|
||||
struct dma_iova_state *state;
|
||||
size_t size;
|
||||
};
|
||||
|
||||
/**
|
||||
* dma_buf_phys_vec_to_sgt - Returns the scatterlist table of the attachment
|
||||
* from arrays of physical vectors. This funciton is intended for MMIO memory
|
||||
* only.
|
||||
* @attach: [in] attachment whose scatterlist is to be returned
|
||||
* @provider: [in] p2pdma provider
|
||||
* @phys_vec: [in] array of physical vectors
|
||||
* @nr_ranges: [in] number of entries in phys_vec array
|
||||
* @size: [in] total size of phys_vec
|
||||
* @dir: [in] direction of DMA transfer
|
||||
*
|
||||
* Returns sg_table containing the scatterlist to be returned; returns ERR_PTR
|
||||
* on error. May return -EINTR if it is interrupted by a signal.
|
||||
*
|
||||
* On success, the DMA addresses and lengths in the returned scatterlist are
|
||||
* PAGE_SIZE aligned.
|
||||
*
|
||||
* A mapping must be unmapped by using dma_buf_free_sgt().
|
||||
*
|
||||
* NOTE: This function is intended for exporters. If direct traffic routing is
|
||||
* mandatory exporter should call routing pci_p2pdma_map_type() before calling
|
||||
* this function.
|
||||
*/
|
||||
struct sg_table *dma_buf_phys_vec_to_sgt(struct dma_buf_attachment *attach,
|
||||
struct p2pdma_provider *provider,
|
||||
struct dma_buf_phys_vec *phys_vec,
|
||||
size_t nr_ranges, size_t size,
|
||||
enum dma_data_direction dir)
|
||||
{
|
||||
unsigned int nents, mapped_len = 0;
|
||||
struct dma_buf_dma *dma;
|
||||
struct scatterlist *sgl;
|
||||
dma_addr_t addr;
|
||||
size_t i;
|
||||
int ret;
|
||||
|
||||
dma_resv_assert_held(attach->dmabuf->resv);
|
||||
|
||||
if (WARN_ON(!attach || !attach->dmabuf || !provider))
|
||||
/* This function is supposed to work on MMIO memory only */
|
||||
return ERR_PTR(-EINVAL);
|
||||
|
||||
dma = kzalloc(sizeof(*dma), GFP_KERNEL);
|
||||
if (!dma)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
switch (pci_p2pdma_map_type(provider, attach->dev)) {
|
||||
case PCI_P2PDMA_MAP_BUS_ADDR:
|
||||
/*
|
||||
* There is no need in IOVA at all for this flow.
|
||||
*/
|
||||
break;
|
||||
case PCI_P2PDMA_MAP_THRU_HOST_BRIDGE:
|
||||
dma->state = kzalloc(sizeof(*dma->state), GFP_KERNEL);
|
||||
if (!dma->state) {
|
||||
ret = -ENOMEM;
|
||||
goto err_free_dma;
|
||||
}
|
||||
|
||||
dma_iova_try_alloc(attach->dev, dma->state, 0, size);
|
||||
break;
|
||||
default:
|
||||
ret = -EINVAL;
|
||||
goto err_free_dma;
|
||||
}
|
||||
|
||||
nents = calc_sg_nents(dma->state, phys_vec, nr_ranges, size);
|
||||
ret = sg_alloc_table(&dma->sgt, nents, GFP_KERNEL | __GFP_ZERO);
|
||||
if (ret)
|
||||
goto err_free_state;
|
||||
|
||||
sgl = dma->sgt.sgl;
|
||||
|
||||
for (i = 0; i < nr_ranges; i++) {
|
||||
if (!dma->state) {
|
||||
addr = pci_p2pdma_bus_addr_map(provider,
|
||||
phys_vec[i].paddr);
|
||||
} else if (dma_use_iova(dma->state)) {
|
||||
ret = dma_iova_link(attach->dev, dma->state,
|
||||
phys_vec[i].paddr, 0,
|
||||
phys_vec[i].len, dir,
|
||||
DMA_ATTR_MMIO);
|
||||
if (ret)
|
||||
goto err_unmap_dma;
|
||||
|
||||
mapped_len += phys_vec[i].len;
|
||||
} else {
|
||||
addr = dma_map_phys(attach->dev, phys_vec[i].paddr,
|
||||
phys_vec[i].len, dir,
|
||||
DMA_ATTR_MMIO);
|
||||
ret = dma_mapping_error(attach->dev, addr);
|
||||
if (ret)
|
||||
goto err_unmap_dma;
|
||||
}
|
||||
|
||||
if (!dma->state || !dma_use_iova(dma->state))
|
||||
sgl = fill_sg_entry(sgl, phys_vec[i].len, addr);
|
||||
}
|
||||
|
||||
if (dma->state && dma_use_iova(dma->state)) {
|
||||
WARN_ON_ONCE(mapped_len != size);
|
||||
ret = dma_iova_sync(attach->dev, dma->state, 0, mapped_len);
|
||||
if (ret)
|
||||
goto err_unmap_dma;
|
||||
|
||||
sgl = fill_sg_entry(sgl, mapped_len, dma->state->addr);
|
||||
}
|
||||
|
||||
dma->size = size;
|
||||
|
||||
/*
|
||||
* No CPU list included — set orig_nents = 0 so others can detect
|
||||
* this via SG table (use nents only).
|
||||
*/
|
||||
dma->sgt.orig_nents = 0;
|
||||
|
||||
|
||||
/*
|
||||
* SGL must be NULL to indicate that SGL is the last one
|
||||
* and we allocated correct number of entries in sg_alloc_table()
|
||||
*/
|
||||
WARN_ON_ONCE(sgl);
|
||||
return &dma->sgt;
|
||||
|
||||
err_unmap_dma:
|
||||
if (!i || !dma->state) {
|
||||
; /* Do nothing */
|
||||
} else if (dma_use_iova(dma->state)) {
|
||||
dma_iova_destroy(attach->dev, dma->state, mapped_len, dir,
|
||||
DMA_ATTR_MMIO);
|
||||
} else {
|
||||
for_each_sgtable_dma_sg(&dma->sgt, sgl, i)
|
||||
dma_unmap_phys(attach->dev, sg_dma_address(sgl),
|
||||
sg_dma_len(sgl), dir, DMA_ATTR_MMIO);
|
||||
}
|
||||
sg_free_table(&dma->sgt);
|
||||
err_free_state:
|
||||
kfree(dma->state);
|
||||
err_free_dma:
|
||||
kfree(dma);
|
||||
return ERR_PTR(ret);
|
||||
}
|
||||
EXPORT_SYMBOL_NS_GPL(dma_buf_phys_vec_to_sgt, "DMA_BUF");
|
||||
|
||||
/**
|
||||
* dma_buf_free_sgt- unmaps the buffer
|
||||
* @attach: [in] attachment to unmap buffer from
|
||||
* @sgt: [in] scatterlist info of the buffer to unmap
|
||||
* @dir: [in] direction of DMA transfer
|
||||
*
|
||||
* This unmaps a DMA mapping for @attached obtained
|
||||
* by dma_buf_phys_vec_to_sgt().
|
||||
*/
|
||||
void dma_buf_free_sgt(struct dma_buf_attachment *attach, struct sg_table *sgt,
|
||||
enum dma_data_direction dir)
|
||||
{
|
||||
struct dma_buf_dma *dma = container_of(sgt, struct dma_buf_dma, sgt);
|
||||
int i;
|
||||
|
||||
dma_resv_assert_held(attach->dmabuf->resv);
|
||||
|
||||
if (!dma->state) {
|
||||
; /* Do nothing */
|
||||
} else if (dma_use_iova(dma->state)) {
|
||||
dma_iova_destroy(attach->dev, dma->state, dma->size, dir,
|
||||
DMA_ATTR_MMIO);
|
||||
} else {
|
||||
struct scatterlist *sgl;
|
||||
|
||||
for_each_sgtable_dma_sg(sgt, sgl, i)
|
||||
dma_unmap_phys(attach->dev, sg_dma_address(sgl),
|
||||
sg_dma_len(sgl), dir, DMA_ATTR_MMIO);
|
||||
}
|
||||
|
||||
sg_free_table(sgt);
|
||||
kfree(dma->state);
|
||||
kfree(dma);
|
||||
|
||||
}
|
||||
EXPORT_SYMBOL_NS_GPL(dma_buf_free_sgt, "DMA_BUF");
|
||||
+117
-146
@@ -1141,6 +1141,122 @@ static int intel_vgpu_set_irqs(struct intel_vgpu *vgpu, u32 flags,
|
||||
return func(vgpu, index, start, count, flags, data);
|
||||
}
|
||||
|
||||
static int intel_vgpu_ioctl_get_region_info(struct vfio_device *vfio_dev,
|
||||
struct vfio_region_info *info,
|
||||
struct vfio_info_cap *caps)
|
||||
{
|
||||
struct vfio_region_info_cap_sparse_mmap *sparse = NULL;
|
||||
struct intel_vgpu *vgpu = vfio_dev_to_vgpu(vfio_dev);
|
||||
int nr_areas = 1;
|
||||
int cap_type_id;
|
||||
unsigned int i;
|
||||
int ret;
|
||||
|
||||
switch (info->index) {
|
||||
case VFIO_PCI_CONFIG_REGION_INDEX:
|
||||
info->offset = VFIO_PCI_INDEX_TO_OFFSET(info->index);
|
||||
info->size = vgpu->gvt->device_info.cfg_space_size;
|
||||
info->flags = VFIO_REGION_INFO_FLAG_READ |
|
||||
VFIO_REGION_INFO_FLAG_WRITE;
|
||||
break;
|
||||
case VFIO_PCI_BAR0_REGION_INDEX:
|
||||
info->offset = VFIO_PCI_INDEX_TO_OFFSET(info->index);
|
||||
info->size = vgpu->cfg_space.bar[info->index].size;
|
||||
if (!info->size) {
|
||||
info->flags = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
info->flags = VFIO_REGION_INFO_FLAG_READ |
|
||||
VFIO_REGION_INFO_FLAG_WRITE;
|
||||
break;
|
||||
case VFIO_PCI_BAR1_REGION_INDEX:
|
||||
info->offset = VFIO_PCI_INDEX_TO_OFFSET(info->index);
|
||||
info->size = 0;
|
||||
info->flags = 0;
|
||||
break;
|
||||
case VFIO_PCI_BAR2_REGION_INDEX:
|
||||
info->offset = VFIO_PCI_INDEX_TO_OFFSET(info->index);
|
||||
info->flags = VFIO_REGION_INFO_FLAG_CAPS |
|
||||
VFIO_REGION_INFO_FLAG_MMAP |
|
||||
VFIO_REGION_INFO_FLAG_READ |
|
||||
VFIO_REGION_INFO_FLAG_WRITE;
|
||||
info->size = gvt_aperture_sz(vgpu->gvt);
|
||||
|
||||
sparse = kzalloc(struct_size(sparse, areas, nr_areas),
|
||||
GFP_KERNEL);
|
||||
if (!sparse)
|
||||
return -ENOMEM;
|
||||
|
||||
sparse->header.id = VFIO_REGION_INFO_CAP_SPARSE_MMAP;
|
||||
sparse->header.version = 1;
|
||||
sparse->nr_areas = nr_areas;
|
||||
cap_type_id = VFIO_REGION_INFO_CAP_SPARSE_MMAP;
|
||||
sparse->areas[0].offset =
|
||||
PAGE_ALIGN(vgpu_aperture_offset(vgpu));
|
||||
sparse->areas[0].size = vgpu_aperture_sz(vgpu);
|
||||
break;
|
||||
|
||||
case VFIO_PCI_BAR3_REGION_INDEX ... VFIO_PCI_BAR5_REGION_INDEX:
|
||||
info->offset = VFIO_PCI_INDEX_TO_OFFSET(info->index);
|
||||
info->size = 0;
|
||||
info->flags = 0;
|
||||
|
||||
gvt_dbg_core("get region info bar:%d\n", info->index);
|
||||
break;
|
||||
|
||||
case VFIO_PCI_ROM_REGION_INDEX:
|
||||
case VFIO_PCI_VGA_REGION_INDEX:
|
||||
info->offset = VFIO_PCI_INDEX_TO_OFFSET(info->index);
|
||||
info->size = 0;
|
||||
info->flags = 0;
|
||||
|
||||
gvt_dbg_core("get region info index:%d\n", info->index);
|
||||
break;
|
||||
default: {
|
||||
struct vfio_region_info_cap_type cap_type = {
|
||||
.header.id = VFIO_REGION_INFO_CAP_TYPE,
|
||||
.header.version = 1
|
||||
};
|
||||
|
||||
if (info->index >= VFIO_PCI_NUM_REGIONS + vgpu->num_regions)
|
||||
return -EINVAL;
|
||||
info->index = array_index_nospec(
|
||||
info->index, VFIO_PCI_NUM_REGIONS + vgpu->num_regions);
|
||||
|
||||
i = info->index - VFIO_PCI_NUM_REGIONS;
|
||||
|
||||
info->offset = VFIO_PCI_INDEX_TO_OFFSET(info->index);
|
||||
info->size = vgpu->region[i].size;
|
||||
info->flags = vgpu->region[i].flags;
|
||||
|
||||
cap_type.type = vgpu->region[i].type;
|
||||
cap_type.subtype = vgpu->region[i].subtype;
|
||||
|
||||
ret = vfio_info_add_capability(caps, &cap_type.header,
|
||||
sizeof(cap_type));
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
if ((info->flags & VFIO_REGION_INFO_FLAG_CAPS) && sparse) {
|
||||
ret = -EINVAL;
|
||||
if (cap_type_id == VFIO_REGION_INFO_CAP_SPARSE_MMAP) {
|
||||
ret = vfio_info_add_capability(
|
||||
caps, &sparse->header,
|
||||
struct_size(sparse, areas, sparse->nr_areas));
|
||||
}
|
||||
if (ret) {
|
||||
kfree(sparse);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
kfree(sparse);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static long intel_vgpu_ioctl(struct vfio_device *vfio_dev, unsigned int cmd,
|
||||
unsigned long arg)
|
||||
{
|
||||
@@ -1169,152 +1285,6 @@ static long intel_vgpu_ioctl(struct vfio_device *vfio_dev, unsigned int cmd,
|
||||
return copy_to_user((void __user *)arg, &info, minsz) ?
|
||||
-EFAULT : 0;
|
||||
|
||||
} else if (cmd == VFIO_DEVICE_GET_REGION_INFO) {
|
||||
struct vfio_region_info info;
|
||||
struct vfio_info_cap caps = { .buf = NULL, .size = 0 };
|
||||
unsigned int i;
|
||||
int ret;
|
||||
struct vfio_region_info_cap_sparse_mmap *sparse = NULL;
|
||||
int nr_areas = 1;
|
||||
int cap_type_id;
|
||||
|
||||
minsz = offsetofend(struct vfio_region_info, offset);
|
||||
|
||||
if (copy_from_user(&info, (void __user *)arg, minsz))
|
||||
return -EFAULT;
|
||||
|
||||
if (info.argsz < minsz)
|
||||
return -EINVAL;
|
||||
|
||||
switch (info.index) {
|
||||
case VFIO_PCI_CONFIG_REGION_INDEX:
|
||||
info.offset = VFIO_PCI_INDEX_TO_OFFSET(info.index);
|
||||
info.size = vgpu->gvt->device_info.cfg_space_size;
|
||||
info.flags = VFIO_REGION_INFO_FLAG_READ |
|
||||
VFIO_REGION_INFO_FLAG_WRITE;
|
||||
break;
|
||||
case VFIO_PCI_BAR0_REGION_INDEX:
|
||||
info.offset = VFIO_PCI_INDEX_TO_OFFSET(info.index);
|
||||
info.size = vgpu->cfg_space.bar[info.index].size;
|
||||
if (!info.size) {
|
||||
info.flags = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
info.flags = VFIO_REGION_INFO_FLAG_READ |
|
||||
VFIO_REGION_INFO_FLAG_WRITE;
|
||||
break;
|
||||
case VFIO_PCI_BAR1_REGION_INDEX:
|
||||
info.offset = VFIO_PCI_INDEX_TO_OFFSET(info.index);
|
||||
info.size = 0;
|
||||
info.flags = 0;
|
||||
break;
|
||||
case VFIO_PCI_BAR2_REGION_INDEX:
|
||||
info.offset = VFIO_PCI_INDEX_TO_OFFSET(info.index);
|
||||
info.flags = VFIO_REGION_INFO_FLAG_CAPS |
|
||||
VFIO_REGION_INFO_FLAG_MMAP |
|
||||
VFIO_REGION_INFO_FLAG_READ |
|
||||
VFIO_REGION_INFO_FLAG_WRITE;
|
||||
info.size = gvt_aperture_sz(vgpu->gvt);
|
||||
|
||||
sparse = kzalloc(struct_size(sparse, areas, nr_areas),
|
||||
GFP_KERNEL);
|
||||
if (!sparse)
|
||||
return -ENOMEM;
|
||||
|
||||
sparse->header.id = VFIO_REGION_INFO_CAP_SPARSE_MMAP;
|
||||
sparse->header.version = 1;
|
||||
sparse->nr_areas = nr_areas;
|
||||
cap_type_id = VFIO_REGION_INFO_CAP_SPARSE_MMAP;
|
||||
sparse->areas[0].offset =
|
||||
PAGE_ALIGN(vgpu_aperture_offset(vgpu));
|
||||
sparse->areas[0].size = vgpu_aperture_sz(vgpu);
|
||||
break;
|
||||
|
||||
case VFIO_PCI_BAR3_REGION_INDEX ... VFIO_PCI_BAR5_REGION_INDEX:
|
||||
info.offset = VFIO_PCI_INDEX_TO_OFFSET(info.index);
|
||||
info.size = 0;
|
||||
info.flags = 0;
|
||||
|
||||
gvt_dbg_core("get region info bar:%d\n", info.index);
|
||||
break;
|
||||
|
||||
case VFIO_PCI_ROM_REGION_INDEX:
|
||||
case VFIO_PCI_VGA_REGION_INDEX:
|
||||
info.offset = VFIO_PCI_INDEX_TO_OFFSET(info.index);
|
||||
info.size = 0;
|
||||
info.flags = 0;
|
||||
|
||||
gvt_dbg_core("get region info index:%d\n", info.index);
|
||||
break;
|
||||
default:
|
||||
{
|
||||
struct vfio_region_info_cap_type cap_type = {
|
||||
.header.id = VFIO_REGION_INFO_CAP_TYPE,
|
||||
.header.version = 1 };
|
||||
|
||||
if (info.index >= VFIO_PCI_NUM_REGIONS +
|
||||
vgpu->num_regions)
|
||||
return -EINVAL;
|
||||
info.index =
|
||||
array_index_nospec(info.index,
|
||||
VFIO_PCI_NUM_REGIONS +
|
||||
vgpu->num_regions);
|
||||
|
||||
i = info.index - VFIO_PCI_NUM_REGIONS;
|
||||
|
||||
info.offset =
|
||||
VFIO_PCI_INDEX_TO_OFFSET(info.index);
|
||||
info.size = vgpu->region[i].size;
|
||||
info.flags = vgpu->region[i].flags;
|
||||
|
||||
cap_type.type = vgpu->region[i].type;
|
||||
cap_type.subtype = vgpu->region[i].subtype;
|
||||
|
||||
ret = vfio_info_add_capability(&caps,
|
||||
&cap_type.header,
|
||||
sizeof(cap_type));
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
if ((info.flags & VFIO_REGION_INFO_FLAG_CAPS) && sparse) {
|
||||
ret = -EINVAL;
|
||||
if (cap_type_id == VFIO_REGION_INFO_CAP_SPARSE_MMAP)
|
||||
ret = vfio_info_add_capability(&caps,
|
||||
&sparse->header,
|
||||
struct_size(sparse, areas,
|
||||
sparse->nr_areas));
|
||||
if (ret) {
|
||||
kfree(sparse);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
if (caps.size) {
|
||||
info.flags |= VFIO_REGION_INFO_FLAG_CAPS;
|
||||
if (info.argsz < sizeof(info) + caps.size) {
|
||||
info.argsz = sizeof(info) + caps.size;
|
||||
info.cap_offset = 0;
|
||||
} else {
|
||||
vfio_info_cap_shift(&caps, sizeof(info));
|
||||
if (copy_to_user((void __user *)arg +
|
||||
sizeof(info), caps.buf,
|
||||
caps.size)) {
|
||||
kfree(caps.buf);
|
||||
kfree(sparse);
|
||||
return -EFAULT;
|
||||
}
|
||||
info.cap_offset = sizeof(info);
|
||||
}
|
||||
|
||||
kfree(caps.buf);
|
||||
}
|
||||
|
||||
kfree(sparse);
|
||||
return copy_to_user((void __user *)arg, &info, minsz) ?
|
||||
-EFAULT : 0;
|
||||
} else if (cmd == VFIO_DEVICE_GET_IRQ_INFO) {
|
||||
struct vfio_irq_info info;
|
||||
|
||||
@@ -1477,6 +1447,7 @@ static const struct vfio_device_ops intel_vgpu_dev_ops = {
|
||||
.write = intel_vgpu_write,
|
||||
.mmap = intel_vgpu_mmap,
|
||||
.ioctl = intel_vgpu_ioctl,
|
||||
.get_region_info_caps = intel_vgpu_ioctl_get_region_info,
|
||||
.dma_unmap = intel_vgpu_dma_unmap,
|
||||
.bind_iommufd = vfio_iommufd_emulated_bind,
|
||||
.unbind_iommufd = vfio_iommufd_emulated_unbind,
|
||||
|
||||
@@ -1439,8 +1439,8 @@ int iommu_dma_map_sg(struct device *dev, struct scatterlist *sg, int nents,
|
||||
* as a bus address, __finalise_sg() will copy the dma
|
||||
* address into the output segment.
|
||||
*/
|
||||
s->dma_address = pci_p2pdma_bus_addr_map(&p2pdma_state,
|
||||
sg_phys(s));
|
||||
s->dma_address = pci_p2pdma_bus_addr_map(
|
||||
p2pdma_state.mem, sg_phys(s));
|
||||
sg_dma_len(s) = sg->length;
|
||||
sg_dma_mark_bus_address(s);
|
||||
continue;
|
||||
|
||||
+145
-43
@@ -25,12 +25,12 @@ struct pci_p2pdma {
|
||||
struct gen_pool *pool;
|
||||
bool p2pmem_published;
|
||||
struct xarray map_types;
|
||||
struct p2pdma_provider mem[PCI_STD_NUM_BARS];
|
||||
};
|
||||
|
||||
struct pci_p2pdma_pagemap {
|
||||
struct pci_dev *provider;
|
||||
u64 bus_offset;
|
||||
struct dev_pagemap pgmap;
|
||||
struct p2pdma_provider *mem;
|
||||
};
|
||||
|
||||
static struct pci_p2pdma_pagemap *to_p2p_pgmap(struct dev_pagemap *pgmap)
|
||||
@@ -204,8 +204,8 @@ static void p2pdma_page_free(struct page *page)
|
||||
{
|
||||
struct pci_p2pdma_pagemap *pgmap = to_p2p_pgmap(page_pgmap(page));
|
||||
/* safe to dereference while a reference is held to the percpu ref */
|
||||
struct pci_p2pdma *p2pdma =
|
||||
rcu_dereference_protected(pgmap->provider->p2pdma, 1);
|
||||
struct pci_p2pdma *p2pdma = rcu_dereference_protected(
|
||||
to_pci_dev(pgmap->mem->owner)->p2pdma, 1);
|
||||
struct percpu_ref *ref;
|
||||
|
||||
gen_pool_free_owner(p2pdma->pool, (uintptr_t)page_to_virt(page),
|
||||
@@ -228,56 +228,136 @@ static void pci_p2pdma_release(void *data)
|
||||
|
||||
/* Flush and disable pci_alloc_p2p_mem() */
|
||||
pdev->p2pdma = NULL;
|
||||
synchronize_rcu();
|
||||
if (p2pdma->pool)
|
||||
synchronize_rcu();
|
||||
xa_destroy(&p2pdma->map_types);
|
||||
|
||||
if (!p2pdma->pool)
|
||||
return;
|
||||
|
||||
gen_pool_destroy(p2pdma->pool);
|
||||
sysfs_remove_group(&pdev->dev.kobj, &p2pmem_group);
|
||||
xa_destroy(&p2pdma->map_types);
|
||||
}
|
||||
|
||||
static int pci_p2pdma_setup(struct pci_dev *pdev)
|
||||
/**
|
||||
* pcim_p2pdma_init - Initialise peer-to-peer DMA providers
|
||||
* @pdev: The PCI device to enable P2PDMA for
|
||||
*
|
||||
* This function initializes the peer-to-peer DMA infrastructure
|
||||
* for a PCI device. It allocates and sets up the necessary data
|
||||
* structures to support P2PDMA operations, including mapping type
|
||||
* tracking.
|
||||
*/
|
||||
int pcim_p2pdma_init(struct pci_dev *pdev)
|
||||
{
|
||||
int error = -ENOMEM;
|
||||
struct pci_p2pdma *p2p;
|
||||
int i, ret;
|
||||
|
||||
p2p = rcu_dereference_protected(pdev->p2pdma, 1);
|
||||
if (p2p)
|
||||
return 0;
|
||||
|
||||
p2p = devm_kzalloc(&pdev->dev, sizeof(*p2p), GFP_KERNEL);
|
||||
if (!p2p)
|
||||
return -ENOMEM;
|
||||
|
||||
xa_init(&p2p->map_types);
|
||||
/*
|
||||
* Iterate over all standard PCI BARs and record only those that
|
||||
* correspond to MMIO regions. Skip non-memory resources (e.g. I/O
|
||||
* port BARs) since they cannot be used for peer-to-peer (P2P)
|
||||
* transactions.
|
||||
*/
|
||||
for (i = 0; i < PCI_STD_NUM_BARS; i++) {
|
||||
if (!(pci_resource_flags(pdev, i) & IORESOURCE_MEM))
|
||||
continue;
|
||||
|
||||
p2p->pool = gen_pool_create(PAGE_SHIFT, dev_to_node(&pdev->dev));
|
||||
if (!p2p->pool)
|
||||
goto out;
|
||||
p2p->mem[i].owner = &pdev->dev;
|
||||
p2p->mem[i].bus_offset =
|
||||
pci_bus_address(pdev, i) - pci_resource_start(pdev, i);
|
||||
}
|
||||
|
||||
error = devm_add_action_or_reset(&pdev->dev, pci_p2pdma_release, pdev);
|
||||
if (error)
|
||||
goto out_pool_destroy;
|
||||
|
||||
error = sysfs_create_group(&pdev->dev.kobj, &p2pmem_group);
|
||||
if (error)
|
||||
goto out_pool_destroy;
|
||||
ret = devm_add_action_or_reset(&pdev->dev, pci_p2pdma_release, pdev);
|
||||
if (ret)
|
||||
goto out_p2p;
|
||||
|
||||
rcu_assign_pointer(pdev->p2pdma, p2p);
|
||||
return 0;
|
||||
|
||||
out_pool_destroy:
|
||||
gen_pool_destroy(p2p->pool);
|
||||
out:
|
||||
out_p2p:
|
||||
devm_kfree(&pdev->dev, p2p);
|
||||
return error;
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(pcim_p2pdma_init);
|
||||
|
||||
/**
|
||||
* pcim_p2pdma_provider - Get peer-to-peer DMA provider
|
||||
* @pdev: The PCI device to enable P2PDMA for
|
||||
* @bar: BAR index to get provider
|
||||
*
|
||||
* This function gets peer-to-peer DMA provider for a PCI device. The lifetime
|
||||
* of the provider (and of course the MMIO) is bound to the lifetime of the
|
||||
* driver. A driver calling this function must ensure that all references to the
|
||||
* provider, and any DMA mappings created for any MMIO, are all cleaned up
|
||||
* before the driver remove() completes.
|
||||
*
|
||||
* Since P2P is almost always shared with a second driver this means some system
|
||||
* to notify, invalidate and revoke the MMIO's DMA must be in place to use this
|
||||
* function. For example a revoke can be built using DMABUF.
|
||||
*/
|
||||
struct p2pdma_provider *pcim_p2pdma_provider(struct pci_dev *pdev, int bar)
|
||||
{
|
||||
struct pci_p2pdma *p2p;
|
||||
|
||||
if (!(pci_resource_flags(pdev, bar) & IORESOURCE_MEM))
|
||||
return NULL;
|
||||
|
||||
p2p = rcu_dereference_protected(pdev->p2pdma, 1);
|
||||
if (WARN_ON(!p2p))
|
||||
/* Someone forgot to call to pcim_p2pdma_init() before */
|
||||
return NULL;
|
||||
|
||||
return &p2p->mem[bar];
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(pcim_p2pdma_provider);
|
||||
|
||||
static int pci_p2pdma_setup_pool(struct pci_dev *pdev)
|
||||
{
|
||||
struct pci_p2pdma *p2pdma;
|
||||
int ret;
|
||||
|
||||
p2pdma = rcu_dereference_protected(pdev->p2pdma, 1);
|
||||
if (p2pdma->pool)
|
||||
/* We already setup pools, do nothing, */
|
||||
return 0;
|
||||
|
||||
p2pdma->pool = gen_pool_create(PAGE_SHIFT, dev_to_node(&pdev->dev));
|
||||
if (!p2pdma->pool)
|
||||
return -ENOMEM;
|
||||
|
||||
ret = sysfs_create_group(&pdev->dev.kobj, &p2pmem_group);
|
||||
if (ret)
|
||||
goto out_pool_destroy;
|
||||
|
||||
return 0;
|
||||
|
||||
out_pool_destroy:
|
||||
gen_pool_destroy(p2pdma->pool);
|
||||
p2pdma->pool = NULL;
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void pci_p2pdma_unmap_mappings(void *data)
|
||||
{
|
||||
struct pci_dev *pdev = data;
|
||||
struct pci_p2pdma_pagemap *p2p_pgmap = data;
|
||||
|
||||
/*
|
||||
* Removing the alloc attribute from sysfs will call
|
||||
* unmap_mapping_range() on the inode, teardown any existing userspace
|
||||
* mappings and prevent new ones from being created.
|
||||
*/
|
||||
sysfs_remove_file_from_group(&pdev->dev.kobj, &p2pmem_alloc_attr.attr,
|
||||
sysfs_remove_file_from_group(&p2p_pgmap->mem->owner->kobj,
|
||||
&p2pmem_alloc_attr.attr,
|
||||
p2pmem_group.name);
|
||||
}
|
||||
|
||||
@@ -295,6 +375,7 @@ int pci_p2pdma_add_resource(struct pci_dev *pdev, int bar, size_t size,
|
||||
u64 offset)
|
||||
{
|
||||
struct pci_p2pdma_pagemap *p2p_pgmap;
|
||||
struct p2pdma_provider *mem;
|
||||
struct dev_pagemap *pgmap;
|
||||
struct pci_p2pdma *p2pdma;
|
||||
void *addr;
|
||||
@@ -312,11 +393,21 @@ int pci_p2pdma_add_resource(struct pci_dev *pdev, int bar, size_t size,
|
||||
if (size + offset > pci_resource_len(pdev, bar))
|
||||
return -EINVAL;
|
||||
|
||||
if (!pdev->p2pdma) {
|
||||
error = pci_p2pdma_setup(pdev);
|
||||
if (error)
|
||||
return error;
|
||||
}
|
||||
error = pcim_p2pdma_init(pdev);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
error = pci_p2pdma_setup_pool(pdev);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
mem = pcim_p2pdma_provider(pdev, bar);
|
||||
/*
|
||||
* We checked validity of BAR prior to call
|
||||
* to pcim_p2pdma_provider. It should never return NULL.
|
||||
*/
|
||||
if (WARN_ON(!mem))
|
||||
return -EINVAL;
|
||||
|
||||
p2p_pgmap = devm_kzalloc(&pdev->dev, sizeof(*p2p_pgmap), GFP_KERNEL);
|
||||
if (!p2p_pgmap)
|
||||
@@ -328,10 +419,7 @@ int pci_p2pdma_add_resource(struct pci_dev *pdev, int bar, size_t size,
|
||||
pgmap->nr_range = 1;
|
||||
pgmap->type = MEMORY_DEVICE_PCI_P2PDMA;
|
||||
pgmap->ops = &p2pdma_pgmap_ops;
|
||||
|
||||
p2p_pgmap->provider = pdev;
|
||||
p2p_pgmap->bus_offset = pci_bus_address(pdev, bar) -
|
||||
pci_resource_start(pdev, bar);
|
||||
p2p_pgmap->mem = mem;
|
||||
|
||||
addr = devm_memremap_pages(&pdev->dev, pgmap);
|
||||
if (IS_ERR(addr)) {
|
||||
@@ -340,7 +428,7 @@ int pci_p2pdma_add_resource(struct pci_dev *pdev, int bar, size_t size,
|
||||
}
|
||||
|
||||
error = devm_add_action_or_reset(&pdev->dev, pci_p2pdma_unmap_mappings,
|
||||
pdev);
|
||||
p2p_pgmap);
|
||||
if (error)
|
||||
goto pages_free;
|
||||
|
||||
@@ -972,16 +1060,26 @@ void pci_p2pmem_publish(struct pci_dev *pdev, bool publish)
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(pci_p2pmem_publish);
|
||||
|
||||
static enum pci_p2pdma_map_type pci_p2pdma_map_type(struct dev_pagemap *pgmap,
|
||||
struct device *dev)
|
||||
/**
|
||||
* pci_p2pdma_map_type - Determine the mapping type for P2PDMA transfers
|
||||
* @provider: P2PDMA provider structure
|
||||
* @dev: Target device for the transfer
|
||||
*
|
||||
* Determines how peer-to-peer DMA transfers should be mapped between
|
||||
* the provider and the target device. The mapping type indicates whether
|
||||
* the transfer can be done directly through PCI switches or must go
|
||||
* through the host bridge.
|
||||
*/
|
||||
enum pci_p2pdma_map_type pci_p2pdma_map_type(struct p2pdma_provider *provider,
|
||||
struct device *dev)
|
||||
{
|
||||
enum pci_p2pdma_map_type type = PCI_P2PDMA_MAP_NOT_SUPPORTED;
|
||||
struct pci_dev *provider = to_p2p_pgmap(pgmap)->provider;
|
||||
struct pci_dev *pdev = to_pci_dev(provider->owner);
|
||||
struct pci_dev *client;
|
||||
struct pci_p2pdma *p2pdma;
|
||||
int dist;
|
||||
|
||||
if (!provider->p2pdma)
|
||||
if (!pdev->p2pdma)
|
||||
return PCI_P2PDMA_MAP_NOT_SUPPORTED;
|
||||
|
||||
if (!dev_is_pci(dev))
|
||||
@@ -990,7 +1088,7 @@ static enum pci_p2pdma_map_type pci_p2pdma_map_type(struct dev_pagemap *pgmap,
|
||||
client = to_pci_dev(dev);
|
||||
|
||||
rcu_read_lock();
|
||||
p2pdma = rcu_dereference(provider->p2pdma);
|
||||
p2pdma = rcu_dereference(pdev->p2pdma);
|
||||
|
||||
if (p2pdma)
|
||||
type = xa_to_value(xa_load(&p2pdma->map_types,
|
||||
@@ -998,7 +1096,7 @@ static enum pci_p2pdma_map_type pci_p2pdma_map_type(struct dev_pagemap *pgmap,
|
||||
rcu_read_unlock();
|
||||
|
||||
if (type == PCI_P2PDMA_MAP_UNKNOWN)
|
||||
return calc_map_type_and_dist(provider, client, &dist, true);
|
||||
return calc_map_type_and_dist(pdev, client, &dist, true);
|
||||
|
||||
return type;
|
||||
}
|
||||
@@ -1006,9 +1104,13 @@ static enum pci_p2pdma_map_type pci_p2pdma_map_type(struct dev_pagemap *pgmap,
|
||||
void __pci_p2pdma_update_state(struct pci_p2pdma_map_state *state,
|
||||
struct device *dev, struct page *page)
|
||||
{
|
||||
state->pgmap = page_pgmap(page);
|
||||
state->map = pci_p2pdma_map_type(state->pgmap, dev);
|
||||
state->bus_off = to_p2p_pgmap(state->pgmap)->bus_offset;
|
||||
struct pci_p2pdma_pagemap *p2p_pgmap = to_p2p_pgmap(page_pgmap(page));
|
||||
|
||||
if (state->mem == p2p_pgmap->mem)
|
||||
return;
|
||||
|
||||
state->mem = p2p_pgmap->mem;
|
||||
state->map = pci_p2pdma_map_type(p2p_pgmap->mem, dev);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -313,10 +313,12 @@ static int vfio_ccw_mdev_get_device_info(struct vfio_ccw_private *private,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int vfio_ccw_mdev_get_region_info(struct vfio_ccw_private *private,
|
||||
struct vfio_region_info *info,
|
||||
unsigned long arg)
|
||||
static int vfio_ccw_mdev_ioctl_get_region_info(struct vfio_device *vdev,
|
||||
struct vfio_region_info *info,
|
||||
struct vfio_info_cap *caps)
|
||||
{
|
||||
struct vfio_ccw_private *private =
|
||||
container_of(vdev, struct vfio_ccw_private, vdev);
|
||||
int i;
|
||||
|
||||
switch (info->index) {
|
||||
@@ -328,7 +330,6 @@ static int vfio_ccw_mdev_get_region_info(struct vfio_ccw_private *private,
|
||||
return 0;
|
||||
default: /* all other regions are handled via capability chain */
|
||||
{
|
||||
struct vfio_info_cap caps = { .buf = NULL, .size = 0 };
|
||||
struct vfio_region_info_cap_type cap_type = {
|
||||
.header.id = VFIO_REGION_INFO_CAP_TYPE,
|
||||
.header.version = 1 };
|
||||
@@ -351,27 +352,10 @@ static int vfio_ccw_mdev_get_region_info(struct vfio_ccw_private *private,
|
||||
cap_type.type = private->region[i].type;
|
||||
cap_type.subtype = private->region[i].subtype;
|
||||
|
||||
ret = vfio_info_add_capability(&caps, &cap_type.header,
|
||||
ret = vfio_info_add_capability(caps, &cap_type.header,
|
||||
sizeof(cap_type));
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
info->flags |= VFIO_REGION_INFO_FLAG_CAPS;
|
||||
if (info->argsz < sizeof(*info) + caps.size) {
|
||||
info->argsz = sizeof(*info) + caps.size;
|
||||
info->cap_offset = 0;
|
||||
} else {
|
||||
vfio_info_cap_shift(&caps, sizeof(*info));
|
||||
if (copy_to_user((void __user *)arg + sizeof(*info),
|
||||
caps.buf, caps.size)) {
|
||||
kfree(caps.buf);
|
||||
return -EFAULT;
|
||||
}
|
||||
info->cap_offset = sizeof(*info);
|
||||
}
|
||||
|
||||
kfree(caps.buf);
|
||||
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
@@ -532,24 +516,6 @@ static ssize_t vfio_ccw_mdev_ioctl(struct vfio_device *vdev,
|
||||
|
||||
return copy_to_user((void __user *)arg, &info, minsz) ? -EFAULT : 0;
|
||||
}
|
||||
case VFIO_DEVICE_GET_REGION_INFO:
|
||||
{
|
||||
struct vfio_region_info info;
|
||||
|
||||
minsz = offsetofend(struct vfio_region_info, offset);
|
||||
|
||||
if (copy_from_user(&info, (void __user *)arg, minsz))
|
||||
return -EFAULT;
|
||||
|
||||
if (info.argsz < minsz)
|
||||
return -EINVAL;
|
||||
|
||||
ret = vfio_ccw_mdev_get_region_info(private, &info, arg);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return copy_to_user((void __user *)arg, &info, minsz) ? -EFAULT : 0;
|
||||
}
|
||||
case VFIO_DEVICE_GET_IRQ_INFO:
|
||||
{
|
||||
struct vfio_irq_info info;
|
||||
@@ -627,6 +593,7 @@ static const struct vfio_device_ops vfio_ccw_dev_ops = {
|
||||
.read = vfio_ccw_mdev_read,
|
||||
.write = vfio_ccw_mdev_write,
|
||||
.ioctl = vfio_ccw_mdev_ioctl,
|
||||
.get_region_info_caps = vfio_ccw_mdev_ioctl_get_region_info,
|
||||
.request = vfio_ccw_mdev_request,
|
||||
.dma_unmap = vfio_ccw_dma_unmap,
|
||||
.bind_iommufd = vfio_iommufd_emulated_bind,
|
||||
|
||||
+11
-18
@@ -129,28 +129,22 @@ static int vfio_cdx_ioctl_get_info(struct vfio_cdx_device *vdev,
|
||||
return copy_to_user(arg, &info, minsz) ? -EFAULT : 0;
|
||||
}
|
||||
|
||||
static int vfio_cdx_ioctl_get_region_info(struct vfio_cdx_device *vdev,
|
||||
struct vfio_region_info __user *arg)
|
||||
static int vfio_cdx_ioctl_get_region_info(struct vfio_device *core_vdev,
|
||||
struct vfio_region_info *info,
|
||||
struct vfio_info_cap *caps)
|
||||
{
|
||||
unsigned long minsz = offsetofend(struct vfio_region_info, offset);
|
||||
struct vfio_cdx_device *vdev =
|
||||
container_of(core_vdev, struct vfio_cdx_device, vdev);
|
||||
struct cdx_device *cdx_dev = to_cdx_device(vdev->vdev.dev);
|
||||
struct vfio_region_info info;
|
||||
|
||||
if (copy_from_user(&info, arg, minsz))
|
||||
return -EFAULT;
|
||||
|
||||
if (info.argsz < minsz)
|
||||
return -EINVAL;
|
||||
|
||||
if (info.index >= cdx_dev->res_count)
|
||||
if (info->index >= cdx_dev->res_count)
|
||||
return -EINVAL;
|
||||
|
||||
/* map offset to the physical address */
|
||||
info.offset = vfio_cdx_index_to_offset(info.index);
|
||||
info.size = vdev->regions[info.index].size;
|
||||
info.flags = vdev->regions[info.index].flags;
|
||||
|
||||
return copy_to_user(arg, &info, minsz) ? -EFAULT : 0;
|
||||
info->offset = vfio_cdx_index_to_offset(info->index);
|
||||
info->size = vdev->regions[info->index].size;
|
||||
info->flags = vdev->regions[info->index].flags;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int vfio_cdx_ioctl_get_irq_info(struct vfio_cdx_device *vdev,
|
||||
@@ -219,8 +213,6 @@ static long vfio_cdx_ioctl(struct vfio_device *core_vdev,
|
||||
switch (cmd) {
|
||||
case VFIO_DEVICE_GET_INFO:
|
||||
return vfio_cdx_ioctl_get_info(vdev, uarg);
|
||||
case VFIO_DEVICE_GET_REGION_INFO:
|
||||
return vfio_cdx_ioctl_get_region_info(vdev, uarg);
|
||||
case VFIO_DEVICE_GET_IRQ_INFO:
|
||||
return vfio_cdx_ioctl_get_irq_info(vdev, uarg);
|
||||
case VFIO_DEVICE_SET_IRQS:
|
||||
@@ -284,6 +276,7 @@ static const struct vfio_device_ops vfio_cdx_ops = {
|
||||
.open_device = vfio_cdx_open_device,
|
||||
.close_device = vfio_cdx_close_device,
|
||||
.ioctl = vfio_cdx_ioctl,
|
||||
.get_region_info_caps = vfio_cdx_ioctl_get_region_info,
|
||||
.device_feature = vfio_cdx_ioctl_feature,
|
||||
.mmap = vfio_cdx_mmap,
|
||||
.bind_iommufd = vfio_iommufd_physical_bind,
|
||||
|
||||
@@ -99,7 +99,7 @@ long vfio_df_ioctl_bind_iommufd(struct vfio_device_file *df,
|
||||
return ret;
|
||||
if (user_size < minsz)
|
||||
return -EINVAL;
|
||||
ret = copy_struct_from_user(&bind, minsz, arg, user_size);
|
||||
ret = copy_struct_from_user(&bind, sizeof(bind), arg, user_size);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
|
||||
@@ -117,6 +117,24 @@ static void vfio_fsl_mc_close_device(struct vfio_device *core_vdev)
|
||||
fsl_mc_cleanup_irq_pool(mc_cont);
|
||||
}
|
||||
|
||||
static int vfio_fsl_mc_ioctl_get_region_info(struct vfio_device *core_vdev,
|
||||
struct vfio_region_info *info,
|
||||
struct vfio_info_cap *caps)
|
||||
{
|
||||
struct vfio_fsl_mc_device *vdev =
|
||||
container_of(core_vdev, struct vfio_fsl_mc_device, vdev);
|
||||
struct fsl_mc_device *mc_dev = vdev->mc_dev;
|
||||
|
||||
if (info->index >= mc_dev->obj_desc.region_count)
|
||||
return -EINVAL;
|
||||
|
||||
/* map offset to the physical address */
|
||||
info->offset = VFIO_FSL_MC_INDEX_TO_OFFSET(info->index);
|
||||
info->size = vdev->regions[info->index].size;
|
||||
info->flags = vdev->regions[info->index].flags;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static long vfio_fsl_mc_ioctl(struct vfio_device *core_vdev,
|
||||
unsigned int cmd, unsigned long arg)
|
||||
{
|
||||
@@ -149,30 +167,6 @@ static long vfio_fsl_mc_ioctl(struct vfio_device *core_vdev,
|
||||
return copy_to_user((void __user *)arg, &info, minsz) ?
|
||||
-EFAULT : 0;
|
||||
}
|
||||
case VFIO_DEVICE_GET_REGION_INFO:
|
||||
{
|
||||
struct vfio_region_info info;
|
||||
|
||||
minsz = offsetofend(struct vfio_region_info, offset);
|
||||
|
||||
if (copy_from_user(&info, (void __user *)arg, minsz))
|
||||
return -EFAULT;
|
||||
|
||||
if (info.argsz < minsz)
|
||||
return -EINVAL;
|
||||
|
||||
if (info.index >= mc_dev->obj_desc.region_count)
|
||||
return -EINVAL;
|
||||
|
||||
/* map offset to the physical address */
|
||||
info.offset = VFIO_FSL_MC_INDEX_TO_OFFSET(info.index);
|
||||
info.size = vdev->regions[info.index].size;
|
||||
info.flags = vdev->regions[info.index].flags;
|
||||
|
||||
if (copy_to_user((void __user *)arg, &info, minsz))
|
||||
return -EFAULT;
|
||||
return 0;
|
||||
}
|
||||
case VFIO_DEVICE_GET_IRQ_INFO:
|
||||
{
|
||||
struct vfio_irq_info info;
|
||||
@@ -589,6 +583,7 @@ static const struct vfio_device_ops vfio_fsl_mc_ops = {
|
||||
.open_device = vfio_fsl_mc_open_device,
|
||||
.close_device = vfio_fsl_mc_close_device,
|
||||
.ioctl = vfio_fsl_mc_ioctl,
|
||||
.get_region_info_caps = vfio_fsl_mc_ioctl_get_region_info,
|
||||
.read = vfio_fsl_mc_read,
|
||||
.write = vfio_fsl_mc_write,
|
||||
.mmap = vfio_fsl_mc_mmap,
|
||||
|
||||
@@ -55,6 +55,9 @@ config VFIO_PCI_ZDEV_KVM
|
||||
|
||||
To enable s390x KVM vfio-pci extensions, say Y.
|
||||
|
||||
config VFIO_PCI_DMABUF
|
||||
def_bool y if VFIO_PCI_CORE && PCI_P2PDMA && DMA_SHARED_BUFFER
|
||||
|
||||
source "drivers/vfio/pci/mlx5/Kconfig"
|
||||
|
||||
source "drivers/vfio/pci/hisilicon/Kconfig"
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
vfio-pci-core-y := vfio_pci_core.o vfio_pci_intrs.o vfio_pci_rdwr.o vfio_pci_config.o
|
||||
vfio-pci-core-$(CONFIG_VFIO_PCI_ZDEV_KVM) += vfio_pci_zdev.o
|
||||
vfio-pci-core-$(CONFIG_VFIO_PCI_DMABUF) += vfio_pci_dmabuf.o
|
||||
obj-$(CONFIG_VFIO_PCI_CORE) += vfio-pci-core.o
|
||||
|
||||
vfio-pci-y := vfio_pci.o
|
||||
|
||||
@@ -125,9 +125,25 @@ static int qm_get_cqc(struct hisi_qm *qm, u64 *addr)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void qm_xqc_reg_offsets(struct hisi_qm *qm,
|
||||
u32 *eqc_addr, u32 *aeqc_addr)
|
||||
{
|
||||
struct hisi_acc_vf_core_device *hisi_acc_vdev =
|
||||
container_of(qm, struct hisi_acc_vf_core_device, vf_qm);
|
||||
|
||||
if (hisi_acc_vdev->drv_mode == HW_ACC_MIG_VF_CTRL) {
|
||||
*eqc_addr = QM_EQC_VF_DW0;
|
||||
*aeqc_addr = QM_AEQC_VF_DW0;
|
||||
} else {
|
||||
*eqc_addr = QM_EQC_PF_DW0;
|
||||
*aeqc_addr = QM_AEQC_PF_DW0;
|
||||
}
|
||||
}
|
||||
|
||||
static int qm_get_regs(struct hisi_qm *qm, struct acc_vf_data *vf_data)
|
||||
{
|
||||
struct device *dev = &qm->pdev->dev;
|
||||
u32 eqc_addr, aeqc_addr;
|
||||
int ret;
|
||||
|
||||
ret = qm_read_regs(qm, QM_VF_AEQ_INT_MASK, &vf_data->aeq_int_mask, 1);
|
||||
@@ -167,15 +183,16 @@ static int qm_get_regs(struct hisi_qm *qm, struct acc_vf_data *vf_data)
|
||||
return ret;
|
||||
}
|
||||
|
||||
qm_xqc_reg_offsets(qm, &eqc_addr, &aeqc_addr);
|
||||
/* QM_EQC_DW has 7 regs */
|
||||
ret = qm_read_regs(qm, QM_EQC_DW0, vf_data->qm_eqc_dw, 7);
|
||||
ret = qm_read_regs(qm, eqc_addr, vf_data->qm_eqc_dw, 7);
|
||||
if (ret) {
|
||||
dev_err(dev, "failed to read QM_EQC_DW\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* QM_AEQC_DW has 7 regs */
|
||||
ret = qm_read_regs(qm, QM_AEQC_DW0, vf_data->qm_aeqc_dw, 7);
|
||||
ret = qm_read_regs(qm, aeqc_addr, vf_data->qm_aeqc_dw, 7);
|
||||
if (ret) {
|
||||
dev_err(dev, "failed to read QM_AEQC_DW\n");
|
||||
return ret;
|
||||
@@ -187,6 +204,7 @@ static int qm_get_regs(struct hisi_qm *qm, struct acc_vf_data *vf_data)
|
||||
static int qm_set_regs(struct hisi_qm *qm, struct acc_vf_data *vf_data)
|
||||
{
|
||||
struct device *dev = &qm->pdev->dev;
|
||||
u32 eqc_addr, aeqc_addr;
|
||||
int ret;
|
||||
|
||||
/* Check VF state */
|
||||
@@ -239,15 +257,16 @@ static int qm_set_regs(struct hisi_qm *qm, struct acc_vf_data *vf_data)
|
||||
return ret;
|
||||
}
|
||||
|
||||
qm_xqc_reg_offsets(qm, &eqc_addr, &aeqc_addr);
|
||||
/* QM_EQC_DW has 7 regs */
|
||||
ret = qm_write_regs(qm, QM_EQC_DW0, vf_data->qm_eqc_dw, 7);
|
||||
ret = qm_write_regs(qm, eqc_addr, vf_data->qm_eqc_dw, 7);
|
||||
if (ret) {
|
||||
dev_err(dev, "failed to write QM_EQC_DW\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* QM_AEQC_DW has 7 regs */
|
||||
ret = qm_write_regs(qm, QM_AEQC_DW0, vf_data->qm_aeqc_dw, 7);
|
||||
ret = qm_write_regs(qm, aeqc_addr, vf_data->qm_aeqc_dw, 7);
|
||||
if (ret) {
|
||||
dev_err(dev, "failed to write QM_AEQC_DW\n");
|
||||
return ret;
|
||||
@@ -1186,34 +1205,52 @@ static int hisi_acc_vf_qm_init(struct hisi_acc_vf_core_device *hisi_acc_vdev)
|
||||
{
|
||||
struct vfio_pci_core_device *vdev = &hisi_acc_vdev->core_device;
|
||||
struct hisi_qm *vf_qm = &hisi_acc_vdev->vf_qm;
|
||||
struct hisi_qm *pf_qm = hisi_acc_vdev->pf_qm;
|
||||
struct pci_dev *vf_dev = vdev->pdev;
|
||||
u32 val;
|
||||
|
||||
/*
|
||||
* ACC VF dev BAR2 region consists of both functional register space
|
||||
* and migration control register space. For migration to work, we
|
||||
* need access to both. Hence, we map the entire BAR2 region here.
|
||||
* But unnecessarily exposing the migration BAR region to the Guest
|
||||
* has the potential to prevent/corrupt the Guest migration. Hence,
|
||||
* we restrict access to the migration control space from
|
||||
* Guest(Please see mmap/ioctl/read/write override functions).
|
||||
*
|
||||
* Please note that it is OK to expose the entire VF BAR if migration
|
||||
* is not supported or required as this cannot affect the ACC PF
|
||||
* configurations.
|
||||
*
|
||||
* Also the HiSilicon ACC VF devices supported by this driver on
|
||||
* HiSilicon hardware platforms are integrated end point devices
|
||||
* and the platform lacks the capability to perform any PCIe P2P
|
||||
* between these devices.
|
||||
*/
|
||||
val = readl(pf_qm->io_base + QM_MIG_REGION_SEL);
|
||||
if (pf_qm->ver > QM_HW_V3 && (val & QM_MIG_REGION_EN))
|
||||
hisi_acc_vdev->drv_mode = HW_ACC_MIG_PF_CTRL;
|
||||
else
|
||||
hisi_acc_vdev->drv_mode = HW_ACC_MIG_VF_CTRL;
|
||||
|
||||
vf_qm->io_base =
|
||||
ioremap(pci_resource_start(vf_dev, VFIO_PCI_BAR2_REGION_INDEX),
|
||||
pci_resource_len(vf_dev, VFIO_PCI_BAR2_REGION_INDEX));
|
||||
if (!vf_qm->io_base)
|
||||
return -EIO;
|
||||
if (hisi_acc_vdev->drv_mode == HW_ACC_MIG_PF_CTRL) {
|
||||
/*
|
||||
* On hardware platforms greater than QM_HW_V3, the migration function
|
||||
* register is placed in the BAR2 configuration region of the PF,
|
||||
* and each VF device occupies 8KB of configuration space.
|
||||
*/
|
||||
vf_qm->io_base = pf_qm->io_base + QM_MIG_REGION_OFFSET +
|
||||
hisi_acc_vdev->vf_id * QM_MIG_REGION_SIZE;
|
||||
} else {
|
||||
/*
|
||||
* ACC VF dev BAR2 region consists of both functional register space
|
||||
* and migration control register space. For migration to work, we
|
||||
* need access to both. Hence, we map the entire BAR2 region here.
|
||||
* But unnecessarily exposing the migration BAR region to the Guest
|
||||
* has the potential to prevent/corrupt the Guest migration. Hence,
|
||||
* we restrict access to the migration control space from
|
||||
* Guest(Please see mmap/ioctl/read/write override functions).
|
||||
*
|
||||
* Please note that it is OK to expose the entire VF BAR if migration
|
||||
* is not supported or required as this cannot affect the ACC PF
|
||||
* configurations.
|
||||
*
|
||||
* Also the HiSilicon ACC VF devices supported by this driver on
|
||||
* HiSilicon hardware platforms are integrated end point devices
|
||||
* and the platform lacks the capability to perform any PCIe P2P
|
||||
* between these devices.
|
||||
*/
|
||||
|
||||
vf_qm->io_base =
|
||||
ioremap(pci_resource_start(vf_dev, VFIO_PCI_BAR2_REGION_INDEX),
|
||||
pci_resource_len(vf_dev, VFIO_PCI_BAR2_REGION_INDEX));
|
||||
if (!vf_qm->io_base)
|
||||
return -EIO;
|
||||
}
|
||||
vf_qm->fun_type = QM_HW_VF;
|
||||
vf_qm->ver = pf_qm->ver;
|
||||
vf_qm->pdev = vf_dev;
|
||||
mutex_init(&vf_qm->mailbox_lock);
|
||||
|
||||
@@ -1250,6 +1287,28 @@ static struct hisi_qm *hisi_acc_get_pf_qm(struct pci_dev *pdev)
|
||||
return !IS_ERR(pf_qm) ? pf_qm : NULL;
|
||||
}
|
||||
|
||||
static size_t hisi_acc_get_resource_len(struct vfio_pci_core_device *vdev,
|
||||
unsigned int index)
|
||||
{
|
||||
struct hisi_acc_vf_core_device *hisi_acc_vdev =
|
||||
hisi_acc_drvdata(vdev->pdev);
|
||||
|
||||
/*
|
||||
* On the old HW_ACC_MIG_VF_CTRL mode device, the ACC VF device
|
||||
* BAR2 region encompasses both functional register space
|
||||
* and migration control register space.
|
||||
* only the functional region should be report to Guest.
|
||||
*/
|
||||
if (hisi_acc_vdev->drv_mode == HW_ACC_MIG_VF_CTRL)
|
||||
return (pci_resource_len(vdev->pdev, index) >> 1);
|
||||
/*
|
||||
* On the new HW device, the migration control register
|
||||
* has been moved to the PF device BAR2 region.
|
||||
* The VF device BAR2 is entirely functional register space.
|
||||
*/
|
||||
return pci_resource_len(vdev->pdev, index);
|
||||
}
|
||||
|
||||
static int hisi_acc_pci_rw_access_check(struct vfio_device *core_vdev,
|
||||
size_t count, loff_t *ppos,
|
||||
size_t *new_count)
|
||||
@@ -1260,8 +1319,9 @@ static int hisi_acc_pci_rw_access_check(struct vfio_device *core_vdev,
|
||||
|
||||
if (index == VFIO_PCI_BAR2_REGION_INDEX) {
|
||||
loff_t pos = *ppos & VFIO_PCI_OFFSET_MASK;
|
||||
resource_size_t end = pci_resource_len(vdev->pdev, index) / 2;
|
||||
resource_size_t end;
|
||||
|
||||
end = hisi_acc_get_resource_len(vdev, index);
|
||||
/* Check if access is for migration control region */
|
||||
if (pos >= end)
|
||||
return -EINVAL;
|
||||
@@ -1282,8 +1342,9 @@ static int hisi_acc_vfio_pci_mmap(struct vfio_device *core_vdev,
|
||||
index = vma->vm_pgoff >> (VFIO_PCI_OFFSET_SHIFT - PAGE_SHIFT);
|
||||
if (index == VFIO_PCI_BAR2_REGION_INDEX) {
|
||||
u64 req_len, pgoff, req_start;
|
||||
resource_size_t end = pci_resource_len(vdev->pdev, index) / 2;
|
||||
resource_size_t end;
|
||||
|
||||
end = hisi_acc_get_resource_len(vdev, index);
|
||||
req_len = vma->vm_end - vma->vm_start;
|
||||
pgoff = vma->vm_pgoff &
|
||||
((1U << (VFIO_PCI_OFFSET_SHIFT - PAGE_SHIFT)) - 1);
|
||||
@@ -1324,43 +1385,23 @@ static ssize_t hisi_acc_vfio_pci_read(struct vfio_device *core_vdev,
|
||||
return vfio_pci_core_read(core_vdev, buf, new_count, ppos);
|
||||
}
|
||||
|
||||
static long hisi_acc_vfio_pci_ioctl(struct vfio_device *core_vdev, unsigned int cmd,
|
||||
unsigned long arg)
|
||||
static int hisi_acc_vfio_ioctl_get_region(struct vfio_device *core_vdev,
|
||||
struct vfio_region_info *info,
|
||||
struct vfio_info_cap *caps)
|
||||
{
|
||||
if (cmd == VFIO_DEVICE_GET_REGION_INFO) {
|
||||
struct vfio_pci_core_device *vdev =
|
||||
container_of(core_vdev, struct vfio_pci_core_device, vdev);
|
||||
struct pci_dev *pdev = vdev->pdev;
|
||||
struct vfio_region_info info;
|
||||
unsigned long minsz;
|
||||
struct vfio_pci_core_device *vdev =
|
||||
container_of(core_vdev, struct vfio_pci_core_device, vdev);
|
||||
|
||||
minsz = offsetofend(struct vfio_region_info, offset);
|
||||
if (info->index != VFIO_PCI_BAR2_REGION_INDEX)
|
||||
return vfio_pci_ioctl_get_region_info(core_vdev, info, caps);
|
||||
|
||||
if (copy_from_user(&info, (void __user *)arg, minsz))
|
||||
return -EFAULT;
|
||||
info->offset = VFIO_PCI_INDEX_TO_OFFSET(info->index);
|
||||
|
||||
if (info.argsz < minsz)
|
||||
return -EINVAL;
|
||||
info->size = hisi_acc_get_resource_len(vdev, info->index);
|
||||
|
||||
if (info.index == VFIO_PCI_BAR2_REGION_INDEX) {
|
||||
info.offset = VFIO_PCI_INDEX_TO_OFFSET(info.index);
|
||||
|
||||
/*
|
||||
* ACC VF dev BAR2 region consists of both functional
|
||||
* register space and migration control register space.
|
||||
* Report only the functional region to Guest.
|
||||
*/
|
||||
info.size = pci_resource_len(pdev, info.index) / 2;
|
||||
|
||||
info.flags = VFIO_REGION_INFO_FLAG_READ |
|
||||
VFIO_REGION_INFO_FLAG_WRITE |
|
||||
VFIO_REGION_INFO_FLAG_MMAP;
|
||||
|
||||
return copy_to_user((void __user *)arg, &info, minsz) ?
|
||||
-EFAULT : 0;
|
||||
}
|
||||
}
|
||||
return vfio_pci_core_ioctl(core_vdev, cmd, arg);
|
||||
info->flags = VFIO_REGION_INFO_FLAG_READ | VFIO_REGION_INFO_FLAG_WRITE |
|
||||
VFIO_REGION_INFO_FLAG_MMAP;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int hisi_acc_vf_debug_check(struct seq_file *seq, struct vfio_device *vdev)
|
||||
@@ -1521,7 +1562,8 @@ static void hisi_acc_vfio_pci_close_device(struct vfio_device *core_vdev)
|
||||
hisi_acc_vf_disable_fds(hisi_acc_vdev);
|
||||
mutex_lock(&hisi_acc_vdev->open_mutex);
|
||||
hisi_acc_vdev->dev_opened = false;
|
||||
iounmap(vf_qm->io_base);
|
||||
if (hisi_acc_vdev->drv_mode == HW_ACC_MIG_VF_CTRL)
|
||||
iounmap(vf_qm->io_base);
|
||||
mutex_unlock(&hisi_acc_vdev->open_mutex);
|
||||
vfio_pci_core_close_device(core_vdev);
|
||||
}
|
||||
@@ -1557,13 +1599,15 @@ static const struct vfio_device_ops hisi_acc_vfio_pci_migrn_ops = {
|
||||
.release = vfio_pci_core_release_dev,
|
||||
.open_device = hisi_acc_vfio_pci_open_device,
|
||||
.close_device = hisi_acc_vfio_pci_close_device,
|
||||
.ioctl = hisi_acc_vfio_pci_ioctl,
|
||||
.ioctl = vfio_pci_core_ioctl,
|
||||
.get_region_info_caps = hisi_acc_vfio_ioctl_get_region,
|
||||
.device_feature = vfio_pci_core_ioctl_feature,
|
||||
.read = hisi_acc_vfio_pci_read,
|
||||
.write = hisi_acc_vfio_pci_write,
|
||||
.mmap = hisi_acc_vfio_pci_mmap,
|
||||
.request = vfio_pci_core_request,
|
||||
.match = vfio_pci_core_match,
|
||||
.match_token_uuid = vfio_pci_core_match_token_uuid,
|
||||
.bind_iommufd = vfio_iommufd_physical_bind,
|
||||
.unbind_iommufd = vfio_iommufd_physical_unbind,
|
||||
.attach_ioas = vfio_iommufd_physical_attach_ioas,
|
||||
@@ -1577,6 +1621,7 @@ static const struct vfio_device_ops hisi_acc_vfio_pci_ops = {
|
||||
.open_device = hisi_acc_vfio_pci_open_device,
|
||||
.close_device = vfio_pci_core_close_device,
|
||||
.ioctl = vfio_pci_core_ioctl,
|
||||
.get_region_info_caps = vfio_pci_ioctl_get_region_info,
|
||||
.device_feature = vfio_pci_core_ioctl_feature,
|
||||
.read = vfio_pci_core_read,
|
||||
.write = vfio_pci_core_write,
|
||||
|
||||
@@ -50,8 +50,10 @@
|
||||
#define QM_QUE_ISO_CFG_V 0x0030
|
||||
#define QM_PAGE_SIZE 0x0034
|
||||
|
||||
#define QM_EQC_DW0 0X8000
|
||||
#define QM_AEQC_DW0 0X8020
|
||||
#define QM_EQC_VF_DW0 0X8000
|
||||
#define QM_AEQC_VF_DW0 0X8020
|
||||
#define QM_EQC_PF_DW0 0x1c00
|
||||
#define QM_AEQC_PF_DW0 0x1c20
|
||||
|
||||
#define ACC_DRV_MAJOR_VER 1
|
||||
#define ACC_DRV_MINOR_VER 0
|
||||
@@ -59,6 +61,22 @@
|
||||
#define ACC_DEV_MAGIC_V1 0XCDCDCDCDFEEDAACC
|
||||
#define ACC_DEV_MAGIC_V2 0xAACCFEEDDECADEDE
|
||||
|
||||
#define QM_MIG_REGION_OFFSET 0x180000
|
||||
#define QM_MIG_REGION_SIZE 0x2000
|
||||
|
||||
/**
|
||||
* On HW_ACC_MIG_VF_CTRL mode, the configuration domain supporting live
|
||||
* migration functionality is located in the latter 32KB of the VF's BAR2.
|
||||
* The Guest is only provided with the first 32KB of the VF's BAR2.
|
||||
* On HW_ACC_MIG_PF_CTRL mode, the configuration domain supporting live
|
||||
* migration functionality is located in the PF's BAR2, and the entire 64KB
|
||||
* of the VF's BAR2 is allocated to the Guest.
|
||||
*/
|
||||
enum hw_drv_mode {
|
||||
HW_ACC_MIG_VF_CTRL = 0,
|
||||
HW_ACC_MIG_PF_CTRL,
|
||||
};
|
||||
|
||||
struct acc_vf_data {
|
||||
#define QM_MATCH_SIZE offsetofend(struct acc_vf_data, qm_rsv_state)
|
||||
/* QM match information */
|
||||
@@ -125,6 +143,7 @@ struct hisi_acc_vf_core_device {
|
||||
struct pci_dev *vf_dev;
|
||||
struct hisi_qm *pf_qm;
|
||||
struct hisi_qm vf_qm;
|
||||
enum hw_drv_mode drv_mode;
|
||||
/*
|
||||
* vf_qm_state represents the QM_VF_STATE register value.
|
||||
* It is set by Guest driver for the ACC VF dev indicating
|
||||
|
||||
@@ -1366,6 +1366,7 @@ static const struct vfio_device_ops mlx5vf_pci_ops = {
|
||||
.open_device = mlx5vf_pci_open_device,
|
||||
.close_device = mlx5vf_pci_close_device,
|
||||
.ioctl = vfio_pci_core_ioctl,
|
||||
.get_region_info_caps = vfio_pci_ioctl_get_region_info,
|
||||
.device_feature = vfio_pci_core_ioctl_feature,
|
||||
.read = vfio_pci_core_read,
|
||||
.write = vfio_pci_core_write,
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -195,6 +195,7 @@ static const struct vfio_device_ops pds_vfio_ops = {
|
||||
.open_device = pds_vfio_open_device,
|
||||
.close_device = pds_vfio_close_device,
|
||||
.ioctl = vfio_pci_core_ioctl,
|
||||
.get_region_info_caps = vfio_pci_ioctl_get_region_info,
|
||||
.device_feature = vfio_pci_core_ioctl_feature,
|
||||
.read = vfio_pci_core_read,
|
||||
.write = vfio_pci_core_write,
|
||||
|
||||
@@ -609,6 +609,7 @@ static const struct vfio_device_ops qat_vf_pci_ops = {
|
||||
.open_device = qat_vf_pci_open_device,
|
||||
.close_device = qat_vf_pci_close_device,
|
||||
.ioctl = vfio_pci_core_ioctl,
|
||||
.get_region_info_caps = vfio_pci_ioctl_get_region_info,
|
||||
.read = vfio_pci_core_read,
|
||||
.write = vfio_pci_core_write,
|
||||
.mmap = vfio_pci_core_mmap,
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user