Commit 7f928917 authored by Alexey Kardashevskiy's avatar Alexey Kardashevskiy Committed by Michael Ellerman

vfio_pci: Add NVIDIA GV100GL [Tesla V100 SXM2] subdriver

POWER9 Witherspoon machines come with 4 or 6 V100 GPUs which are not
pluggable PCIe devices but still have PCIe links which are used
for config space and MMIO. In addition to that the GPUs have 6 NVLinks
which are connected to other GPUs and the POWER9 CPU. POWER9 chips
have a special unit on a die called an NPU which is an NVLink2 host bus
adapter with p2p connections to 2 to 3 GPUs, 3 or 2 NVLinks to each.
These systems also support ATS (address translation services) which is
a part of the NVLink2 protocol. Such GPUs also share on-board RAM
(16GB or 32GB) to the system via the same NVLink2 so a CPU has
cache-coherent access to a GPU RAM.

This exports GPU RAM to the userspace as a new VFIO device region. This
preregisters the new memory as device memory as it might be used for DMA.
This inserts pfns from the fault handler as the GPU memory is not onlined
until the vendor driver is loaded and trained the NVLinks so doing this
earlier causes low level errors which we fence in the firmware so
it does not hurt the host system but still better be avoided; for the same
reason this does not map GPU RAM into the host kernel (usual thing for
emulated access otherwise).

This exports an ATSD (Address Translation Shootdown) register of NPU which
allows TLB invalidations inside GPU for an operating system. The register
conveniently occupies a single 64k page. It is also presented to
the userspace as a new VFIO device region. One NPU has 8 ATSD registers,
each of them can be used for TLB invalidation in a GPU linked to this NPU.
This allocates one ATSD register per an NVLink bridge allowing passing
up to 6 registers. Due to the host firmware bug (just recently fixed),
only 1 ATSD register per NPU was actually advertised to the host system
so this passes that alone register via the first NVLink bridge device in
the group which is still enough as QEMU collects them all back and
presents to the guest via vPHB to mimic the emulated NPU PHB on the host.

In order to provide the userspace with the information about GPU-to-NVLink
connections, this exports an additional capability called "tgt"
(which is an abbreviated host system bus address). The "tgt" property
tells the GPU its own system address and allows the guest driver to
conglomerate the routing information so each GPU knows how to get directly
to the other GPUs.

For ATS to work, the nest MMU (an NVIDIA block in a P9 CPU) needs to
know LPID (a logical partition ID or a KVM guest hardware ID in other
words) and PID (a memory context ID of a userspace process, not to be
confused with a linux pid). This assigns a GPU to LPID in the NPU and
this is why this adds a listener for KVM on an IOMMU group. A PID comes
via NVLink from a GPU and NPU uses a PID wildcard to pass it through.

This requires coherent memory and ATSD to be available on the host as
the GPU vendor only supports configurations with both features enabled
and other configurations are known not to work. Because of this and
because of the ways the features are advertised to the host system
(which is a device tree with very platform specific properties),
this requires enabled POWERNV platform.

The V100 GPUs do not advertise any of these capabilities via the config
space and there are more than just one device ID so this relies on
the platform to tell whether these GPUs have special abilities such as
NVLinks.
Signed-off-by: default avatarAlexey Kardashevskiy <aik@ozlabs.ru>
Acked-by: default avatarAlex Williamson <alex.williamson@redhat.com>
Signed-off-by: default avatarMichael Ellerman <mpe@ellerman.id.au>
parent c2c0f1cd
......@@ -38,3 +38,9 @@ config VFIO_PCI_IGD
and LPC bridge config space.
To enable Intel IGD assignment through vfio-pci, say Y.
config VFIO_PCI_NVLINK2
def_bool y
depends on VFIO_PCI && PPC_POWERNV
help
VFIO PCI support for P9 Witherspoon machine with NVIDIA V100 GPUs
vfio-pci-y := vfio_pci.o vfio_pci_intrs.o vfio_pci_rdwr.o vfio_pci_config.o
vfio-pci-$(CONFIG_VFIO_PCI_IGD) += vfio_pci_igd.o
vfio-pci-$(CONFIG_VFIO_PCI_NVLINK2) += vfio_pci_nvlink2.o
obj-$(CONFIG_VFIO_PCI) += vfio-pci.o
/* SPDX-License-Identifier: GPL-2.0+ */
/*
* VFIO PCI mmap/mmap_fault tracepoints
*
* Copyright (C) 2018 IBM Corp. All rights reserved.
* Author: Alexey Kardashevskiy <aik@ozlabs.ru>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#undef TRACE_SYSTEM
#define TRACE_SYSTEM vfio_pci
#if !defined(_TRACE_VFIO_PCI_H) || defined(TRACE_HEADER_MULTI_READ)
#define _TRACE_VFIO_PCI_H
#include <linux/tracepoint.h>
TRACE_EVENT(vfio_pci_nvgpu_mmap_fault,
TP_PROTO(struct pci_dev *pdev, unsigned long hpa, unsigned long ua,
vm_fault_t ret),
TP_ARGS(pdev, hpa, ua, ret),
TP_STRUCT__entry(
__field(const char *, name)
__field(unsigned long, hpa)
__field(unsigned long, ua)
__field(int, ret)
),
TP_fast_assign(
__entry->name = dev_name(&pdev->dev),
__entry->hpa = hpa;
__entry->ua = ua;
__entry->ret = ret;
),
TP_printk("%s: %lx -> %lx ret=%d", __entry->name, __entry->hpa,
__entry->ua, __entry->ret)
);
TRACE_EVENT(vfio_pci_nvgpu_mmap,
TP_PROTO(struct pci_dev *pdev, unsigned long hpa, unsigned long ua,
unsigned long size, int ret),
TP_ARGS(pdev, hpa, ua, size, ret),
TP_STRUCT__entry(
__field(const char *, name)
__field(unsigned long, hpa)
__field(unsigned long, ua)
__field(unsigned long, size)
__field(int, ret)
),
TP_fast_assign(
__entry->name = dev_name(&pdev->dev),
__entry->hpa = hpa;
__entry->ua = ua;
__entry->size = size;
__entry->ret = ret;
),
TP_printk("%s: %lx -> %lx size=%lx ret=%d", __entry->name, __entry->hpa,
__entry->ua, __entry->size, __entry->ret)
);
TRACE_EVENT(vfio_pci_npu2_mmap,
TP_PROTO(struct pci_dev *pdev, unsigned long hpa, unsigned long ua,
unsigned long size, int ret),
TP_ARGS(pdev, hpa, ua, size, ret),
TP_STRUCT__entry(
__field(const char *, name)
__field(unsigned long, hpa)
__field(unsigned long, ua)
__field(unsigned long, size)
__field(int, ret)
),
TP_fast_assign(
__entry->name = dev_name(&pdev->dev),
__entry->hpa = hpa;
__entry->ua = ua;
__entry->size = size;
__entry->ret = ret;
),
TP_printk("%s: %lx -> %lx size=%lx ret=%d", __entry->name, __entry->hpa,
__entry->ua, __entry->size, __entry->ret)
);
#endif /* _TRACE_VFIO_PCI_H */
#undef TRACE_INCLUDE_PATH
#define TRACE_INCLUDE_PATH .
#undef TRACE_INCLUDE_FILE
#define TRACE_INCLUDE_FILE trace
/* This part must be outside protection */
#include <trace/define_trace.h>
......@@ -289,14 +289,37 @@ static int vfio_pci_enable(struct vfio_pci_device *vdev)
if (ret) {
dev_warn(&vdev->pdev->dev,
"Failed to setup Intel IGD regions\n");
vfio_pci_disable(vdev);
return ret;
goto disable_exit;
}
}
if (pdev->vendor == PCI_VENDOR_ID_NVIDIA &&
IS_ENABLED(CONFIG_VFIO_PCI_NVLINK2)) {
ret = vfio_pci_nvdia_v100_nvlink2_init(vdev);
if (ret && ret != -ENODEV) {
dev_warn(&vdev->pdev->dev,
"Failed to setup NVIDIA NV2 RAM region\n");
goto disable_exit;
}
}
if (pdev->vendor == PCI_VENDOR_ID_IBM &&
IS_ENABLED(CONFIG_VFIO_PCI_NVLINK2)) {
ret = vfio_pci_ibm_npu2_init(vdev);
if (ret && ret != -ENODEV) {
dev_warn(&vdev->pdev->dev,
"Failed to setup NVIDIA NV2 ATSD region\n");
goto disable_exit;
}
}
vfio_pci_probe_mmaps(vdev);
return 0;
disable_exit:
vfio_pci_disable(vdev);
return ret;
}
static void vfio_pci_disable(struct vfio_pci_device *vdev)
......
This diff is collapsed.
......@@ -163,4 +163,18 @@ static inline int vfio_pci_igd_init(struct vfio_pci_device *vdev)
return -ENODEV;
}
#endif
#ifdef CONFIG_VFIO_PCI_NVLINK2
extern int vfio_pci_nvdia_v100_nvlink2_init(struct vfio_pci_device *vdev);
extern int vfio_pci_ibm_npu2_init(struct vfio_pci_device *vdev);
#else
static inline int vfio_pci_nvdia_v100_nvlink2_init(struct vfio_pci_device *vdev)
{
return -ENODEV;
}
static inline int vfio_pci_ibm_npu2_init(struct vfio_pci_device *vdev)
{
return -ENODEV;
}
#endif
#endif /* VFIO_PCI_PRIVATE_H */
......@@ -353,6 +353,21 @@ struct vfio_region_gfx_edid {
#define VFIO_DEVICE_GFX_LINK_STATE_DOWN 2
};
/*
* 10de vendor sub-type
*
* NVIDIA GPU NVlink2 RAM is coherent RAM mapped onto the host address space.
*/
#define VFIO_REGION_SUBTYPE_NVIDIA_NVLINK2_RAM (1)
/*
* 1014 vendor sub-type
*
* IBM NPU NVlink2 ATSD (Address Translation Shootdown) register of NPU
* to do TLB invalidation on a GPU.
*/
#define VFIO_REGION_SUBTYPE_IBM_NVLINK2_ATSD (1)
/*
* The MSIX mappable capability informs that MSIX data of a BAR can be mmapped
* which allows direct access to non-MSIX registers which happened to be within
......@@ -363,6 +378,33 @@ struct vfio_region_gfx_edid {
*/
#define VFIO_REGION_INFO_CAP_MSIX_MAPPABLE 3
/*
* Capability with compressed real address (aka SSA - small system address)
* where GPU RAM is mapped on a system bus. Used by a GPU for DMA routing
* and by the userspace to associate a NVLink bridge with a GPU.
*/
#define VFIO_REGION_INFO_CAP_NVLINK2_SSATGT 4
struct vfio_region_info_cap_nvlink2_ssatgt {
struct vfio_info_cap_header header;
__u64 tgt;
};
/*
* Capability with an NVLink link speed. The value is read by
* the NVlink2 bridge driver from the bridge's "ibm,nvlink-speed"
* property in the device tree. The value is fixed in the hardware
* and failing to provide the correct value results in the link
* not working with no indication from the driver why.
*/
#define VFIO_REGION_INFO_CAP_NVLINK2_LNKSPD 5
struct vfio_region_info_cap_nvlink2_lnkspd {
struct vfio_info_cap_header header;
__u32 link_speed;
__u32 __pad;
};
/**
* VFIO_DEVICE_GET_IRQ_INFO - _IOWR(VFIO_TYPE, VFIO_BASE + 9,
* struct vfio_irq_info)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment