From: Oliver O'Halloran <oohall@gmail.com> Date: 2020-07-10 05:32:46
Finally bit the bullet and learned how all the MMIO->PE mapping setup
actually works. As a side effect I found a bunch of oddities in how
PowerNV SR-IOV support is implemented. This series mostly sorts that
out with a few more generic cleanups along the way.
This is largely prep work for supporting VFs in the 32bit MMIO window.
This is an unfortunate necessity due to how the Linux BAR allocator
handles BARs marked as non-prefetchable. The distinction
between prefetch and non-prefetchable BARs was made largely irrelevant
with the introduction of PCIe, but the BAR allocator is overly
conservative. It will always place non-pref bars in the prefetchable
window, which is 32bit only. This results in us being unable to use VFs
from NVMe drives and a few different RAID cards.
This series is based on top of these two:
https://patchwork.ozlabs.org/project/linuxppc-dev/list/?series=187630https://patchwork.ozlabs.org/project/linuxppc-dev/list/?series=187688
Rebases cleanly on top of the first, but I haven't tested that one plus
this extensively.
Oliver
From: Oliver O'Halloran <oohall@gmail.com> Date: 2020-07-10 05:34:23
Add a helper to go from a pci_bus structure to the pnv_phb that hosts that
bus. There's a lot of instances of the following pattern:
struct pci_controller *hose = pci_bus_to_host(pdev->bus);
struct pnv_phb *phb = hose->private_data;
Without any other uses of the pci_controller inside the function. This is
hard to read since it requires you to memorise the contents of the
private data fields and kind of error prone since it involves blindly
assigning a void pointer. Add a helper to make it more concise and
explicit.
Signed-off-by: Oliver O'Halloran <oohall@gmail.com>
---
arch/powerpc/platforms/powernv/pci-ioda.c | 88 +++++++----------------
arch/powerpc/platforms/powernv/pci.c | 14 ++--
arch/powerpc/platforms/powernv/pci.h | 10 +++
3 files changed, 38 insertions(+), 74 deletions(-)
@@ -3274,8 +3238,7 @@ static resource_size_t pnv_pci_iov_resource_alignment(struct pci_dev *pdev,*/staticboolpnv_pci_enable_device_hook(structpci_dev*dev){-structpci_controller*hose=pci_bus_to_host(dev->bus);-structpnv_phb*phb=hose->private_data;+structpnv_phb*phb=pci_bus_to_pnvhb(dev->bus);structpci_dn*pdn;/* The function is probably called while the PEs have
@@ -211,8 +210,7 @@ int pnv_setup_msi_irqs(struct pci_dev *pdev, int nvec, int type)voidpnv_teardown_msi_irqs(structpci_dev*pdev){-structpci_controller*hose=pci_bus_to_host(pdev->bus);-structpnv_phb*phb=hose->private_data;+structpnv_phb*phb=pci_bus_to_pnvhb(pdev->bus);structmsi_desc*entry;irq_hw_number_thwirq;
From: Oliver O'Halloran <oohall@gmail.com> Date: 2020-07-10 05:36:00
Currently we have these two functions:
pnv_pci_ioda2_release_dma_pe(), and
pnv_pci_ioda2_release_pe_dma()
The first is used when tearing down VF PEs and the other is used for normal
devices. There's very little difference between the two though. The latter
(non-VF) will skip a call to pnv_pci_ioda2_unset_window() unless
CONFIG_IOMMU_API=y is set. There's no real point in doing this so fold the
two together.
Signed-off-by: Oliver O'Halloran <oohall@gmail.com>
---
arch/powerpc/platforms/powernv/pci-ioda.c | 30 +++--------------------
1 file changed, 3 insertions(+), 27 deletions(-)
From: Oliver O'Halloran <oohall@gmail.com> Date: 2020-07-10 05:37:43
There's an optimisation in the PE setup which skips performing DMA
setup for a PE if we only have bridges in a PE. The assumption being
that only "real" devices will DMA to system memory, which is probably
fair. However, if we start off with only bridge devices in a PE then
add a non-bridge device the new device won't be able to use DMA because
we never configured it.
Fix this (admittedly pretty weird) edge case by tracking whether we've done
the DMA setup for the PE or not. If a non-bridge device is added to the PE
(via rescan or hotplug, or whatever) we can set up DMA on demand.
This also means the only remaining user of the old "DMA Weight" code is
the IODA1 DMA setup code that it was originally added for, which is good.
Cc: Alexey Kardashevskiy <redacted>
Signed-off-by: Oliver O'Halloran <oohall@gmail.com>
---
Alexey, do we need to have the IOMMU API stuff set/clear this flag?
---
arch/powerpc/platforms/powernv/pci-ioda.c | 48 ++++++++++++++---------
arch/powerpc/platforms/powernv/pci.h | 7 ++++
2 files changed, 36 insertions(+), 19 deletions(-)
@@ -87,6 +87,13 @@ struct pnv_ioda_pe {booltce_bypass_enabled;uint64_ttce_bypass_base;+/*+*Usedtotrackwhetherwe'vedoneDMAsetupforthisPEornot.We+*wanttodeferallocatingTCEtables,etcuntilwe'veaddeda+*non-bridgedevicetothePE.+*/+booldma_setup_done;+/* MSIs. MVE index is identical for for 32 and 64 bit MSI*and-1ifnotsupported.(It'sactuallyidenticaltothe*PEnumber)
From: Oliver O'Halloran <oohall@gmail.com> Date: 2020-07-10 05:39:17
We pre-configure the m64 window for IODA1 as a 1-1 segment-PE mapping,
similar to PHB3. Currently the actual mapping of segments occurs in
pnv_ioda_pick_m64_pe(), but we can move it into pnv_ioda1_init_m64() and
drop the IODA1 specific code paths in the PE setup / teardown.
Signed-off-by: Oliver O'Halloran <oohall@gmail.com>
---
arch/powerpc/platforms/powernv/pci-ioda.c | 55 +++++++++++------------
1 file changed, 25 insertions(+), 30 deletions(-)
From: Oliver O'Halloran <oohall@gmail.com> Date: 2020-07-10 05:41:11
SR-IOV support on PowerNV is a byzantine maze of hooks. I have no idea
how anyone is supposed to know how it works except through a lot of
stuffering. Write up some docs about the overall story to help out
the next sucker^Wperson who needs to tinker with it.
Signed-off-by: Oliver O'Halloran <oohall@gmail.com>
---
arch/powerpc/platforms/powernv/pci-sriov.c | 130 +++++++++++++++++++++
1 file changed, 130 insertions(+)
@@ -12,6 +12,136 @@/* for pci_dev_is_added() */#include"../../../../drivers/pci/pci.h"+/*+*ThemajorityofthecomplexityinsupportingSR-IOVonPowerNVcomesfrom+*theneedtoputtheMMIOspaceforeachVFintoaseparatePE.Internally+*thePHBmapsMMIOaddressestoaspecificPEusingthe"Memory BAR Table".+*TheMBThistoricallyonlyappliedtothe64bitMMIOwindowofthePHB+*soit'scommontoseeitreferredtoasthe"M64BT".+*+*AnMBTentrystoresthemappedrangeasan<base>,<mask>pair.Thisforces+*theaddressrangethatwewanttomaptobepower-of-twosizedandaligned.+*ForconventionalPCIdevicesthisisn'treallyanissuesincePCIdeviceBARs+*havethesamerequirement.+*+*ForaSR-IOVBARthingsarealittlemoreawkwardsincesizeandalignment+*arenotcoupled.Thealignmentissetbasedonthetheper-VFBARsize,but+*thetotalBARareais:number-of-vfs*per-vf-size.ThenumberofVFs+*isn'tnecessarilyapoweroftwo,soneitheristhetotalsize.Tofixthat+*weneedtofinesse(read:hack)theLinuxBARallocatorsothatitwill+*allocatetheSR-IOVBARsinawaythatletsusmapthemusingtheMBT.+*+*Thechangestosizeandalignmentthatweneedtododependonthe"mode"+*ofMBTentrythatweuse.WeonlysupportSR-IOVonPHB3(IODA2)andabove,+*soasabaselinewecanassumethatwehavethefollowingBARmodes+*available:+*+*NB:$PE_COUNTisthenumberofPEsthatthePHBsupports.+*+*a)AsegmentedBARthatsplitsthemappedrangeinto$PE_COUNTequallysized+*segments.Then'thsegmentismappedtothen'thPE.+*b)Anun-segmentedBARthatmapsthewholeaddressrangetoaspecificPE.+*+*+*Weprefertousemodea)sinceitonlyrequiresoneMBTentryperSR-IOVBAR+*Forcomparisonb)requiresoneentryper-VFper-BAR,or:+*(num-vfs*num-sriov-bars)intotal.Tousea)weneedthesizeofeachsegment+*toequalthesizeoftheper-VFBARarea.So:+*+*new_size=per-vf-size*number-of-PEs+*+*ThealignmentfortheSR-IOVBARalsoneedstobechangedfromper-vf-size+*to"new_size",calculatedabove.Implementingthisisaconvolutedprocess+*whichrequiresseveralhooksinthePCIcore:+*+*1.Inpcibios_add_device()wecallpnv_pci_ioda_fixup_iov().+*+*Atthispointthedevicehasbeenprobedandthedevice'sBARsaresized,+*butnoresourceallocationshavebeendone.TheSR-IOVBARsaresized+*basedonthemaximumnumberofVFssupportedbythedeviceandweneed+*toincreasethattonew_size.+*+*2.Later,whenLinuxactuallyassignsresourcesittriestomaketheresource+*allocationsforeachPCIbusascompactaspossible.Asapartofthatit+*sortstheBARsonabusbytheirrequiredalignment,whichiscalculated+*usingpci_resource_alignment().+*+*ForIOVresourcesthisgoes:+*pci_resource_alignment()+*pci_sriov_resource_alignment()+*pcibios_sriov_resource_alignment()+*pnv_pci_iov_resource_alignment()+*+*Ourhookoverridesthedefaultalignment,equaltotheper-vf-size,with+*new_sizecomputedabove.+*+*3.WhenuserspaceenablesVFsforadevice:+*+*sriov_enable()+*pcibios_sriov_enable()+*pnv_pcibios_sriov_enable()+*+*ThisiswhereweactuallyallocatePEnumbersforeachVFandsetupthe+*MBTmappingforeachSR-IOVBAR.Insteps1)and2)wesetupan"arena"+*whereeachMBTsegmentisequalinsizetotheVFBARsowecanshift+*aroundtheactualSR-IOVBARlocationwithinthisarena.Weneedthis+*abilitybecausethePEspaceissharedbyalldevicesonthesamePHB.+*Whenusingmodea)describedabovesegment0inmapstoPE#0whichmight+*bealreadybeingusedbyanotherdeviceonthePHB.+*+*AsaresultweneedallocateacontigiousrangeofPEnumbers,thenshift+*theaddressprogrammedintotheSR-IOVBARofthePFsothattheaddress+*ofVF0matchesupwiththesegmentcorrespondingtothefirstallocated+*PEnumber.Thisishandledinpnv_pci_vf_resource_shift().+*+*OnceallthatisdonewereturntothePCIcorewhichthenenablesVFs,+*scansthemandcreatespci_devsforeach.TheinitprocessforaVFis+*largelythesameasanormaldevice,buttheVFisinsertedintotheIODA+*PEthatweallocatedforitratherthanthePEassociatedwiththebus.+*+*4.WhenuserspacedisablesVFsweunwindtheabovein+*pnv_pcibios_sriov_disable().Fortunatelythisisrelativelysimplesince+*wedon'tneedtovalidateanything,justteardownthemappingsand+*moveSR-IOVresourcebacktoits"proper"location.+*+*That'showmodea)works.Intheorymodeb)(singlePEmapping)islesswork+*sincewecanmapeachindividualVFwithaseparateBAR.However,there'sa+*fewlimitations:+*+*1)ForIODA2modeb)hasaminimumalignmentrequirementof32MB.Thismakes+*itonlyusablefordeviceswithverylargeper-VFBARs.Suchdevicesare+*similartoBigFoot.Theydefinitelyexist,butI'veneverseenone.+*+*2)ThenumberofMBTentriesthatwehaveislimited.PHB3andPHB4only+*16totalandsomeareneededfor.MostSR-IOVcapablenetworkcardscansupport+*morethan16VFsoneachport.+*+*Weuseb)whenusinga)wouldusemorethan1/4oftheentire64bitMMIO+*windowofthePHB.+*+*+*+*PHB4(IODA3)addedafewnewfeaturesthatwouldbeusefulforSR-IOV.It+*allowedtheMBTtomap32bitMMIOspaceinadditionto64bitwhichallows+*ustosupportSR-IOVBARsinthe32bitMMIOwindow.Thisisusefulsince+*theLinuxBARallocationwillplaceanyBARmarkedasnon-prefetchableinto+*thenon-prefetchablebridgewindow,whichis32bitonly.Italsoaddedtwo+*newmodes:+*+*c)AsegmentedBARsimilartoa),buteachsegmentcanbeindividually+*mappedtoanyPE.Thisismatcheshowthe32bitMMIOwindowworkedon+*IODA1&2.+*+*d)AsegmentedBARwith8,64,or128segments.Thisworkssimilarlytoa),+*butwithfewersegmentsandconfigurablebasePE.+*+*i.e.Then'thsegmentmapstothe(n+base)'thPE.+*+*ThebasePEisalsorequiredtobeamultipleofthewindowsize.+*+*Unfortunately,theOPALAPIdoesn'tcurrently(asofskibootv6.6)allowus+*toexploitanyoftheIODA3features.+*/staticvoidpnv_pci_ioda_fixup_iov_resources(structpci_dev*pdev){
From: Oliver O'Halloran <oohall@gmail.com> Date: 2020-07-10 05:43:05
pci-ioda.c is getting a bit unwieldly due to the amount of stuff jammed in
there. The SR-IOV support can be extracted easily enough and is mostly
standalone, so move it into a seperate file.
This patch also moves the PowerNV SR-IOV specific fields from pci_dn and moves them
into a platform specific structure. I'm not sure how they ended up in there
in the first place, but leaking platform specifics into common code has
proven to be a terrible idea so far so lets stop doing that.
Signed-off-by: Oliver O'Halloran <oohall@gmail.com>
---
The pci_dn change and the pci-sriov.c changes originally separate patches.
I accidently squashed them together while rebasing and fixing that seemed
like more pain that it was worth. I kind of like it this way though since
they did cause a lot of churn on the same set of functions.
I'll split them up again if you really want (please don't want this).
---
arch/powerpc/include/asm/device.h | 3 +
arch/powerpc/platforms/powernv/Makefile | 1 +
arch/powerpc/platforms/powernv/pci-ioda.c | 673 +--------------------
arch/powerpc/platforms/powernv/pci-sriov.c | 642 ++++++++++++++++++++
arch/powerpc/platforms/powernv/pci.h | 74 +++
5 files changed, 738 insertions(+), 655 deletions(-)
create mode 100644 arch/powerpc/platforms/powernv/pci-sriov.c
@@ -982,91 +962,6 @@ static int pnv_ioda_configure_pe(struct pnv_phb *phb, struct pnv_ioda_pe *pe)return0;}-#ifdef CONFIG_PCI_IOV-staticintpnv_pci_vf_resource_shift(structpci_dev*dev,intoffset)-{-structpci_dn*pdn=pci_get_pdn(dev);-inti;-structresource*res,res2;-resource_size_tsize;-u16num_vfs;--if(!dev->is_physfn)-return-EINVAL;--/*-*"offset"isinVFs.TheM64windowsaresizedsothatwhenthey-*aresegmented,eachsegmentisthesamesizeastheIOVBAR.-*EachsegmentisinaseparatePE,andthehighorderbitsofthe-*addressarethePEnumber.Therefore,eachVF'sBARisina-*separatePE,andchangingtheIOVBARstartaddresschangesthe-*rangeofPEstheVFsarein.-*/-num_vfs=pdn->num_vfs;-for(i=0;i<PCI_SRIOV_NUM_BARS;i++){-res=&dev->resource[i+PCI_IOV_RESOURCES];-if(!res->flags||!res->parent)-continue;--/*-*TheactualIOVBARrangeisdeterminedbythestartaddress-*andtheactualsizefornum_vfsVFsBAR.Thischeckisto-*makesurethataftershifting,therangewillnotoverlap-*withanotherdevice.-*/-size=pci_iov_resource_size(dev,i+PCI_IOV_RESOURCES);-res2.flags=res->flags;-res2.start=res->start+(size*offset);-res2.end=res2.start+(size*num_vfs)-1;--if(res2.end>res->end){-dev_err(&dev->dev,"VF BAR%d: %pR would extend past %pR (trying to enable %d VFs shifted by %d)\n",-i,&res2,res,num_vfs,offset);-return-EBUSY;-}-}--/*-*SinceM64BARsharessegmentsamongallpossible256PEs,-*wehavetoshiftthebeginningofPFIOVBARtomakeitstartfrom-*thesegmentwhichbelongstothePEnumberassignedtothefirstVF.-*Thiscreatesa"hole"inthe/proc/iomemwhichcouldbeusedfor-*allocatingotherresourcessowereservethisareabelowand-*releasewhenIOVisreleased.-*/-for(i=0;i<PCI_SRIOV_NUM_BARS;i++){-res=&dev->resource[i+PCI_IOV_RESOURCES];-if(!res->flags||!res->parent)-continue;--size=pci_iov_resource_size(dev,i+PCI_IOV_RESOURCES);-res2=*res;-res->start+=size*offset;--dev_info(&dev->dev,"VF BAR%d: %pR shifted to %pR (%sabling %d VFs shifted by %d)\n",-i,&res2,res,(offset>0)?"En":"Dis",-num_vfs,offset);--if(offset<0){-devm_release_resource(&dev->dev,&pdn->holes[i]);-memset(&pdn->holes[i],0,sizeof(pdn->holes[i]));-}--pci_update_resource(dev,i+PCI_IOV_RESOURCES);--if(offset>0){-pdn->holes[i].start=res2.start;-pdn->holes[i].end=res2.start+size*offset-1;-pdn->holes[i].flags=IORESOURCE_BUS;-pdn->holes[i].name="pnv_iov_reserved";-devm_request_resource(&dev->dev,res->parent,-&pdn->holes[i]);-}-}-return0;-}-#endif /* CONFIG_PCI_IOV */-staticstructpnv_ioda_pe*pnv_ioda_setup_dev_PE(structpci_dev*dev){structpnv_phb*phb=pci_bus_to_pnvhb(dev->bus);
@@ -1294,406 +1189,9 @@ static void pnv_pci_ioda_setup_nvlink(void)#endif}-#ifdef CONFIG_PCI_IOV-staticintpnv_pci_vf_release_m64(structpci_dev*pdev,u16num_vfs)-{-structpnv_phb*phb;-structpci_dn*pdn;-inti,j;-intm64_bars;--phb=pci_bus_to_pnvhb(pdev->bus);-pdn=pci_get_pdn(pdev);--if(pdn->m64_single_mode)-m64_bars=num_vfs;-else-m64_bars=1;--for(i=0;i<PCI_SRIOV_NUM_BARS;i++)-for(j=0;j<m64_bars;j++){-if(pdn->m64_map[j][i]==IODA_INVALID_M64)-continue;-opal_pci_phb_mmio_enable(phb->opal_id,-OPAL_M64_WINDOW_TYPE,pdn->m64_map[j][i],0);-clear_bit(pdn->m64_map[j][i],&phb->ioda.m64_bar_alloc);-pdn->m64_map[j][i]=IODA_INVALID_M64;-}--kfree(pdn->m64_map);-return0;-}--staticintpnv_pci_vf_assign_m64(structpci_dev*pdev,u16num_vfs)-{-structpnv_phb*phb;-structpci_dn*pdn;-unsignedintwin;-structresource*res;-inti,j;-int64_trc;-inttotal_vfs;-resource_size_tsize,start;-intpe_num;-intm64_bars;--phb=pci_bus_to_pnvhb(pdev->bus);-pdn=pci_get_pdn(pdev);-total_vfs=pci_sriov_get_totalvfs(pdev);--if(pdn->m64_single_mode)-m64_bars=num_vfs;-else-m64_bars=1;--pdn->m64_map=kmalloc_array(m64_bars,-sizeof(*pdn->m64_map),-GFP_KERNEL);-if(!pdn->m64_map)-return-ENOMEM;-/* Initialize the m64_map to IODA_INVALID_M64 */-for(i=0;i<m64_bars;i++)-for(j=0;j<PCI_SRIOV_NUM_BARS;j++)-pdn->m64_map[i][j]=IODA_INVALID_M64;---for(i=0;i<PCI_SRIOV_NUM_BARS;i++){-res=&pdev->resource[i+PCI_IOV_RESOURCES];-if(!res->flags||!res->parent)-continue;--for(j=0;j<m64_bars;j++){-do{-win=find_next_zero_bit(&phb->ioda.m64_bar_alloc,-phb->ioda.m64_bar_idx+1,0);--if(win>=phb->ioda.m64_bar_idx+1)-gotom64_failed;-}while(test_and_set_bit(win,&phb->ioda.m64_bar_alloc));--pdn->m64_map[j][i]=win;--if(pdn->m64_single_mode){-size=pci_iov_resource_size(pdev,-PCI_IOV_RESOURCES+i);-start=res->start+size*j;-}else{-size=resource_size(res);-start=res->start;-}--/* Map the M64 here */-if(pdn->m64_single_mode){-pe_num=pdn->pe_num_map[j];-rc=opal_pci_map_pe_mmio_window(phb->opal_id,-pe_num,OPAL_M64_WINDOW_TYPE,-pdn->m64_map[j][i],0);-}--rc=opal_pci_set_phb_mem_window(phb->opal_id,-OPAL_M64_WINDOW_TYPE,-pdn->m64_map[j][i],-start,-0,/* unused */-size);---if(rc!=OPAL_SUCCESS){-dev_err(&pdev->dev,"Failed to map M64 window #%d: %lld\n",-win,rc);-gotom64_failed;-}--if(pdn->m64_single_mode)-rc=opal_pci_phb_mmio_enable(phb->opal_id,-OPAL_M64_WINDOW_TYPE,pdn->m64_map[j][i],2);-else-rc=opal_pci_phb_mmio_enable(phb->opal_id,-OPAL_M64_WINDOW_TYPE,pdn->m64_map[j][i],1);--if(rc!=OPAL_SUCCESS){-dev_err(&pdev->dev,"Failed to enable M64 window #%d: %llx\n",-win,rc);-gotom64_failed;-}-}-}-return0;--m64_failed:-pnv_pci_vf_release_m64(pdev,num_vfs);-return-EBUSY;-}--staticvoidpnv_pci_ioda2_release_pe_dma(structpnv_ioda_pe*pe);--staticvoidpnv_ioda_release_vf_PE(structpci_dev*pdev)-{-structpnv_phb*phb;-structpnv_ioda_pe*pe,*pe_n;-structpci_dn*pdn;--phb=pci_bus_to_pnvhb(pdev->bus);-pdn=pci_get_pdn(pdev);--if(!pdev->is_physfn)-return;--/* FIXME: Use pnv_ioda_release_pe()? */-list_for_each_entry_safe(pe,pe_n,&phb->ioda.pe_list,list){-if(pe->parent_dev!=pdev)-continue;--pnv_pci_ioda2_release_pe_dma(pe);--/* Remove from list */-mutex_lock(&phb->ioda.pe_list_mutex);-list_del(&pe->list);-mutex_unlock(&phb->ioda.pe_list_mutex);--pnv_ioda_deconfigure_pe(phb,pe);--pnv_ioda_free_pe(pe);-}-}--staticvoidpnv_pci_sriov_disable(structpci_dev*pdev)-{-structpnv_phb*phb;-structpnv_ioda_pe*pe;-structpci_dn*pdn;-u16num_vfs,i;--phb=pci_bus_to_pnvhb(pdev->bus);-pdn=pci_get_pdn(pdev);-num_vfs=pdn->num_vfs;--/* Release VF PEs */-pnv_ioda_release_vf_PE(pdev);--if(phb->type==PNV_PHB_IODA2){-if(!pdn->m64_single_mode)-pnv_pci_vf_resource_shift(pdev,-*pdn->pe_num_map);--/* Release M64 windows */-pnv_pci_vf_release_m64(pdev,num_vfs);--/* Release PE numbers */-if(pdn->m64_single_mode){-for(i=0;i<num_vfs;i++){-if(pdn->pe_num_map[i]==IODA_INVALID_PE)-continue;--pe=&phb->ioda.pe_array[pdn->pe_num_map[i]];-pnv_ioda_free_pe(pe);-}-}else-bitmap_clear(phb->ioda.pe_alloc,*pdn->pe_num_map,num_vfs);-/* Releasing pe_num_map */-kfree(pdn->pe_num_map);-}-}--staticvoidpnv_pci_ioda2_setup_dma_pe(structpnv_phb*phb,-structpnv_ioda_pe*pe);-staticvoidpnv_ioda_setup_vf_PE(structpci_dev*pdev,u16num_vfs)-{-structpnv_phb*phb;-structpnv_ioda_pe*pe;-intpe_num;-u16vf_index;-structpci_dn*pdn;--phb=pci_bus_to_pnvhb(pdev->bus);-pdn=pci_get_pdn(pdev);--if(!pdev->is_physfn)-return;--/* Reserve PE for each VF */-for(vf_index=0;vf_index<num_vfs;vf_index++){-intvf_devfn=pci_iov_virtfn_devfn(pdev,vf_index);-intvf_bus=pci_iov_virtfn_bus(pdev,vf_index);-structpci_dn*vf_pdn;--if(pdn->m64_single_mode)-pe_num=pdn->pe_num_map[vf_index];-else-pe_num=*pdn->pe_num_map+vf_index;--pe=&phb->ioda.pe_array[pe_num];-pe->pe_number=pe_num;-pe->phb=phb;-pe->flags=PNV_IODA_PE_VF;-pe->pbus=NULL;-pe->parent_dev=pdev;-pe->mve_number=-1;-pe->rid=(vf_bus<<8)|vf_devfn;--pe_info(pe,"VF %04d:%02d:%02d.%d associated with PE#%x\n",-pci_domain_nr(pdev->bus),pdev->bus->number,-PCI_SLOT(vf_devfn),PCI_FUNC(vf_devfn),pe_num);--if(pnv_ioda_configure_pe(phb,pe)){-/* XXX What do we do here ? */-pnv_ioda_free_pe(pe);-pe->pdev=NULL;-continue;-}--/* Put PE to the list */-mutex_lock(&phb->ioda.pe_list_mutex);-list_add_tail(&pe->list,&phb->ioda.pe_list);-mutex_unlock(&phb->ioda.pe_list_mutex);--/* associate this pe to it's pdn */-list_for_each_entry(vf_pdn,&pdn->parent->child_list,list){-if(vf_pdn->busno==vf_bus&&-vf_pdn->devfn==vf_devfn){-vf_pdn->pe_number=pe_num;-break;-}-}--pnv_pci_ioda2_setup_dma_pe(phb,pe);-}-}--staticintpnv_pci_sriov_enable(structpci_dev*pdev,u16num_vfs)-{-structpnv_phb*phb;-structpnv_ioda_pe*pe;-structpci_dn*pdn;-intret;-u16i;--phb=pci_bus_to_pnvhb(pdev->bus);-pdn=pci_get_pdn(pdev);--if(phb->type==PNV_PHB_IODA2){-if(!pdn->vfs_expanded){-dev_info(&pdev->dev,"don't support this SRIOV device"-" with non 64bit-prefetchable IOV BAR\n");-return-ENOSPC;-}--/*-*WhenM64BARsfunctionsinSinglePEmode,thenumberofVFs-*couldbeenabledmustbelessthanthenumberofM64BARs.-*/-if(pdn->m64_single_mode&&num_vfs>phb->ioda.m64_bar_idx){-dev_info(&pdev->dev,"Not enough M64 BAR for VFs\n");-return-EBUSY;-}--/* Allocating pe_num_map */-if(pdn->m64_single_mode)-pdn->pe_num_map=kmalloc_array(num_vfs,-sizeof(*pdn->pe_num_map),-GFP_KERNEL);-else-pdn->pe_num_map=kmalloc(sizeof(*pdn->pe_num_map),GFP_KERNEL);--if(!pdn->pe_num_map)-return-ENOMEM;--if(pdn->m64_single_mode)-for(i=0;i<num_vfs;i++)-pdn->pe_num_map[i]=IODA_INVALID_PE;--/* Calculate available PE for required VFs */-if(pdn->m64_single_mode){-for(i=0;i<num_vfs;i++){-pe=pnv_ioda_alloc_pe(phb);-if(!pe){-ret=-EBUSY;-gotom64_failed;-}--pdn->pe_num_map[i]=pe->pe_number;-}-}else{-mutex_lock(&phb->ioda.pe_alloc_mutex);-*pdn->pe_num_map=bitmap_find_next_zero_area(-phb->ioda.pe_alloc,phb->ioda.total_pe_num,-0,num_vfs,0);-if(*pdn->pe_num_map>=phb->ioda.total_pe_num){-mutex_unlock(&phb->ioda.pe_alloc_mutex);-dev_info(&pdev->dev,"Failed to enable VF%d\n",num_vfs);-kfree(pdn->pe_num_map);-return-EBUSY;-}-bitmap_set(phb->ioda.pe_alloc,*pdn->pe_num_map,num_vfs);-mutex_unlock(&phb->ioda.pe_alloc_mutex);-}-pdn->num_vfs=num_vfs;--/* Assign M64 window accordingly */-ret=pnv_pci_vf_assign_m64(pdev,num_vfs);-if(ret){-dev_info(&pdev->dev,"Not enough M64 window resources\n");-gotom64_failed;-}--/*-*WhenusingoneM64BARtomaponeIOVBAR,weneedtoshift-*theIOVBARaccordingtothePE#allocatedtotheVFs.-*Otherwise,thePE#fortheVFwillconflictwithothers.-*/-if(!pdn->m64_single_mode){-ret=pnv_pci_vf_resource_shift(pdev,*pdn->pe_num_map);-if(ret)-gotom64_failed;-}-}--/* Setup VF PEs */-pnv_ioda_setup_vf_PE(pdev,num_vfs);--return0;--m64_failed:-if(pdn->m64_single_mode){-for(i=0;i<num_vfs;i++){-if(pdn->pe_num_map[i]==IODA_INVALID_PE)-continue;--pe=&phb->ioda.pe_array[pdn->pe_num_map[i]];-pnv_ioda_free_pe(pe);-}-}else-bitmap_clear(phb->ioda.pe_alloc,*pdn->pe_num_map,num_vfs);--/* Releasing pe_num_map */-kfree(pdn->pe_num_map);--returnret;-}--staticintpnv_pcibios_sriov_disable(structpci_dev*pdev)-{-pnv_pci_sriov_disable(pdev);--/* Release PCI data */-remove_sriov_vf_pdns(pdev);-return0;-}--staticintpnv_pcibios_sriov_enable(structpci_dev*pdev,u16num_vfs)-{-/* Allocate PCI data */-add_sriov_vf_pdns(pdev);--returnpnv_pci_sriov_enable(pdev,num_vfs);-}-#endif /* CONFIG_PCI_IOV */-staticvoidpnv_pci_ioda1_setup_dma_pe(structpnv_phb*phb,structpnv_ioda_pe*pe);-staticvoidpnv_pci_ioda2_setup_dma_pe(structpnv_phb*phb,-structpnv_ioda_pe*pe);-staticvoidpnv_pci_ioda_dma_dev_setup(structpci_dev*pdev){structpnv_phb*phb=pci_bus_to_pnvhb(pdev->bus);
@@ -2737,117 +2235,6 @@ static void pnv_pci_init_ioda_msis(struct pnv_phb *phb)count,phb->msi_base);}-#ifdef CONFIG_PCI_IOV-staticvoidpnv_pci_ioda_fixup_iov_resources(structpci_dev*pdev)-{-structpnv_phb*phb=pci_bus_to_pnvhb(pdev->bus);-constresource_size_tgate=phb->ioda.m64_segsize>>2;-structresource*res;-inti;-resource_size_tsize,total_vf_bar_sz;-structpci_dn*pdn;-intmul,total_vfs;--pdn=pci_get_pdn(pdev);-pdn->vfs_expanded=0;-pdn->m64_single_mode=false;--total_vfs=pci_sriov_get_totalvfs(pdev);-mul=phb->ioda.total_pe_num;-total_vf_bar_sz=0;--for(i=0;i<PCI_SRIOV_NUM_BARS;i++){-res=&pdev->resource[i+PCI_IOV_RESOURCES];-if(!res->flags||res->parent)-continue;-if(!pnv_pci_is_m64_flags(res->flags)){-dev_warn(&pdev->dev,"Don't support SR-IOV with"-" non M64 VF BAR%d: %pR. \n",-i,res);-gototruncate_iov;-}--total_vf_bar_sz+=pci_iov_resource_size(pdev,-i+PCI_IOV_RESOURCES);--/*-*IfbiggerthanquarterofM64segmentsize,justroundup-*poweroftwo.-*-*Generally,oneM64BARmapsoneIOVBAR.Toavoidconflict-*withotherdevices,IOVBARsizeisexpandedtobe-*(total_pe*VF_BAR_size).WhenVF_BAR_sizeishalfofM64-*segmentsize,theexpandedsizewouldequaltohalfofthe-*wholeM64spacesize,whichwillexhausttheM64Spaceand-*limitthesystemflexibility.Thisisadesigndecisionto-*settheboundarytoquarteroftheM64segmentsize.-*/-if(total_vf_bar_sz>gate){-mul=roundup_pow_of_two(total_vfs);-dev_info(&pdev->dev,-"VF BAR Total IOV size %llx > %llx, roundup to %d VFs\n",-total_vf_bar_sz,gate,mul);-pdn->m64_single_mode=true;-break;-}-}--for(i=0;i<PCI_SRIOV_NUM_BARS;i++){-res=&pdev->resource[i+PCI_IOV_RESOURCES];-if(!res->flags||res->parent)-continue;--size=pci_iov_resource_size(pdev,i+PCI_IOV_RESOURCES);-/*-*OnPHB3,theminimumsizealignmentofM64BARinsingle-*modeis32MB.-*/-if(pdn->m64_single_mode&&(size<SZ_32M))-gototruncate_iov;-dev_dbg(&pdev->dev," Fixing VF BAR%d: %pR to\n",i,res);-res->end=res->start+size*mul-1;-dev_dbg(&pdev->dev," %pR\n",res);-dev_info(&pdev->dev,"VF BAR%d: %pR (expanded to %d VFs for PE alignment)",-i,res,mul);-}-pdn->vfs_expanded=mul;--return;--truncate_iov:-/* To save MMIO space, IOV BAR is truncated. */-for(i=0;i<PCI_SRIOV_NUM_BARS;i++){-res=&pdev->resource[i+PCI_IOV_RESOURCES];-res->flags=0;-res->end=res->start-1;-}-}--staticvoidpnv_pci_ioda_fixup_iov(structpci_dev*pdev)-{-if(WARN_ON(pci_dev_is_added(pdev)))-return;--if(pdev->is_virtfn){-structpnv_ioda_pe*pe=pnv_ioda_get_pe(pdev);--/*-*VFPEsaresingle-devicePEssotheirpdevpointerneedsto-*beset.Thepdevdoesn'texistwhenthePEisallocated(in-*(pcibios_sriov_enable())sowefixituphere.-*/-pe->pdev=pdev;-WARN_ON(!(pe->flags&PNV_IODA_PE_VF));-}elseif(pdev->is_physfn){-/*-*ForPFsadjusttheirallocatedIOVresourcestomatchwhat-*thePHBcansupportusingit'sM64BARtable.-*/-pnv_pci_ioda_fixup_iov_resources(pdev);-}-}-#endif /* CONFIG_PCI_IOV */-staticvoidpnv_ioda_setup_pe_res(structpnv_ioda_pe*pe,structresource*res){
@@ -3192,41 +2579,6 @@ static resource_size_t pnv_pci_default_alignment(void)returnPAGE_SIZE;}-#ifdef CONFIG_PCI_IOV-staticresource_size_tpnv_pci_iov_resource_alignment(structpci_dev*pdev,-intresno)-{-structpnv_phb*phb=pci_bus_to_pnvhb(pdev->bus);-structpci_dn*pdn=pci_get_pdn(pdev);-resource_size_talign;--/*-*OnPowerNVplatform,IOVBARismappedbyM64BARtoenablethe-*SR-IOV.Whilefromhardwareperspective,therangemappedbyM64-*BARshouldbesizealigned.-*-*WhenIOVBARismappedwithM64BARinSinglePEmode,theextra-*powernv-specifichardwarerestrictionisgone.Butifjustusethe-*VFBARsizeasthealignment,PFBAR/VFBARmaybeallocatedwith-*inonesegmentofM64#15,whichintroducesthePEconflictbetween-*PFandVF.Basedonthis,theminimumalignmentofanIOVBARis-*m64_segsize.-*-*ThisfunctionreturnsthetotalIOVBARsizeifM64BARisin-*SharedPEmodeorjustVFBARsizeifnot.-*IftheM64BARisinSinglePEmode,returntheVFBARsizeor-*M64segmentsizeifIOVBARsizeisless.-*/-align=pci_iov_resource_size(pdev,resno);-if(!pdn->vfs_expanded)-returnalign;-if(pdn->m64_single_mode)-returnmax(align,(resource_size_t)phb->ioda.m64_segsize);--returnpdn->vfs_expanded*align;-}-#endif /* CONFIG_PCI_IOV */-/* Prevent enabling devices for which we couldn't properly*assignaPE*/
@@ -3436,12 +2788,23 @@ static void pnv_pci_release_device(struct pci_dev *pdev)structpci_dn*pdn=pci_get_pdn(pdev);structpnv_ioda_pe*pe;+/* The VF PE state is torn down when sriov_disable() is called */if(pdev->is_virtfn)return;if(!pdn||pdn->pe_number==IODA_INVALID_PE)return;+#ifdef CONFIG_PCI_IOV+/*+*FIXME:Trymovethistosriov_disable().It'sheresinceweallocate+*theiovstateatprobetimesinceweneedtofiddlewiththeIOV+*resources.+*/+if(pdev->is_physfn)+kfree(pdev->dev.archdata.iov_data);+#endif+/**PCIhotplugcanhappenaspartofEEHerrorrecovery.The@pdn*isn'tremovedandaddedafterwardsinthisscenario.Weshould
@@ -0,0 +1,642 @@+// SPDX-License-Identifier: GPL-2.0++#include<linux/kernel.h>+#include<linux/ioport.h>+#include<linux/bitmap.h>+#include<linux/pci.h>++#include<asm/opal.h>++#include"pci.h"++/* for pci_dev_is_added() */+#include"../../../../drivers/pci/pci.h"+++staticvoidpnv_pci_ioda_fixup_iov_resources(structpci_dev*pdev)+{+structpnv_phb*phb=pci_bus_to_pnvhb(pdev->bus);+constresource_size_tgate=phb->ioda.m64_segsize>>2;+structresource*res;+inti;+resource_size_tsize,total_vf_bar_sz;+structpnv_iov_data*iov;+intmul,total_vfs;++iov=kzalloc(sizeof(*iov),GFP_KERNEL);+if(!iov)+gototruncate_iov;+pdev->dev.archdata.iov_data=iov;++total_vfs=pci_sriov_get_totalvfs(pdev);+mul=phb->ioda.total_pe_num;+total_vf_bar_sz=0;++for(i=0;i<PCI_SRIOV_NUM_BARS;i++){+res=&pdev->resource[i+PCI_IOV_RESOURCES];+if(!res->flags||res->parent)+continue;+if(!pnv_pci_is_m64_flags(res->flags)){+dev_warn(&pdev->dev,"Don't support SR-IOV with"+" non M64 VF BAR%d: %pR. \n",+i,res);+gototruncate_iov;+}++total_vf_bar_sz+=pci_iov_resource_size(pdev,+i+PCI_IOV_RESOURCES);++/*+*IfbiggerthanquarterofM64segmentsize,justroundup+*poweroftwo.+*+*Generally,oneM64BARmapsoneIOVBAR.Toavoidconflict+*withotherdevices,IOVBARsizeisexpandedtobe+*(total_pe*VF_BAR_size).WhenVF_BAR_sizeishalfofM64+*segmentsize,theexpandedsizewouldequaltohalfofthe+*wholeM64spacesize,whichwillexhausttheM64Spaceand+*limitthesystemflexibility.Thisisadesigndecisionto+*settheboundarytoquarteroftheM64segmentsize.+*/+if(total_vf_bar_sz>gate){+mul=roundup_pow_of_two(total_vfs);+dev_info(&pdev->dev,+"VF BAR Total IOV size %llx > %llx, roundup to %d VFs\n",+total_vf_bar_sz,gate,mul);+iov->m64_single_mode=true;+break;+}+}++for(i=0;i<PCI_SRIOV_NUM_BARS;i++){+res=&pdev->resource[i+PCI_IOV_RESOURCES];+if(!res->flags||res->parent)+continue;++size=pci_iov_resource_size(pdev,i+PCI_IOV_RESOURCES);+/*+*OnPHB3,theminimumsizealignmentofM64BARinsingle+*modeis32MB.+*/+if(iov->m64_single_mode&&(size<SZ_32M))+gototruncate_iov;+dev_dbg(&pdev->dev," Fixing VF BAR%d: %pR to\n",i,res);+res->end=res->start+size*mul-1;+dev_dbg(&pdev->dev," %pR\n",res);+dev_info(&pdev->dev,"VF BAR%d: %pR (expanded to %d VFs for PE alignment)",+i,res,mul);+}+iov->vfs_expanded=mul;++return;++truncate_iov:+/* To save MMIO space, IOV BAR is truncated. */+for(i=0;i<PCI_SRIOV_NUM_BARS;i++){+res=&pdev->resource[i+PCI_IOV_RESOURCES];+res->flags=0;+res->end=res->start-1;+}++pdev->dev.archdata.iov_data=NULL;+kfree(iov);+}++voidpnv_pci_ioda_fixup_iov(structpci_dev*pdev)+{+if(WARN_ON(pci_dev_is_added(pdev)))+return;++if(pdev->is_virtfn){+structpnv_ioda_pe*pe=pnv_ioda_get_pe(pdev);++/*+*VFPEsaresingle-devicePEssotheirpdevpointerneedsto+*beset.Thepdevdoesn'texistwhenthePEisallocated(in+*(pcibios_sriov_enable())sowefixituphere.+*/+pe->pdev=pdev;+WARN_ON(!(pe->flags&PNV_IODA_PE_VF));+}elseif(pdev->is_physfn){+/*+*ForPFsadjusttheirallocatedIOVresourcestomatchwhat+*thePHBcansupportusingit'sM64BARtable.+*/+pnv_pci_ioda_fixup_iov_resources(pdev);+}+}++resource_size_tpnv_pci_iov_resource_alignment(structpci_dev*pdev,+intresno)+{+structpnv_phb*phb=pci_bus_to_pnvhb(pdev->bus);+structpnv_iov_data*iov=pnv_iov_get(pdev);+resource_size_talign;++/*+*OnPowerNVplatform,IOVBARismappedbyM64BARtoenablethe+*SR-IOV.Whilefromhardwareperspective,therangemappedbyM64+*BARshouldbesizealigned.+*+*WhenIOVBARismappedwithM64BARinSinglePEmode,theextra+*powernv-specifichardwarerestrictionisgone.Butifjustusethe+*VFBARsizeasthealignment,PFBAR/VFBARmaybeallocatedwith+*inonesegmentofM64#15,whichintroducesthePEconflictbetween+*PFandVF.Basedonthis,theminimumalignmentofanIOVBARis+*m64_segsize.+*+*ThisfunctionreturnsthetotalIOVBARsizeifM64BARisin+*SharedPEmodeorjustVFBARsizeifnot.+*IftheM64BARisinSinglePEmode,returntheVFBARsizeor+*M64segmentsizeifIOVBARsizeisless.+*/+align=pci_iov_resource_size(pdev,resno);++/*+*iovcanbenullifwehaveanSR-IOVdevicewithIOVBARthatcan't+*beplacedinthem64space(i.e.TheBARis32bitornon-prefetch).+*Inthatcasewedon'tallowVFstobeenabledsojustreturnthe+*defaultalignment.+*/+if(!iov)+returnalign;+if(!iov->vfs_expanded)+returnalign;+if(iov->m64_single_mode)+returnmax(align,(resource_size_t)phb->ioda.m64_segsize);++returniov->vfs_expanded*align;+}++staticintpnv_pci_vf_release_m64(structpci_dev*pdev,u16num_vfs)+{+structpnv_iov_data*iov;+structpnv_phb*phb;+inti,j;+intm64_bars;++phb=pci_bus_to_pnvhb(pdev->bus);+iov=pnv_iov_get(pdev);++if(iov->m64_single_mode)+m64_bars=num_vfs;+else+m64_bars=1;++for(i=0;i<PCI_SRIOV_NUM_BARS;i++)+for(j=0;j<m64_bars;j++){+if(iov->m64_map[j][i]==IODA_INVALID_M64)+continue;+opal_pci_phb_mmio_enable(phb->opal_id,+OPAL_M64_WINDOW_TYPE,iov->m64_map[j][i],0);+clear_bit(iov->m64_map[j][i],&phb->ioda.m64_bar_alloc);+iov->m64_map[j][i]=IODA_INVALID_M64;+}++kfree(iov->m64_map);+return0;+}++staticintpnv_pci_vf_assign_m64(structpci_dev*pdev,u16num_vfs)+{+structpnv_iov_data*iov;+structpnv_phb*phb;+unsignedintwin;+structresource*res;+inti,j;+int64_trc;+inttotal_vfs;+resource_size_tsize,start;+intpe_num;+intm64_bars;++phb=pci_bus_to_pnvhb(pdev->bus);+iov=pnv_iov_get(pdev);+total_vfs=pci_sriov_get_totalvfs(pdev);++if(iov->m64_single_mode)+m64_bars=num_vfs;+else+m64_bars=1;++iov->m64_map=kmalloc_array(m64_bars,+sizeof(*iov->m64_map),+GFP_KERNEL);+if(!iov->m64_map)+return-ENOMEM;+/* Initialize the m64_map to IODA_INVALID_M64 */+for(i=0;i<m64_bars;i++)+for(j=0;j<PCI_SRIOV_NUM_BARS;j++)+iov->m64_map[i][j]=IODA_INVALID_M64;+++for(i=0;i<PCI_SRIOV_NUM_BARS;i++){+res=&pdev->resource[i+PCI_IOV_RESOURCES];+if(!res->flags||!res->parent)+continue;++for(j=0;j<m64_bars;j++){+do{+win=find_next_zero_bit(&phb->ioda.m64_bar_alloc,+phb->ioda.m64_bar_idx+1,0);++if(win>=phb->ioda.m64_bar_idx+1)+gotom64_failed;+}while(test_and_set_bit(win,&phb->ioda.m64_bar_alloc));++iov->m64_map[j][i]=win;++if(iov->m64_single_mode){+size=pci_iov_resource_size(pdev,+PCI_IOV_RESOURCES+i);+start=res->start+size*j;+}else{+size=resource_size(res);+start=res->start;+}++/* Map the M64 here */+if(iov->m64_single_mode){+pe_num=iov->pe_num_map[j];+rc=opal_pci_map_pe_mmio_window(phb->opal_id,+pe_num,OPAL_M64_WINDOW_TYPE,+iov->m64_map[j][i],0);+}++rc=opal_pci_set_phb_mem_window(phb->opal_id,+OPAL_M64_WINDOW_TYPE,+iov->m64_map[j][i],+start,+0,/* unused */+size);+++if(rc!=OPAL_SUCCESS){+dev_err(&pdev->dev,"Failed to map M64 window #%d: %lld\n",+win,rc);+gotom64_failed;+}++if(iov->m64_single_mode)+rc=opal_pci_phb_mmio_enable(phb->opal_id,+OPAL_M64_WINDOW_TYPE,iov->m64_map[j][i],2);+else+rc=opal_pci_phb_mmio_enable(phb->opal_id,+OPAL_M64_WINDOW_TYPE,iov->m64_map[j][i],1);++if(rc!=OPAL_SUCCESS){+dev_err(&pdev->dev,"Failed to enable M64 window #%d: %llx\n",+win,rc);+gotom64_failed;+}+}+}+return0;++m64_failed:+pnv_pci_vf_release_m64(pdev,num_vfs);+return-EBUSY;+}++staticvoidpnv_ioda_release_vf_PE(structpci_dev*pdev)+{+structpnv_phb*phb;+structpnv_ioda_pe*pe,*pe_n;++phb=pci_bus_to_pnvhb(pdev->bus);++if(!pdev->is_physfn)+return;++/* FIXME: Use pnv_ioda_release_pe()? */+list_for_each_entry_safe(pe,pe_n,&phb->ioda.pe_list,list){+if(pe->parent_dev!=pdev)+continue;++pnv_pci_ioda2_release_pe_dma(pe);++/* Remove from list */+mutex_lock(&phb->ioda.pe_list_mutex);+list_del(&pe->list);+mutex_unlock(&phb->ioda.pe_list_mutex);++pnv_ioda_deconfigure_pe(phb,pe);++pnv_ioda_free_pe(pe);+}+}++staticintpnv_pci_vf_resource_shift(structpci_dev*dev,intoffset)+{+structresource*res,res2;+structpnv_iov_data*iov;+resource_size_tsize;+u16num_vfs;+inti;++if(!dev->is_physfn)+return-EINVAL;+iov=pnv_iov_get(dev);++/*+*"offset"isinVFs.TheM64windowsaresizedsothatwhenthey+*aresegmented,eachsegmentisthesamesizeastheIOVBAR.+*EachsegmentisinaseparatePE,andthehighorderbitsofthe+*addressarethePEnumber.Therefore,eachVF'sBARisina+*separatePE,andchangingtheIOVBARstartaddresschangesthe+*rangeofPEstheVFsarein.+*/+num_vfs=iov->num_vfs;+for(i=0;i<PCI_SRIOV_NUM_BARS;i++){+res=&dev->resource[i+PCI_IOV_RESOURCES];+if(!res->flags||!res->parent)+continue;++/*+*TheactualIOVBARrangeisdeterminedbythestartaddress+*andtheactualsizefornum_vfsVFsBAR.Thischeckisto+*makesurethataftershifting,therangewillnotoverlap+*withanotherdevice.+*/+size=pci_iov_resource_size(dev,i+PCI_IOV_RESOURCES);+res2.flags=res->flags;+res2.start=res->start+(size*offset);+res2.end=res2.start+(size*num_vfs)-1;++if(res2.end>res->end){+dev_err(&dev->dev,"VF BAR%d: %pR would extend past %pR (trying to enable %d VFs shifted by %d)\n",+i,&res2,res,num_vfs,offset);+return-EBUSY;+}+}++/*+*SinceM64BARsharessegmentsamongallpossible256PEs,+*wehavetoshiftthebeginningofPFIOVBARtomakeitstartfrom+*thesegmentwhichbelongstothePEnumberassignedtothefirstVF.+*Thiscreatesa"hole"inthe/proc/iomemwhichcouldbeusedfor+*allocatingotherresourcessowereservethisareabelowand+*releasewhenIOVisreleased.+*/+for(i=0;i<PCI_SRIOV_NUM_BARS;i++){+res=&dev->resource[i+PCI_IOV_RESOURCES];+if(!res->flags||!res->parent)+continue;++size=pci_iov_resource_size(dev,i+PCI_IOV_RESOURCES);+res2=*res;+res->start+=size*offset;++dev_info(&dev->dev,"VF BAR%d: %pR shifted to %pR (%sabling %d VFs shifted by %d)\n",+i,&res2,res,(offset>0)?"En":"Dis",+num_vfs,offset);++if(offset<0){+devm_release_resource(&dev->dev,&iov->holes[i]);+memset(&iov->holes[i],0,sizeof(iov->holes[i]));+}++pci_update_resource(dev,i+PCI_IOV_RESOURCES);++if(offset>0){+iov->holes[i].start=res2.start;+iov->holes[i].end=res2.start+size*offset-1;+iov->holes[i].flags=IORESOURCE_BUS;+iov->holes[i].name="pnv_iov_reserved";+devm_request_resource(&dev->dev,res->parent,+&iov->holes[i]);+}+}+return0;+}++staticvoidpnv_pci_sriov_disable(structpci_dev*pdev)+{+structpnv_phb*phb;+structpnv_ioda_pe*pe;+structpnv_iov_data*iov;+u16num_vfs,i;++phb=pci_bus_to_pnvhb(pdev->bus);+iov=pnv_iov_get(pdev);+num_vfs=iov->num_vfs;++/* Release VF PEs */+pnv_ioda_release_vf_PE(pdev);++if(phb->type==PNV_PHB_IODA2){+if(!iov->m64_single_mode)+pnv_pci_vf_resource_shift(pdev,-*iov->pe_num_map);++/* Release M64 windows */+pnv_pci_vf_release_m64(pdev,num_vfs);++/* Release PE numbers */+if(iov->m64_single_mode){+for(i=0;i<num_vfs;i++){+if(iov->pe_num_map[i]==IODA_INVALID_PE)+continue;++pe=&phb->ioda.pe_array[iov->pe_num_map[i]];+pnv_ioda_free_pe(pe);+}+}else+bitmap_clear(phb->ioda.pe_alloc,*iov->pe_num_map,num_vfs);+/* Releasing pe_num_map */+kfree(iov->pe_num_map);+}+}++staticvoidpnv_ioda_setup_vf_PE(structpci_dev*pdev,u16num_vfs)+{+structpnv_phb*phb;+structpnv_ioda_pe*pe;+intpe_num;+u16vf_index;+structpnv_iov_data*iov;+structpci_dn*pdn;++if(!pdev->is_physfn)+return;++phb=pci_bus_to_pnvhb(pdev->bus);+pdn=pci_get_pdn(pdev);+iov=pnv_iov_get(pdev);++/* Reserve PE for each VF */+for(vf_index=0;vf_index<num_vfs;vf_index++){+intvf_devfn=pci_iov_virtfn_devfn(pdev,vf_index);+intvf_bus=pci_iov_virtfn_bus(pdev,vf_index);+structpci_dn*vf_pdn;++if(iov->m64_single_mode)+pe_num=iov->pe_num_map[vf_index];+else+pe_num=*iov->pe_num_map+vf_index;++pe=&phb->ioda.pe_array[pe_num];+pe->pe_number=pe_num;+pe->phb=phb;+pe->flags=PNV_IODA_PE_VF;+pe->pbus=NULL;+pe->parent_dev=pdev;+pe->mve_number=-1;+pe->rid=(vf_bus<<8)|vf_devfn;++pe_info(pe,"VF %04d:%02d:%02d.%d associated with PE#%x\n",+pci_domain_nr(pdev->bus),pdev->bus->number,+PCI_SLOT(vf_devfn),PCI_FUNC(vf_devfn),pe_num);++if(pnv_ioda_configure_pe(phb,pe)){+/* XXX What do we do here ? */+pnv_ioda_free_pe(pe);+pe->pdev=NULL;+continue;+}++/* Put PE to the list */+mutex_lock(&phb->ioda.pe_list_mutex);+list_add_tail(&pe->list,&phb->ioda.pe_list);+mutex_unlock(&phb->ioda.pe_list_mutex);++/* associate this pe to it's pdn */+list_for_each_entry(vf_pdn,&pdn->parent->child_list,list){+if(vf_pdn->busno==vf_bus&&+vf_pdn->devfn==vf_devfn){+vf_pdn->pe_number=pe_num;+break;+}+}++pnv_pci_ioda2_setup_dma_pe(phb,pe);+}+}++staticintpnv_pci_sriov_enable(structpci_dev*pdev,u16num_vfs)+{+structpnv_iov_data*iov;+structpnv_phb*phb;+structpnv_ioda_pe*pe;+intret;+u16i;++phb=pci_bus_to_pnvhb(pdev->bus);+iov=pnv_iov_get(pdev);++if(phb->type==PNV_PHB_IODA2){+if(!iov->vfs_expanded){+dev_info(&pdev->dev,"don't support this SRIOV device"+" with non 64bit-prefetchable IOV BAR\n");+return-ENOSPC;+}++/*+*WhenM64BARsfunctionsinSinglePEmode,thenumberofVFs+*couldbeenabledmustbelessthanthenumberofM64BARs.+*/+if(iov->m64_single_mode&&num_vfs>phb->ioda.m64_bar_idx){+dev_info(&pdev->dev,"Not enough M64 BAR for VFs\n");+return-EBUSY;+}++/* Allocating pe_num_map */+if(iov->m64_single_mode)+iov->pe_num_map=kmalloc_array(num_vfs,+sizeof(*iov->pe_num_map),+GFP_KERNEL);+else+iov->pe_num_map=kmalloc(sizeof(*iov->pe_num_map),GFP_KERNEL);++if(!iov->pe_num_map)+return-ENOMEM;++if(iov->m64_single_mode)+for(i=0;i<num_vfs;i++)+iov->pe_num_map[i]=IODA_INVALID_PE;++/* Calculate available PE for required VFs */+if(iov->m64_single_mode){+for(i=0;i<num_vfs;i++){+pe=pnv_ioda_alloc_pe(phb);+if(!pe){+ret=-EBUSY;+gotom64_failed;+}++iov->pe_num_map[i]=pe->pe_number;+}+}else{+mutex_lock(&phb->ioda.pe_alloc_mutex);+*iov->pe_num_map=bitmap_find_next_zero_area(+phb->ioda.pe_alloc,phb->ioda.total_pe_num,+0,num_vfs,0);+if(*iov->pe_num_map>=phb->ioda.total_pe_num){+mutex_unlock(&phb->ioda.pe_alloc_mutex);+dev_info(&pdev->dev,"Failed to enable VF%d\n",num_vfs);+kfree(iov->pe_num_map);+return-EBUSY;+}+bitmap_set(phb->ioda.pe_alloc,*iov->pe_num_map,num_vfs);+mutex_unlock(&phb->ioda.pe_alloc_mutex);+}+iov->num_vfs=num_vfs;++/* Assign M64 window accordingly */+ret=pnv_pci_vf_assign_m64(pdev,num_vfs);+if(ret){+dev_info(&pdev->dev,"Not enough M64 window resources\n");+gotom64_failed;+}++/*+*WhenusingoneM64BARtomaponeIOVBAR,weneedtoshift+*theIOVBARaccordingtothePE#allocatedtotheVFs.+*Otherwise,thePE#fortheVFwillconflictwithothers.+*/+if(!iov->m64_single_mode){+ret=pnv_pci_vf_resource_shift(pdev,*iov->pe_num_map);+if(ret)+gotom64_failed;+}+}++/* Setup VF PEs */+pnv_ioda_setup_vf_PE(pdev,num_vfs);++return0;++m64_failed:+if(iov->m64_single_mode){+for(i=0;i<num_vfs;i++){+if(iov->pe_num_map[i]==IODA_INVALID_PE)+continue;++pe=&phb->ioda.pe_array[iov->pe_num_map[i]];+pnv_ioda_free_pe(pe);+}+}else+bitmap_clear(phb->ioda.pe_alloc,*iov->pe_num_map,num_vfs);++/* Releasing pe_num_map */+kfree(iov->pe_num_map);++returnret;+}++intpnv_pcibios_sriov_disable(structpci_dev*pdev)+{+pnv_pci_sriov_disable(pdev);++/* Release PCI data */+remove_sriov_vf_pdns(pdev);+return0;+}++intpnv_pcibios_sriov_enable(structpci_dev*pdev,u16num_vfs)+{+/* Allocate PCI data */+add_sriov_vf_pdns(pdev);++returnpnv_pci_sriov_enable(pdev,num_vfs);+}+
@@ -194,6 +194,80 @@ struct pnv_phb {u8*diag_data;};++/* IODA PE management */++staticinlineboolpnv_pci_is_m64(structpnv_phb*phb,structresource*r)+{+/*+*WARNING:Wecannotrelyontheresourceflags.TheLinuxPCI+*allocationcodesometimesdecidestoputa64-bitprefetchable+*BARinthe32-bitwindow,sowehavetocomparetheaddresses.+*+*Forsimplicityweonlytestresourcestart.+*/+return(r->start>=phb->ioda.m64_base&&+r->start<(phb->ioda.m64_base+phb->ioda.m64_size));+}++staticinlineboolpnv_pci_is_m64_flags(unsignedlongresource_flags)+{+unsignedlongflags=(IORESOURCE_MEM_64|IORESOURCE_PREFETCH);++return(resource_flags&flags)==flags;+}++intpnv_ioda_configure_pe(structpnv_phb*phb,structpnv_ioda_pe*pe);+intpnv_ioda_deconfigure_pe(structpnv_phb*phb,structpnv_ioda_pe*pe);++voidpnv_pci_ioda2_setup_dma_pe(structpnv_phb*phb,structpnv_ioda_pe*pe);+voidpnv_pci_ioda2_release_pe_dma(structpnv_ioda_pe*pe);++structpnv_ioda_pe*pnv_ioda_alloc_pe(structpnv_phb*phb);+voidpnv_ioda_free_pe(structpnv_ioda_pe*pe);++#ifdef CONFIG_PCI_IOV+/*+*ForSR-IOVwewanttoputeachVF'sMMIOresourceintoaseparatePE.+*ThisrequiresabitofacrobaticswiththeMMIO->PEconfiguration+*andthisstructureisusedtokeeptrackofitall.+*/+structpnv_iov_data{+/* number of VFs IOV BAR expanded. FIXME: rename this to something less bad */+u16vfs_expanded;++/* number of VFs enabled */+u16num_vfs;+unsignedint*pe_num_map;/* PE# for the first VF PE or array */++/* Did we map the VF BARs with single-PE IODA BARs? */+boolm64_single_mode;++int(*m64_map)[PCI_SRIOV_NUM_BARS];+#define IODA_INVALID_M64 (-1)++/*+*IfwemaptheSR-IOVBARswithasegmentedwindowthen+*partsofthatwindowwillbe"claimed"byotherPEs.+*+*"holes"hereisusedtoreservetheleadingportion+*ofthewindowthatisusedbyother(nonVF)PEs.+*/+structresourceholes[PCI_SRIOV_NUM_BARS];+};++staticinlinestructpnv_iov_data*pnv_iov_get(structpci_dev*pdev)+{+returnpdev->dev.archdata.iov_data;+}++voidpnv_pci_ioda_fixup_iov(structpci_dev*pdev);+resource_size_tpnv_pci_iov_resource_alignment(structpci_dev*pdev,intresno);++intpnv_pcibios_sriov_enable(structpci_dev*pdev,u16num_vfs);+intpnv_pcibios_sriov_disable(structpci_dev*pdev);+#endif /* CONFIG_PCI_IOV */+externstructpci_opspnv_pci_ops;voidpnv_pci_dump_phb_diag_data(structpci_controller*hose,
From: Oliver O'Halloran <oohall@gmail.com> Date: 2020-07-10 05:44:48
This prevents SR-IOV being used by making the SR-IOV BAR resources
unallocatable. Rename it to reflect what it actually does.
Signed-off-by: Oliver O'Halloran <oohall@gmail.com>
---
arch/powerpc/platforms/powernv/pci-sriov.c | 11 ++++++-----
1 file changed, 6 insertions(+), 5 deletions(-)
@@ -220,8 +221,8 @@ static void pnv_pci_ioda_fixup_iov_resources(struct pci_dev *pdev)return;-truncate_iov:-/* To save MMIO space, IOV BAR is truncated. */+disable_iov:+/* Save ourselves some MMIO space by disabling the unusable BARs */for(i=0;i<PCI_SRIOV_NUM_BARS;i++){res=&pdev->resource[i+PCI_IOV_RESOURCES];res->flags=0;
From: Oliver O'Halloran <oohall@gmail.com> Date: 2020-07-10 05:47:19
No need for the multi-dimensional arrays, just use a bitmap.
Signed-off-by: Oliver O'Halloran <oohall@gmail.com>
---
arch/powerpc/platforms/powernv/pci-sriov.c | 48 +++++++---------------
arch/powerpc/platforms/powernv/pci.h | 7 +++-
2 files changed, 20 insertions(+), 35 deletions(-)
@@ -350,23 +342,14 @@ static int pnv_pci_vf_assign_m64(struct pci_dev *pdev, u16 num_vfs)elsem64_bars=1;-iov->m64_map=kmalloc_array(m64_bars,-sizeof(*iov->m64_map),-GFP_KERNEL);-if(!iov->m64_map)-return-ENOMEM;-/* Initialize the m64_map to IODA_INVALID_M64 */-for(i=0;i<m64_bars;i++)-for(j=0;j<PCI_SRIOV_NUM_BARS;j++)-iov->m64_map[i][j]=IODA_INVALID_M64;--for(i=0;i<PCI_SRIOV_NUM_BARS;i++){res=&pdev->resource[i+PCI_IOV_RESOURCES];if(!res->flags||!res->parent)continue;for(j=0;j<m64_bars;j++){++/* allocate a window ID for this BAR */do{win=find_next_zero_bit(&phb->ioda.m64_bar_alloc,phb->ioda.m64_bar_idx+1,0);
@@ -243,8 +243,11 @@ struct pnv_iov_data {/* Did we map the VF BARs with single-PE IODA BARs? */boolm64_single_mode;-int(*m64_map)[PCI_SRIOV_NUM_BARS];-#define IODA_INVALID_M64 (-1)+/*+*Bitmaskusedtotrackwhichm64windowsthatweusedtomapthe+*SR-IOVBARsforthisdevice.+*/+DECLARE_BITMAP(used_m64_bar_mask,64);/**IfwemaptheSR-IOVBARswithasegmentedwindowthen
From: Oliver O'Halloran <oohall@gmail.com> Date: 2020-07-10 05:48:56
The sequence required to use the single PE BAR mode is kinda janky and
requires a little explanation. The API was designed with P7-IOC style
windows where the setup process is something like:
1. Configure the window start / end address
2. Enable the window
3. Map the segments of each window to the PE
For Single PE BARs the process is:
1. Set the PE for segment zero on a disabled window
2. Set the range
3. Enable the window
Move the OPAL calls into their own helper functions where the quirks can be
contained.
Signed-off-by: Oliver O'Halloran <oohall@gmail.com>
---
arch/powerpc/platforms/powernv/pci-sriov.c | 132 ++++++++++++++++-----
1 file changed, 103 insertions(+), 29 deletions(-)
@@ -320,6 +320,102 @@ static int pnv_pci_vf_release_m64(struct pci_dev *pdev, u16 num_vfs)return0;}++/*+*PHB3andbeyondsupport"accordion"windows.Thewindow'saddressrange+*issubdividedintophb->ioda.total_pe_numsegmentsandthere'sa1-1+*mappingbetweenPEsandsegments.+*+*They'recalledthatbecauseasthewindowsizechangesthesegmentsizes+*changewithit.Sortoflikeanaccordion,sortof.+*/+staticint64_tpnv_ioda_map_m64_accordion(structpnv_phb*phb,+intwindow_id,+resource_size_tstart,+resource_size_tsize)+{+int64_trc;++rc=opal_pci_set_phb_mem_window(phb->opal_id,+OPAL_M64_WINDOW_TYPE,+window_id,+start,+0,/* unused */+size);+if(rc)+gotoout;++rc=opal_pci_phb_mmio_enable(phb->opal_id,+OPAL_M64_WINDOW_TYPE,+window_id,+OPAL_ENABLE_M64_SPLIT);+out:+if(rc)+pr_err("Failed to map M64 window #%d: %lld\n",window_id,rc);++returnrc;+}++staticint64_tpnv_ioda_map_m64_single(structpnv_phb*phb,+intpe_num,+intwindow_id,+resource_size_tstart,+resource_size_tsize)+{+int64_trc;++/*+*TheAPIforsettingupm64mmiowindowsseemstohavebeendesigned+*withP7-IOCinmind.ForthatchipeachM64BAR(window)hadafixed+*splitof8equallysizedsegmentseachofwhichcouldindividually+*assignedtoaPE.+*+*TheproblemwiththisisthattheAPIdoesn'thaveanywayto+*communicatethenumberofsegmentswewantonaBAR.Thiswasn't+*aproblemforp7-iocsinceyoudidn'thaveachoice,butthe+*singlePEwindowsaddedinPHB3don'tmapcleanlytothisAPI.+*+*Asaresultwe'vegotthisslightlyawkwardprocesswherewe+*callopal_pci_map_pe_mmio_window()toputthesingleinsingle+*PEmode,andsetthePEforthewindowbeforesettingtheaddress+*bounds.WeneedtodoitthiswaybecausethesinglePEwindows+*forPHB3havedifferentalignmentrequirementsonPHB3.+*/+rc=opal_pci_map_pe_mmio_window(phb->opal_id,+pe_num,+OPAL_M64_WINDOW_TYPE,+window_id,+0);+if(rc)+gotoout;++/*+*NB:InsinglePEmodethewindowneedstobealignedto32MB+*/+rc=opal_pci_set_phb_mem_window(phb->opal_id,+OPAL_M64_WINDOW_TYPE,+window_id,+start,+0,/* ignored by FW, m64 is 1-1 */+size);+if(rc)+gotoout;++/*+*Nowactuallyenableit.WespecifiedtheBARshouldbein"non-split"+*modesoFWwillvalidatethattheBARisinsinglePEmode.+*/+rc=opal_pci_phb_mmio_enable(phb->opal_id,+OPAL_M64_WINDOW_TYPE,+window_id,+OPAL_ENABLE_M64_NON_SPLIT);+out:+if(rc)+pr_err("Error mapping single PE BAR\n");++returnrc;+}+staticintpnv_pci_vf_assign_m64(structpci_dev*pdev,u16num_vfs){structpnv_iov_data*iov;
From: Oliver O'Halloran <oohall@gmail.com> Date: 2020-07-10 05:50:55
Rework the PE allocation logic to allow allocating blocks of PEs rather
than individually. We'll use this to allocate contigious blocks of PEs for
the SR-IOVs.
Signed-off-by: Oliver O'Halloran <oohall@gmail.com>
---
arch/powerpc/platforms/powernv/pci-ioda.c | 41 ++++++++++++++++++-----
arch/powerpc/platforms/powernv/pci.h | 2 +-
2 files changed, 34 insertions(+), 9 deletions(-)
@@ -145,23 +145,45 @@ static void pnv_ioda_reserve_pe(struct pnv_phb *phb, int pe_no)return;}+mutex_lock(&phb->ioda.pe_alloc_mutex);if(test_and_set_bit(pe_no,phb->ioda.pe_alloc))pr_debug("%s: PE %x was reserved on PHB#%x\n",__func__,pe_no,phb->hose->global_number);+mutex_unlock(&phb->ioda.pe_alloc_mutex);pnv_ioda_init_pe(phb,pe_no);}-structpnv_ioda_pe*pnv_ioda_alloc_pe(structpnv_phb*phb)+structpnv_ioda_pe*pnv_ioda_alloc_pe(structpnv_phb*phb,intcount){-longpe;+structpnv_ioda_pe*ret=NULL;+intrun=0,pe,i;+mutex_lock(&phb->ioda.pe_alloc_mutex);++/* scan backwards for a run of @count cleared bits */for(pe=phb->ioda.total_pe_num-1;pe>=0;pe--){-if(!test_and_set_bit(pe,phb->ioda.pe_alloc))-returnpnv_ioda_init_pe(phb,pe);+if(test_bit(pe,phb->ioda.pe_alloc)){+run=0;+continue;+}++run++;+if(run==count)+break;}+if(run!=count)+gotoout;-returnNULL;+for(i=pe;i<pe+count;i++){+set_bit(i,phb->ioda.pe_alloc);+pnv_ioda_init_pe(phb,i);+}+ret=&phb->ioda.pe_array[pe];++out:+mutex_unlock(&phb->ioda.pe_alloc_mutex);+returnret;}voidpnv_ioda_free_pe(structpnv_ioda_pe*pe)
@@ -173,7 +195,10 @@ void pnv_ioda_free_pe(struct pnv_ioda_pe *pe)WARN_ON(pe->npucomp);/* NPUs for nvlink are not supposed to be freed */kfree(pe->npucomp);memset(pe,0,sizeof(structpnv_ioda_pe));++mutex_lock(&phb->ioda.pe_alloc_mutex);clear_bit(pe_num,phb->ioda.pe_alloc);+mutex_unlock(&phb->ioda.pe_alloc_mutex);}/* The default M64 BAR is shared by all PEs */
@@ -1047,7 +1072,7 @@ static struct pnv_ioda_pe *pnv_ioda_setup_bus_PE(struct pci_bus *bus, bool all)/* The PE number isn't pinned by M64 */if(!pe)-pe=pnv_ioda_alloc_pe(phb);+pe=pnv_ioda_alloc_pe(phb,1);if(!pe){pr_warn("%s: Not enough PE# available for PCI bus %04x:%02x\n",
@@ -3065,7 +3090,7 @@ static void __init pnv_pci_init_ioda_phb(struct device_node *np,pnv_ioda_reserve_pe(phb,phb->ioda.root_pe_idx);}else{/* otherwise just allocate one */-root_pe=pnv_ioda_alloc_pe(phb);+root_pe=pnv_ioda_alloc_pe(phb,1);phb->ioda.root_pe_idx=root_pe->pe_number;}
From: Oliver O'Halloran <oohall@gmail.com> Date: 2020-07-10 05:52:42
Currently the iov->pe_num_map[] does one of two things depending on
whether single PE mode is being used or not. When it is, this contains an
array which maps a vf_index to the corresponding PE number. When single PE
mode is not being used this contains a scalar which is the base PE for the
set of enabled VFs (for for VFn is base + n).
The array was necessary because when calling pnv_ioda_alloc_pe() there is
no guarantee that the allocated PEs would be contigious. We can now
allocate contigious blocks of PEs so this is no longer an issue. This
allows us to drop the if (single_mode) {} .. else {} block scattered
through the SR-IOV code which is a nice clean up.
Signed-off-by: Oliver O'Halloran <oohall@gmail.com>
---
arch/powerpc/platforms/powernv/pci-sriov.c | 109 +++++----------------
arch/powerpc/platforms/powernv/pci.h | 4 +-
2 files changed, 25 insertions(+), 88 deletions(-)
@@ -717,55 +700,14 @@ static int pnv_pci_sriov_enable(struct pci_dev *pdev, u16 num_vfs)return-ENOSPC;}-/*-*WhenM64BARsfunctionsinSinglePEmode,thenumberofVFs-*couldbeenabledmustbelessthanthenumberofM64BARs.-*/-if(iov->m64_single_mode&&num_vfs>phb->ioda.m64_bar_idx){-dev_info(&pdev->dev,"Not enough M64 BAR for VFs\n");+/* allocate a contigious block of PEs for our VFs */+base_pe=pnv_ioda_alloc_pe(phb,num_vfs);+if(!base_pe){+pci_err(pdev,"Unable to allocate PEs for %d VFs\n",num_vfs);return-EBUSY;}-/* Allocating pe_num_map */-if(iov->m64_single_mode)-iov->pe_num_map=kmalloc_array(num_vfs,-sizeof(*iov->pe_num_map),-GFP_KERNEL);-else-iov->pe_num_map=kmalloc(sizeof(*iov->pe_num_map),GFP_KERNEL);--if(!iov->pe_num_map)-return-ENOMEM;--if(iov->m64_single_mode)-for(i=0;i<num_vfs;i++)-iov->pe_num_map[i]=IODA_INVALID_PE;--/* Calculate available PE for required VFs */-if(iov->m64_single_mode){-for(i=0;i<num_vfs;i++){-pe=pnv_ioda_alloc_pe(phb);-if(!pe){-ret=-EBUSY;-gotom64_failed;-}--iov->pe_num_map[i]=pe->pe_number;-}-}else{-mutex_lock(&phb->ioda.pe_alloc_mutex);-*iov->pe_num_map=bitmap_find_next_zero_area(-phb->ioda.pe_alloc,phb->ioda.total_pe_num,-0,num_vfs,0);-if(*iov->pe_num_map>=phb->ioda.total_pe_num){-mutex_unlock(&phb->ioda.pe_alloc_mutex);-dev_info(&pdev->dev,"Failed to enable VF%d\n",num_vfs);-kfree(iov->pe_num_map);-return-EBUSY;-}-bitmap_set(phb->ioda.pe_alloc,*iov->pe_num_map,num_vfs);-mutex_unlock(&phb->ioda.pe_alloc_mutex);-}+iov->vf_pe_arr=base_pe;iov->num_vfs=num_vfs;/* Assign M64 window accordingly */
@@ -238,7 +238,9 @@ struct pnv_iov_data {/* number of VFs enabled */u16num_vfs;-unsignedint*pe_num_map;/* PE# for the first VF PE or array */++/* pointer to the array of VF PEs. num_vfs long*/+structpnv_ioda_pe*vf_pe_arr;/* Did we map the VF BARs with single-PE IODA BARs? */boolm64_single_mode;
From: Oliver O'Halloran <oohall@gmail.com> Date: 2020-07-10 05:54:43
Remove the IODA2 PHB checks. We already assume IODA2 in several places so
there's not much point in wrapping most of the setup and teardown process
in an if block.
Signed-off-by: Oliver O'Halloran <oohall@gmail.com>
---
arch/powerpc/platforms/powernv/pci-sriov.c | 86 ++++++++++++----------
1 file changed, 49 insertions(+), 37 deletions(-)
@@ -610,16 +610,18 @@ static void pnv_pci_sriov_disable(struct pci_dev *pdev)num_vfs=iov->num_vfs;base_pe=iov->vf_pe_arr[0].pe_number;+if(WARN_ON(!iov))+return;+/* Release VF PEs */pnv_ioda_release_vf_PE(pdev);-if(phb->type==PNV_PHB_IODA2){-if(!iov->m64_single_mode)-pnv_pci_vf_resource_shift(pdev,-base_pe);+/* Un-shift the IOV BAR resources */+if(!iov->m64_single_mode)+pnv_pci_vf_resource_shift(pdev,-base_pe);-/* Release M64 windows */-pnv_pci_vf_release_m64(pdev,num_vfs);-}+/* Release M64 windows */+pnv_pci_vf_release_m64(pdev,num_vfs);}staticvoidpnv_ioda_setup_vf_PE(structpci_dev*pdev,u16num_vfs)
@@ -693,41 +695,51 @@ static int pnv_pci_sriov_enable(struct pci_dev *pdev, u16 num_vfs)phb=pci_bus_to_pnvhb(pdev->bus);iov=pnv_iov_get(pdev);-if(phb->type==PNV_PHB_IODA2){-if(!iov->vfs_expanded){-dev_info(&pdev->dev,"don't support this SRIOV device"-" with non 64bit-prefetchable IOV BAR\n");-return-ENOSPC;-}+/*+*There'sacallstoIODA2PEsetupcodelitteredthroughout.Wecould+*probablyfixthat,butwe'dstillhaveproblemsduetothe+*restrictioninherentonIODA1PHBs.+*+*NB:WeclassIODA3asIODA2sincethey'reverysimilar.+*/+if(phb->type!=PNV_PHB_IODA2){+pci_err(pdev,"SR-IOV is not supported on this PHB\n");+return-ENXIO;+}-/* allocate a contigious block of PEs for our VFs */-base_pe=pnv_ioda_alloc_pe(phb,num_vfs);-if(!base_pe){-pci_err(pdev,"Unable to allocate PEs for %d VFs\n",num_vfs);-return-EBUSY;-}+if(!iov->vfs_expanded){+dev_info(&pdev->dev,"don't support this SRIOV device"+" with non 64bit-prefetchable IOV BAR\n");+return-ENOSPC;+}-iov->vf_pe_arr=base_pe;-iov->num_vfs=num_vfs;+/* allocate a contigious block of PEs for our VFs */+base_pe=pnv_ioda_alloc_pe(phb,num_vfs);+if(!base_pe){+pci_err(pdev,"Unable to allocate PEs for %d VFs\n",num_vfs);+return-EBUSY;+}-/* Assign M64 window accordingly */-ret=pnv_pci_vf_assign_m64(pdev,num_vfs);-if(ret){-dev_info(&pdev->dev,"Not enough M64 window resources\n");-gotom64_failed;-}+iov->vf_pe_arr=base_pe;+iov->num_vfs=num_vfs;-/*-*WhenusingoneM64BARtomaponeIOVBAR,weneedtoshift-*theIOVBARaccordingtothePE#allocatedtotheVFs.-*Otherwise,thePE#fortheVFwillconflictwithothers.-*/-if(!iov->m64_single_mode){-ret=pnv_pci_vf_resource_shift(pdev,-base_pe->pe_number);-if(ret)-gotoshift_failed;-}+/* Assign M64 window accordingly */+ret=pnv_pci_vf_assign_m64(pdev,num_vfs);+if(ret){+dev_info(&pdev->dev,"Not enough M64 window resources\n");+gotom64_failed;+}++/*+*WhenusingoneM64BARtomaponeIOVBAR,weneedtoshift+*theIOVBARaccordingtothePE#allocatedtotheVFs.+*Otherwise,thePE#fortheVFwillconflictwithothers.+*/+if(!iov->m64_single_mode){+ret=pnv_pci_vf_resource_shift(pdev,+base_pe->pe_number);+if(ret)+gotoshift_failed;}/* Setup VF PEs */
From: Oliver O'Halloran <oohall@gmail.com> Date: 2020-07-10 05:56:28
I want to refactor the loop this code is currently inside of. Hoist it on
out.
Signed-off-by: Oliver O'Halloran <oohall@gmail.com>
---
arch/powerpc/platforms/powernv/pci-sriov.c | 31 ++++++++++++++--------
1 file changed, 20 insertions(+), 11 deletions(-)
@@ -443,17 +460,9 @@ static int pnv_pci_vf_assign_m64(struct pci_dev *pdev, u16 num_vfs)continue;for(j=0;j<m64_bars;j++){--/* allocate a window ID for this BAR */-do{-win=find_next_zero_bit(&phb->ioda.m64_bar_alloc,-phb->ioda.m64_bar_idx+1,0);--if(win>=phb->ioda.m64_bar_idx+1)-gotom64_failed;-}while(test_and_set_bit(win,&phb->ioda.m64_bar_alloc));-set_bit(win,iov->used_m64_bar_mask);-+win=pnv_pci_alloc_m64_bar(phb,iov);+if(win<0)+gotom64_failed;if(iov->m64_single_mode){intpe_num=iov->vf_pe_arr[j].pe_number;
From: Oliver O'Halloran <oohall@gmail.com> Date: 2020-07-10 05:58:39
Split up the logic so that we have one branch that handles setting up a
segmented window and another that handles setting up single PE windows for
each VF.
Signed-off-by: Oliver O'Halloran <oohall@gmail.com>
---
This patch could be folded into the previous one. I've kept it
seperate mainly because the diff is *horrific* when they're merged.
---
arch/powerpc/platforms/powernv/pci-sriov.c | 57 ++++++++++------------
1 file changed, 27 insertions(+), 30 deletions(-)
@@ -441,52 +441,49 @@ static int pnv_pci_vf_assign_m64(struct pci_dev *pdev, u16 num_vfs)structresource*res;inti,j;int64_trc;-inttotal_vfs;resource_size_tsize,start;-intm64_bars;+intbase_pe_num;phb=pci_bus_to_pnvhb(pdev->bus);iov=pnv_iov_get(pdev);-total_vfs=pci_sriov_get_totalvfs(pdev);--if(iov->m64_single_mode)-m64_bars=num_vfs;-else-m64_bars=1;for(i=0;i<PCI_SRIOV_NUM_BARS;i++){res=&pdev->resource[i+PCI_IOV_RESOURCES];if(!res->flags||!res->parent)continue;-for(j=0;j<m64_bars;j++){+/* don't need single mode? map everything in one go! */+if(!iov->m64_single_mode){win=pnv_pci_alloc_m64_bar(phb,iov);if(win<0)gotom64_failed;-if(iov->m64_single_mode){-intpe_num=iov->vf_pe_arr[j].pe_number;--size=pci_iov_resource_size(pdev,-PCI_IOV_RESOURCES+i);-start=res->start+size*j;-rc=pnv_ioda_map_m64_single(phb,win,-pe_num,-start,-size);-}else{-size=resource_size(res);-start=res->start;--rc=pnv_ioda_map_m64_accordion(phb,win,start,-size);-}+size=resource_size(res);+start=res->start;-if(rc!=OPAL_SUCCESS){-dev_err(&pdev->dev,"Failed to map M64 window #%d: %lld\n",-win,rc);+rc=pnv_ioda_map_m64_accordion(phb,win,start,size);+if(rc)+gotom64_failed;++continue;+}++/* otherwise map each VF with single PE BARs */+size=pci_iov_resource_size(pdev,PCI_IOV_RESOURCES+i);+base_pe_num=iov->vf_pe_arr[0].pe_number;++for(j=0;j<num_vfs;j++){+win=pnv_pci_alloc_m64_bar(phb,iov);+if(win<0)+gotom64_failed;++start=res->start+size*j;+rc=pnv_ioda_map_m64_single(phb,win,+base_pe_num+j,+start,+size);+if(rc)gotom64_failed;-}}}return0;
From: Oliver O'Halloran <oohall@gmail.com> Date: 2020-07-10 06:00:24
Using single PE BARs to map an SR-IOV BAR is really a choice about what
strategy to use when mapping a BAR. It doesn't make much sense for this to
be a global setting since a device might have one large BAR which needs to
be mapped with single PE windows and another smaller BAR that can be mapped
with a regular segmented window. Make the segmented vs single decision a
per-BAR setting and clean up the logic that decides which mode to use.
Signed-off-by: Oliver O'Halloran <oohall@gmail.com>
---
arch/powerpc/platforms/powernv/pci-sriov.c | 131 +++++++++++----------
arch/powerpc/platforms/powernv/pci.h | 10 +-
2 files changed, 75 insertions(+), 66 deletions(-)
@@ -158,9 +157,9 @@ static void pnv_pci_ioda_fixup_iov_resources(struct pci_dev *pdev)gotodisable_iov;pdev->dev.archdata.iov_data=iov;+/* FIXME: totalvfs > phb->ioda.total_pe_num is going to be a problem */total_vfs=pci_sriov_get_totalvfs(pdev);mul=phb->ioda.total_pe_num;-total_vf_bar_sz=0;for(i=0;i<PCI_SRIOV_NUM_BARS;i++){res=&pdev->resource[i+PCI_IOV_RESOURCES];
@@ -173,50 +172,51 @@ static void pnv_pci_ioda_fixup_iov_resources(struct pci_dev *pdev)gotodisable_iov;}-total_vf_bar_sz+=pci_iov_resource_size(pdev,-i+PCI_IOV_RESOURCES);+vf_bar_sz=pci_iov_resource_size(pdev,i+PCI_IOV_RESOURCES);/*-*IfbiggerthanquarterofM64segmentsize,justroundup-*poweroftwo.+*Generally,onesegmentedM64BARmapsoneIOVBAR.However,+*ifaVFBARistoolargeweendupwastingalotofspace.+*Ifwe'vegotaBARthat'sbiggerthangreaterthan1/4ofthe+*defaultwindow'ssegmentsizethenswitchtousingsinglePE+*windows.ThislimitsthetotalnumberofVFswecansupport.*-*Generally,oneM64BARmapsoneIOVBAR.Toavoidconflict-*withotherdevices,IOVBARsizeisexpandedtobe-*(total_pe*VF_BAR_size).WhenVF_BAR_sizeishalfofM64-*segmentsize,theexpandedsizewouldequaltohalfofthe-*wholeM64spacesize,whichwillexhausttheM64Spaceand-*limitthesystemflexibility.Thisisadesigndecisionto-*settheboundarytoquarteroftheM64segmentsize.+*The1/4limitisarbitraryandcanbetweaked.*/-if(total_vf_bar_sz>gate){-mul=roundup_pow_of_two(total_vfs);-dev_info(&pdev->dev,-"VF BAR Total IOV size %llx > %llx, roundup to %d VFs\n",-total_vf_bar_sz,gate,mul);-iov->m64_single_mode=true;-break;-}-}+if(vf_bar_sz>(phb->ioda.m64_segsize>>2)){+/*+*OnPHB3,theminimumsizealignmentofM64BARin+*singlemodeis32MB.IfthisVFBARissmallerthan+*32MB,butstilltoolargeforasegmentedwindow+*thenwecan'tmapitandneedtodisableSR-IOVfor+*thisdevice.+*/+if(vf_bar_sz<SZ_32M){+pci_err(pdev,"VF BAR%d: %pR can't be mapped in single PE mode\n",+i,res);+gotodisable_iov;+}-for(i=0;i<PCI_SRIOV_NUM_BARS;i++){-res=&pdev->resource[i+PCI_IOV_RESOURCES];-if(!res->flags||res->parent)+iov->m64_single_mode[i]=true;continue;+}+-size=pci_iov_resource_size(pdev,i+PCI_IOV_RESOURCES);/*-*OnPHB3,theminimumsizealignmentofM64BARinsingle-*modeis32MB.+*ThisBARcanbemappedwithonesegmentedwindow,soadjust+*teresourcesizetoaccommodate.*/-if(iov->m64_single_mode&&(size<SZ_32M))-gotodisable_iov;+pci_dbg(pdev," Fixing VF BAR%d: %pR to\n",i,res);+res->end=res->start+vf_bar_sz*mul-1;+pci_dbg(pdev," %pR\n",res);-dev_dbg(&pdev->dev," Fixing VF BAR%d: %pR to\n",i,res);-res->end=res->start+size*mul-1;-dev_dbg(&pdev->dev," %pR\n",res);-dev_info(&pdev->dev,"VF BAR%d: %pR (expanded to %d VFs for PE alignment)",+pci_info(pdev,"VF BAR%d: %pR (expanded to %d VFs for PE alignment)",i,res,mul);++iov->need_shift=true;}++// what should this be?iov->vfs_expanded=mul;return;
@@ -453,7 +453,7 @@ static int pnv_pci_vf_assign_m64(struct pci_dev *pdev, u16 num_vfs)continue;/* don't need single mode? map everything in one go! */-if(!iov->m64_single_mode){+if(!iov->m64_single_mode[i]){win=pnv_pci_alloc_m64_bar(phb,iov);if(win<0)gotom64_failed;
@@ -546,6 +546,8 @@ static int pnv_pci_vf_resource_shift(struct pci_dev *dev, int offset)res=&dev->resource[i+PCI_IOV_RESOURCES];if(!res->flags||!res->parent)continue;+if(iov->m64_single_mode[i])+continue;/**TheactualIOVBARrangeisdeterminedbythestartaddress
@@ -577,6 +579,8 @@ static int pnv_pci_vf_resource_shift(struct pci_dev *dev, int offset)res=&dev->resource[i+PCI_IOV_RESOURCES];if(!res->flags||!res->parent)continue;+if(iov->m64_single_mode[i])+continue;size=pci_iov_resource_size(dev,i+PCI_IOV_RESOURCES);res2=*res;
@@ -622,8 +626,8 @@ static void pnv_pci_sriov_disable(struct pci_dev *pdev)/* Release VF PEs */pnv_ioda_release_vf_PE(pdev);-/* Un-shift the IOV BAR resources */-if(!iov->m64_single_mode)+/* Un-shift the IOV BARs if we need to */+if(iov->need_shift)pnv_pci_vf_resource_shift(pdev,-base_pe);/* Release M64 windows */
@@ -236,14 +236,20 @@ struct pnv_iov_data {/* number of VFs IOV BAR expanded. FIXME: rename this to something less bad */u16vfs_expanded;+/*+*indicatesifweneedtomoveourIOVBARtoaccountforour+*allocatedPEnumberwhenenablingVFs.+*/+boolneed_shift;+/* number of VFs enabled */u16num_vfs;/* pointer to the array of VF PEs. num_vfs long*/structpnv_ioda_pe*vf_pe_arr;-/* Did we map the VF BARs with single-PE IODA BARs? */-boolm64_single_mode;+/* Did we map the VF BAR with single-PE IODA BARs? */+boolm64_single_mode[PCI_SRIOV_NUM_BARS];/**Bitmaskusedtotrackwhichm64windowsthatweusedtomapthe
From: Christoph Hellwig <hch@infradead.org> Date: 2020-07-10 06:47:34
On Fri, Jul 10, 2020 at 03:23:25PM +1000, Oliver O'Halloran wrote:
This is largely prep work for supporting VFs in the 32bit MMIO window.
This is an unfortunate necessity due to how the Linux BAR allocator
handles BARs marked as non-prefetchable. The distinction
between prefetch and non-prefetchable BARs was made largely irrelevant
with the introduction of PCIe, but the BAR allocator is overly
conservative. It will always place non-pref bars in the prefetchable
window, which is 32bit only. This results in us being unable to use VFs
from NVMe drives and a few different RAID cards.
How about fixing that in the core PCI code?
(nothing against this series through, as it seems like a massive
cleanup)
On Fri, Jul 10, 2020 at 4:45 PM Christoph Hellwig [off-list ref] wrote:
On Fri, Jul 10, 2020 at 03:23:25PM +1000, Oliver O'Halloran wrote:
quoted
This is largely prep work for supporting VFs in the 32bit MMIO window.
This is an unfortunate necessity due to how the Linux BAR allocator
handles BARs marked as non-prefetchable. The distinction
between prefetch and non-prefetchable BARs was made largely irrelevant
with the introduction of PCIe, but the BAR allocator is overly
conservative. It will always place non-pref bars in the prefetchable
window, which is 32bit only. This results in us being unable to use VFs
from NVMe drives and a few different RAID cards.
How about fixing that in the core PCI code?
I've been kicking around the idea but I've never managed to convince
myself that ignoring the non-prefetchable bit is a safe thing to do in
generic code. Since Gen3 at least the PCIe Base spec has provided some
guidance about when you can put non-prefetchable BARs in the
prefetchable window and as of the Gen5 spec it lists these conditions:
1) The entire path from the host to the adapter is over PCI Express.
2) No conventional PCI or PCI-X devices do peer-to-peer reads to the range mapped by the BAR.
3) The PCI Express Host Bridge does no byte merging. (This is believed to be true on most platforms.)
4) Any locations with read side-effects are never the target of Memory Reads with the TH bit Set.
5) The range mapped by the BAR is never the target of a speculative Memory Read, either Host initiated or peer-to-peer.
1) Is easy enough to verify.
2) Is probably true, but who knows.
3) I know this is true for the platforms I'm looking at since the HW
designers assure me there is no merging happening at the host-bridge
level. Merging of MMIO ops does seem like an insane thing to do so
it's probably true in general too, but there's no real way to tell.
4) Is also *probably* true since the TH bit is only set when it's
explicitly enabled via the TLP Processing Hints extended capability in
config space. I guess it's possible firmware might enable that without
Linux realising, but in that case Linux is probably not doing BAR
allocation.
5) I have no idea about, but it seems difficult to make any kind of
general statement about.
I might just be being paranoid.
Oliver
Add a helper to go from a pci_bus structure to the pnv_phb that hosts that
bus. There's a lot of instances of the following pattern:
struct pci_controller *hose = pci_bus_to_host(pdev->bus);
struct pnv_phb *phb = hose->private_data;
Without any other uses of the pci_controller inside the function. This is
hard to read since it requires you to memorise the contents of the
private data fields and kind of error prone since it involves blindly
assigning a void pointer. Add a helper to make it more concise and
explicit.
Signed-off-by: Oliver O'Halloran <oohall@gmail.com>
---
arch/powerpc/platforms/powernv/pci-ioda.c | 88 +++++++----------------
arch/powerpc/platforms/powernv/pci.c | 14 ++--
arch/powerpc/platforms/powernv/pci.h | 10 +++
3 files changed, 38 insertions(+), 74 deletions(-)
@@ -3274,8 +3238,7 @@ static resource_size_t pnv_pci_iov_resource_alignment(struct pci_dev *pdev,*/staticboolpnv_pci_enable_device_hook(structpci_dev*dev){-structpci_controller*hose=pci_bus_to_host(dev->bus);-structpnv_phb*phb=hose->private_data;+structpnv_phb*phb=pci_bus_to_pnvhb(dev->bus);structpci_dn*pdn;/* The function is probably called while the PEs have
@@ -211,8 +210,7 @@ int pnv_setup_msi_irqs(struct pci_dev *pdev, int nvec, int type)voidpnv_teardown_msi_irqs(structpci_dev*pdev){-structpci_controller*hose=pci_bus_to_host(pdev->bus);-structpnv_phb*phb=hose->private_data;+structpnv_phb*phb=pci_bus_to_pnvhb(pdev->bus);structmsi_desc*entry;irq_hw_number_thwirq;
Currently we have these two functions:
pnv_pci_ioda2_release_dma_pe(), and
pnv_pci_ioda2_release_pe_dma()
The first is used when tearing down VF PEs and the other is used for normal
devices. There's very little difference between the two though. The latter
(non-VF) will skip a call to pnv_pci_ioda2_unset_window() unless
CONFIG_IOMMU_API=y is set. There's no real point in doing this so fold the
two together.
Signed-off-by: Oliver O'Halloran <oohall@gmail.com>
There's an optimisation in the PE setup which skips performing DMA
setup for a PE if we only have bridges in a PE. The assumption being
that only "real" devices will DMA to system memory, which is probably
fair. However, if we start off with only bridge devices in a PE then
add a non-bridge device the new device won't be able to use DMA because
we never configured it.
Fix this (admittedly pretty weird) edge case by tracking whether we've done
the DMA setup for the PE or not. If a non-bridge device is added to the PE
(via rescan or hotplug, or whatever) we can set up DMA on demand.
So hotplug does not work on powernv then, right? I thought you tested it
a while ago, or this patch is the result of that attempt? If it is, then
Reviewed-by: Alexey Kardashevskiy <redacted>
This also means the only remaining user of the old "DMA Weight" code is
the IODA1 DMA setup code that it was originally added for, which is good.
Is ditching IODA1 in the plan? :)
Cc: Alexey Kardashevskiy <redacted>
Signed-off-by: Oliver O'Halloran <oohall@gmail.com>
---
Alexey, do we need to have the IOMMU API stuff set/clear this flag?
I'd say no as that API only cares if a device is in a PE and for those
the PE DMA setup optimization is skipped. Thanks,
@@ -87,6 +87,13 @@ struct pnv_ioda_pe {booltce_bypass_enabled;uint64_ttce_bypass_base;+/*+*Usedtotrackwhetherwe'vedoneDMAsetupforthisPEornot.We+*wanttodeferallocatingTCEtables,etcuntilwe'veaddeda+*non-bridgedevicetothePE.+*/+booldma_setup_done;+/* MSIs. MVE index is identical for for 32 and 64 bit MSI*and-1ifnotsupported.(It'sactuallyidenticaltothe*PEnumber)
On Tue, Jul 14, 2020 at 3:37 PM Alexey Kardashevskiy [off-list ref] wrote:
On 10/07/2020 15:23, Oliver O'Halloran wrote:
quoted
There's an optimisation in the PE setup which skips performing DMA
setup for a PE if we only have bridges in a PE. The assumption being
that only "real" devices will DMA to system memory, which is probably
fair. However, if we start off with only bridge devices in a PE then
add a non-bridge device the new device won't be able to use DMA because
we never configured it.
Fix this (admittedly pretty weird) edge case by tracking whether we've done
the DMA setup for the PE or not. If a non-bridge device is added to the PE
(via rescan or hotplug, or whatever) we can set up DMA on demand.
So hotplug does not work on powernv then, right? I thought you tested it
a while ago, or this patch is the result of that attempt? If it is, then
It mostly works. Just the really niche case of hot plugging a bridge,
then later on hot plugging a device into the same bus which wouldn't
work.
Reviewed-by: Alexey Kardashevskiy <redacted>
quoted
This also means the only remaining user of the old "DMA Weight" code is
the IODA1 DMA setup code that it was originally added for, which is good.
Is ditching IODA1 in the plan? :)
That or separating out the pci_controller_ops for IODA1 and IODA2 so
we can stop any IODA2 specific changes from breaking it. For the most
part keeping around IODA1 support isn't hurting anyone, but I wanted
to re-work how the BDFN->PE assignment works so that we'd delay
assigning a BDFN to a PE until the device is probed. Right now when
we're configuring the PE for a bus we map all 255 devfn's to that PE.
This is mostly fine, but if you do a bus rescan and there's no device
present we'll get a spurious EEH on that PE since the PHB sees that
there's no device responding to the CFG cycle. We stop the spurious
EEH freeze today by only allowing config cycles if we can find a
pci_dn for that bdfn, but I want to get rid of pci_dn.
Mapping each BDFN to a PE after the device is probed is easy enough to
do on PHB3 and above since the mapping is handled by an in-memory
table which is indexed by the BDFN. Earlier PHBs (i.e. IODA1) use a
table of bask & mask values which match on the BDFN, so assigning a
whole bus at once is easy, but adding individual BDFNs is hard. It's
still possible to do in the HW, but the way the OPAL API works makes
it impossible.
quoted
Cc: Alexey Kardashevskiy <redacted>
Signed-off-by: Oliver O'Halloran <oohall@gmail.com>
---
Alexey, do we need to have the IOMMU API stuff set/clear this flag?
I'd say no as that API only cares if a device is in a PE and for those
the PE DMA setup optimization is skipped. Thanks,
On Tue, Jul 14, 2020 at 3:37 PM Alexey Kardashevskiy [off-list ref] wrote:
quoted
On 10/07/2020 15:23, Oliver O'Halloran wrote:
quoted
There's an optimisation in the PE setup which skips performing DMA
setup for a PE if we only have bridges in a PE. The assumption being
that only "real" devices will DMA to system memory, which is probably
fair. However, if we start off with only bridge devices in a PE then
add a non-bridge device the new device won't be able to use DMA because
we never configured it.
Fix this (admittedly pretty weird) edge case by tracking whether we've done
the DMA setup for the PE or not. If a non-bridge device is added to the PE
(via rescan or hotplug, or whatever) we can set up DMA on demand.
So hotplug does not work on powernv then, right? I thought you tested it
a while ago, or this patch is the result of that attempt? If it is, then
It mostly works. Just the really niche case of hot plugging a bridge,
then later on hot plugging a device into the same bus which wouldn't
work.
Do not you have to have a slot (which is a bridge) for hotplug in the
first place, to hotplug the bridge?
quoted
Reviewed-by: Alexey Kardashevskiy <redacted>
quoted
This also means the only remaining user of the old "DMA Weight" code is
the IODA1 DMA setup code that it was originally added for, which is good.
Is ditching IODA1 in the plan? :)
That or separating out the pci_controller_ops for IODA1 and IODA2 so
we can stop any IODA2 specific changes from breaking it.
Is IODA1 tested at all these days? Or, is anyone running upstream
kernels anywhere and keeps shouting when it does not work on IODA1? Thanks,
For the most
part keeping around IODA1 support isn't hurting anyone, but I wanted
to re-work how the BDFN->PE assignment works so that we'd delay
assigning a BDFN to a PE until the device is probed. Right now when
we're configuring the PE for a bus we map all 255 devfn's to that PE.
This is mostly fine, but if you do a bus rescan and there's no device
present we'll get a spurious EEH on that PE since the PHB sees that
there's no device responding to the CFG cycle. We stop the spurious
EEH freeze today by only allowing config cycles if we can find a
pci_dn for that bdfn, but I want to get rid of pci_dn.
Mapping each BDFN to a PE after the device is probed is easy enough to
do on PHB3 and above since the mapping is handled by an in-memory
table which is indexed by the BDFN. Earlier PHBs (i.e. IODA1) use a
table of bask & mask values which match on the BDFN, so assigning a
whole bus at once is easy, but adding individual BDFNs is hard. It's
still possible to do in the HW, but the way the OPAL API works makes
it impossible.
quoted
quoted
Cc: Alexey Kardashevskiy <redacted>
Signed-off-by: Oliver O'Halloran <oohall@gmail.com>
---
Alexey, do we need to have the IOMMU API stuff set/clear this flag?
I'd say no as that API only cares if a device is in a PE and for those
the PE DMA setup optimization is skipped. Thanks,
We pre-configure the m64 window for IODA1 as a 1-1 segment-PE mapping,
similar to PHB3. Currently the actual mapping of segments occurs in
pnv_ioda_pick_m64_pe(), but we can move it into pnv_ioda1_init_m64() and
drop the IODA1 specific code paths in the PE setup / teardown.
Signed-off-by: Oliver O'Halloran <oohall@gmail.com>
pci-ioda.c is getting a bit unwieldly due to the amount of stuff jammed in
there. The SR-IOV support can be extracted easily enough and is mostly
standalone, so move it into a seperate file.
This patch also moves the PowerNV SR-IOV specific fields from pci_dn and moves them
into a platform specific structure. I'm not sure how they ended up in there
in the first place, but leaking platform specifics into common code has
proven to be a terrible idea so far so lets stop doing that.
Signed-off-by: Oliver O'Halloran <oohall@gmail.com>
---
The pci_dn change and the pci-sriov.c changes originally separate patches.
I accidently squashed them together while rebasing and fixing that seemed
like more pain that it was worth. I kind of like it this way though since
they did cause a lot of churn on the same set of functions.
I'll split them up again if you really want (please don't want this).
Nah, not worth it splitting it this way. However it would be nice to not
to have a (small?) functional change in the same patch, there is a small
new piece (below).
@@ -982,91 +962,6 @@ static int pnv_ioda_configure_pe(struct pnv_phb *phb, struct pnv_ioda_pe *pe)return0;}-#ifdef CONFIG_PCI_IOV-staticintpnv_pci_vf_resource_shift(structpci_dev*dev,intoffset)-{-structpci_dn*pdn=pci_get_pdn(dev);-inti;-structresource*res,res2;-resource_size_tsize;-u16num_vfs;--if(!dev->is_physfn)-return-EINVAL;--/*-*"offset"isinVFs.TheM64windowsaresizedsothatwhenthey-*aresegmented,eachsegmentisthesamesizeastheIOVBAR.-*EachsegmentisinaseparatePE,andthehighorderbitsofthe-*addressarethePEnumber.Therefore,eachVF'sBARisina-*separatePE,andchangingtheIOVBARstartaddresschangesthe-*rangeofPEstheVFsarein.-*/-num_vfs=pdn->num_vfs;-for(i=0;i<PCI_SRIOV_NUM_BARS;i++){-res=&dev->resource[i+PCI_IOV_RESOURCES];-if(!res->flags||!res->parent)-continue;--/*-*TheactualIOVBARrangeisdeterminedbythestartaddress-*andtheactualsizefornum_vfsVFsBAR.Thischeckisto-*makesurethataftershifting,therangewillnotoverlap-*withanotherdevice.-*/-size=pci_iov_resource_size(dev,i+PCI_IOV_RESOURCES);-res2.flags=res->flags;-res2.start=res->start+(size*offset);-res2.end=res2.start+(size*num_vfs)-1;--if(res2.end>res->end){-dev_err(&dev->dev,"VF BAR%d: %pR would extend past %pR (trying to enable %d VFs shifted by %d)\n",-i,&res2,res,num_vfs,offset);-return-EBUSY;-}-}--/*-*SinceM64BARsharessegmentsamongallpossible256PEs,-*wehavetoshiftthebeginningofPFIOVBARtomakeitstartfrom-*thesegmentwhichbelongstothePEnumberassignedtothefirstVF.-*Thiscreatesa"hole"inthe/proc/iomemwhichcouldbeusedfor-*allocatingotherresourcessowereservethisareabelowand-*releasewhenIOVisreleased.-*/-for(i=0;i<PCI_SRIOV_NUM_BARS;i++){-res=&dev->resource[i+PCI_IOV_RESOURCES];-if(!res->flags||!res->parent)-continue;--size=pci_iov_resource_size(dev,i+PCI_IOV_RESOURCES);-res2=*res;-res->start+=size*offset;--dev_info(&dev->dev,"VF BAR%d: %pR shifted to %pR (%sabling %d VFs shifted by %d)\n",-i,&res2,res,(offset>0)?"En":"Dis",-num_vfs,offset);--if(offset<0){-devm_release_resource(&dev->dev,&pdn->holes[i]);-memset(&pdn->holes[i],0,sizeof(pdn->holes[i]));-}--pci_update_resource(dev,i+PCI_IOV_RESOURCES);--if(offset>0){-pdn->holes[i].start=res2.start;-pdn->holes[i].end=res2.start+size*offset-1;-pdn->holes[i].flags=IORESOURCE_BUS;-pdn->holes[i].name="pnv_iov_reserved";-devm_request_resource(&dev->dev,res->parent,-&pdn->holes[i]);-}-}-return0;-}-#endif /* CONFIG_PCI_IOV */-staticstructpnv_ioda_pe*pnv_ioda_setup_dev_PE(structpci_dev*dev){structpnv_phb*phb=pci_bus_to_pnvhb(dev->bus);
@@ -1294,406 +1189,9 @@ static void pnv_pci_ioda_setup_nvlink(void)#endif}-#ifdef CONFIG_PCI_IOV-staticintpnv_pci_vf_release_m64(structpci_dev*pdev,u16num_vfs)-{-structpnv_phb*phb;-structpci_dn*pdn;-inti,j;-intm64_bars;--phb=pci_bus_to_pnvhb(pdev->bus);-pdn=pci_get_pdn(pdev);--if(pdn->m64_single_mode)-m64_bars=num_vfs;-else-m64_bars=1;--for(i=0;i<PCI_SRIOV_NUM_BARS;i++)-for(j=0;j<m64_bars;j++){-if(pdn->m64_map[j][i]==IODA_INVALID_M64)-continue;-opal_pci_phb_mmio_enable(phb->opal_id,-OPAL_M64_WINDOW_TYPE,pdn->m64_map[j][i],0);-clear_bit(pdn->m64_map[j][i],&phb->ioda.m64_bar_alloc);-pdn->m64_map[j][i]=IODA_INVALID_M64;-}--kfree(pdn->m64_map);-return0;-}--staticintpnv_pci_vf_assign_m64(structpci_dev*pdev,u16num_vfs)-{-structpnv_phb*phb;-structpci_dn*pdn;-unsignedintwin;-structresource*res;-inti,j;-int64_trc;-inttotal_vfs;-resource_size_tsize,start;-intpe_num;-intm64_bars;--phb=pci_bus_to_pnvhb(pdev->bus);-pdn=pci_get_pdn(pdev);-total_vfs=pci_sriov_get_totalvfs(pdev);--if(pdn->m64_single_mode)-m64_bars=num_vfs;-else-m64_bars=1;--pdn->m64_map=kmalloc_array(m64_bars,-sizeof(*pdn->m64_map),-GFP_KERNEL);-if(!pdn->m64_map)-return-ENOMEM;-/* Initialize the m64_map to IODA_INVALID_M64 */-for(i=0;i<m64_bars;i++)-for(j=0;j<PCI_SRIOV_NUM_BARS;j++)-pdn->m64_map[i][j]=IODA_INVALID_M64;---for(i=0;i<PCI_SRIOV_NUM_BARS;i++){-res=&pdev->resource[i+PCI_IOV_RESOURCES];-if(!res->flags||!res->parent)-continue;--for(j=0;j<m64_bars;j++){-do{-win=find_next_zero_bit(&phb->ioda.m64_bar_alloc,-phb->ioda.m64_bar_idx+1,0);--if(win>=phb->ioda.m64_bar_idx+1)-gotom64_failed;-}while(test_and_set_bit(win,&phb->ioda.m64_bar_alloc));--pdn->m64_map[j][i]=win;--if(pdn->m64_single_mode){-size=pci_iov_resource_size(pdev,-PCI_IOV_RESOURCES+i);-start=res->start+size*j;-}else{-size=resource_size(res);-start=res->start;-}--/* Map the M64 here */-if(pdn->m64_single_mode){-pe_num=pdn->pe_num_map[j];-rc=opal_pci_map_pe_mmio_window(phb->opal_id,-pe_num,OPAL_M64_WINDOW_TYPE,-pdn->m64_map[j][i],0);-}--rc=opal_pci_set_phb_mem_window(phb->opal_id,-OPAL_M64_WINDOW_TYPE,-pdn->m64_map[j][i],-start,-0,/* unused */-size);---if(rc!=OPAL_SUCCESS){-dev_err(&pdev->dev,"Failed to map M64 window #%d: %lld\n",-win,rc);-gotom64_failed;-}--if(pdn->m64_single_mode)-rc=opal_pci_phb_mmio_enable(phb->opal_id,-OPAL_M64_WINDOW_TYPE,pdn->m64_map[j][i],2);-else-rc=opal_pci_phb_mmio_enable(phb->opal_id,-OPAL_M64_WINDOW_TYPE,pdn->m64_map[j][i],1);--if(rc!=OPAL_SUCCESS){-dev_err(&pdev->dev,"Failed to enable M64 window #%d: %llx\n",-win,rc);-gotom64_failed;-}-}-}-return0;--m64_failed:-pnv_pci_vf_release_m64(pdev,num_vfs);-return-EBUSY;-}--staticvoidpnv_pci_ioda2_release_pe_dma(structpnv_ioda_pe*pe);--staticvoidpnv_ioda_release_vf_PE(structpci_dev*pdev)-{-structpnv_phb*phb;-structpnv_ioda_pe*pe,*pe_n;-structpci_dn*pdn;--phb=pci_bus_to_pnvhb(pdev->bus);-pdn=pci_get_pdn(pdev);--if(!pdev->is_physfn)-return;--/* FIXME: Use pnv_ioda_release_pe()? */-list_for_each_entry_safe(pe,pe_n,&phb->ioda.pe_list,list){-if(pe->parent_dev!=pdev)-continue;--pnv_pci_ioda2_release_pe_dma(pe);--/* Remove from list */-mutex_lock(&phb->ioda.pe_list_mutex);-list_del(&pe->list);-mutex_unlock(&phb->ioda.pe_list_mutex);--pnv_ioda_deconfigure_pe(phb,pe);--pnv_ioda_free_pe(pe);-}-}--staticvoidpnv_pci_sriov_disable(structpci_dev*pdev)-{-structpnv_phb*phb;-structpnv_ioda_pe*pe;-structpci_dn*pdn;-u16num_vfs,i;--phb=pci_bus_to_pnvhb(pdev->bus);-pdn=pci_get_pdn(pdev);-num_vfs=pdn->num_vfs;--/* Release VF PEs */-pnv_ioda_release_vf_PE(pdev);--if(phb->type==PNV_PHB_IODA2){-if(!pdn->m64_single_mode)-pnv_pci_vf_resource_shift(pdev,-*pdn->pe_num_map);--/* Release M64 windows */-pnv_pci_vf_release_m64(pdev,num_vfs);--/* Release PE numbers */-if(pdn->m64_single_mode){-for(i=0;i<num_vfs;i++){-if(pdn->pe_num_map[i]==IODA_INVALID_PE)-continue;--pe=&phb->ioda.pe_array[pdn->pe_num_map[i]];-pnv_ioda_free_pe(pe);-}-}else-bitmap_clear(phb->ioda.pe_alloc,*pdn->pe_num_map,num_vfs);-/* Releasing pe_num_map */-kfree(pdn->pe_num_map);-}-}--staticvoidpnv_pci_ioda2_setup_dma_pe(structpnv_phb*phb,-structpnv_ioda_pe*pe);-staticvoidpnv_ioda_setup_vf_PE(structpci_dev*pdev,u16num_vfs)-{-structpnv_phb*phb;-structpnv_ioda_pe*pe;-intpe_num;-u16vf_index;-structpci_dn*pdn;--phb=pci_bus_to_pnvhb(pdev->bus);-pdn=pci_get_pdn(pdev);--if(!pdev->is_physfn)-return;--/* Reserve PE for each VF */-for(vf_index=0;vf_index<num_vfs;vf_index++){-intvf_devfn=pci_iov_virtfn_devfn(pdev,vf_index);-intvf_bus=pci_iov_virtfn_bus(pdev,vf_index);-structpci_dn*vf_pdn;--if(pdn->m64_single_mode)-pe_num=pdn->pe_num_map[vf_index];-else-pe_num=*pdn->pe_num_map+vf_index;--pe=&phb->ioda.pe_array[pe_num];-pe->pe_number=pe_num;-pe->phb=phb;-pe->flags=PNV_IODA_PE_VF;-pe->pbus=NULL;-pe->parent_dev=pdev;-pe->mve_number=-1;-pe->rid=(vf_bus<<8)|vf_devfn;--pe_info(pe,"VF %04d:%02d:%02d.%d associated with PE#%x\n",-pci_domain_nr(pdev->bus),pdev->bus->number,-PCI_SLOT(vf_devfn),PCI_FUNC(vf_devfn),pe_num);--if(pnv_ioda_configure_pe(phb,pe)){-/* XXX What do we do here ? */-pnv_ioda_free_pe(pe);-pe->pdev=NULL;-continue;-}--/* Put PE to the list */-mutex_lock(&phb->ioda.pe_list_mutex);-list_add_tail(&pe->list,&phb->ioda.pe_list);-mutex_unlock(&phb->ioda.pe_list_mutex);--/* associate this pe to it's pdn */-list_for_each_entry(vf_pdn,&pdn->parent->child_list,list){-if(vf_pdn->busno==vf_bus&&-vf_pdn->devfn==vf_devfn){-vf_pdn->pe_number=pe_num;-break;-}-}--pnv_pci_ioda2_setup_dma_pe(phb,pe);-}-}--staticintpnv_pci_sriov_enable(structpci_dev*pdev,u16num_vfs)-{-structpnv_phb*phb;-structpnv_ioda_pe*pe;-structpci_dn*pdn;-intret;-u16i;--phb=pci_bus_to_pnvhb(pdev->bus);-pdn=pci_get_pdn(pdev);--if(phb->type==PNV_PHB_IODA2){-if(!pdn->vfs_expanded){-dev_info(&pdev->dev,"don't support this SRIOV device"-" with non 64bit-prefetchable IOV BAR\n");-return-ENOSPC;-}--/*-*WhenM64BARsfunctionsinSinglePEmode,thenumberofVFs-*couldbeenabledmustbelessthanthenumberofM64BARs.-*/-if(pdn->m64_single_mode&&num_vfs>phb->ioda.m64_bar_idx){-dev_info(&pdev->dev,"Not enough M64 BAR for VFs\n");-return-EBUSY;-}--/* Allocating pe_num_map */-if(pdn->m64_single_mode)-pdn->pe_num_map=kmalloc_array(num_vfs,-sizeof(*pdn->pe_num_map),-GFP_KERNEL);-else-pdn->pe_num_map=kmalloc(sizeof(*pdn->pe_num_map),GFP_KERNEL);--if(!pdn->pe_num_map)-return-ENOMEM;--if(pdn->m64_single_mode)-for(i=0;i<num_vfs;i++)-pdn->pe_num_map[i]=IODA_INVALID_PE;--/* Calculate available PE for required VFs */-if(pdn->m64_single_mode){-for(i=0;i<num_vfs;i++){-pe=pnv_ioda_alloc_pe(phb);-if(!pe){-ret=-EBUSY;-gotom64_failed;-}--pdn->pe_num_map[i]=pe->pe_number;-}-}else{-mutex_lock(&phb->ioda.pe_alloc_mutex);-*pdn->pe_num_map=bitmap_find_next_zero_area(-phb->ioda.pe_alloc,phb->ioda.total_pe_num,-0,num_vfs,0);-if(*pdn->pe_num_map>=phb->ioda.total_pe_num){-mutex_unlock(&phb->ioda.pe_alloc_mutex);-dev_info(&pdev->dev,"Failed to enable VF%d\n",num_vfs);-kfree(pdn->pe_num_map);-return-EBUSY;-}-bitmap_set(phb->ioda.pe_alloc,*pdn->pe_num_map,num_vfs);-mutex_unlock(&phb->ioda.pe_alloc_mutex);-}-pdn->num_vfs=num_vfs;--/* Assign M64 window accordingly */-ret=pnv_pci_vf_assign_m64(pdev,num_vfs);-if(ret){-dev_info(&pdev->dev,"Not enough M64 window resources\n");-gotom64_failed;-}--/*-*WhenusingoneM64BARtomaponeIOVBAR,weneedtoshift-*theIOVBARaccordingtothePE#allocatedtotheVFs.-*Otherwise,thePE#fortheVFwillconflictwithothers.-*/-if(!pdn->m64_single_mode){-ret=pnv_pci_vf_resource_shift(pdev,*pdn->pe_num_map);-if(ret)-gotom64_failed;-}-}--/* Setup VF PEs */-pnv_ioda_setup_vf_PE(pdev,num_vfs);--return0;--m64_failed:-if(pdn->m64_single_mode){-for(i=0;i<num_vfs;i++){-if(pdn->pe_num_map[i]==IODA_INVALID_PE)-continue;--pe=&phb->ioda.pe_array[pdn->pe_num_map[i]];-pnv_ioda_free_pe(pe);-}-}else-bitmap_clear(phb->ioda.pe_alloc,*pdn->pe_num_map,num_vfs);--/* Releasing pe_num_map */-kfree(pdn->pe_num_map);--returnret;-}--staticintpnv_pcibios_sriov_disable(structpci_dev*pdev)-{-pnv_pci_sriov_disable(pdev);--/* Release PCI data */-remove_sriov_vf_pdns(pdev);-return0;-}--staticintpnv_pcibios_sriov_enable(structpci_dev*pdev,u16num_vfs)-{-/* Allocate PCI data */-add_sriov_vf_pdns(pdev);--returnpnv_pci_sriov_enable(pdev,num_vfs);-}-#endif /* CONFIG_PCI_IOV */-staticvoidpnv_pci_ioda1_setup_dma_pe(structpnv_phb*phb,structpnv_ioda_pe*pe);-staticvoidpnv_pci_ioda2_setup_dma_pe(structpnv_phb*phb,-structpnv_ioda_pe*pe);-staticvoidpnv_pci_ioda_dma_dev_setup(structpci_dev*pdev){structpnv_phb*phb=pci_bus_to_pnvhb(pdev->bus);
@@ -2737,117 +2235,6 @@ static void pnv_pci_init_ioda_msis(struct pnv_phb *phb)count,phb->msi_base);}-#ifdef CONFIG_PCI_IOV-staticvoidpnv_pci_ioda_fixup_iov_resources(structpci_dev*pdev)-{-structpnv_phb*phb=pci_bus_to_pnvhb(pdev->bus);-constresource_size_tgate=phb->ioda.m64_segsize>>2;-structresource*res;-inti;-resource_size_tsize,total_vf_bar_sz;-structpci_dn*pdn;-intmul,total_vfs;--pdn=pci_get_pdn(pdev);-pdn->vfs_expanded=0;-pdn->m64_single_mode=false;--total_vfs=pci_sriov_get_totalvfs(pdev);-mul=phb->ioda.total_pe_num;-total_vf_bar_sz=0;--for(i=0;i<PCI_SRIOV_NUM_BARS;i++){-res=&pdev->resource[i+PCI_IOV_RESOURCES];-if(!res->flags||res->parent)-continue;-if(!pnv_pci_is_m64_flags(res->flags)){-dev_warn(&pdev->dev,"Don't support SR-IOV with"-" non M64 VF BAR%d: %pR. \n",-i,res);-gototruncate_iov;-}--total_vf_bar_sz+=pci_iov_resource_size(pdev,-i+PCI_IOV_RESOURCES);--/*-*IfbiggerthanquarterofM64segmentsize,justroundup-*poweroftwo.-*-*Generally,oneM64BARmapsoneIOVBAR.Toavoidconflict-*withotherdevices,IOVBARsizeisexpandedtobe-*(total_pe*VF_BAR_size).WhenVF_BAR_sizeishalfofM64-*segmentsize,theexpandedsizewouldequaltohalfofthe-*wholeM64spacesize,whichwillexhausttheM64Spaceand-*limitthesystemflexibility.Thisisadesigndecisionto-*settheboundarytoquarteroftheM64segmentsize.-*/-if(total_vf_bar_sz>gate){-mul=roundup_pow_of_two(total_vfs);-dev_info(&pdev->dev,-"VF BAR Total IOV size %llx > %llx, roundup to %d VFs\n",-total_vf_bar_sz,gate,mul);-pdn->m64_single_mode=true;-break;-}-}--for(i=0;i<PCI_SRIOV_NUM_BARS;i++){-res=&pdev->resource[i+PCI_IOV_RESOURCES];-if(!res->flags||res->parent)-continue;--size=pci_iov_resource_size(pdev,i+PCI_IOV_RESOURCES);-/*-*OnPHB3,theminimumsizealignmentofM64BARinsingle-*modeis32MB.-*/-if(pdn->m64_single_mode&&(size<SZ_32M))-gototruncate_iov;-dev_dbg(&pdev->dev," Fixing VF BAR%d: %pR to\n",i,res);-res->end=res->start+size*mul-1;-dev_dbg(&pdev->dev," %pR\n",res);-dev_info(&pdev->dev,"VF BAR%d: %pR (expanded to %d VFs for PE alignment)",-i,res,mul);-}-pdn->vfs_expanded=mul;--return;--truncate_iov:-/* To save MMIO space, IOV BAR is truncated. */-for(i=0;i<PCI_SRIOV_NUM_BARS;i++){-res=&pdev->resource[i+PCI_IOV_RESOURCES];-res->flags=0;-res->end=res->start-1;-}-}--staticvoidpnv_pci_ioda_fixup_iov(structpci_dev*pdev)-{-if(WARN_ON(pci_dev_is_added(pdev)))-return;--if(pdev->is_virtfn){-structpnv_ioda_pe*pe=pnv_ioda_get_pe(pdev);--/*-*VFPEsaresingle-devicePEssotheirpdevpointerneedsto-*beset.Thepdevdoesn'texistwhenthePEisallocated(in-*(pcibios_sriov_enable())sowefixituphere.-*/-pe->pdev=pdev;-WARN_ON(!(pe->flags&PNV_IODA_PE_VF));-}elseif(pdev->is_physfn){-/*-*ForPFsadjusttheirallocatedIOVresourcestomatchwhat-*thePHBcansupportusingit'sM64BARtable.-*/-pnv_pci_ioda_fixup_iov_resources(pdev);-}-}-#endif /* CONFIG_PCI_IOV */-staticvoidpnv_ioda_setup_pe_res(structpnv_ioda_pe*pe,structresource*res){
@@ -3192,41 +2579,6 @@ static resource_size_t pnv_pci_default_alignment(void)returnPAGE_SIZE;}-#ifdef CONFIG_PCI_IOV-staticresource_size_tpnv_pci_iov_resource_alignment(structpci_dev*pdev,-intresno)-{-structpnv_phb*phb=pci_bus_to_pnvhb(pdev->bus);-structpci_dn*pdn=pci_get_pdn(pdev);-resource_size_talign;--/*-*OnPowerNVplatform,IOVBARismappedbyM64BARtoenablethe-*SR-IOV.Whilefromhardwareperspective,therangemappedbyM64-*BARshouldbesizealigned.-*-*WhenIOVBARismappedwithM64BARinSinglePEmode,theextra-*powernv-specifichardwarerestrictionisgone.Butifjustusethe-*VFBARsizeasthealignment,PFBAR/VFBARmaybeallocatedwith-*inonesegmentofM64#15,whichintroducesthePEconflictbetween-*PFandVF.Basedonthis,theminimumalignmentofanIOVBARis-*m64_segsize.-*-*ThisfunctionreturnsthetotalIOVBARsizeifM64BARisin-*SharedPEmodeorjustVFBARsizeifnot.-*IftheM64BARisinSinglePEmode,returntheVFBARsizeor-*M64segmentsizeifIOVBARsizeisless.-*/-align=pci_iov_resource_size(pdev,resno);-if(!pdn->vfs_expanded)-returnalign;-if(pdn->m64_single_mode)-returnmax(align,(resource_size_t)phb->ioda.m64_segsize);--returnpdn->vfs_expanded*align;-}-#endif /* CONFIG_PCI_IOV */-/* Prevent enabling devices for which we couldn't properly*assignaPE*/
@@ -3436,12 +2788,23 @@ static void pnv_pci_release_device(struct pci_dev *pdev)structpci_dn*pdn=pci_get_pdn(pdev);structpnv_ioda_pe*pe;+/* The VF PE state is torn down when sriov_disable() is called */if(pdev->is_virtfn)return;if(!pdn||pdn->pe_number==IODA_INVALID_PE)return;+#ifdef CONFIG_PCI_IOV+/*+*FIXME:Trymovethistosriov_disable().It'sheresinceweallocate+*theiovstateatprobetimesinceweneedtofiddlewiththeIOV+*resources.+*/+if(pdev->is_physfn)+kfree(pdev->dev.archdata.iov_data);+#endif+/**PCIhotplugcanhappenaspartofEEHerrorrecovery.The@pdn*isn'tremovedandaddedafterwardsinthisscenario.Weshould
+
+#include <linux/kernel.h>
+#include <linux/ioport.h>
+#include <linux/bitmap.h>
+#include <linux/pci.h>
+
+#include <asm/opal.h>
+
+#include "pci.h"
+
+/* for pci_dev_is_added() */
+#include "../../../../drivers/pci/pci.h"
+
+
+static void pnv_pci_ioda_fixup_iov_resources(struct pci_dev *pdev)
+{
+ struct pnv_phb *phb = pci_bus_to_pnvhb(pdev->bus);
+ const resource_size_t gate = phb->ioda.m64_segsize >> 2;
+ struct resource *res;
+ int i;
+ resource_size_t size, total_vf_bar_sz;
+ struct pnv_iov_data *iov;
+ int mul, total_vfs;
+
+ iov = kzalloc(sizeof(*iov), GFP_KERNEL);
+ if (!iov)
+ goto truncate_iov;
+ pdev->dev.archdata.iov_data = iov;
+
+ total_vfs = pci_sriov_get_totalvfs(pdev);
+ mul = phb->ioda.total_pe_num;
+ total_vf_bar_sz = 0;
+
+ for (i = 0; i < PCI_SRIOV_NUM_BARS; i++) {
+ res = &pdev->resource[i + PCI_IOV_RESOURCES];
+ if (!res->flags || res->parent)
+ continue;
+ if (!pnv_pci_is_m64_flags(res->flags)) {
+ dev_warn(&pdev->dev, "Don't support SR-IOV with"
+ " non M64 VF BAR%d: %pR. \n",
+ i, res);
+ goto truncate_iov;
+ }
+
+ total_vf_bar_sz += pci_iov_resource_size(pdev,
+ i + PCI_IOV_RESOURCES);
+
+ /*
+ * If bigger than quarter of M64 segment size, just round up
+ * power of two.
+ *
+ * Generally, one M64 BAR maps one IOV BAR. To avoid conflict
+ * with other devices, IOV BAR size is expanded to be
+ * (total_pe * VF_BAR_size). When VF_BAR_size is half of M64
+ * segment size , the expanded size would equal to half of the
+ * whole M64 space size, which will exhaust the M64 Space and
+ * limit the system flexibility. This is a design decision to
+ * set the boundary to quarter of the M64 segment size.
+ */
+ if (total_vf_bar_sz > gate) {
+ mul = roundup_pow_of_two(total_vfs);
+ dev_info(&pdev->dev,
+ "VF BAR Total IOV size %llx > %llx, roundup to %d VFs\n",
+ total_vf_bar_sz, gate, mul);
+ iov->m64_single_mode = true;
+ break;
+ }
+ }
+
+ for (i = 0; i < PCI_SRIOV_NUM_BARS; i++) {
+ res = &pdev->resource[i + PCI_IOV_RESOURCES];
+ if (!res->flags || res->parent)
+ continue;
+
+ size = pci_iov_resource_size(pdev, i + PCI_IOV_RESOURCES);
+ /*
+ * On PHB3, the minimum size alignment of M64 BAR in single
+ * mode is 32MB.
+ */
+ if (iov->m64_single_mode && (size < SZ_32M))
+ goto truncate_iov;
+ dev_dbg(&pdev->dev, " Fixing VF BAR%d: %pR to\n", i, res);
+ res->end = res->start + size * mul - 1;
+ dev_dbg(&pdev->dev, " %pR\n", res);
+ dev_info(&pdev->dev, "VF BAR%d: %pR (expanded to %d VFs for PE alignment)",
+ i, res, mul);
+ }
+ iov->vfs_expanded = mul;
+
+ return;
+
+truncate_iov:
+ /* To save MMIO space, IOV BAR is truncated. */
+ for (i = 0; i < PCI_SRIOV_NUM_BARS; i++) {
+ res = &pdev->resource[i + PCI_IOV_RESOURCES];
+ res->flags = 0;
+ res->end = res->start - 1;
+ }
+
+ pdev->dev.archdata.iov_data = NULL;
+ kfree(iov);
+}
+
+void pnv_pci_ioda_fixup_iov(struct pci_dev *pdev)
+{
+ if (WARN_ON(pci_dev_is_added(pdev)))
+ return;
+
+ if (pdev->is_virtfn) {
+ struct pnv_ioda_pe *pe = pnv_ioda_get_pe(pdev);
+
+ /*
+ * VF PEs are single-device PEs so their pdev pointer needs to
+ * be set. The pdev doesn't exist when the PE is allocated (in
+ * (pcibios_sriov_enable()) so we fix it up here.
+ */
+ pe->pdev = pdev;
+ WARN_ON(!(pe->flags & PNV_IODA_PE_VF));
+ } else if (pdev->is_physfn) {
+ /*
+ * For PFs adjust their allocated IOV resources to match what
+ * the PHB can support using it's M64 BAR table.
+ */
+ pnv_pci_ioda_fixup_iov_resources(pdev);
+ }
+}
+
+resource_size_t pnv_pci_iov_resource_alignment(struct pci_dev *pdev,
+ int resno)
+{
+ struct pnv_phb *phb = pci_bus_to_pnvhb(pdev->bus);
+ struct pnv_iov_data *iov = pnv_iov_get(pdev);
+ resource_size_t align;
+
+ /*
+ * On PowerNV platform, IOV BAR is mapped by M64 BAR to enable the
+ * SR-IOV. While from hardware perspective, the range mapped by M64
+ * BAR should be size aligned.
+ *
+ * When IOV BAR is mapped with M64 BAR in Single PE mode, the extra
+ * powernv-specific hardware restriction is gone. But if just use the
+ * VF BAR size as the alignment, PF BAR / VF BAR may be allocated with
+ * in one segment of M64 #15, which introduces the PE conflict between
+ * PF and VF. Based on this, the minimum alignment of an IOV BAR is
+ * m64_segsize.
+ *
+ * This function returns the total IOV BAR size if M64 BAR is in
+ * Shared PE mode or just VF BAR size if not.
+ * If the M64 BAR is in Single PE mode, return the VF BAR size or
+ * M64 segment size if IOV BAR size is less.
+ */
+ align = pci_iov_resource_size(pdev, resno);
+
+ /*
+ * iov can be null if we have an SR-IOV device with IOV BAR that can't
+ * be placed in the m64 space (i.e. The BAR is 32bit or non-prefetch).
+ * In that case we don't allow VFs to be enabled so just return the
+ * default alignment.
+ */
+ if (!iov)
+ return align;
This is the new chunk. What would happen before? Non-prefetch BAR would
still go to m64 space?
The rest is accurate.
--
Alexey
On Tue, Jul 14, 2020 at 3:37 PM Alexey Kardashevskiy [off-list ref] wrote:
quoted
On 10/07/2020 15:23, Oliver O'Halloran wrote:
quoted
There's an optimisation in the PE setup which skips performing DMA
setup for a PE if we only have bridges in a PE. The assumption being
that only "real" devices will DMA to system memory, which is probably
fair. However, if we start off with only bridge devices in a PE then
add a non-bridge device the new device won't be able to use DMA because
we never configured it.
Fix this (admittedly pretty weird) edge case by tracking whether we've done
the DMA setup for the PE or not. If a non-bridge device is added to the PE
(via rescan or hotplug, or whatever) we can set up DMA on demand.
So hotplug does not work on powernv then, right? I thought you tested it
a while ago, or this patch is the result of that attempt? If it is, then
It mostly works. Just the really niche case of hot plugging a bridge,
then later on hot plugging a device into the same bus which wouldn't
work.
Do not you have to have a slot (which is a bridge) for hotplug in the
first place, to hotplug the bridge?
As discussed elsewhere, I missed that it is a non bridge device on the
same bus with previously plugged bridge. Now it all makes sense and
Reviewed-by: Alexey Kardashevskiy <redacted>
--
Alexey
SR-IOV support on PowerNV is a byzantine maze of hooks. I have no idea
how anyone is supposed to know how it works except through a lot of
stuffering. Write up some docs about the overall story to help out
the next sucker^Wperson who needs to tinker with it.
Sounds about right :)
Reviewed-by: Alexey Kardashevskiy <redacted>
@@ -12,6 +12,136 @@/* for pci_dev_is_added() */#include"../../../../drivers/pci/pci.h"+/*+*ThemajorityofthecomplexityinsupportingSR-IOVonPowerNVcomesfrom+*theneedtoputtheMMIOspaceforeachVFintoaseparatePE.Internally+*thePHBmapsMMIOaddressestoaspecificPEusingthe"Memory BAR Table".+*TheMBThistoricallyonlyappliedtothe64bitMMIOwindowofthePHB+*soit'scommontoseeitreferredtoasthe"M64BT".+*+*AnMBTentrystoresthemappedrangeasan<base>,<mask>pair.Thisforces+*theaddressrangethatwewanttomaptobepower-of-twosizedandaligned.+*ForconventionalPCIdevicesthisisn'treallyanissuesincePCIdeviceBARs+*havethesamerequirement.+*+*ForaSR-IOVBARthingsarealittlemoreawkwardsincesizeandalignment+*arenotcoupled.Thealignmentissetbasedonthetheper-VFBARsize,but+*thetotalBARareais:number-of-vfs*per-vf-size.ThenumberofVFs+*isn'tnecessarilyapoweroftwo,soneitheristhetotalsize.Tofixthat+*weneedtofinesse(read:hack)theLinuxBARallocatorsothatitwill+*allocatetheSR-IOVBARsinawaythatletsusmapthemusingtheMBT.+*+*Thechangestosizeandalignmentthatweneedtododependonthe"mode"+*ofMBTentrythatweuse.WeonlysupportSR-IOVonPHB3(IODA2)andabove,+*soasabaselinewecanassumethatwehavethefollowingBARmodes+*available:+*+*NB:$PE_COUNTisthenumberofPEsthatthePHBsupports.+*+*a)AsegmentedBARthatsplitsthemappedrangeinto$PE_COUNTequallysized+*segments.Then'thsegmentismappedtothen'thPE.+*b)Anun-segmentedBARthatmapsthewholeaddressrangetoaspecificPE.+*+*+*Weprefertousemodea)sinceitonlyrequiresoneMBTentryperSR-IOVBAR+*Forcomparisonb)requiresoneentryper-VFper-BAR,or:+*(num-vfs*num-sriov-bars)intotal.Tousea)weneedthesizeofeachsegment+*toequalthesizeoftheper-VFBARarea.So:+*+*new_size=per-vf-size*number-of-PEs+*+*ThealignmentfortheSR-IOVBARalsoneedstobechangedfromper-vf-size+*to"new_size",calculatedabove.Implementingthisisaconvolutedprocess+*whichrequiresseveralhooksinthePCIcore:+*+*1.Inpcibios_add_device()wecallpnv_pci_ioda_fixup_iov().+*+*Atthispointthedevicehasbeenprobedandthedevice'sBARsaresized,+*butnoresourceallocationshavebeendone.TheSR-IOVBARsaresized+*basedonthemaximumnumberofVFssupportedbythedeviceandweneed+*toincreasethattonew_size.+*+*2.Later,whenLinuxactuallyassignsresourcesittriestomaketheresource+*allocationsforeachPCIbusascompactaspossible.Asapartofthatit+*sortstheBARsonabusbytheirrequiredalignment,whichiscalculated+*usingpci_resource_alignment().+*+*ForIOVresourcesthisgoes:+*pci_resource_alignment()+*pci_sriov_resource_alignment()+*pcibios_sriov_resource_alignment()+*pnv_pci_iov_resource_alignment()+*+*Ourhookoverridesthedefaultalignment,equaltotheper-vf-size,with+*new_sizecomputedabove.+*+*3.WhenuserspaceenablesVFsforadevice:+*+*sriov_enable()+*pcibios_sriov_enable()+*pnv_pcibios_sriov_enable()+*+*ThisiswhereweactuallyallocatePEnumbersforeachVFandsetupthe+*MBTmappingforeachSR-IOVBAR.Insteps1)and2)wesetupan"arena"+*whereeachMBTsegmentisequalinsizetotheVFBARsowecanshift+*aroundtheactualSR-IOVBARlocationwithinthisarena.Weneedthis+*abilitybecausethePEspaceissharedbyalldevicesonthesamePHB.+*Whenusingmodea)describedabovesegment0inmapstoPE#0whichmight+*bealreadybeingusedbyanotherdeviceonthePHB.+*+*AsaresultweneedallocateacontigiousrangeofPEnumbers,thenshift+*theaddressprogrammedintotheSR-IOVBARofthePFsothattheaddress+*ofVF0matchesupwiththesegmentcorrespondingtothefirstallocated+*PEnumber.Thisishandledinpnv_pci_vf_resource_shift().+*+*OnceallthatisdonewereturntothePCIcorewhichthenenablesVFs,+*scansthemandcreatespci_devsforeach.TheinitprocessforaVFis+*largelythesameasanormaldevice,buttheVFisinsertedintotheIODA+*PEthatweallocatedforitratherthanthePEassociatedwiththebus.+*+*4.WhenuserspacedisablesVFsweunwindtheabovein+*pnv_pcibios_sriov_disable().Fortunatelythisisrelativelysimplesince+*wedon'tneedtovalidateanything,justteardownthemappingsand+*moveSR-IOVresourcebacktoits"proper"location.+*+*That'showmodea)works.Intheorymodeb)(singlePEmapping)islesswork+*sincewecanmapeachindividualVFwithaseparateBAR.However,there'sa+*fewlimitations:+*+*1)ForIODA2modeb)hasaminimumalignmentrequirementof32MB.Thismakes+*itonlyusablefordeviceswithverylargeper-VFBARs.Suchdevicesare+*similartoBigFoot.Theydefinitelyexist,butI'veneverseenone.+*+*2)ThenumberofMBTentriesthatwehaveislimited.PHB3andPHB4only+*16totalandsomeareneededfor.MostSR-IOVcapablenetworkcardscansupport+*morethan16VFsoneachport.+*+*Weuseb)whenusinga)wouldusemorethan1/4oftheentire64bitMMIO+*windowofthePHB.+*+*+*+*PHB4(IODA3)addedafewnewfeaturesthatwouldbeusefulforSR-IOV.It+*allowedtheMBTtomap32bitMMIOspaceinadditionto64bitwhichallows+*ustosupportSR-IOVBARsinthe32bitMMIOwindow.Thisisusefulsince+*theLinuxBARallocationwillplaceanyBARmarkedasnon-prefetchableinto+*thenon-prefetchablebridgewindow,whichis32bitonly.Italsoaddedtwo+*newmodes:+*+*c)AsegmentedBARsimilartoa),buteachsegmentcanbeindividually+*mappedtoanyPE.Thisismatcheshowthe32bitMMIOwindowworkedon+*IODA1&2.+*+*d)AsegmentedBARwith8,64,or128segments.Thisworkssimilarlytoa),+*butwithfewersegmentsandconfigurablebasePE.+*+*i.e.Then'thsegmentmapstothe(n+base)'thPE.+*+*ThebasePEisalsorequiredtobeamultipleofthewindowsize.+*+*Unfortunately,theOPALAPIdoesn'tcurrently(asofskibootv6.6)allowus+*toexploitanyoftheIODA3features.+*/staticvoidpnv_pci_ioda_fixup_iov_resources(structpci_dev*pdev){
This prevents SR-IOV being used by making the SR-IOV BAR resources
unallocatable. Rename it to reflect what it actually does.
Signed-off-by: Oliver O'Halloran <oohall@gmail.com>
@@ -220,8 +221,8 @@ static void pnv_pci_ioda_fixup_iov_resources(struct pci_dev *pdev)return;-truncate_iov:-/* To save MMIO space, IOV BAR is truncated. */+disable_iov:+/* Save ourselves some MMIO space by disabling the unusable BARs */for(i=0;i<PCI_SRIOV_NUM_BARS;i++){res=&pdev->resource[i+PCI_IOV_RESOURCES];res->flags=0;
No need for the multi-dimensional arrays, just use a bitmap.
Signed-off-by: Oliver O'Halloran <oohall@gmail.com>
---
arch/powerpc/platforms/powernv/pci-sriov.c | 48 +++++++---------------
arch/powerpc/platforms/powernv/pci.h | 7 +++-
2 files changed, 20 insertions(+), 35 deletions(-)
@@ -350,23 +342,14 @@ static int pnv_pci_vf_assign_m64(struct pci_dev *pdev, u16 num_vfs)elsem64_bars=1;-iov->m64_map=kmalloc_array(m64_bars,-sizeof(*iov->m64_map),-GFP_KERNEL);-if(!iov->m64_map)-return-ENOMEM;-/* Initialize the m64_map to IODA_INVALID_M64 */-for(i=0;i<m64_bars;i++)-for(j=0;j<PCI_SRIOV_NUM_BARS;j++)-iov->m64_map[i][j]=IODA_INVALID_M64;--for(i=0;i<PCI_SRIOV_NUM_BARS;i++){res=&pdev->resource[i+PCI_IOV_RESOURCES];if(!res->flags||!res->parent)continue;for(j=0;j<m64_bars;j++){++/* allocate a window ID for this BAR */do{win=find_next_zero_bit(&phb->ioda.m64_bar_alloc,phb->ioda.m64_bar_idx+1,0);
@@ -243,8 +243,11 @@ struct pnv_iov_data {/* Did we map the VF BARs with single-PE IODA BARs? */boolm64_single_mode;-int(*m64_map)[PCI_SRIOV_NUM_BARS];-#define IODA_INVALID_M64 (-1)+/*+*Bitmaskusedtotrackwhichm64windowsthatweusedtomapthe
Language question: either "which" or "that" but both?
+ * SR-IOV BARs for this device.
+ */
+ DECLARE_BITMAP(used_m64_bar_mask, 64);
64 here is the maximum number of M64's (which is 16 at the moment)? Can
we define this 64 somehow (appears twice in this patch alone)?
Anyway, the change is correct.
Reviewed-by: Alexey Kardashevskiy <redacted>
/*
* If we map the SR-IOV BARs with a segmented window then
@@ -243,8 +243,11 @@ struct pnv_iov_data {/* Did we map the VF BARs with single-PE IODA BARs? */boolm64_single_mode;-int(*m64_map)[PCI_SRIOV_NUM_BARS];-#define IODA_INVALID_M64 (-1)+/*+*Bitmaskusedtotrackwhichm64windowsthatweusedtomapthe
Language question: either "which" or "that" but both?
The sequence required to use the single PE BAR mode is kinda janky and
requires a little explanation. The API was designed with P7-IOC style
windows where the setup process is something like:
1. Configure the window start / end address
2. Enable the window
3. Map the segments of each window to the PE
For Single PE BARs the process is:
1. Set the PE for segment zero on a disabled window
2. Set the range
3. Enable the window
Move the OPAL calls into their own helper functions where the quirks can be
contained.
Signed-off-by: Oliver O'Halloran <oohall@gmail.com>
I'd use "segmented" instead of "accordion". Otherwise,
Reviewed-by: Alexey Kardashevskiy <redacted>
@@ -320,6 +320,102 @@ static int pnv_pci_vf_release_m64(struct pci_dev *pdev, u16 num_vfs)return0;}++/*+*PHB3andbeyondsupport"accordion"windows.Thewindow'saddressrange+*issubdividedintophb->ioda.total_pe_numsegmentsandthere'sa1-1+*mappingbetweenPEsandsegments.+*+*They'recalledthatbecauseasthewindowsizechangesthesegmentsizes+*changewithit.Sortoflikeanaccordion,sortof.+*/+staticint64_tpnv_ioda_map_m64_accordion(structpnv_phb*phb,+intwindow_id,+resource_size_tstart,+resource_size_tsize)+{+int64_trc;++rc=opal_pci_set_phb_mem_window(phb->opal_id,+OPAL_M64_WINDOW_TYPE,+window_id,+start,+0,/* unused */+size);+if(rc)+gotoout;++rc=opal_pci_phb_mmio_enable(phb->opal_id,+OPAL_M64_WINDOW_TYPE,+window_id,+OPAL_ENABLE_M64_SPLIT);+out:+if(rc)+pr_err("Failed to map M64 window #%d: %lld\n",window_id,rc);++returnrc;+}++staticint64_tpnv_ioda_map_m64_single(structpnv_phb*phb,+intpe_num,+intwindow_id,+resource_size_tstart,+resource_size_tsize)+{+int64_trc;++/*+*TheAPIforsettingupm64mmiowindowsseemstohavebeendesigned+*withP7-IOCinmind.ForthatchipeachM64BAR(window)hadafixed+*splitof8equallysizedsegmentseachofwhichcouldindividually+*assignedtoaPE.+*+*TheproblemwiththisisthattheAPIdoesn'thaveanywayto+*communicatethenumberofsegmentswewantonaBAR.Thiswasn't+*aproblemforp7-iocsinceyoudidn'thaveachoice,butthe+*singlePEwindowsaddedinPHB3don'tmapcleanlytothisAPI.+*+*Asaresultwe'vegotthisslightlyawkwardprocesswherewe+*callopal_pci_map_pe_mmio_window()toputthesingleinsingle+*PEmode,andsetthePEforthewindowbeforesettingtheaddress+*bounds.WeneedtodoitthiswaybecausethesinglePEwindows+*forPHB3havedifferentalignmentrequirementsonPHB3.+*/+rc=opal_pci_map_pe_mmio_window(phb->opal_id,+pe_num,+OPAL_M64_WINDOW_TYPE,+window_id,+0);+if(rc)+gotoout;++/*+*NB:InsinglePEmodethewindowneedstobealignedto32MB+*/+rc=opal_pci_set_phb_mem_window(phb->opal_id,+OPAL_M64_WINDOW_TYPE,+window_id,+start,+0,/* ignored by FW, m64 is 1-1 */+size);+if(rc)+gotoout;++/*+*Nowactuallyenableit.WespecifiedtheBARshouldbein"non-split"+*modesoFWwillvalidatethattheBARisinsinglePEmode.+*/+rc=opal_pci_phb_mmio_enable(phb->opal_id,+OPAL_M64_WINDOW_TYPE,+window_id,+OPAL_ENABLE_M64_NON_SPLIT);+out:+if(rc)+pr_err("Error mapping single PE BAR\n");++returnrc;+}+staticintpnv_pci_vf_assign_m64(structpci_dev*pdev,u16num_vfs){structpnv_iov_data*iov;
Rework the PE allocation logic to allow allocating blocks of PEs rather
than individually. We'll use this to allocate contigious blocks of PEs for
the SR-IOVs.
The patch does not do just this, it also adds missing mutexes (which is
good) but still misses them in pnv_pci_sriov_disable() and
pnv_pci_ioda_pe_dump().
@@ -145,23 +145,45 @@ static void pnv_ioda_reserve_pe(struct pnv_phb *phb, int pe_no)return;}+mutex_lock(&phb->ioda.pe_alloc_mutex);if(test_and_set_bit(pe_no,phb->ioda.pe_alloc))pr_debug("%s: PE %x was reserved on PHB#%x\n",__func__,pe_no,phb->hose->global_number);+mutex_unlock(&phb->ioda.pe_alloc_mutex);pnv_ioda_init_pe(phb,pe_no);}-structpnv_ioda_pe*pnv_ioda_alloc_pe(structpnv_phb*phb)+structpnv_ioda_pe*pnv_ioda_alloc_pe(structpnv_phb*phb,intcount){-longpe;+structpnv_ioda_pe*ret=NULL;+intrun=0,pe,i;+mutex_lock(&phb->ioda.pe_alloc_mutex);++/* scan backwards for a run of @count cleared bits */for(pe=phb->ioda.total_pe_num-1;pe>=0;pe--){-if(!test_and_set_bit(pe,phb->ioda.pe_alloc))-returnpnv_ioda_init_pe(phb,pe);+if(test_bit(pe,phb->ioda.pe_alloc)){+run=0;+continue;+}++run++;+if(run==count)+break;}+if(run!=count)+gotoout;-returnNULL;+for(i=pe;i<pe+count;i++){+set_bit(i,phb->ioda.pe_alloc);+pnv_ioda_init_pe(phb,i);+}+ret=&phb->ioda.pe_array[pe];++out:+mutex_unlock(&phb->ioda.pe_alloc_mutex);+returnret;}voidpnv_ioda_free_pe(structpnv_ioda_pe*pe)
@@ -173,7 +195,10 @@ void pnv_ioda_free_pe(struct pnv_ioda_pe *pe)WARN_ON(pe->npucomp);/* NPUs for nvlink are not supposed to be freed */kfree(pe->npucomp);memset(pe,0,sizeof(structpnv_ioda_pe));++mutex_lock(&phb->ioda.pe_alloc_mutex);clear_bit(pe_num,phb->ioda.pe_alloc);+mutex_unlock(&phb->ioda.pe_alloc_mutex);}/* The default M64 BAR is shared by all PEs */
@@ -1047,7 +1072,7 @@ static struct pnv_ioda_pe *pnv_ioda_setup_bus_PE(struct pci_bus *bus, bool all)/* The PE number isn't pinned by M64 */if(!pe)-pe=pnv_ioda_alloc_pe(phb);+pe=pnv_ioda_alloc_pe(phb,1);if(!pe){pr_warn("%s: Not enough PE# available for PCI bus %04x:%02x\n",
@@ -3065,7 +3090,7 @@ static void __init pnv_pci_init_ioda_phb(struct device_node *np,pnv_ioda_reserve_pe(phb,phb->ioda.root_pe_idx);}else{/* otherwise just allocate one */-root_pe=pnv_ioda_alloc_pe(phb);+root_pe=pnv_ioda_alloc_pe(phb,1);phb->ioda.root_pe_idx=root_pe->pe_number;}
On Wed, Jul 15, 2020 at 12:29 PM Alexey Kardashevskiy [off-list ref] wrote:
On 10/07/2020 15:23, Oliver O'Halloran wrote:
quoted
Rework the PE allocation logic to allow allocating blocks of PEs rather
than individually. We'll use this to allocate contigious blocks of PEs for
the SR-IOVs.
The patch does not do just this, it also adds missing mutexes (which is
good) but still misses them in pnv_pci_sriov_disable() and
pnv_pci_ioda_pe_dump().
The current implementation doesn't need the mutex because alloc,
reserve and free all use atomic bit ops. The mutex has been there
forever with nothing actually using it, but with the change we need to
prevent modifications to the bitmap while alloc() is scanning it. I
probably should have mentioned that in the commit message.
On Wed, Jul 15, 2020 at 12:29 PM Alexey Kardashevskiy [off-list ref] wrote:
quoted
On 10/07/2020 15:23, Oliver O'Halloran wrote:
quoted
Rework the PE allocation logic to allow allocating blocks of PEs rather
than individually. We'll use this to allocate contigious blocks of PEs for
the SR-IOVs.
The patch does not do just this, it also adds missing mutexes (which is
good) but still misses them in pnv_pci_sriov_disable() and
pnv_pci_ioda_pe_dump().
The current implementation doesn't need the mutex because alloc,
reserve and free all use atomic bit ops.
Ah, ok.
The mutex has been there
forever with nothing actually using it, but with the change we need to
prevent modifications to the bitmap while alloc() is scanning it. I
probably should have mentioned that in the commit message.
but bitmap_clear() (from pnv_pci_sriov_disable()) is not atomic. It
probably does not matter as the next patch gets rid of it anyway.
--
Alexey
Currently the iov->pe_num_map[] does one of two things depending on
whether single PE mode is being used or not. When it is, this contains an
array which maps a vf_index to the corresponding PE number. When single PE
mode is not being used this contains a scalar which is the base PE for the
set of enabled VFs (for for VFn is base + n).
The array was necessary because when calling pnv_ioda_alloc_pe() there is
no guarantee that the allocated PEs would be contigious. We can now
s/contigious/contiguous/ here and below.
quoted hunk
allocate contigious blocks of PEs so this is no longer an issue. This
allows us to drop the if (single_mode) {} .. else {} block scattered
through the SR-IOV code which is a nice clean up.
Signed-off-by: Oliver O'Halloran <oohall@gmail.com>
---
arch/powerpc/platforms/powernv/pci-sriov.c | 109 +++++----------------
arch/powerpc/platforms/powernv/pci.h | 4 +-
2 files changed, 25 insertions(+), 88 deletions(-)
@@ -717,55 +700,14 @@ static int pnv_pci_sriov_enable(struct pci_dev *pdev, u16 num_vfs)return-ENOSPC;}-/*-*WhenM64BARsfunctionsinSinglePEmode,thenumberofVFs-*couldbeenabledmustbelessthanthenumberofM64BARs.-*/-if(iov->m64_single_mode&&num_vfs>phb->ioda.m64_bar_idx){-dev_info(&pdev->dev,"Not enough M64 BAR for VFs\n");+/* allocate a contigious block of PEs for our VFs */+base_pe=pnv_ioda_alloc_pe(phb,num_vfs);+if(!base_pe){+pci_err(pdev,"Unable to allocate PEs for %d VFs\n",num_vfs);return-EBUSY;}-/* Allocating pe_num_map */-if(iov->m64_single_mode)-iov->pe_num_map=kmalloc_array(num_vfs,-sizeof(*iov->pe_num_map),-GFP_KERNEL);-else-iov->pe_num_map=kmalloc(sizeof(*iov->pe_num_map),GFP_KERNEL);--if(!iov->pe_num_map)-return-ENOMEM;--if(iov->m64_single_mode)-for(i=0;i<num_vfs;i++)-iov->pe_num_map[i]=IODA_INVALID_PE;--/* Calculate available PE for required VFs */-if(iov->m64_single_mode){-for(i=0;i<num_vfs;i++){-pe=pnv_ioda_alloc_pe(phb);-if(!pe){-ret=-EBUSY;-gotom64_failed;-}--iov->pe_num_map[i]=pe->pe_number;-}-}else{-mutex_lock(&phb->ioda.pe_alloc_mutex);-*iov->pe_num_map=bitmap_find_next_zero_area(-phb->ioda.pe_alloc,phb->ioda.total_pe_num,-0,num_vfs,0);-if(*iov->pe_num_map>=phb->ioda.total_pe_num){-mutex_unlock(&phb->ioda.pe_alloc_mutex);-dev_info(&pdev->dev,"Failed to enable VF%d\n",num_vfs);-kfree(iov->pe_num_map);-return-EBUSY;-}-bitmap_set(phb->ioda.pe_alloc,*iov->pe_num_map,num_vfs);-mutex_unlock(&phb->ioda.pe_alloc_mutex);-}+iov->vf_pe_arr=base_pe;iov->num_vfs=num_vfs;/* Assign M64 window accordingly */
@@ -238,7 +238,9 @@ struct pnv_iov_data {/* number of VFs enabled */u16num_vfs;-unsignedint*pe_num_map;/* PE# for the first VF PE or array */++/* pointer to the array of VF PEs. num_vfs long*/
I read the comment and for a second I thought that now you are storing
pnv_ioda_pe structs in pnv_iov_data which is not true: vf_pe_arr
actually points inside phb->ioda.pe_array[]. May be add this to the
comment please.
Otherwise good,
Reviewed-by: Alexey Kardashevskiy <redacted>
+ struct pnv_ioda_pe *vf_pe_arr;
/* Did we map the VF BARs with single-PE IODA BARs? */
bool m64_single_mode;
Remove the IODA2 PHB checks. We already assume IODA2 in several places so
there's not much point in wrapping most of the setup and teardown process
in an if block.
Signed-off-by: Oliver O'Halloran <oohall@gmail.com>
---
arch/powerpc/platforms/powernv/pci-sriov.c | 86 ++++++++++++----------
1 file changed, 49 insertions(+), 37 deletions(-)
@@ -610,16 +610,18 @@ static void pnv_pci_sriov_disable(struct pci_dev *pdev)num_vfs=iov->num_vfs;base_pe=iov->vf_pe_arr[0].pe_number;+if(WARN_ON(!iov))+return;+/* Release VF PEs */pnv_ioda_release_vf_PE(pdev);-if(phb->type==PNV_PHB_IODA2){-if(!iov->m64_single_mode)-pnv_pci_vf_resource_shift(pdev,-base_pe);+/* Un-shift the IOV BAR resources */+if(!iov->m64_single_mode)+pnv_pci_vf_resource_shift(pdev,-base_pe);-/* Release M64 windows */-pnv_pci_vf_release_m64(pdev,num_vfs);-}+/* Release M64 windows */+pnv_pci_vf_release_m64(pdev,num_vfs);}staticvoidpnv_ioda_setup_vf_PE(structpci_dev*pdev,u16num_vfs)
@@ -693,41 +695,51 @@ static int pnv_pci_sriov_enable(struct pci_dev *pdev, u16 num_vfs)phb=pci_bus_to_pnvhb(pdev->bus);iov=pnv_iov_get(pdev);-if(phb->type==PNV_PHB_IODA2){-if(!iov->vfs_expanded){-dev_info(&pdev->dev,"don't support this SRIOV device"-" with non 64bit-prefetchable IOV BAR\n");-return-ENOSPC;-}+/*+*There'sacallstoIODA2PEsetupcodelitteredthroughout.Wecould+*probablyfixthat,butwe'dstillhaveproblemsduetothe+*restrictioninherentonIODA1PHBs.+*+*NB:WeclassIODA3asIODA2sincethey'reverysimilar.+*/+if(phb->type!=PNV_PHB_IODA2){+pci_err(pdev,"SR-IOV is not supported on this PHB\n");+return-ENXIO;+}
or we could just skip setting
ppc_md.pcibios_sriov_enable = pnv_pcibios_sriov_enable;
for uninteresting platforms in pnv_pci_init_ioda_phb().
- /* allocate a contigious block of PEs for our VFs */
- base_pe = pnv_ioda_alloc_pe(phb, num_vfs);
- if (!base_pe) {
- pci_err(pdev, "Unable to allocate PEs for %d VFs\n", num_vfs);
- return -EBUSY;
- }
+ if (!iov->vfs_expanded) {
+ dev_info(&pdev->dev, "don't support this SRIOV device"
+ " with non 64bit-prefetchable IOV BAR\n");
+ return -ENOSPC;
+ }
- iov->vf_pe_arr = base_pe;
- iov->num_vfs = num_vfs;
+ /* allocate a contigious block of PEs for our VFs */
+ base_pe = pnv_ioda_alloc_pe(phb, num_vfs);
+ if (!base_pe) {
+ pci_err(pdev, "Unable to allocate PEs for %d VFs\n", num_vfs);
+ return -EBUSY;
+ }
- /* Assign M64 window accordingly */
- ret = pnv_pci_vf_assign_m64(pdev, num_vfs);
- if (ret) {
- dev_info(&pdev->dev, "Not enough M64 window resources\n");
- goto m64_failed;
- }
+ iov->vf_pe_arr = base_pe;
+ iov->num_vfs = num_vfs;
- /*
- * When using one M64 BAR to map one IOV BAR, we need to shift
- * the IOV BAR according to the PE# allocated to the VFs.
- * Otherwise, the PE# for the VF will conflict with others.
- */
- if (!iov->m64_single_mode) {
- ret = pnv_pci_vf_resource_shift(pdev,
- base_pe->pe_number);
- if (ret)
- goto shift_failed;
- }
+ /* Assign M64 window accordingly */
+ ret = pnv_pci_vf_assign_m64(pdev, num_vfs);
+ if (ret) {
+ dev_info(&pdev->dev, "Not enough M64 window resources\n");
+ goto m64_failed;
+ }
+
+ /*
+ * When using one M64 BAR to map one IOV BAR, we need to shift
+ * the IOV BAR according to the PE# allocated to the VFs.
+ * Otherwise, the PE# for the VF will conflict with others.
+ */
+ if (!iov->m64_single_mode) {
+ ret = pnv_pci_vf_resource_shift(pdev,
+ base_pe->pe_number);
This can be a single line now. Thanks,
+ if (ret)
+ goto shift_failed;
}
/* Setup VF PEs */
@@ -443,17 +460,9 @@ static int pnv_pci_vf_assign_m64(struct pci_dev *pdev, u16 num_vfs)continue;for(j=0;j<m64_bars;j++){--/* allocate a window ID for this BAR */-do{-win=find_next_zero_bit(&phb->ioda.m64_bar_alloc,-phb->ioda.m64_bar_idx+1,0);--if(win>=phb->ioda.m64_bar_idx+1)-gotom64_failed;-}while(test_and_set_bit(win,&phb->ioda.m64_bar_alloc));-set_bit(win,iov->used_m64_bar_mask);-+win=pnv_pci_alloc_m64_bar(phb,iov);+if(win<0)+gotom64_failed;if(iov->m64_single_mode){intpe_num=iov->vf_pe_arr[j].pe_number;
On Wed, Jul 15, 2020 at 2:00 PM Alexey Kardashevskiy [off-list ref] wrote:
or we could just skip setting
ppc_md.pcibios_sriov_enable = pnv_pcibios_sriov_enable;
for uninteresting platforms in pnv_pci_init_ioda_phb().
I don't think so. ppc_md is per-platform, not per-PHB andw e still
have to deal with a mixture of IODA/NVLink/OpenCAPI PHBs on a single
system. We could make it a callback in pnv_phb, but it seemed like
more indirection than it's worth.
On Wed, Jul 15, 2020 at 2:41 PM Alexey Kardashevskiy [off-list ref] wrote:
On 15/07/2020 14:21, Oliver O'Halloran wrote:
quoted
On Wed, Jul 15, 2020 at 2:00 PM Alexey Kardashevskiy [off-list ref] wrote:
quoted
or we could just skip setting
ppc_md.pcibios_sriov_enable = pnv_pcibios_sriov_enable;
for uninteresting platforms in pnv_pci_init_ioda_phb().
I don't think so. ppc_md is per-platform, not per-PHB andw e still
have to deal with a mixture of IODA/NVLink/OpenCAPI PHBs on a single
system.
NVLink/OpenCAPI won't have SRIOV devices.
...OR WILL THEY?
Other types won't appear on
the same platform simultaneously. It is not too clean, yes.
Sure, my point is that's a per-PHB setting rather than a per-platform
one so we should set it up like that.
quoted
We could make it a callback in pnv_phb, but it seemed like
more indirection than it's worth.
I genuinely dislike how we use ppc_md so removing things from it is
definitely a good thing.
you wouldn't be able to get rid of it. We'd have something like what
we have for the existing pcibios calls where there's a "generic" one
that bounces it to a member of pci_controller_ops, which then bounces
it to the pnv_phb method. It's bad and I hate it.
Split up the logic so that we have one branch that handles setting up a
segmented window and another that handles setting up single PE windows for
each VF.
Signed-off-by: Oliver O'Halloran <oohall@gmail.com>
Reviewed-by: Alexey Kardashevskiy <redacted>
quoted hunk
---
This patch could be folded into the previous one. I've kept it
seperate mainly because the diff is *horrific* when they're merged.
---
arch/powerpc/platforms/powernv/pci-sriov.c | 57 ++++++++++------------
1 file changed, 27 insertions(+), 30 deletions(-)
@@ -441,52 +441,49 @@ static int pnv_pci_vf_assign_m64(struct pci_dev *pdev, u16 num_vfs)structresource*res;inti,j;int64_trc;-inttotal_vfs;resource_size_tsize,start;-intm64_bars;+intbase_pe_num;phb=pci_bus_to_pnvhb(pdev->bus);iov=pnv_iov_get(pdev);-total_vfs=pci_sriov_get_totalvfs(pdev);--if(iov->m64_single_mode)-m64_bars=num_vfs;-else-m64_bars=1;for(i=0;i<PCI_SRIOV_NUM_BARS;i++){res=&pdev->resource[i+PCI_IOV_RESOURCES];if(!res->flags||!res->parent)continue;-for(j=0;j<m64_bars;j++){+/* don't need single mode? map everything in one go! */+if(!iov->m64_single_mode){win=pnv_pci_alloc_m64_bar(phb,iov);if(win<0)gotom64_failed;-if(iov->m64_single_mode){-intpe_num=iov->vf_pe_arr[j].pe_number;--size=pci_iov_resource_size(pdev,-PCI_IOV_RESOURCES+i);-start=res->start+size*j;-rc=pnv_ioda_map_m64_single(phb,win,-pe_num,-start,-size);-}else{-size=resource_size(res);-start=res->start;--rc=pnv_ioda_map_m64_accordion(phb,win,start,-size);-}+size=resource_size(res);+start=res->start;-if(rc!=OPAL_SUCCESS){-dev_err(&pdev->dev,"Failed to map M64 window #%d: %lld\n",-win,rc);+rc=pnv_ioda_map_m64_accordion(phb,win,start,size);+if(rc)+gotom64_failed;++continue;+}++/* otherwise map each VF with single PE BARs */+size=pci_iov_resource_size(pdev,PCI_IOV_RESOURCES+i);+base_pe_num=iov->vf_pe_arr[0].pe_number;++for(j=0;j<num_vfs;j++){+win=pnv_pci_alloc_m64_bar(phb,iov);+if(win<0)+gotom64_failed;++start=res->start+size*j;+rc=pnv_ioda_map_m64_single(phb,win,+base_pe_num+j,+start,+size);+if(rc)gotom64_failed;-}}}return0;
On Wed, Jul 15, 2020 at 2:41 PM Alexey Kardashevskiy [off-list ref] wrote:
quoted
On 15/07/2020 14:21, Oliver O'Halloran wrote:
quoted
On Wed, Jul 15, 2020 at 2:00 PM Alexey Kardashevskiy [off-list ref] wrote:
quoted
or we could just skip setting
ppc_md.pcibios_sriov_enable = pnv_pcibios_sriov_enable;
for uninteresting platforms in pnv_pci_init_ioda_phb().
I don't think so. ppc_md is per-platform, not per-PHB andw e still
have to deal with a mixture of IODA/NVLink/OpenCAPI PHBs on a single
system.
NVLink/OpenCAPI won't have SRIOV devices.
...OR WILL THEY?
NO!
quoted
Other types won't appear on
the same platform simultaneously. It is not too clean, yes.
Sure, my point is that's a per-PHB setting rather than a per-platform
one so we should set it up like that.
and my point is that you did too good job getting rid of IODA1 vs IODA2
checks to keep this check. But ok.
quoted
quoted
We could make it a callback in pnv_phb, but it seemed like
more indirection than it's worth.
I genuinely dislike how we use ppc_md so removing things from it is
definitely a good thing.
you wouldn't be able to get rid of it. We'd have something like what
we have for the existing pcibios calls where there's a "generic" one
that bounces it to a member of pci_controller_ops, which then bounces
it to the pnv_phb method. It's bad and I hate it.
Using single PE BARs to map an SR-IOV BAR is really a choice about what
strategy to use when mapping a BAR. It doesn't make much sense for this to
be a global setting since a device might have one large BAR which needs to
be mapped with single PE windows and another smaller BAR that can be mapped
with a regular segmented window. Make the segmented vs single decision a
per-BAR setting and clean up the logic that decides which mode to use.
Signed-off-by: Oliver O'Halloran <oohall@gmail.com>
---
arch/powerpc/platforms/powernv/pci-sriov.c | 131 +++++++++++----------
arch/powerpc/platforms/powernv/pci.h | 10 +-
2 files changed, 75 insertions(+), 66 deletions(-)
@@ -158,9 +157,9 @@ static void pnv_pci_ioda_fixup_iov_resources(struct pci_dev *pdev)gotodisable_iov;pdev->dev.archdata.iov_data=iov;+/* FIXME: totalvfs > phb->ioda.total_pe_num is going to be a problem */
WARN_ON_ONCE() then?
quoted hunk
total_vfs = pci_sriov_get_totalvfs(pdev);
mul = phb->ioda.total_pe_num;
- total_vf_bar_sz = 0;
for (i = 0; i < PCI_SRIOV_NUM_BARS; i++) {
res = &pdev->resource[i + PCI_IOV_RESOURCES];
@@ -173,50 +172,51 @@ static void pnv_pci_ioda_fixup_iov_resources(struct pci_dev *pdev) goto disable_iov; }- total_vf_bar_sz += pci_iov_resource_size(pdev,- i + PCI_IOV_RESOURCES);+ vf_bar_sz = pci_iov_resource_size(pdev, i + PCI_IOV_RESOURCES); /*- * If bigger than quarter of M64 segment size, just round up- * power of two.+ * Generally, one segmented M64 BAR maps one IOV BAR. However,+ * if a VF BAR is too large we end up wasting a lot of space.+ * If we've got a BAR that's bigger than greater than 1/4 of the
bigger, greater, huger? :)
Also, a nit: s/got a BAR/got a VF BAR/
+ * default window's segment size then switch to using single PE
+ * windows. This limits the total number of VFs we can support.
Just to get idea about absolute numbers here.
On my P9:
./pciex@600c3c0300000/ibm,opal-m64-window
00060200 00000000 00060200 00000000 00000040 00000000
so that default window's segment size is 0x40.0000.0000/512 = 512MB?
*
- * Generally, one M64 BAR maps one IOV BAR. To avoid conflict
- * with other devices, IOV BAR size is expanded to be
- * (total_pe * VF_BAR_size). When VF_BAR_size is half of M64
- * segment size , the expanded size would equal to half of the
- * whole M64 space size, which will exhaust the M64 Space and
- * limit the system flexibility. This is a design decision to
- * set the boundary to quarter of the M64 segment size.
+ * The 1/4 limit is arbitrary and can be tweaked.
*/
- if (total_vf_bar_sz > gate) {
- mul = roundup_pow_of_two(total_vfs);
- dev_info(&pdev->dev,
- "VF BAR Total IOV size %llx > %llx, roundup to %d VFs\n",
- total_vf_bar_sz, gate, mul);
- iov->m64_single_mode = true;
- break;
- }
- }
+ if (vf_bar_sz > (phb->ioda.m64_segsize >> 2)) {
+ /*
+ * On PHB3, the minimum size alignment of M64 BAR in
+ * single mode is 32MB. If this VF BAR is smaller than
+ * 32MB, but still too large for a segmented window
+ * then we can't map it and need to disable SR-IOV for
+ * this device.
Why not use single PE mode for such BAR? Better than nothing.
quoted hunk
+ */
+ if (vf_bar_sz < SZ_32M) {
+ pci_err(pdev, "VF BAR%d: %pR can't be mapped in single PE mode\n",
+ i, res);
+ goto disable_iov;
+ }
- for (i = 0; i < PCI_SRIOV_NUM_BARS; i++) {
- res = &pdev->resource[i + PCI_IOV_RESOURCES];
- if (!res->flags || res->parent)
+ iov->m64_single_mode[i] = true;
continue;
+ }
+
- size = pci_iov_resource_size(pdev, i + PCI_IOV_RESOURCES);
/*
- * On PHB3, the minimum size alignment of M64 BAR in single
- * mode is 32MB.
+ * This BAR can be mapped with one segmented window, so adjust
+ * te resource size to accommodate.
*/
- if (iov->m64_single_mode && (size < SZ_32M))
- goto disable_iov;
+ pci_dbg(pdev, " Fixing VF BAR%d: %pR to\n", i, res);
+ res->end = res->start + vf_bar_sz * mul - 1;
+ pci_dbg(pdev, " %pR\n", res);
- dev_dbg(&pdev->dev, " Fixing VF BAR%d: %pR to\n", i, res);
- res->end = res->start + size * mul - 1;
- dev_dbg(&pdev->dev, " %pR\n", res);
- dev_info(&pdev->dev, "VF BAR%d: %pR (expanded to %d VFs for PE alignment)",
+ pci_info(pdev, "VF BAR%d: %pR (expanded to %d VFs for PE alignment)",
i, res, mul);
+
+ iov->need_shift = true;
}
+
+ // what should this be?
iov->vfs_expanded = mul;
return;
@@ -260,42 +260,42 @@ void pnv_pci_ioda_fixup_iov(struct pci_dev *pdev) resource_size_t pnv_pci_iov_resource_alignment(struct pci_dev *pdev, int resno) {- struct pnv_phb *phb = pci_bus_to_pnvhb(pdev->bus); struct pnv_iov_data *iov = pnv_iov_get(pdev); resource_size_t align;- /*- * On PowerNV platform, IOV BAR is mapped by M64 BAR to enable the- * SR-IOV. While from hardware perspective, the range mapped by M64- * BAR should be size aligned.- *- * When IOV BAR is mapped with M64 BAR in Single PE mode, the extra- * powernv-specific hardware restriction is gone. But if just use the- * VF BAR size as the alignment, PF BAR / VF BAR may be allocated with- * in one segment of M64 #15, which introduces the PE conflict between- * PF and VF. Based on this, the minimum alignment of an IOV BAR is- * m64_segsize.- *- * This function returns the total IOV BAR size if M64 BAR is in- * Shared PE mode or just VF BAR size if not.- * If the M64 BAR is in Single PE mode, return the VF BAR size or- * M64 segment size if IOV BAR size is less.- */- align = pci_iov_resource_size(pdev, resno);+ int bar_no = resno - PCI_IOV_RESOURCES; /* * iov can be null if we have an SR-IOV device with IOV BAR that can't * be placed in the m64 space (i.e. The BAR is 32bit or non-prefetch).- * In that case we don't allow VFs to be enabled so just return the- * default alignment.+ * In that case we don't allow VFs to be enabled since one of their+ * BARs would not be placed in the correct PE. */ if (!iov) return align; if (!iov->vfs_expanded) return align;- if (iov->m64_single_mode)- return max(align, (resource_size_t)phb->ioda.m64_segsize);+ align = pci_iov_resource_size(pdev, resno);++ /*+ * If we're using single mode then we can just use the native VF BAR+ * alignment. We validated that it's possible to use a single PE+ * window above when we did the fixup.+ */+ if (iov->m64_single_mode[bar_no])+ return align;++ /*+ * On PowerNV platform, IOV BAR is mapped by M64 BAR to enable the+ * SR-IOV. While from hardware perspective, the range mapped by M64+ * BAR should be size aligned.+ *+ * This function returns the total IOV BAR size if M64 BAR is in+ * Shared PE mode or just VF BAR size if not.+ * If the M64 BAR is in Single PE mode, return the VF BAR size or+ * M64 segment size if IOV BAR size is less.+ */ return iov->vfs_expanded * align; }
@@ -453,7 +453,7 @@ static int pnv_pci_vf_assign_m64(struct pci_dev *pdev, u16 num_vfs) continue; /* don't need single mode? map everything in one go! */- if (!iov->m64_single_mode) {+ if (!iov->m64_single_mode[i]) { win = pnv_pci_alloc_m64_bar(phb, iov); if (win < 0) goto m64_failed;
@@ -546,6 +546,8 @@ static int pnv_pci_vf_resource_shift(struct pci_dev *dev, int offset) res = &dev->resource[i + PCI_IOV_RESOURCES]; if (!res->flags || !res->parent) continue;+ if (iov->m64_single_mode[i])+ continue; /* * The actual IOV BAR range is determined by the start address
@@ -577,6 +579,8 @@ static int pnv_pci_vf_resource_shift(struct pci_dev *dev, int offset) res = &dev->resource[i + PCI_IOV_RESOURCES]; if (!res->flags || !res->parent) continue;+ if (iov->m64_single_mode[i])+ continue; size = pci_iov_resource_size(dev, i + PCI_IOV_RESOURCES); res2 = *res;
@@ -622,8 +626,8 @@ static void pnv_pci_sriov_disable(struct pci_dev *pdev) /* Release VF PEs */ pnv_ioda_release_vf_PE(pdev);- /* Un-shift the IOV BAR resources */- if (!iov->m64_single_mode)+ /* Un-shift the IOV BARs if we need to */+ if (iov->need_shift) pnv_pci_vf_resource_shift(pdev, -base_pe); /* Release M64 windows */
@@ -741,9 +745,8 @@ static int pnv_pci_sriov_enable(struct pci_dev *pdev, u16 num_vfs) * the IOV BAR according to the PE# allocated to the VFs. * Otherwise, the PE# for the VF will conflict with others. */- if (!iov->m64_single_mode) {- ret = pnv_pci_vf_resource_shift(pdev,- base_pe->pe_number);+ if (iov->need_shift) {+ ret = pnv_pci_vf_resource_shift(pdev, base_pe->pe_number); if (ret) goto shift_failed; }
@@ -236,14 +236,20 @@ struct pnv_iov_data {/* number of VFs IOV BAR expanded. FIXME: rename this to something less bad */u16vfs_expanded;+/*+*indicatesifweneedtomoveourIOVBARtoaccountforour+*allocatedPEnumberwhenenablingVFs.+*/+boolneed_shift;+/* number of VFs enabled */u16num_vfs;/* pointer to the array of VF PEs. num_vfs long*/structpnv_ioda_pe*vf_pe_arr;-/* Did we map the VF BARs with single-PE IODA BARs? */-boolm64_single_mode;+/* Did we map the VF BAR with single-PE IODA BARs? */+boolm64_single_mode[PCI_SRIOV_NUM_BARS];/**Bitmaskusedtotrackwhichm64windowsthatweusedtomapthe
On Wed, Jul 15, 2020 at 3:24 PM Alexey Kardashevskiy [off-list ref] wrote:
quoted
@@ -158,9 +157,9 @@ static void pnv_pci_ioda_fixup_iov_resources(struct pci_dev *pdev) goto disable_iov; pdev->dev.archdata.iov_data = iov;+ /* FIXME: totalvfs > phb->ioda.total_pe_num is going to be a problem */
WARN_ON_ONCE() then?
can't hurt
quoted
@@ -173,50 +172,51 @@ static void pnv_pci_ioda_fixup_iov_resources(struct pci_dev *pdev) goto disable_iov; }- total_vf_bar_sz += pci_iov_resource_size(pdev,- i + PCI_IOV_RESOURCES);+ vf_bar_sz = pci_iov_resource_size(pdev, i + PCI_IOV_RESOURCES); /*- * If bigger than quarter of M64 segment size, just round up- * power of two.+ * Generally, one segmented M64 BAR maps one IOV BAR. However,+ * if a VF BAR is too large we end up wasting a lot of space.+ * If we've got a BAR that's bigger than greater than 1/4 of the
bigger, greater, huger? :)
Also, a nit: s/got a BAR/got a VF BAR/
whatever, it's just words
quoted
+ * default window's segment size then switch to using single PE
+ * windows. This limits the total number of VFs we can support.
Just to get idea about absolute numbers here.
On my P9:
./pciex@600c3c0300000/ibm,opal-m64-window
00060200 00000000 00060200 00000000 00000040 00000000
so that default window's segment size is 0x40.0000.0000/512 = 512MB?
Yeah. It'll vary a bit since PHB3 and some PHB4s have 256.
quoted
*
- * Generally, one M64 BAR maps one IOV BAR. To avoid conflict
- * with other devices, IOV BAR size is expanded to be
- * (total_pe * VF_BAR_size). When VF_BAR_size is half of M64
- * segment size , the expanded size would equal to half of the
- * whole M64 space size, which will exhaust the M64 Space and
- * limit the system flexibility. This is a design decision to
- * set the boundary to quarter of the M64 segment size.
+ * The 1/4 limit is arbitrary and can be tweaked.
*/
- if (total_vf_bar_sz > gate) {
- mul = roundup_pow_of_two(total_vfs);
- dev_info(&pdev->dev,
- "VF BAR Total IOV size %llx > %llx, roundup to %d VFs\n",
- total_vf_bar_sz, gate, mul);
- iov->m64_single_mode = true;
- break;
- }
- }
+ if (vf_bar_sz > (phb->ioda.m64_segsize >> 2)) {
+ /*
+ * On PHB3, the minimum size alignment of M64 BAR in
+ * single mode is 32MB. If this VF BAR is smaller than
+ * 32MB, but still too large for a segmented window
+ * then we can't map it and need to disable SR-IOV for
+ * this device.
Why not use single PE mode for such BAR? Better than nothing.
Suppose you could, but I figured VFs were mainly interesting since you
could give each VF to a separate guest. If there's multiple VFs under
the same single PE BAR then they'd have to be assigned to the same
guest in order to retain the freeze/unfreeze behaviour that PAPR
requires. I guess that's how it used to work, but it seems better just
to disable them rather than having VFs which sort of work.
On Wed, Jul 15, 2020 at 3:24 PM Alexey Kardashevskiy [off-list ref] wrote:
quoted
quoted
@@ -158,9 +157,9 @@ static void pnv_pci_ioda_fixup_iov_resources(struct pci_dev *pdev) goto disable_iov; pdev->dev.archdata.iov_data = iov;+ /* FIXME: totalvfs > phb->ioda.total_pe_num is going to be a problem */
WARN_ON_ONCE() then?
can't hurt
quoted
quoted
@@ -173,50 +172,51 @@ static void pnv_pci_ioda_fixup_iov_resources(struct pci_dev *pdev) goto disable_iov; }- total_vf_bar_sz += pci_iov_resource_size(pdev,- i + PCI_IOV_RESOURCES);+ vf_bar_sz = pci_iov_resource_size(pdev, i + PCI_IOV_RESOURCES); /*- * If bigger than quarter of M64 segment size, just round up- * power of two.+ * Generally, one segmented M64 BAR maps one IOV BAR. However,+ * if a VF BAR is too large we end up wasting a lot of space.+ * If we've got a BAR that's bigger than greater than 1/4 of the
bigger, greater, huger? :)
Also, a nit: s/got a BAR/got a VF BAR/
whatever, it's just words
You are talking about these BARs and those BARs and since we want "to
help out
the next sucker^Wperson who needs to tinker with it", using precise term
is kinda essential here.
quoted
quoted
+ * default window's segment size then switch to using single PE
+ * windows. This limits the total number of VFs we can support.
Just to get idea about absolute numbers here.
On my P9:
./pciex@600c3c0300000/ibm,opal-m64-window
00060200 00000000 00060200 00000000 00000040 00000000
so that default window's segment size is 0x40.0000.0000/512 = 512MB?
Yeah. It'll vary a bit since PHB3 and some PHB4s have 256.
quoted
quoted
*
- * Generally, one M64 BAR maps one IOV BAR. To avoid conflict
- * with other devices, IOV BAR size is expanded to be
- * (total_pe * VF_BAR_size). When VF_BAR_size is half of M64
- * segment size , the expanded size would equal to half of the
- * whole M64 space size, which will exhaust the M64 Space and
- * limit the system flexibility. This is a design decision to
- * set the boundary to quarter of the M64 segment size.
+ * The 1/4 limit is arbitrary and can be tweaked.
*/
- if (total_vf_bar_sz > gate) {
- mul = roundup_pow_of_two(total_vfs);
- dev_info(&pdev->dev,
- "VF BAR Total IOV size %llx > %llx, roundup to %d VFs\n",
- total_vf_bar_sz, gate, mul);
- iov->m64_single_mode = true;
- break;
- }
- }
+ if (vf_bar_sz > (phb->ioda.m64_segsize >> 2)) {
+ /*
+ * On PHB3, the minimum size alignment of M64 BAR in
+ * single mode is 32MB. If this VF BAR is smaller than
+ * 32MB, but still too large for a segmented window
+ * then we can't map it and need to disable SR-IOV for
+ * this device.
Why not use single PE mode for such BAR? Better than nothing.
Suppose you could, but I figured VFs were mainly interesting since you
could give each VF to a separate guest. If there's multiple VFs under
the same single PE BAR then they'd have to be assigned to the same
True. But with one PE per VF we can still have 15 (or 14?) isolated VFs
which is not hundreds but better than 0.
guest in order to retain the freeze/unfreeze behaviour that PAPR
requires. I guess that's how it used to work, but it seems better just
to disable them rather than having VFs which sort of work.
Well, realistically the segment size should be 8MB to make this matter
(or the whole window 2GB) which does not seem to happen so it does not
matter.
--
Alexey
On Wed, Jul 15, 2020 at 5:05 PM Cédric Le Goater [off-list ref] wrote:
I could but can we fix the issue below before I reboot ? I don't have a
console anymore on these boxes.
Firmware is :
*snip*
Do you know when that started happening? I don't think anything
console related has changed in a very long time, but we probably
haven't tested it on p7 in even longer.
From: Cédric Le Goater <clg@kaod.org> Date: 2020-07-15 10:43:09
On 7/15/20 11:00 AM, Oliver O'Halloran wrote:
On Wed, Jul 15, 2020 at 5:05 PM Cédric Le Goater [off-list ref] wrote:
quoted
I could but can we fix the issue below before I reboot ? I don't have a
console anymore on these boxes.
Firmware is :
*snip*
Do you know when that started happening? I don't think anything
console related has changed in a very long time, but we probably
haven't tested it on p7 in even longer.
On Tue, Jul 14, 2020 at 7:16 PM Alexey Kardashevskiy [off-list ref] wrote:
On 10/07/2020 15:23, Oliver O'Halloran wrote:
quoted
+ align = pci_iov_resource_size(pdev, resno);
+
+ /*
+ * iov can be null if we have an SR-IOV device with IOV BAR that can't
+ * be placed in the m64 space (i.e. The BAR is 32bit or non-prefetch).
+ * In that case we don't allow VFs to be enabled so just return the
+ * default alignment.
+ */
+ if (!iov)
+ return align;
This is the new chunk. What would happen before? Non-prefetch BAR would
still go to m64 space?
I don't think there's any real change. Currently if the setup in
pnv_pci_ioda_fixup_iov_resources() fails then pdn->vfs_expanded will
be zero. The !iov check here fills the same role, but it's more
explicit. vfs_expanded has some other behaviour too so we can't get
rid of it entirely (yet).
On Wed, Jul 15, 2020 at 6:00 PM Alexey Kardashevskiy [off-list ref] wrote:
quoted
quoted
quoted
*
- * Generally, one M64 BAR maps one IOV BAR. To avoid conflict
- * with other devices, IOV BAR size is expanded to be
- * (total_pe * VF_BAR_size). When VF_BAR_size is half of M64
- * segment size , the expanded size would equal to half of the
- * whole M64 space size, which will exhaust the M64 Space and
- * limit the system flexibility. This is a design decision to
- * set the boundary to quarter of the M64 segment size.
+ * The 1/4 limit is arbitrary and can be tweaked.
*/
- if (total_vf_bar_sz > gate) {
- mul = roundup_pow_of_two(total_vfs);
- dev_info(&pdev->dev,
- "VF BAR Total IOV size %llx > %llx, roundup to %d VFs\n",
- total_vf_bar_sz, gate, mul);
- iov->m64_single_mode = true;
- break;
- }
- }
+ if (vf_bar_sz > (phb->ioda.m64_segsize >> 2)) {
+ /*
+ * On PHB3, the minimum size alignment of M64 BAR in
+ * single mode is 32MB. If this VF BAR is smaller than
+ * 32MB, but still too large for a segmented window
+ * then we can't map it and need to disable SR-IOV for
+ * this device.
Why not use single PE mode for such BAR? Better than nothing.
Suppose you could, but I figured VFs were mainly interesting since you
could give each VF to a separate guest. If there's multiple VFs under
the same single PE BAR then they'd have to be assigned to the same
True. But with one PE per VF we can still have 15 (or 14?) isolated VFs
which is not hundreds but better than 0.
We can only use single PE BARs if the per-VF size is >= 32MB due to
the alignment requirements on P8. If the per-VF size is smaller then
we're stuck with multiple VFs inside the same BAR which is bad due to
the PAPR requirements mentioned below. Sure we could look at doing
something else, but considering this matches the current behaviour
it's a bit hard to care...
quoted
guest in order to retain the freeze/unfreeze behaviour that PAPR
requires. I guess that's how it used to work, but it seems better just
to disable them rather than having VFs which sort of work.
Well, realistically the segment size should be 8MB to make this matter
(or the whole window 2GB) which does not seem to happen so it does not
matter.
On Tue, Jul 14, 2020 at 7:16 PM Alexey Kardashevskiy [off-list ref] wrote:
quoted
On 10/07/2020 15:23, Oliver O'Halloran wrote:
quoted
+ align = pci_iov_resource_size(pdev, resno);
+
+ /*
+ * iov can be null if we have an SR-IOV device with IOV BAR that can't
+ * be placed in the m64 space (i.e. The BAR is 32bit or non-prefetch).
+ * In that case we don't allow VFs to be enabled so just return the
+ * default alignment.
+ */
+ if (!iov)
+ return align;
This is the new chunk. What would happen before? Non-prefetch BAR would
still go to m64 space?
I don't think there's any real change. Currently if the setup in
pnv_pci_ioda_fixup_iov_resources() fails then pdn->vfs_expanded will
be zero. The !iov check here fills the same role, but it's more
explicit. vfs_expanded has some other behaviour too so we can't get
rid of it entirely (yet).
The check is fine, you have to have one as @iov can be NULL (unlike
pci_dn). The comment is what bothered me. It would make more sense
somewhere in pnv_pci_ioda_fixup_iov_resources() near
"dev_warn(&pdev->dev, "Don't support SR-IOV with"" as now it suggests
there is one reason for the failed iov configuration only while there
are two reasons.
--
Alexey
On Wed, Jul 15, 2020 at 6:00 PM Alexey Kardashevskiy [off-list ref] wrote:
quoted
quoted
quoted
quoted
*
- * Generally, one M64 BAR maps one IOV BAR. To avoid conflict
- * with other devices, IOV BAR size is expanded to be
- * (total_pe * VF_BAR_size). When VF_BAR_size is half of M64
- * segment size , the expanded size would equal to half of the
- * whole M64 space size, which will exhaust the M64 Space and
- * limit the system flexibility. This is a design decision to
- * set the boundary to quarter of the M64 segment size.
+ * The 1/4 limit is arbitrary and can be tweaked.
*/
- if (total_vf_bar_sz > gate) {
- mul = roundup_pow_of_two(total_vfs);
- dev_info(&pdev->dev,
- "VF BAR Total IOV size %llx > %llx, roundup to %d VFs\n",
- total_vf_bar_sz, gate, mul);
- iov->m64_single_mode = true;
- break;
- }
- }
+ if (vf_bar_sz > (phb->ioda.m64_segsize >> 2)) {
+ /*
+ * On PHB3, the minimum size alignment of M64 BAR in
+ * single mode is 32MB. If this VF BAR is smaller than
+ * 32MB, but still too large for a segmented window
+ * then we can't map it and need to disable SR-IOV for
+ * this device.
Why not use single PE mode for such BAR? Better than nothing.
Suppose you could, but I figured VFs were mainly interesting since you
could give each VF to a separate guest. If there's multiple VFs under
the same single PE BAR then they'd have to be assigned to the same
True. But with one PE per VF we can still have 15 (or 14?) isolated VFs
which is not hundreds but better than 0.
We can only use single PE BARs if the per-VF size is >= 32MB due to
the alignment requirements on P8. If the per-VF size is smaller then
we're stuck with multiple VFs inside the same BAR which is bad due to
the PAPR requirements mentioned below. Sure we could look at doing
something else, but considering this matches the current behaviour
it's a bit hard to care...
quoted
quoted
guest in order to retain the freeze/unfreeze behaviour that PAPR
requires. I guess that's how it used to work, but it seems better just
to disable them rather than having VFs which sort of work.
Well, realistically the segment size should be 8MB to make this matter
(or the whole window 2GB) which does not seem to happen so it does not
matter.
I'm not sure what you mean.
I mean how can we possibly hit this case, what m64_segsize would the
platform have to trigger this. The whole check seems useless but whatever.
--
Alexey
On Wed, Jul 22, 2020 at 8:06 PM Alexey Kardashevskiy [off-list ref] wrote:
quoted
quoted
Well, realistically the segment size should be 8MB to make this matter
(or the whole window 2GB) which does not seem to happen so it does not
matter.
I'm not sure what you mean.
I mean how can we possibly hit this case, what m64_segsize would the
platform have to trigger this. The whole check seems useless but whatever.
Yeah maybe.
IIRC some old P8 FSP systems had tiny M64 windows so it might have
been an issue there. Maybe we can get rid of it., but I'd rather just
leave the behaviour as-is for now.