Re: [RFC PATCH kernel v2] powerpc/powernv: Isolate NVLinks between GV100GL on Witherspoon

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



On Thu, Apr 04, 2019 at 02:22:25PM -0600, Alex Williamson wrote:
> On Thu,  4 Apr 2019 16:23:24 +1100
> Alexey Kardashevskiy <aik@xxxxxxxxx> wrote:
> 
> > The NVIDIA V100 SXM2 GPUs are connected to the CPU via PCIe links and
> > (on POWER9) NVLinks. In addition to that, GPUs themselves have direct
> > peer to peer NVLinks in groups of 2 to 4 GPUs. At the moment the POWERNV
> > platform puts all interconnected GPUs to the same IOMMU group.
> > 
> > However the user may want to pass individual GPUs to the userspace so
> > in order to do so we need to put them into separate IOMMU groups and
> > cut off the interconnects.
> > 
> > Thankfully V100 GPUs implement an interface to do by programming link
> > disabling mask to BAR0 of a GPU. Once a link is disabled in a GPU using
> > this interface, it cannot be re-enabled until the secondary bus reset is
> > issued to the GPU.
> > 
> > This adds an extra step to the secondary bus reset handler (the one used
> > for such GPUs) to block NVLinks to GPUs which do not belong to the same
> > group as the GPU being reset.
> > 
> > This adds a new "isolate_nvlink" kernel parameter to allow GPU isolation;
> > when enabled, every GPU gets its own IOMMU group. The new parameter is off
> > by default to preserve the existing behaviour.
> > 
> > Signed-off-by: Alexey Kardashevskiy <aik@xxxxxxxxx>
> > ---
> > Changes:
> > v2:
> > * this is rework of [PATCH kernel RFC 0/2] vfio, powerpc/powernv: Isolate GV100GL
> > but this time it is contained in the powernv platform
> > ---
> >  arch/powerpc/platforms/powernv/Makefile      |   2 +-
> >  arch/powerpc/platforms/powernv/pci.h         |   1 +
> >  arch/powerpc/platforms/powernv/eeh-powernv.c |   1 +
> >  arch/powerpc/platforms/powernv/npu-dma.c     |  24 +++-
> >  arch/powerpc/platforms/powernv/nvlinkgpu.c   | 131 +++++++++++++++++++
> >  5 files changed, 156 insertions(+), 3 deletions(-)
> >  create mode 100644 arch/powerpc/platforms/powernv/nvlinkgpu.c
> > 
> > diff --git a/arch/powerpc/platforms/powernv/Makefile b/arch/powerpc/platforms/powernv/Makefile
> > index da2e99efbd04..60a10d3b36eb 100644
> > --- a/arch/powerpc/platforms/powernv/Makefile
> > +++ b/arch/powerpc/platforms/powernv/Makefile
> > @@ -6,7 +6,7 @@ obj-y			+= opal-msglog.o opal-hmi.o opal-power.o opal-irqchip.o
> >  obj-y			+= opal-kmsg.o opal-powercap.o opal-psr.o opal-sensor-groups.o
> >  
> >  obj-$(CONFIG_SMP)	+= smp.o subcore.o subcore-asm.o
> > -obj-$(CONFIG_PCI)	+= pci.o pci-ioda.o npu-dma.o pci-ioda-tce.o
> > +obj-$(CONFIG_PCI)	+= pci.o pci-ioda.o npu-dma.o pci-ioda-tce.o nvlinkgpu.o
> >  obj-$(CONFIG_CXL_BASE)	+= pci-cxl.o
> >  obj-$(CONFIG_EEH)	+= eeh-powernv.o
> >  obj-$(CONFIG_PPC_SCOM)	+= opal-xscom.o
> > diff --git a/arch/powerpc/platforms/powernv/pci.h b/arch/powerpc/platforms/powernv/pci.h
> > index 8e36da379252..9fd3f391482c 100644
> > --- a/arch/powerpc/platforms/powernv/pci.h
> > +++ b/arch/powerpc/platforms/powernv/pci.h
> > @@ -250,5 +250,6 @@ extern void pnv_pci_unlink_table_and_group(struct iommu_table *tbl,
> >  extern void pnv_pci_setup_iommu_table(struct iommu_table *tbl,
> >  		void *tce_mem, u64 tce_size,
> >  		u64 dma_offset, unsigned int page_shift);
> > +extern void pnv_try_isolate_nvidia_v100(struct pci_dev *gpdev);
> >  
> >  #endif /* __POWERNV_PCI_H */
> > diff --git a/arch/powerpc/platforms/powernv/eeh-powernv.c b/arch/powerpc/platforms/powernv/eeh-powernv.c
> > index f38078976c5d..464b097d9635 100644
> > --- a/arch/powerpc/platforms/powernv/eeh-powernv.c
> > +++ b/arch/powerpc/platforms/powernv/eeh-powernv.c
> > @@ -937,6 +937,7 @@ void pnv_pci_reset_secondary_bus(struct pci_dev *dev)
> >  		pnv_eeh_bridge_reset(dev, EEH_RESET_HOT);
> >  		pnv_eeh_bridge_reset(dev, EEH_RESET_DEACTIVATE);
> >  	}
> > +	pnv_try_isolate_nvidia_v100(dev);
> >  }
> >  
> >  static void pnv_eeh_wait_for_pending(struct pci_dn *pdn, const char *type,
> > diff --git a/arch/powerpc/platforms/powernv/npu-dma.c b/arch/powerpc/platforms/powernv/npu-dma.c
> > index dc23d9d2a7d9..017eae8197e7 100644
> > --- a/arch/powerpc/platforms/powernv/npu-dma.c
> > +++ b/arch/powerpc/platforms/powernv/npu-dma.c
> > @@ -529,6 +529,23 @@ static void pnv_comp_attach_table_group(struct npu_comp *npucomp,
> >  	++npucomp->pe_num;
> >  }
> >  
> > +static bool isolate_nvlink;
> > +
> > +static int __init parse_isolate_nvlink(char *p)
> > +{
> > +	bool val;
> > +
> > +	if (!p)
> > +		val = true;
> > +	else if (kstrtobool(p, &val))
> > +		return -EINVAL;
> > +
> > +	isolate_nvlink = val;
> > +
> > +	return 0;
> > +}
> > +early_param("isolate_nvlink", parse_isolate_nvlink);
> > +
> >  struct iommu_table_group *pnv_try_setup_npu_table_group(struct pnv_ioda_pe *pe)
> >  {
> >  	struct iommu_table_group *table_group;
> > @@ -549,7 +566,7 @@ struct iommu_table_group *pnv_try_setup_npu_table_group(struct pnv_ioda_pe *pe)
> >  
> >  	hose = pci_bus_to_host(npdev->bus);
> >  
> > -	if (hose->npu) {
> > +	if (hose->npu && !isolate_nvlink) {
> >  		table_group = &hose->npu->npucomp.table_group;
> >  
> >  		if (!table_group->group) {
> > @@ -559,7 +576,10 @@ struct iommu_table_group *pnv_try_setup_npu_table_group(struct pnv_ioda_pe *pe)
> >  					pe->pe_number);
> >  		}
> >  	} else {
> > -		/* Create a group for 1 GPU and attached NPUs for POWER8 */
> > +		/*
> > +		 * Create a group for 1 GPU and attached NPUs for
> > +		 * POWER8 (always) or POWER9 (when isolate_nvlink).
> > +		 */
> >  		pe->npucomp = kzalloc(sizeof(*pe->npucomp), GFP_KERNEL);
> >  		table_group = &pe->npucomp->table_group;
> >  		table_group->ops = &pnv_npu_peers_ops;
> > diff --git a/arch/powerpc/platforms/powernv/nvlinkgpu.c b/arch/powerpc/platforms/powernv/nvlinkgpu.c
> > new file mode 100644
> > index 000000000000..dbd8e9d47a05
> > --- /dev/null
> > +++ b/arch/powerpc/platforms/powernv/nvlinkgpu.c
> > @@ -0,0 +1,131 @@
> > +// SPDX-License-Identifier: GPL-2.0+
> > +/*
> > + * A helper to disable NVLinks between GPUs on IBM Withersponn platform.
> > + *
> > + * Copyright (C) 2019 IBM Corp.  All rights reserved.
> > + *     Author: Alexey Kardashevskiy <aik@xxxxxxxxx>
> > + *
> > + * 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.
> > + */
> > +
> > +#include <linux/module.h>
> > +#include <linux/device.h>
> > +#include <linux/of.h>
> > +#include <linux/iommu.h>
> > +#include <linux/pci.h>
> > +
> > +static int nvlinkgpu_is_ph_in_group(struct device *dev, void *data)
> > +{
> > +	return dev->of_node->phandle == *(phandle *) data;
> > +}
> > +
> > +static u32 nvlinkgpu_get_disable_mask(struct device *dev)
> > +{
> > +	int npu, peer;
> > +	u32 mask;
> > +	struct device_node *dn;
> > +	struct iommu_group *group;
> > +
> > +	dn = dev->of_node;
> > +	if (!of_find_property(dn, "ibm,nvlink-peers", NULL))
> > +		return 0;
> > +
> > +	group = iommu_group_get(dev);
> > +	if (!group)
> > +		return 0;
> > +
> > +	/*
> > +	 * Collect links to keep which includes links to NPU and links to
> > +	 * other GPUs in the same IOMMU group.
> > +	 */
> > +	for (npu = 0, mask = 0; ; ++npu) {
> > +		u32 npuph = 0;
> > +
> > +		if (of_property_read_u32_index(dn, "ibm,npu", npu, &npuph))
> > +			break;
> > +
> > +		for (peer = 0; ; ++peer) {
> > +			u32 peerph = 0;
> > +
> > +			if (of_property_read_u32_index(dn, "ibm,nvlink-peers",
> > +					peer, &peerph))
> > +				break;
> > +
> > +			if (peerph != npuph &&
> > +				!iommu_group_for_each_dev(group, &peerph,
> > +					nvlinkgpu_is_ph_in_group))
> > +				continue;
> > +
> > +			mask |= 1 << (peer + 16);
> > +		}
> > +	}
> > +	iommu_group_put(group);
> > +
> > +	/* Disabling mechanism takes links to disable so invert it here */
> > +	mask = ~mask & 0x3F0000;
> > +
> > +	return mask;
> > +}
> > +
> > +void pnv_try_isolate_nvidia_v100(struct pci_dev *bridge)
> > +{
> > +	u32 mask;
> > +	void __iomem *bar0_0, *bar0_120000, *bar0_a00000;
> > +	struct pci_dev *pdev;
> > +
> > +	if (!bridge->subordinate)
> > +		return;
> > +
> > +	pdev = list_first_entry_or_null(&bridge->subordinate->devices,
> > +			struct pci_dev, bus_list);
> > +	if (!pdev)
> > +		return;
> > +
> > +	if (pdev->vendor != PCI_VENDOR_ID_NVIDIA)
> > +		return;
> > +
> > +	mask = nvlinkgpu_get_disable_mask(&pdev->dev);
> > +	if (!mask)
> > +		return;
> > +
> > +	bar0_0 = pci_iomap_range(pdev, 0, 0, 0x10000);
> > +	bar0_120000 = pci_iomap_range(pdev, 0, 0x120000, 0x10000);
> > +	bar0_a00000 = pci_iomap_range(pdev, 0, 0xA00000, 0x10000);
> > +
> > +	if (bar0_120000 && bar0_0 && bar0_a00000) {
> > +		u32 val;
> > +		u16 cmd = 0, cmdmask = PCI_COMMAND_MEMORY;
> > +
> > +		pci_restore_state(pdev);
> > +		pci_read_config_word(pdev, PCI_COMMAND, &cmd);
> > +		if ((cmd & cmdmask) != cmdmask)
> > +			pci_write_config_word(pdev, PCI_COMMAND, cmd | cmdmask);
> > +
> > +		/*
> > +		 * The sequence is from "Tesla P100 and V100 SXM2 NVLink
> > +		 * Isolation on Multi-Tenant Systems".
> > +		 * The register names are not provided there either,
> > +		 * hence raw values.
> > +		 */
> > +		iowrite32(0x4, bar0_120000 + 0x4C);
> > +		iowrite32(0x2, bar0_120000 + 0x2204);
> > +		val = ioread32(bar0_0 + 0x200);
> > +		val |= 0x02000000;
> > +		iowrite32(val, bar0_0 + 0x200);
> > +		val = ioread32(bar0_a00000 + 0x148);
> > +		val |= mask;
> > +		iowrite32(val, bar0_a00000 + 0x148);
> > +
> > +		if ((cmd | cmdmask) != cmd)
> > +			pci_write_config_word(pdev, PCI_COMMAND, cmd);
> > +	}
> > +
> 
> 
> It's a little troubling that if something goes wrong with an iomap
> we'll silently fail to re-enable the expected isolation.  Seems worthy
> of at least a pci_warn/err.  Thanks,

Agreed, but apart from that LGTM.

Regardless of Alex and my disagreement on what the best way to handle
this long term is, this seems like a reasonable interim approach.

-- 
David Gibson			| I'll have my music baroque, and my code
david AT gibson.dropbear.id.au	| minimalist, thank you.  NOT _the_ _other_
				| _way_ _around_!
http://www.ozlabs.org/~dgibson

Attachment: signature.asc
Description: PGP signature


[Index of Archives]     [KVM ARM]     [KVM ia64]     [KVM ppc]     [Virtualization Tools]     [Spice Development]     [Libvirt]     [Libvirt Users]     [Linux USB Devel]     [Linux Audio Users]     [Yosemite Questions]     [Linux Kernel]     [Linux SCSI]     [XFree86]

  Powered by Linux