IBM POWER8 NVlink systems contain usual Tesla K40-ish GPUs but also
contain a couple of really fast links between GPU and CPU. These links
are exposed to the userspace by the OPAL firmware as bridges.
In order to make these links work when GPU is passed to the guest,
these bridges need to be passed as well; otherwise performance will
degrade. More details are in 10/10.
This reworks the existing NPU support in the powernv platform and adds
VFIO support on top of that.
This was tested on POWER8NVL platform. pvr=0x004c0100.
Please comment. Thanks.
Alexey Kardashevskiy (10):
vfio/spapr: Relax the IOMMU compatibility check
powerpc/powernv: Rename pnv_pci_ioda2_tce_invalidate_entire
powerpc/powernv: Define TCE Kill flags
powerpc/powernv/npu: TCE Kill helpers cleanup
powerpc/powernv/npu: Use the correct IOMMU page size
powerpc/powernv/npu: Simplify DMA setup
powerpc/powernv/npu: Rework TCE Kill handling
powerpc/powernv/npu: Add NPU devices to IOMMU group
powerpc/powernv/ioda2: Export some helpers
powerpc/powernv/npu: Enable passing through via VFIO
arch/powerpc/platforms/powernv/npu-dma.c | 387 ++++++++++++++++++------------
arch/powerpc/platforms/powernv/pci-ioda.c | 134 +++++------
arch/powerpc/platforms/powernv/pci.h | 32 +--
drivers/vfio/vfio_iommu_spapr_tce.c | 3 +-
4 files changed, 309 insertions(+), 247 deletions(-)
--
2.5.0.rc3
As in fact pnv_pci_ioda2_tce_invalidate_entire() invalidates TCEs for
the specific PE rather than the entire cache, rename it to
pnv_pci_ioda2_tce_invalidate_pe(). In later patches we will add
a proper pnv_pci_ioda2_tce_invalidate_entire().
Signed-off-by: Alexey Kardashevskiy <redacted>
---
arch/powerpc/platforms/powernv/pci-ioda.c | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
This uses the page size from iommu_table instead of hard-coded 4K.
This should cause no change in behavior.
While we are here, move bits around to prepare for further rework
which will define and use iommu_table_group_ops.
Signed-off-by: Alexey Kardashevskiy <redacted>
---
arch/powerpc/platforms/powernv/npu-dma.c | 11 +++++------
1 file changed, 5 insertions(+), 6 deletions(-)
We are going to have multiple different types of PHB on the same system
with POWER8 + NVLink and PHBs will have different IOMMU ops. However
we only really care about one callback - create_table - so we can
relax the compatibility check here.
Signed-off-by: Alexey Kardashevskiy <redacted>
---
drivers/vfio/vfio_iommu_spapr_tce.c | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
@@ -1188,7 +1188,8 @@ static int tce_iommu_attach_group(void *iommu_data,gotounlock_exit;}table_group_tmp=iommu_group_get_iommudata(tcegrp->grp);-if(table_group_tmp->ops!=table_group->ops){+if(table_group_tmp->ops->create_table!=+table_group->ops->create_table){pr_warn("tce_vfio: Group %d is incompatible with group %d\n",iommu_group_id(iommu_group),iommu_group_id(tcegrp->grp));
IBM POWER8 NVlink systems contain usual Tesla K40-ish GPUs but also
contain a couple of really fast links between GPU and CPU. These links
are exposed to the userspace by the OPAL firmware as bridges.
The device tree has references from GPU to NPU and vice versa via
"ibm,npu" and "ibm,gpu" properties which are "linux,phandle" of
the counterparts. The typical GPU looks like:
0003:01:00.0 3D controller: NVIDIA Corporation Device 15ff (rev a1)
0008:00:00.0 Bridge: IBM Device 04ea (prog-if 01)
0008:00:00.1 Bridge: IBM Device 04ea (prog-if 01)
In the host kernel, couple of links of the same GPU make a new PE.
A PHB with these links has a different type - PNV_PHB_NPU (the standard
IODA2 bridge type is PNV_PHB_IODA2). The previos patch added these links
to a new IOMMU group.
In order to make these links work when GPU is passed to the guest,
these bridges need to be passed as well; otherwise performance will
degrade. The previous patch adds these bridges to a new IOMMU group,
this patch adds the bits required by VFIO SPAPR TCE driver to pass it
to the userspace.
This defines pnv_pci_npu_ops and initializes it. It reuses
pnv_pci_ioda2_get_table_size() and pnv_pci_ioda2_create_table() as
the table will be programmed to both NPU and IODA2 bridge types so
it needs to be compatible with both bridge types.
As it is not known in what order the userspace will be adding IOMMU
groups to a VFIO container, we need to maintain PHB type compatibility.
This sets up table_group properties from the linked GPU. This initializes
@tce_bypass_base from GPU as well as this is used by
pnv_pci_ioda2_create_table(). This is a bit ugly but the only place
it is actually used in the NPU PHB is enabling bypass mode and there
we can safely use plain zero.
NPU PHB has just a single TVE per NVLink so it can have either 32bit or
64bit window but never both. Nevertheless the NPU table_group is added
to both iommu_table in the VFIO container for simpler design.
Note that the userspace should pass GPU with corresponding NPUs,
otherwise isolation is not guaranteed.
Signed-off-by: Alexey Kardashevskiy <redacted>
---
arch/powerpc/platforms/powernv/npu-dma.c | 128 ++++++++++++++++++++++++++++++-
1 file changed, 126 insertions(+), 2 deletions(-)
@@ -216,13 +216,12 @@ static int pnv_npu_dma_set_bypass(struct pnv_ioda_pe *npe)&npe->table_group);npe->table_group.tables[0]=NULL;-npe->tce_bypass_base=0;top=roundup_pow_of_two(top);dev_info(&npe->pdev->dev,"Enabling bypass for PE %d\n",npe->pe_number);rc=opal_pci_map_pe_dma_window_real(phb->opal_id,npe->pe_number,npe->pe_number,-npe->tce_bypass_base,top);+0/* bypass base */,top);if(rc==OPAL_SUCCESS)pnv_pci_ioda2_tce_invalidate_entire(phb,false);
@@ -264,6 +263,120 @@ void pnv_npu_try_dma_set_bypass(struct pci_dev *gpdev, bool bypass)}}+staticlongpnv_pci_npu_set_window(structiommu_table_group*table_group,+intnum,structiommu_table*tbl)+{+structpnv_ioda_pe*npe=container_of(table_group,structpnv_ioda_pe,+table_group);+structpnv_phb*phb=npe->phb;+int64_trc;+constunsignedlongsize=tbl->it_indirect_levels?+tbl->it_level_size:tbl->it_size;+const__u64start_addr=tbl->it_offset<<tbl->it_page_shift;+const__u64win_size=tbl->it_size<<tbl->it_page_shift;++pe_info(npe,"Setting up window#%d %llx..%llx pg=%lx\n",num,+start_addr,start_addr+win_size-1,+IOMMU_PAGE_SIZE(tbl));++rc=opal_pci_map_pe_dma_window(phb->opal_id,+npe->pe_number,+npe->pe_number,+tbl->it_indirect_levels+1,+__pa(tbl->it_base),+size<<3,+IOMMU_PAGE_SIZE(tbl));+if(rc){+pe_err(npe,"Failed to configure TCE table, err %lld\n",rc);+returnrc;+}++pnv_pci_link_table_and_group(phb->hose->node,num,+tbl,&npe->table_group);+pnv_pci_ioda2_tce_invalidate_entire(npe->phb,false);++returnrc;+}++staticlongpnv_pci_npu_unset_window(structiommu_table_group*table_group,+intnum)+{+structpnv_ioda_pe*npe=container_of(table_group,structpnv_ioda_pe,+table_group);+structpnv_phb*phb=npe->phb;+longret;++pe_info(npe,"Removing DMA window #%d\n",num);++ret=opal_pci_map_pe_dma_window(phb->opal_id,npe->pe_number,+npe->pe_number,+0/* levels */,0/* table address */,+0/* table size */,0/* page size */);+if(ret)+pe_warn(npe,"Unmapping failed, ret = %ld\n",ret);+else+pnv_pci_ioda2_tce_invalidate_entire(npe->phb,false);++pnv_pci_unlink_table_and_group(table_group->tables[num],table_group);++returnret;+}++/* Switch ownership from platform code to external user (e.g. VFIO) */+staticvoidpnv_pci_npu_take_ownership(structiommu_table_group*table_group)+{+structpnv_ioda_pe*npe=container_of(table_group,structpnv_ioda_pe,+table_group);+structpnv_phb*phb=npe->phb;+int64_tret;++if(npe->table_group.tables[0]){+pnv_pci_unlink_table_and_group(npe->table_group.tables[0],+&npe->table_group);+npe->table_group.tables[0]=NULL;+ret=opal_pci_map_pe_dma_window(phb->opal_id,npe->pe_number,+npe->pe_number,+0/* levels */,0/* table address */,+0/* table size */,0/* page size */);+}else{+ret=opal_pci_map_pe_dma_window_real(phb->opal_id,+npe->pe_number,npe->pe_number,+0/* bypass base */,0);+}++if(ret!=OPAL_SUCCESS)+pe_err(npe,"Failed to remove DMA window");+else+pnv_pci_ioda2_tce_invalidate_entire(npe->phb,false);+}++/* Switch ownership from external user (e.g. VFIO) back to core */+staticvoidpnv_pci_npu_release_ownership(structiommu_table_group*table_group)+{+structpnv_ioda_pe*npe=container_of(table_group,structpnv_ioda_pe,+table_group);+structpnv_phb*phb=npe->phb;+int64_tret;++ret=opal_pci_map_pe_dma_window(phb->opal_id,npe->pe_number,+npe->pe_number,+0/* levels */,0/* table address */,+0/* table size */,0/* page size */);+if(ret!=OPAL_SUCCESS)+pe_err(npe,"Failed to remove DMA window");+else+pnv_pci_ioda2_tce_invalidate_entire(npe->phb,false);+}++staticstructiommu_table_group_opspnv_pci_npu_ops={+.get_table_size=pnv_pci_ioda2_get_table_size,+.create_table=pnv_pci_ioda2_create_table,+.set_window=pnv_pci_npu_set_window,+.unset_window=pnv_pci_npu_unset_window,+.take_ownership=pnv_pci_npu_take_ownership,+.release_ownership=pnv_pci_npu_release_ownership,+};+voidpnv_pci_npu_setup_iommu(structpnv_ioda_pe*npe){structiommu_table*tbl;
NPU devices have their own TVT which means they are isolated and can be
passed to the userspace via VFIO. The first step is to create an IOMMU
group and attach devices there so does the patch.
This adds a helper to npu-dma.c which gets GPU from the NPU's pdev and
then walks through all devices on the same bus to determine which NPUs
belong to the same GPU.
This adds an additional loop over PEs in pnv_ioda_setup_dma() as the main
loop skips NPU PEs as they do not have 32bit DMA segments.
This uses get_gpu_pci_dev_and_pe() to get @gpdev rather than
pnv_pci_get_gpu_dev() as the following patch will use @gpe as well.
Signed-off-by: Alexey Kardashevskiy <redacted>
---
arch/powerpc/platforms/powernv/npu-dma.c | 40 +++++++++++++++++++++++++++++++
arch/powerpc/platforms/powernv/pci-ioda.c | 8 +++++++
arch/powerpc/platforms/powernv/pci.h | 1 +
3 files changed, 49 insertions(+)
The pnv_ioda_pe struct keeps an array of peers. At the moment it is only
used to link GPU and NPU for 2 purposes:
1. Access NPU _quickly_ when configuring DMA for GPU - this was addressed
in the previos patch by removing use of it as DMA setup is not what
the kernel would constantly do.
2. Invalidate TCE cache for NPU when it is invalidated for GPU.
GPU and NPU are in different PE. There is already a mechanism to
attach multiple iommu_table_group to the same iommu_table (used for VFIO),
we can reuse it here so does this patch.
This gets rid of peers[] array and PNV_IODA_PE_PEER flag as they are
not needed anymore.
While we are here, add TCE cache invalidation after changing TVT.
Signed-off-by: Alexey Kardashevskiy <redacted>
---
arch/powerpc/platforms/powernv/npu-dma.c | 75 +++++++++----------------------
arch/powerpc/platforms/powernv/pci-ioda.c | 57 +++--------------------
arch/powerpc/platforms/powernv/pci.h | 6 ---
3 files changed, 29 insertions(+), 109 deletions(-)
@@ -159,42 +154,6 @@ static struct pnv_ioda_pe *get_gpu_pci_dev_and_pe(struct pnv_ioda_pe *npe,returnpe;}-voidpnv_npu_init_dma_pe(structpnv_ioda_pe*npe)-{-structpnv_ioda_pe*gpe;-structpci_dev*gpdev;-inti,avail=-1;--if(!npe->pdev||!(npe->flags&PNV_IODA_PE_DEV))-return;--gpe=get_gpu_pci_dev_and_pe(npe,&gpdev);-if(!gpe)-return;--for(i=0;i<PNV_IODA_MAX_PEER_PES;i++){-/* Nothing to do if the PE is already connected. */-if(gpe->peers[i]==npe)-return;--if(!gpe->peers[i])-avail=i;-}--if(WARN_ON(avail<0))-return;--gpe->peers[avail]=npe;-gpe->flags|=PNV_IODA_PE_PEER;--/*-*WeassumethattheNPUdevicesonlyhaveasinglepeerPE-*(theGPUPCIedevicePE).-*/-npe->peers[0]=gpe;-npe->flags|=PNV_IODA_PE_PEER;-}-/**Enables32bitDMAonNPU.*/
@@ -225,6 +184,13 @@ static void pnv_npu_dma_set_32(struct pnv_ioda_pe *npe)if(rc!=OPAL_SUCCESS)pr_warn("%s: Error %lld setting DMA window on PHB#%d-PE#%d\n",__func__,rc,phb->hose->global_number,npe->pe_number);+else+pnv_pci_ioda2_tce_invalidate_entire(phb,false);++/* Add the table to the list so its TCE cache will get invalidated */+npe->table_group.tables[0]=tbl;+pnv_pci_link_table_and_group(phb->hose->node,0,+tbl,&npe->table_group);/**Wedon'tinitialisenpu_pe->tce32_tableaswealwaysuse
@@ -245,10 +211,10 @@ static int pnv_npu_dma_set_bypass(struct pnv_ioda_pe *npe)int64_trc=0;phys_addr_ttop=memblock_end_of_DRAM();-if(phb->type!=PNV_PHB_NPU||!npe->pdev)-return-EINVAL;-/* Enable the bypass window */+pnv_pci_unlink_table_and_group(npe->table_group.tables[0],+&npe->table_group);+npe->table_group.tables[0]=NULL;npe->tce_bypass_base=0;top=roundup_pow_of_two(top);
@@ -258,6 +224,9 @@ static int pnv_npu_dma_set_bypass(struct pnv_ioda_pe *npe)npe->pe_number,npe->pe_number,npe->tce_bypass_base,top);+if(rc==OPAL_SUCCESS)+pnv_pci_ioda2_tce_invalidate_entire(phb,false);+returnrc;}
@@ -24,7 +24,6 @@ enum pnv_phb_model {#define PNV_IODA_PE_MASTER (1 << 3) /* Master PE in compound case */#define PNV_IODA_PE_SLAVE (1 << 4) /* Slave PE in compound case */#define PNV_IODA_PE_VF (1 << 5) /* PE for one VF */-#define PNV_IODA_PE_PEER (1 << 6) /* PE has peers *//* Data associated with a PE, including IOMMU tracking etc.. */structpnv_phb;
@@ -32,9 +31,6 @@ struct pnv_ioda_pe {unsignedlongflags;structpnv_phb*phb;-#define PNV_IODA_MAX_PEER_PES 8-structpnv_ioda_pe*peers[PNV_IODA_MAX_PEER_PES];-/* A PE can be associated with a single device or an*entirebus(&children).Intheformercase,pdev*ispopulated,inthelatercase,pbusis.
@@ -237,8 +233,6 @@ extern int pnv_setup_msi_irqs(struct pci_dev *pdev, int nvec, int type);externvoidpnv_teardown_msi_irqs(structpci_dev*pdev);/* Nvlink functions */-externvoidpnv_npu_init_dma_pe(structpnv_ioda_pe*npe);-externvoidpnv_npu_setup_dma_pe(structpnv_ioda_pe*npe);externvoidpnv_npu_try_dma_set_bypass(structpci_dev*gpdev,boolbypass);externvoidpnv_pci_ioda2_tce_invalidate_entire(structpnv_phb*phb,boolrm);
NPU devices are quite specific, in fact they represent side DMA channel
of a GPU device. The GPU/NPU driver never actually configures DMA
for NPU devices, instead it relies on the platform code to propagate
DMA setup to NPU devices when a main GPU device is being configured.
When GPU is being set up, the same configuration - bypass or 32bit DMA -
is used for NPU. This makes DMA setup explicit.
pnv_npu_ioda_controller_ops::pnv_npu_dma_set_mask is moved to pci-ioda,
made static and prints warning as dma_set_mask() should never be called
on this function as in any case it will not configure GPU; so we make
this explicit.
Instead of using PNV_IODA_PE_PEER and peers[] (which next patch will
remove), we test every PCI device if there are corresponding NVLink
devices. If there are any, we propagate bypass mode to just found NPU
devices by calling the setup helper directly (which takes @bypass) and
avoid guessing (i.e. calculating from DMA mask) whether we need bypass
or not on NPU devices. Since DMA setup happens in very rare occasion,
this will not slow down booting or VFIO start/stop much.
This renames pnv_npu_disable_bypass to pnv_npu_dma_set_32 to make it
more clear what the function really does which is programming 32bit
table address to the TVT ("disabling bypass" means writing zeroes to
the TVT).
This removes pnv_npu_dma_set_bypass() from pnv_npu_ioda_fixup() as
the DMA configuration on NPU does not matter until dma_set_mask() is
called on GPU and that will do the NPU DMA configuration.
This removes phb->dma_dev_setup initialization for NPU as
pnv_pci_ioda_dma_dev_setup is no-op for it anyway.
Signed-off-by: Alexey Kardashevskiy <redacted>
---
arch/powerpc/platforms/powernv/npu-dma.c | 90 ++++++++++++++-----------------
arch/powerpc/platforms/powernv/pci-ioda.c | 30 +++++------
arch/powerpc/platforms/powernv/pci.h | 3 +-
3 files changed, 54 insertions(+), 69 deletions(-)
@@ -235,72 +234,63 @@ static void pnv_npu_disable_bypass(struct pnv_ioda_pe *npe)}/*-*Enable/disablebypassmodeontheNPU.TheNPUonlysupportsone+*EnablesbypassmodeontheNPU.TheNPUonlysupportsone*windowperlink,sobypassneedstobeexplicitlyenabledor*disabled.UnlikeforaPHB3bypassandnon-bypassmodescan'tbe*activeatthesametime.*/-intpnv_npu_dma_set_bypass(structpnv_ioda_pe*npe,boolenable)+staticintpnv_npu_dma_set_bypass(structpnv_ioda_pe*npe){structpnv_phb*phb=npe->phb;int64_trc=0;+phys_addr_ttop=memblock_end_of_DRAM();if(phb->type!=PNV_PHB_NPU||!npe->pdev)return-EINVAL;-if(enable){-/* Enable the bypass window */-phys_addr_ttop=memblock_end_of_DRAM();+/* Enable the bypass window */-npe->tce_bypass_base=0;-top=roundup_pow_of_two(top);-dev_info(&npe->pdev->dev,"Enabling bypass for PE %d\n",-npe->pe_number);-rc=opal_pci_map_pe_dma_window_real(phb->opal_id,-npe->pe_number,npe->pe_number,-npe->tce_bypass_base,top);-}else{-/*-*Disablethebypasswindowbyreplacingitwiththe-*TCE32window.-*/-pnv_npu_disable_bypass(npe);-}+npe->tce_bypass_base=0;+top=roundup_pow_of_two(top);+dev_info(&npe->pdev->dev,"Enabling bypass for PE %d\n",+npe->pe_number);+rc=opal_pci_map_pe_dma_window_real(phb->opal_id,+npe->pe_number,npe->pe_number,+npe->tce_bypass_base,top);returnrc;}-intpnv_npu_dma_set_mask(structpci_dev*npdev,u64dma_mask)+voidpnv_npu_try_dma_set_bypass(structpci_dev*gpdev,boolbypass){-structpci_controller*hose=pci_bus_to_host(npdev->bus);-structpnv_phb*phb=hose->private_data;-structpci_dn*pdn=pci_get_pdn(npdev);-structpnv_ioda_pe*npe,*gpe;-structpci_dev*gpdev;-uint64_ttop;-boolbypass=false;+inti;+structpnv_phb*phb;+structpci_dn*pdn;+structpnv_ioda_pe*npe;+structpci_dev*npdev;-if(WARN_ON(!pdn||pdn->pe_number==IODA_INVALID_PE))-return-ENXIO;+for(i=0;;++i){+npdev=pnv_pci_get_npu_dev(gpdev,i);-/* We only do bypass if it's enabled on the linked device */-npe=&phb->ioda.pe_array[pdn->pe_number];-gpe=get_gpu_pci_dev_and_pe(npe,&gpdev);-if(!gpe)-return-ENODEV;+if(!npdev)+break;-if(gpe->tce_bypass_enabled){-top=gpe->tce_bypass_base+memblock_end_of_DRAM()-1;-bypass=(dma_mask>=top);+pdn=pci_get_pdn(npdev);+if(WARN_ON(!pdn||pdn->pe_number==IODA_INVALID_PE))+return;++phb=pci_bus_to_host(npdev->bus)->private_data;++/* We only do bypass if it's enabled on the linked device */+npe=&phb->ioda.pe_array[pdn->pe_number];++if(bypass){+dev_info(&npdev->dev,+"Using 64-bit DMA iommu bypass\n");+pnv_npu_dma_set_bypass(npe);+}else{+dev_info(&npdev->dev,"Using 32-bit DMA via iommu\n");+pnv_npu_dma_set_32(npe);+}}--if(bypass)-dev_info(&npdev->dev,"Using 64-bit DMA iommu bypass\n");-else-dev_info(&npdev->dev,"Using 32-bit DMA via iommu\n");--pnv_npu_dma_set_bypass(npe,bypass);-*npdev->dev.dma_mask=dma_mask;--return0;}
NPU PHB TCE Kill register is exactly the same as in the rest of POWER8
so let's reuse the existing code for NPU. The only bit missing is
a helper to reset the entire TCE cache so this moves such a helper
from NPU code and renames it.
Since pnv_npu_tce_invalidate() does really invalidate the entire cache,
this uses pnv_pci_ioda2_tce_invalidate_entire() directly for NPU.
This adds an explicit comment for workaround for invalidating NPU TCE
cache.
Signed-off-by: Alexey Kardashevskiy <redacted>
---
arch/powerpc/platforms/powernv/npu-dma.c | 41 -------------------------------
arch/powerpc/platforms/powernv/pci-ioda.c | 29 ++++++++++++++++++----
arch/powerpc/platforms/powernv/pci.h | 7 +-----
3 files changed, 25 insertions(+), 52 deletions(-)
@@ -1896,14 +1910,19 @@ static void pnv_pci_ioda2_tce_invalidate(struct iommu_table *tbl,index,npages);if(pe->flags&PNV_IODA_PE_PEER)-/* Invalidate PEs using the same TCE table */+/*+*TheNVLinkhardwaredoesnotsupportTCEkill+*perTCEentrysowehavetoinvalidate+*theentirecacheforit.+*/for(i=0;i<PNV_IODA_MAX_PEER_PES;i++){npe=pe->peers[i];-if(!npe||npe->phb->type!=PNV_PHB_NPU)+if(!npe||npe->phb->type!=PNV_PHB_NPU||+!npe->phb->ioda.tce_inval_reg)continue;-pnv_npu_tce_invalidate(npe,tbl,index,-npages,rm);+pnv_pci_ioda2_tce_invalidate_entire(npe->phb,+rm);}}}
We are going to support VFIO on NPU PHB type which will share some code
with the normal IODA2 PHB.
This exports pnv_pci_ioda2_create_table and pnv_pci_ioda2_get_table_size.
This exports debugging helper pe_level_printk() as well.
Signed-off-by: Alexey Kardashevskiy <redacted>
---
arch/powerpc/platforms/powernv/pci-ioda.c | 13 +++----------
arch/powerpc/platforms/powernv/pci.h | 15 +++++++++++++++
2 files changed, 18 insertions(+), 10 deletions(-)
From: David Gibson <hidden> Date: 2016-03-10 05:44:09
On Wed, Mar 09, 2016 at 05:28:57PM +1100, Alexey Kardashevskiy wrote:
We are going to have multiple different types of PHB on the same system
with POWER8 + NVLink and PHBs will have different IOMMU ops. However
we only really care about one callback - create_table - so we can
relax the compatibility check here.
Signed-off-by: Alexey Kardashevskiy <redacted>
@@ -1188,7 +1188,8 @@ static int tce_iommu_attach_group(void *iommu_data,gotounlock_exit;}table_group_tmp=iommu_group_get_iommudata(tcegrp->grp);-if(table_group_tmp->ops!=table_group->ops){+if(table_group_tmp->ops->create_table!=+table_group->ops->create_table){pr_warn("tce_vfio: Group %d is incompatible with group %d\n",iommu_group_id(iommu_group),iommu_group_id(tcegrp->grp));
--
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
From: David Gibson <hidden> Date: 2016-03-10 05:44:10
On Wed, Mar 09, 2016 at 05:28:58PM +1100, Alexey Kardashevskiy wrote:
As in fact pnv_pci_ioda2_tce_invalidate_entire() invalidates TCEs for
the specific PE rather than the entire cache, rename it to
pnv_pci_ioda2_tce_invalidate_pe(). In later patches we will add
a proper pnv_pci_ioda2_tce_invalidate_entire().
Signed-off-by: Alexey Kardashevskiy <redacted>
@@ -1824,7 +1824,7 @@ static struct iommu_table_ops pnv_ioda1_iommu_ops = {.get=pnv_tce_get,};-staticinlinevoidpnv_pci_ioda2_tce_invalidate_entire(structpnv_ioda_pe*pe)+staticinlinevoidpnv_pci_ioda2_tce_invalidate_pe(structpnv_ioda_pe*pe){/* 01xb - invalidate TCEs that match the specified PE# */unsignedlongval=(0x4ull<<60)|(pe->pe_number&0xFF);
@@ -2101,7 +2101,7 @@ static long pnv_pci_ioda2_set_window(struct iommu_table_group *table_group,pnv_pci_link_table_and_group(phb->hose->node,num,tbl,&pe->table_group);-pnv_pci_ioda2_tce_invalidate_entire(pe);+pnv_pci_ioda2_tce_invalidate_pe(pe);return0;}
@@ -2245,7 +2245,7 @@ static long pnv_pci_ioda2_unset_window(struct iommu_table_group *table_group,if(ret)pe_warn(pe,"Unmapping failed, ret = %ld\n",ret);else-pnv_pci_ioda2_tce_invalidate_entire(pe);+pnv_pci_ioda2_tce_invalidate_pe(pe);pnv_pci_unlink_table_and_group(table_group->tables[num],table_group);
--
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
@@ -1824,10 +1824,13 @@ static struct iommu_table_ops pnv_ioda1_iommu_ops = {.get=pnv_tce_get,};+#define TCE_KILL_INVAL_PE PPC_BIT(1)+#define TCE_KILL_INVAL_TCE PPC_BIT(2)+staticinlinevoidpnv_pci_ioda2_tce_invalidate_pe(structpnv_ioda_pe*pe){/* 01xb - invalidate TCEs that match the specified PE# */-unsignedlongval=(0x4ull<<60)|(pe->pe_number&0xFF);+unsignedlongval=TCE_KILL_INVAL_PE|(pe->pe_number&0xFF);structpnv_phb*phb=pe->phb;structpnv_ioda_pe*npe;inti;
@@ -1855,7 +1858,7 @@ static void pnv_pci_ioda2_do_tce_invalidate(unsigned pe_number, bool rm,unsignedlongstart,end,inc;/* We'll invalidate DMA address in PE scope */-start=0x2ull<<60;+start=TCE_KILL_INVAL_TCE;start|=(pe_number&0xFF);end=start;
--
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
From: David Gibson <hidden> Date: 2016-03-10 05:44:10
On Wed, Mar 09, 2016 at 05:29:00PM +1100, Alexey Kardashevskiy wrote:
NPU PHB TCE Kill register is exactly the same as in the rest of POWER8
so let's reuse the existing code for NPU. The only bit missing is
a helper to reset the entire TCE cache so this moves such a helper
from NPU code and renames it.
Since pnv_npu_tce_invalidate() does really invalidate the entire cache,
this uses pnv_pci_ioda2_tce_invalidate_entire() directly for NPU.
This adds an explicit comment for workaround for invalidating NPU TCE
cache.
Signed-off-by: Alexey Kardashevskiy <redacted>
@@ -1896,14 +1910,19 @@ static void pnv_pci_ioda2_tce_invalidate(struct iommu_table *tbl,index,npages);if(pe->flags&PNV_IODA_PE_PEER)-/* Invalidate PEs using the same TCE table */+/*+*TheNVLinkhardwaredoesnotsupportTCEkill+*perTCEentrysowehavetoinvalidate+*theentirecacheforit.+*/for(i=0;i<PNV_IODA_MAX_PEER_PES;i++){npe=pe->peers[i];-if(!npe||npe->phb->type!=PNV_PHB_NPU)+if(!npe||npe->phb->type!=PNV_PHB_NPU||+!npe->phb->ioda.tce_inval_reg)continue;-pnv_npu_tce_invalidate(npe,tbl,index,-npages,rm);+pnv_pci_ioda2_tce_invalidate_entire(npe->phb,+rm);}}}
@@ -237,15 +237,10 @@ extern int pnv_setup_msi_irqs(struct pci_dev *pdev, int nvec, int type);externvoidpnv_teardown_msi_irqs(structpci_dev*pdev);/* Nvlink functions */-externvoidpnv_npu_tce_invalidate_entire(structpnv_ioda_pe*npe);-externvoidpnv_npu_tce_invalidate(structpnv_ioda_pe*npe,-structiommu_table*tbl,-unsignedlongindex,-unsignedlongnpages,-boolrm);externvoidpnv_npu_init_dma_pe(structpnv_ioda_pe*npe);externvoidpnv_npu_setup_dma_pe(structpnv_ioda_pe*npe);externintpnv_npu_dma_set_bypass(structpnv_ioda_pe*npe,boolenabled);externintpnv_npu_dma_set_mask(structpci_dev*npdev,u64dma_mask);+externvoidpnv_pci_ioda2_tce_invalidate_entire(structpnv_phb*phb,boolrm);#endif /* __POWERNV_PCI_H */
--
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
From: David Gibson <hidden> Date: 2016-03-10 05:44:11
On Wed, Mar 09, 2016 at 05:29:01PM +1100, Alexey Kardashevskiy wrote:
This uses the page size from iommu_table instead of hard-coded 4K.
This should cause no change in behavior.
While we are here, move bits around to prepare for further rework
which will define and use iommu_table_group_ops.
Signed-off-by: Alexey Kardashevskiy <redacted>
--
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
From: David Gibson <hidden> Date: 2016-03-16 06:15:47
On Wed, Mar 09, 2016 at 05:29:02PM +1100, Alexey Kardashevskiy wrote:
NPU devices are quite specific, in fact they represent side DMA channel
of a GPU device. The GPU/NPU driver never actually configures DMA
for NPU devices, instead it relies on the platform code to propagate
DMA setup to NPU devices when a main GPU device is being configured.
When GPU is being set up, the same configuration - bypass or 32bit DMA -
is used for NPU. This makes DMA setup explicit.
pnv_npu_ioda_controller_ops::pnv_npu_dma_set_mask is moved to pci-ioda,
made static and prints warning as dma_set_mask() should never be called
on this function as in any case it will not configure GPU; so we make
this explicit.
Instead of using PNV_IODA_PE_PEER and peers[] (which next patch will
remove), we test every PCI device if there are corresponding NVLink
devices. If there are any, we propagate bypass mode to just found NPU
devices by calling the setup helper directly (which takes @bypass) and
avoid guessing (i.e. calculating from DMA mask) whether we need bypass
or not on NPU devices. Since DMA setup happens in very rare occasion,
this will not slow down booting or VFIO start/stop much.
This renames pnv_npu_disable_bypass to pnv_npu_dma_set_32 to make it
more clear what the function really does which is programming 32bit
table address to the TVT ("disabling bypass" means writing zeroes to
the TVT).
This removes pnv_npu_dma_set_bypass() from pnv_npu_ioda_fixup() as
the DMA configuration on NPU does not matter until dma_set_mask() is
called on GPU and that will do the NPU DMA configuration.
This removes phb->dma_dev_setup initialization for NPU as
pnv_pci_ioda_dma_dev_setup is no-op for it anyway.
Signed-off-by: Alexey Kardashevskiy <redacted>
I'm having trouble making sense of the commit message, but the actual
changes look fine as best I can tell.
Reviewed-by: David Gibson <redacted>
--
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
On Wed, 9 Mar 2016 17:29:00 Alexey Kardashevskiy wrote:
NPU PHB TCE Kill register is exactly the same as in the rest of POWER8
so let's reuse the existing code for NPU. The only bit missing is
a helper to reset the entire TCE cache so this moves such a helper
from NPU code and renames it.
Since pnv_npu_tce_invalidate() does really invalidate the entire cache,
this uses pnv_pci_ioda2_tce_invalidate_entire() directly for NPU.
This adds an explicit comment for workaround for invalidating NPU TCE
cache.
Signed-off-by: Alexey Kardashevskiy <redacted>
@@ -1896,14 +1910,19 @@ static void pnv_pci_ioda2_tce_invalidate(struct iommu_table *tbl,index,npages);if(pe->flags&PNV_IODA_PE_PEER)-/* Invalidate PEs using the same TCE table */+/*+*TheNVLinkhardwaredoesnotsupportTCEkill+*perTCEentrysowehavetoinvalidate+*theentirecacheforit.+*/for(i=0;i<PNV_IODA_MAX_PEER_PES;i++){npe=pe->peers[i];-if(!npe||npe->phb->type!=PNV_PHB_NPU)+if(!npe||npe->phb->type!=PNV_PHB_NPU||+!npe->phb->ioda.tce_inval_reg)continue;-pnv_npu_tce_invalidate(npe,tbl,index,-npages,rm);+pnv_pci_ioda2_tce_invalidate_entire(npe->phb,+rm);}}}
Thanks for fixing Alexey!
On Wed, 9 Mar 2016 17:29:01 Alexey Kardashevskiy wrote:
This uses the page size from iommu_table instead of hard-coded 4K.
This should cause no change in behavior.
While we are here, move bits around to prepare for further rework
which will define and use iommu_table_group_ops.
Signed-off-by: Alexey Kardashevskiy <redacted>
On Wed, Mar 09, 2016 at 05:29:02PM +1100, Alexey Kardashevskiy wrote:
quoted
NPU devices are quite specific, in fact they represent side DMA channel
of a GPU device. The GPU/NPU driver never actually configures DMA
for NPU devices, instead it relies on the platform code to propagate
DMA setup to NPU devices when a main GPU device is being configured.
When GPU is being set up, the same configuration - bypass or 32bit DMA -
is used for NPU. This makes DMA setup explicit.
pnv_npu_ioda_controller_ops::pnv_npu_dma_set_mask is moved to pci-ioda,
made static and prints warning as dma_set_mask() should never be called
on this function as in any case it will not configure GPU; so we make
this explicit.
Instead of using PNV_IODA_PE_PEER and peers[] (which next patch will
remove), we test every PCI device if there are corresponding NVLink
devices. If there are any, we propagate bypass mode to just found NPU
devices by calling the setup helper directly (which takes @bypass) and
avoid guessing (i.e. calculating from DMA mask) whether we need bypass
or not on NPU devices. Since DMA setup happens in very rare occasion,
this will not slow down booting or VFIO start/stop much.
This renames pnv_npu_disable_bypass to pnv_npu_dma_set_32 to make it
more clear what the function really does which is programming 32bit
table address to the TVT ("disabling bypass" means writing zeroes to
the TVT).
This removes pnv_npu_dma_set_bypass() from pnv_npu_ioda_fixup() as
the DMA configuration on NPU does not matter until dma_set_mask() is
called on GPU and that will do the NPU DMA configuration.
This removes phb->dma_dev_setup initialization for NPU as
pnv_pci_ioda_dma_dev_setup is no-op for it anyway.
Signed-off-by: Alexey Kardashevskiy <redacted>
I'm having trouble making sense of the commit message, but the actual
changes look fine as best I can tell.
For background the NPU NVLink PCI "devices" are actually emulated in firmware
and are mainly used for link training. Their DMA/TCE setup must match the GPU
which is connected via PCIe and NVLink so any changes to the DMA/TCE setup on
the GPU PCIe device need to be propagated to the NVLink device as this is what
device drivers expect and it doesn't make much sense to do anything else.
Originally we were going to propagate DMA/TCE changes the other way (NVLink
device to PCI device) as well, but it proved unnecessary and unused. This
patch cleans up the last bit of that behaviour and looks good to me as well.
From: David Gibson <hidden> Date: 2016-03-21 04:48:18
On Wed, Mar 09, 2016 at 05:29:04PM +1100, Alexey Kardashevskiy wrote:
NPU devices have their own TVT which means they are isolated and can be
passed to the userspace via VFIO. The first step is to create an IOMMU
group and attach devices there so does the patch.
This adds a helper to npu-dma.c which gets GPU from the NPU's pdev and
then walks through all devices on the same bus to determine which NPUs
belong to the same GPU.
This adds an additional loop over PEs in pnv_ioda_setup_dma() as the main
loop skips NPU PEs as they do not have 32bit DMA segments.
This uses get_gpu_pci_dev_and_pe() to get @gpdev rather than
pnv_pci_get_gpu_dev() as the following patch will use @gpe as well.
Signed-off-by: Alexey Kardashevskiy <redacted>
I'm not entirely clear on how these devices are assigned to groups.
Do they each get their own groups, or is the NPU device in the same
group as its corresponding GPU (I would have thought the latter makes
sense).
--
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
On Wed, 9 Mar 2016 17:29:03 Alexey Kardashevskiy wrote:
The pnv_ioda_pe struct keeps an array of peers. At the moment it is only
used to link GPU and NPU for 2 purposes:
1. Access NPU _quickly_ when configuring DMA for GPU - this was addressed
in the previos patch by removing use of it as DMA setup is not what
the kernel would constantly do.
This was implemented using peers[] because we had peers[] anyway to deal with
TCE cache invalidation. I agree there's no reason to keep it around solely for
speed.
2. Invalidate TCE cache for NPU when it is invalidated for GPU.
GPU and NPU are in different PE. There is already a mechanism to
attach multiple iommu_table_group to the same iommu_table (used for VFIO),
we can reuse it here so does this patch.
Ok, this makes sense. I wasn't aware of iommu_table_groups but it looks like a
more elegant way of solving the problem. I'm not familiar with the way iommu
groups work but the changes make sense to me as far as I can tell.
This gets rid of peers[] array and PNV_IODA_PE_PEER flag as they are
not needed anymore.
While we are here, add TCE cache invalidation after changing TVT.
Good idea, even though I guess we're unlikely to hit a problem in practice as
I'm pretty sure on a normal system the links would get retrained between runs
with different TVTs which implies the NPU gets reset too.
pe_number);
+ else
+ pnv_pci_ioda2_tce_invalidate_entire(phb, false);
+
+ /* Add the table to the list so its TCE cache will get invalidated */
+ npe->table_group.tables[0] = tbl;
+ pnv_pci_link_table_and_group(phb->hose->node, 0,
+ tbl, &npe->table_group);
/*
* We don't initialise npu_pe->tce32_table as we always use
@@ -245,10 +211,10 @@ static int pnv_npu_dma_set_bypass(struct pnv_ioda_pe
*npe)
quoted hunk
int64_t rc = 0;
phys_addr_t top = memblock_end_of_DRAM();
- if (phb->type != PNV_PHB_NPU || !npe->pdev)
- return -EINVAL;
-
/* Enable the bypass window */
+ pnv_pci_unlink_table_and_group(npe->table_group.tables[0],
+ &npe->table_group);
+ npe->table_group.tables[0] = NULL;
npe->tce_bypass_base = 0;
top = roundup_pow_of_two(top);
@@ -258,6 +224,9 @@ static int pnv_npu_dma_set_bypass(struct pnv_ioda_pe
@@ -24,7 +24,6 @@ enum pnv_phb_model {#define PNV_IODA_PE_MASTER (1 << 3) /* Master PE in compound case
*/
#define PNV_IODA_PE_SLAVE (1 << 4) /* Slave PE in compound case
*/
#define PNV_IODA_PE_VF (1 << 5) /* PE for one VF
*/
-#define PNV_IODA_PE_PEER (1 << 6) /* PE has peers
*/
quoted hunk
/* Data associated with a PE, including IOMMU tracking etc.. */
struct pnv_phb;
@@ -32,9 +31,6 @@ struct pnv_ioda_pe { unsigned long flags; struct pnv_phb *phb;-#define PNV_IODA_MAX_PEER_PES 8- struct pnv_ioda_pe *peers[PNV_IODA_MAX_PEER_PES];- /* A PE can be associated with a single device or an * entire bus (& children). In the former case, pdev * is populated, in the later case, pbus is.
@@ -237,8 +233,6 @@ extern int pnv_setup_msi_irqs(struct pci_dev *pdev, int
On Wed, Mar 09, 2016 at 05:29:04PM +1100, Alexey Kardashevskiy wrote:
quoted
NPU devices have their own TVT which means they are isolated and can be
passed to the userspace via VFIO. The first step is to create an IOMMU
group and attach devices there so does the patch.
This adds a helper to npu-dma.c which gets GPU from the NPU's pdev and
then walks through all devices on the same bus to determine which NPUs
belong to the same GPU.
This adds an additional loop over PEs in pnv_ioda_setup_dma() as the main
loop skips NPU PEs as they do not have 32bit DMA segments.
This uses get_gpu_pci_dev_and_pe() to get @gpdev rather than
pnv_pci_get_gpu_dev() as the following patch will use @gpe as well.
Signed-off-by: Alexey Kardashevskiy <redacted>
I'm not entirely clear on how these devices are assigned to groups.
Do they each get their own groups, or is the NPU device in the same
group as its corresponding GPU (I would have thought the latter makes
sense).
I am putting them to a separate group as they have their own TCE table
pointer even though they are expected to share it with GPU.
If I put them to the same group as GPUs, I would have to have
IODA2-linked-to-NPU bridge type with different iommu_table_group_ops or
have multiple hacks everywhere in IODA2 to enable/disable bypass, etc.
From: David Gibson <hidden> Date: 2016-03-22 00:24:40
On Mon, Mar 21, 2016 at 07:25:23PM +1100, Alexey Kardashevskiy wrote:
On 03/21/2016 03:48 PM, David Gibson wrote:
quoted
On Wed, Mar 09, 2016 at 05:29:04PM +1100, Alexey Kardashevskiy wrote:
quoted
NPU devices have their own TVT which means they are isolated and can be
passed to the userspace via VFIO. The first step is to create an IOMMU
group and attach devices there so does the patch.
This adds a helper to npu-dma.c which gets GPU from the NPU's pdev and
then walks through all devices on the same bus to determine which NPUs
belong to the same GPU.
This adds an additional loop over PEs in pnv_ioda_setup_dma() as the main
loop skips NPU PEs as they do not have 32bit DMA segments.
This uses get_gpu_pci_dev_and_pe() to get @gpdev rather than
pnv_pci_get_gpu_dev() as the following patch will use @gpe as well.
Signed-off-by: Alexey Kardashevskiy <redacted>
I'm not entirely clear on how these devices are assigned to groups.
Do they each get their own groups, or is the NPU device in the same
group as its corresponding GPU (I would have thought the latter makes
sense).
I am putting them to a separate group as they have their own TCE table
pointer even though they are expected to share it with GPU.
Hmm.. is this safe? If the GPU and NPU got assigned to different
owners, what would happen? Could the interfere with each other?
If I put them to the same group as GPUs, I would have to have
IODA2-linked-to-NPU bridge type with different iommu_table_group_ops or
have multiple hacks everywhere in IODA2 to enable/disable bypass,
etc.
Well.. I suspect it would mean no longer having a 1:1 correspondance
between user-visible IOMMU groups and the internal iommu_table.
--
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
On Mon, Mar 21, 2016 at 07:25:23PM +1100, Alexey Kardashevskiy wrote:
quoted
On 03/21/2016 03:48 PM, David Gibson wrote:
quoted
On Wed, Mar 09, 2016 at 05:29:04PM +1100, Alexey Kardashevskiy wrote:
quoted
NPU devices have their own TVT which means they are isolated and can be
passed to the userspace via VFIO. The first step is to create an IOMMU
group and attach devices there so does the patch.
This adds a helper to npu-dma.c which gets GPU from the NPU's pdev and
then walks through all devices on the same bus to determine which NPUs
belong to the same GPU.
This adds an additional loop over PEs in pnv_ioda_setup_dma() as the main
loop skips NPU PEs as they do not have 32bit DMA segments.
This uses get_gpu_pci_dev_and_pe() to get @gpdev rather than
pnv_pci_get_gpu_dev() as the following patch will use @gpe as well.
Signed-off-by: Alexey Kardashevskiy <redacted>
I'm not entirely clear on how these devices are assigned to groups.
Do they each get their own groups, or is the NPU device in the same
group as its corresponding GPU (I would have thought the latter makes
sense).
I am putting them to a separate group as they have their own TCE table
pointer even though they are expected to share it with GPU.
Hmm.. is this safe? If the GPU and NPU got assigned to different
owners, what would happen? Could the interfere with each other?
I suppose GPU from guest1 could trigger DMA from NPU to guest2 memory.
Which puts a constrain to management tools not to pass NPU without their
GPU counterparts.
The host can be affected as bypass is not disabled on NPU when GPU is taken
by VFIO, I'll fix this.
quoted
If I put them to the same group as GPUs, I would have to have
IODA2-linked-to-NPU bridge type with different iommu_table_group_ops or
have multiple hacks everywhere in IODA2 to enable/disable bypass,
etc.
Well.. I suspect it would mean no longer having a 1:1 correspondance
between user-visible IOMMU groups and the internal iommu_table.
Right.
Right now each GPU is sitting on a separate PHB and has its own PE. And all
NPUs sit on a separate PHB and each couple of NPUs (2 links of the same
GPU) gets a PE.
So we have separate PEs (struct pnv_ioda_pe) already, each has its own
iommu_table_group_ops with all these VFIO IOMMU callbacks. So to make this
all appear as one IOMMU group in sysfs, I will need to stop embedding
iommu_table_group into pnv_ioda_pe but make it a pointer with reference
counting, etc. Quite a massive change...
From: Benjamin Herrenschmidt <benh@kernel.crashing.org> Date: 2016-03-22 12:42:22
On Tue, 2016-03-22 at 12:48 +1100, Alexey Kardashevskiy wrote:
I suppose GPU from guest1 could trigger DMA from NPU to guest2 memory.
Which puts a constrain to management tools not to pass NPU without their
GPU counterparts.
Management tools will not be taught such constraints. The plan always
was to make sure they are in the same group. So they should be.
The host can be affected as bypass is not disabled on NPU when GPU is taken
by VFIO, I'll fix this.
quoted
quoted
If I put them to the same group as GPUs, I would have to have
IODA2-linked-to-NPU bridge type with different iommu_table_group_ops or
have multiple hacks everywhere in IODA2 to enable/disable bypass,
etc.
Well.. I suspect it would mean no longer having a 1:1 correspondance
between user-visible IOMMU groups and the internal iommu_table.
Right.
They can share the table too ...
Right now each GPU is sitting on a separate PHB and has its own PE. And all
NPUs sit on a separate PHB and each couple of NPUs (2 links of the same
GPU) gets a PE.
So we have separate PEs (struct pnv_ioda_pe) already, each has its own
iommu_table_group_ops with all these VFIO IOMMU callbacks. So to make this
all appear as one IOMMU group in sysfs, I will need to stop embedding
iommu_table_group into pnv_ioda_pe but make it a pointer with reference
counting, etc. Quite a massive change...
Or you just put a quirk flag of some sort and a pointer to the "linked"
PE... sometimes that's a lot easier than lifting up the whole
infrastructure.