This series of patches need corresponding changes in firmware side to
fully function. Without the firmware changes, the PCI hotplug driver
won't detect and populate any PCI slots. So the code changes are compatible
to old firmware. The firmware patches can be found from:
https://patchwork.ozlabs.org/patch/624317/
The series of patches is highlighted as below:
* In order to create PE during PCI hot plugging, pcibios_setup_bridge()
which is called to update bridge's window populates the PE, together
with the associated resources like IO/M32/M64 segments, DMA windows etc.
* One refcount is maintained by each PE to track the number of PCI devices
that are associated with the PE. The refcount is increased by one when
a new PCI device joins the PE. It is decreased by one when a PCI device
is released (pcibios_release_device()). The PE together with the used
resources will be destroyed when refcount reaches to 0, meaning no PCI
device needs the PE any more.
* If the firmware has capability to support PCI slot and reset functionality,
the reset required by EEH recovery is routed to firmware. Otherwise, it
is done in kernel as before.
* PCI hotplug driver for PowerNV platform. The PCI slots are identified by
firmware and exposed to kernel through device tree. Firmware provides APIs
to get presence/power state or set power state from/to PCI slot. The PCI
slot hotplug state is sychronized with its power state. When user changes
PCI slot power state from off to on through sysfs file, the PCI devices
behind the PCI slot will be brought into online. Otherwise, the PCI slot's
subordinate devices will be removed from the system.
Changelog
=========
v10:
* Rebased to linux-next where the prerequisite patches merged to.
* Drop FDT patches that have been merged to linux-next.
* Export pnv_pci_get_slot_id() for CAPI driver to use in future.
* Use asynchronous message, the driver is simplifed greatly. No
need to have workqueue and synchronization mechanism for it.
* Fixed build warning/error when IOMMU_API is disabled.
v9:
* Rebased to linux-powerpc next branch + (A).
* Patch order, split and merge (Alexey / Alistair)
* Lots of misc comments covered, I don't elaborate them one by one (Alexey)
* One more patch to export detach_of_node()
* Fixed uninitialized variables, memory leak on @fdt1. Added flush_work()
and other misc comments (Alexey / Alistair)
* Same testing scenario carried as v8. More will be carried out later.
* The confused function names aren't changed. Will check with Alexey and
Alistair. Or have a separate patch to address it later.
v8:
* Rebased to linux-powerpc next branch.
* Resolve comments from Alexey and Daniel on PCI part
* Resolve comments from Rob on fdt.c
* Retested (refer to the "Testing section")
v7:
* Reworked revision to some extent.
* Rebased to powerpc/next repository.
* Reorder/split/merge/drop according - Alexey.
* Defined macros and use array to track IO/M32/M64/DMA32 segments - Alexey.
* Merged 3 files to one for the hotplug driver - Alexey.
* As part of OPAL API, defined macros for PCI slot power state, hotplug
message type. Defined macros for PCI slot power confirmed state in
hotplug driver.
* Misc comments from Alexey.
* Reworked unflatten_dt_node() to avoid recursive function calls.
* Use EXPORT_SYMBOL_GPL() and document function's input/output - Rob/Frank.
v6:
* Patch reorder, split, squash - Alexey.
* Minor coding style - Alexey.
* Better function names for pcibios_{add,remove}_pci_devices - Bjorn
* Replace pr_warn() with dev_warn() in PowerNV hotplug driver - Bjorn
* Concurrent depth as parameter passed to __unflatten_dt_node() - Grant / Alexey
* Replace overlay with of_changeset - Grant
Gavin Shan (18):
PCI: Add pcibios_setup_bridge()
powerpc/pci: Override pcibios_setup_bridge()
powerpc/powernv: Remove PCI_RESET_DELAY_US
powerpc/powernv: Move pnv_pci_ioda_setup_opal_tce_kill() around
powerpc/powernv: Increase PE# capacity
powerpc/powernv: Allocate PE# in reverse order
powerpc/powernv: Create PEs in pcibios_setup_bridge()
powerpc/powernv: Setup PE for root bus
powerpc/powernv: Extend PCI bridge resources
powerpc/powernv: Make pnv_ioda_deconfigure_pe() visible
powerpc/powernv: Dynamically release PE
powerpc/pci: Update bridge windows on PCI plug
powerpc/pci: Delay populating pdn
powerpc/powernv: Support PCI slot ID
powerpc/powernv: Use PCI slot reset infrastructure
powerpc/powernv: Introduce pnv_pci_get_slot_id()
powerpc/powernv: Functions to get/set PCI slot state
PCI/hotplug: PowerPC PowerNV PCI hotplug driver
MAINTAINERS | 1 +
arch/powerpc/include/asm/eeh.h | 2 +-
arch/powerpc/include/asm/opal-api.h | 19 +-
arch/powerpc/include/asm/opal.h | 10 +-
arch/powerpc/include/asm/pci-bridge.h | 2 +
arch/powerpc/include/asm/pnv-pci.h | 12 +
arch/powerpc/include/asm/ppc-pci.h | 2 -
arch/powerpc/kernel/eeh_dev.c | 17 +-
arch/powerpc/kernel/pci-common.c | 16 +-
arch/powerpc/kernel/pci_dn.c | 23 +-
arch/powerpc/platforms/maple/pci.c | 34 +-
arch/powerpc/platforms/pasemi/pci.c | 3 -
arch/powerpc/platforms/powermac/pci.c | 38 +-
arch/powerpc/platforms/powernv/eeh-powernv.c | 49 +-
arch/powerpc/platforms/powernv/opal-wrappers.S | 4 +
arch/powerpc/platforms/powernv/pci-ioda.c | 484 +++++++++++-----
arch/powerpc/platforms/powernv/pci.c | 124 ++++-
arch/powerpc/platforms/powernv/pci.h | 10 +-
arch/powerpc/platforms/pseries/setup.c | 6 +-
drivers/pci/hotplug/Kconfig | 13 +
drivers/pci/hotplug/Makefile | 3 +
drivers/pci/hotplug/pnv_php.c | 733 +++++++++++++++++++++++++
drivers/pci/setup-bus.c | 5 +
include/linux/pci.h | 1 +
24 files changed, 1397 insertions(+), 214 deletions(-)
create mode 100644 drivers/pci/hotplug/pnv_php.c
--
2.1.0
This overrides pcibios_setup_bridge() that is called to update PCI
bridge windows when PCI resource assignment is completed, to assign
PE and setup various (resource) mapping for the PE in subsequent
patches.
Signed-off-by: Gavin Shan <redacted>
Reviewed-by: Alexey Kardashevskiy <redacted>
---
arch/powerpc/include/asm/pci-bridge.h | 2 ++
arch/powerpc/kernel/pci-common.c | 8 ++++++++
2 files changed, 10 insertions(+)
Currently, PowerPC PowerNV platform utilizes ppc_md.pcibios_fixup(),
which is called for once after PCI probing and resource assignment
are completed, to allocate platform required resources for PCI devices:
PE#, IO and MMIO mapping, DMA address translation (TCE) table etc.
Obviously, it's not hotplug friendly.
This adds weak function pcibios_setup_bridge(), which is called by
pci_setup_bridge(). PowerPC PowerNV platform will reuse the function
to assign above platform required resources to newly plugged PCI devices
during PCI hotplug in subsequent patches.
Signed-off-by: Gavin Shan <redacted>
Acked-by: Bjorn Helgaas <bhelgaas@google.com>
---
drivers/pci/setup-bus.c | 5 +++++
include/linux/pci.h | 1 +
2 files changed, 6 insertions(+)
The macro defined in arch/powerpc/platforms/powernv/pci.c isn't
used by anyone. Just remove it.
Signed-off-by: Gavin Shan <redacted>
---
arch/powerpc/platforms/powernv/pci.c | 3 ---
1 file changed, 3 deletions(-)
Each PHB maintains an array helping to translate 2-bytes Request
ID (RID) to PE# with the assumption that PE# takes one byte, meaning
that we can't have more than 256 PEs. However, pci_dn->pe_number
already had 4-bytes for the PE#.
This extends the PE# capacity for every PHB. After that, the PE number
is represented by 4-bytes value. Then we can reuse IODA_INVALID_PE to
check the PE# in phb->pe_rmap[] is valid or not.
Signed-off-by: Gavin Shan <redacted>
Reviewed-by: Daniel Axtens <redacted>
---
arch/powerpc/platforms/powernv/pci-ioda.c | 6 +++++-
arch/powerpc/platforms/powernv/pci.h | 7 ++-----
2 files changed, 7 insertions(+), 6 deletions(-)
PE number for one particular PE can be allocated dynamically or
reserved according to the consumed M64 (64-bits prefetchable)
segments of the PE. The M64 segment can't be remapped to arbitrary
PE, meaning the PE number is determined according to the index
of the consumed M64 segment. As below figure shows, M64 resource
grows from low to high end, meaning the PE (number) reserved
according to M64 segment grows from low to high end as well,
so does the dynamically allocated PE number. It will lead to
conflict: PE number (M64 segment) reserved by dynamic allocation
is required by hot added PCI adapter at later point. It fails
the PCI hotplug because of the PE number can't be reserved
based on the index of the consumed M64 segment.
+---+---+---+---+---+--------------------------------+-----+
| 0 | 1 | 2 | 3 | 4 | ....... | 255 |
+---+---+---+---+---+--------------------------------+-----+
PE number for dynamic allocation ----------------->
PE number reserved for M64 segment ----------------->
To resolve above conflicts, this forces the PE number to be
allocated dynamically in reverse order. With this patch applied,
the PE numbers are reserved in ascending order, but allocated
dynamically in reverse order.
Signed-off-by: Gavin Shan <redacted>
---
arch/powerpc/platforms/powernv/pci-ioda.c | 14 ++++++--------
1 file changed, 6 insertions(+), 8 deletions(-)
There is no parent bridge for root bus, meaning pcibios_setup_bridge()
isn't invoked for root bus. The PE for root bus is the ancestor of
other PEs in PELTV. It means we need PE for root bus populated before
all others.
This populates the PE for root bus in pcibios_setup_bridge() path
if it's not populated yet. The PE number next to the reserved one
is used as the PE# to avoid holes in continuous M64 space.
Signed-off-by: Gavin Shan <redacted>
---
arch/powerpc/platforms/powernv/pci-ioda.c | 49 ++++++++++++++++++++++++-------
arch/powerpc/platforms/powernv/pci.h | 2 ++
2 files changed, 41 insertions(+), 10 deletions(-)
@@ -194,14 +194,14 @@ static int pnv_ioda2_init_m64(struct pnv_phb *phb)set_bit(phb->ioda.m64_bar_idx,&phb->ioda.m64_bar_alloc);/*-*StripoffthesegmentusedbythereservedPE,whichis-*expectedtobe0orlastoneofPEcapabicity.+*ExcludethesegmentsforreservedandrootbusPE,which+*arefirstorlasttwoPEs.*/r=&phb->hose->mem_resources[1];if(phb->ioda.reserved_pe_idx==0)-r->start+=phb->ioda.m64_segsize;+r->start+=(2*phb->ioda.m64_segsize);elseif(phb->ioda.reserved_pe_idx==(phb->ioda.total_pe_num-1))-r->end-=phb->ioda.m64_segsize;+r->end-=(2*phb->ioda.m64_segsize);elsepr_warn(" Cannot strip M64 segment for reserved PE#%d\n",phb->ioda.reserved_pe_idx);
@@ -281,14 +281,14 @@ static int pnv_ioda1_init_m64(struct pnv_phb *phb)}/*-*ExcludethesegmentusedbythereservedPE,which-*isexpectedtobe0orlastsupportedPE#.+*ExcludethesegmentsforreservedandrootbusPE,which+*arefirstorlasttwoPEs.*/r=&phb->hose->mem_resources[1];if(phb->ioda.reserved_pe_idx==0)-r->start+=phb->ioda.m64_segsize;+r->start+=(2*phb->ioda.m64_segsize);elseif(phb->ioda.reserved_pe_idx==(phb->ioda.total_pe_num-1))-r->end-=phb->ioda.m64_segsize;+r->end-=(2*phb->ioda.m64_segsize);elseWARN(1,"Wrong reserved PE#%d on PHB#%d\n",phb->ioda.reserved_pe_idx,phb->hose->global_number);
@@ -1062,8 +1062,13 @@ static struct pnv_ioda_pe *pnv_ioda_setup_bus_PE(struct pci_bus *bus, bool all)returnNULL;}+/* PE number for root bus should have been reserved */+if(pci_is_root_bus(bus)&&+phb->ioda.root_pe_idx!=IODA_INVALID_PE)+pe=&phb->ioda.pe_array[phb->ioda.root_pe_idx];+/* Check if PE is determined by M64 */-if(phb->pick_m64_pe)+if(!pe&&phb->pick_m64_pe)pe=phb->pick_m64_pe(bus,all);/* The PE number isn't pinned by M64 */
@@ -3224,6 +3229,15 @@ static void pnv_pci_setup_bridge(struct pci_bus *bus, unsigned long type)structpnv_ioda_pe*pe;boolall=(pci_pcie_type(bridge)==PCI_EXP_TYPE_PCI_BRIDGE);+/* The PE for root bus should be realized before any one else */+if(!phb->ioda.root_pe_populated){+pe=pnv_ioda_setup_bus_PE(phb->hose->bus,false);+if(pe){+phb->ioda.root_pe_idx=pe->pe_number;+phb->ioda.root_pe_populated=true;+}+}+/* Don't assign PE to PCI bus, which doesn't have subordinate devices */if(list_empty(&bus->devices))return;
Currently, the PEs and their associated resources are assigned in
ppc_md.pcibios_fixup() except those used by SRIOV VFs. The function
is called for once after PCI probing and resources assignment is
completed. So it's obviously not hotplug friendly.
This creates PEs dynamically in pcibios_setup_bridge() that is
called for the event during system bootup and PCI hotplug: updating
PCI bridge's windows after resource assignment/reassignment are done.
In partial hotplug case, not all PCI devices included to one particular
PE are unplugged and plugged again, we just need unbinding/binding the
hot added PCI devices with the corresponding PE without creating new
one. The change is applied to IODA1 and IODA2 PHBs only. The behaviour
on NPU PHBs aren't changed. There are no PCI bridges on NPU PHBs,
meaning pcibios_setup_bridge() won't be invoked there. We have to use
old path (pnv_pci_ioda_fixup()) to setup PEs on NPU PHBs.
Signed-off-by: Gavin Shan <redacted>
---
arch/powerpc/platforms/powernv/pci-ioda.c | 184 +++++++++++-------------------
1 file changed, 69 insertions(+), 115 deletions(-)
@@ -1040,6 +1049,18 @@ static struct pnv_ioda_pe *pnv_ioda_setup_bus_PE(struct pci_bus *bus, bool all)structpci_controller*hose=pci_bus_to_host(bus);structpnv_phb*phb=hose->private_data;structpnv_ioda_pe*pe=NULL;+unsignedintpe_num;++/*+*Inpartialhotplugcase,thePEinstancemightbestillalive.+*Weshouldreuseitinsteadofallocatinganewone.+*/+pe_num=phb->ioda.pe_rmap[bus->number<<8];+if(pe_num!=IODA_INVALID_PE){+pe=&phb->ioda.pe_array[pe_num];+pnv_ioda_setup_same_PE(bus,pe);+returnNULL;+}/* Check if PE is determined by M64 */if(phb->pick_m64_pe)
@@ -1185,22 +1182,11 @@ static void pnv_pci_ioda_setup_PEs(void)list_for_each_entry_safe(hose,tmp,&hose_list,list_node){phb=hose->private_data;--/* M64 layout might affect PE allocation */-if(phb->reserve_m64_pe)-phb->reserve_m64_pe(hose->bus,NULL,true);--/*-*OnNPUPHB,weexpectseparatePEsforindividualPCI-*functions.PCIbusdependentPEsarerequiredforthe-*remainingtypesofPHBs.-*/if(phb->type==PNV_PHB_NPU){/* PE#0 is needed for error reporting */pnv_ioda_reserve_pe(phb,0);pnv_ioda_setup_npu_PEs(hose->bus);-}else-pnv_ioda_setup_PEs(hose->bus);+}}}
@@ -2655,6 +2641,9 @@ static void pnv_pci_ioda2_setup_dma_pe(struct pnv_phb *phb,{int64_trc;+if(!pnv_pci_ioda_pe_dma_weight(pe))+return;+/* TVE #1 is selected by PCI address bit 59 */pe->tce_bypass_base=1ull<<59;
@@ -2686,47 +2675,6 @@ static void pnv_pci_ioda2_setup_dma_pe(struct pnv_phb *phb,pnv_ioda_setup_bus_dma(pe,pe->pbus);}-staticvoidpnv_ioda_setup_dma(structpnv_phb*phb)-{-structpci_controller*hose=phb->hose;-structpnv_ioda_pe*pe;-unsignedintweight;--/* If we have more PE# than segments available, hand out one-*perPEuntilwerunoutandlettherestfail.Ifnot,-*thenweassignatleastonesegmentperPE,plusmorebased-*ontheamountofdevicesunderthatPE-*/-pr_info("PCI: Domain %04x has %d available 32-bit DMA segments\n",-hose->global_number,phb->ioda.dma32_count);--/* Walk our PE list and configure their DMA segments */-list_for_each_entry(pe,&phb->ioda.pe_list,list){-weight=pnv_pci_ioda_pe_dma_weight(pe);-if(!weight)-continue;--/*-*ForIODA2compliantPHB3,weneedn'tcareabouttheweight.-*Theallavailable32-bitsDMAspacewillbeassignedto-*thespecificPE.-*/-if(phb->type==PNV_PHB_IODA1){-pnv_pci_ioda1_setup_dma_pe(phb,pe);-}elseif(phb->type==PNV_PHB_IODA2){-pe_info(pe,"Assign DMA32 space\n");-pnv_pci_ioda2_setup_dma_pe(phb,pe);-}elseif(phb->type==PNV_PHB_NPU){-/*-*WeinitialisetheDMAspaceforanNPUPHB-*aftersetupofthePHBiscompleteaswe-*pointtheNPUTVTtothethesamelocation-*asthePHB3TVT.-*/-}-}-}-#ifdef CONFIG_PCI_MSIstaticvoidpnv_ioda2_msi_eoi(structirq_data*d){
@@ -3193,41 +3141,6 @@ static void pnv_ioda_setup_pe_seg(struct pnv_ioda_pe *pe)}}-staticvoidpnv_pci_ioda_setup_seg(void)-{-structpci_controller*tmp,*hose;-structpnv_phb*phb;-structpnv_ioda_pe*pe;--list_for_each_entry_safe(hose,tmp,&hose_list,list_node){-phb=hose->private_data;--/* NPU PHB does not support IO or MMIO segmentation */-if(phb->type==PNV_PHB_NPU)-continue;--list_for_each_entry(pe,&phb->ioda.pe_list,list){-pnv_ioda_setup_pe_seg(pe);-}-}-}--staticvoidpnv_pci_ioda_setup_DMA(void)-{-structpci_controller*hose,*tmp;-structpnv_phb*phb;--list_for_each_entry_safe(hose,tmp,&hose_list,list_node){-pnv_ioda_setup_dma(hose->private_data);--/* Mark the PHB initialization done */-phb=hose->private_data;-phb->initialized=1;-}--pnv_pci_ioda_setup_iommu_api();-}-staticvoidpnv_pci_ioda_create_dbgfs(void){#ifdef CONFIG_DEBUG_FS
@@ -3302,6 +3216,45 @@ static resource_size_t pnv_pci_window_alignment(struct pci_bus *bus,returnphb->ioda.io_segsize;}+staticvoidpnv_pci_setup_bridge(structpci_bus*bus,unsignedlongtype)+{+structpci_controller*hose=pci_bus_to_host(bus);+structpnv_phb*phb=hose->private_data;+structpci_dev*bridge=bus->self;+structpnv_ioda_pe*pe;+boolall=(pci_pcie_type(bridge)==PCI_EXP_TYPE_PCI_BRIDGE);++/* Don't assign PE to PCI bus, which doesn't have subordinate devices */+if(list_empty(&bus->devices))+return;++/* Reserve PEs according to used M64 resources */+if(phb->reserve_m64_pe)+phb->reserve_m64_pe(bus,NULL,all);++/*+*AssignPE.Wemightrunherebecauseofpartialhotplug.+*Forthecase,wejustpickuptheexistingPEandshould+*notallocateresourcesagain.+*/+pe=pnv_ioda_setup_bus_PE(bus,all);+if(!pe)+return;++pnv_ioda_setup_pe_seg(pe);+switch(phb->type){+casePNV_PHB_IODA1:+pnv_pci_ioda1_setup_dma_pe(phb,pe);+break;+casePNV_PHB_IODA2:+pnv_pci_ioda2_setup_dma_pe(phb,pe);+break;+default:+pr_warn("%s: No DMA for PHB#%d (type %d)\n",+__func__,phb->hose->global_number,phb->type);+}+}+#ifdef CONFIG_PCI_IOVstaticresource_size_tpnv_pci_iov_resource_alignment(structpci_dev*pdev,intresno)
The (OPAL) firmware might provide the PCI slot reset capability
which is identified by property "ibm,reset-by-firmware" on the
PCI slot associated device node.
This routes the reset request to firmware if "ibm,reset-by-firmware"
exists in the PCI slot device node. Otherwise, the reset is done
inside kernel as before.
Signed-off-by: Gavin Shan <redacted>
---
arch/powerpc/platforms/powernv/eeh-powernv.c | 41 +++++++++++++++++++++++++++-
1 file changed, 40 insertions(+), 1 deletion(-)
@@ -866,6 +867,44 @@ static int pnv_eeh_bridge_reset(struct pci_dev *dev, int option)return0;}+staticintpnv_eeh_bridge_reset(structpci_dev*pdev,intoption)+{+structpci_controller*hose=pci_bus_to_host(pdev->bus);+structpnv_phb*phb=hose->private_data;+structdevice_node*dn=pci_device_to_OF_node(pdev);+uint64_tid=PCI_SLOT_ID(phb->opal_id,+(pdev->bus->number<<8)|pdev->devfn);+uint8_tscope;+int64_trc;++/* Hot reset to the bus if firmware cannot handle */+if(!dn||!of_get_property(dn,"ibm,reset-by-firmware",NULL))+return__pnv_eeh_bridge_reset(pdev,option);++switch(option){+caseEEH_RESET_FUNDAMENTAL:+scope=OPAL_RESET_PCI_FUNDAMENTAL;+break;+caseEEH_RESET_HOT:+scope=OPAL_RESET_PCI_HOT;+break;+caseEEH_RESET_DEACTIVATE:+return0;+default:+dev_dbg(&pdev->dev,"%s: Unsupported reset %d\n",+__func__,option);+return-EINVAL;+}++rc=opal_pci_reset(id,scope,OPAL_ASSERT_RESET);+if(rc<=OPAL_SUCCESS)+gotoout;++rc=pnv_eeh_poll(id);+out:+return(rc==OPAL_SUCCESS)?0:-EIO;+}+voidpnv_pci_reset_secondary_bus(structpci_dev*dev){structpci_controller*hose;
This supports releasing PEs dynamically. A reference count is
introduced to PE representing number of PCI devices associated
with the PE. The reference count is increased when PCI device
joins the PE and decreased when PCI device leaves the PE in
pnv_pci_release_device(). When the count becomes zero, the PE
and its consumed resources are released. Note that the count
is accessed concurrently. So a counter with "int" type is enough
here.
In order to release the sources consumed by the PE, couple of
helper functions are introduced as below:
* pnv_pci_ioda1_unset_window() - Unset IODA1 DMA32 window
* pnv_pci_ioda1_release_dma_pe() - Release IODA1 DMA32 segments
* pnv_pci_ioda2_release_dma_pe() - Release IODA2 DMA resource
* pnv_ioda_release_pe_seg() - Unmap IO/M32/M64 segments
Signed-off-by: Gavin Shan <redacted>
---
arch/powerpc/platforms/powernv/pci-ioda.c | 174 ++++++++++++++++++++++++++++++
arch/powerpc/platforms/powernv/pci.h | 1 +
2 files changed, 175 insertions(+)
@@ -30,6 +30,7 @@ struct pnv_phb;structpnv_ioda_pe{unsignedlongflags;structpnv_phb*phb;+intdevice_count;/* A PE can be associated with a single device or an*entirebus(&children).Intheformercase,pdev
This adds standalone driver to support PCI hotplug for PowerPC PowerNV
platform that runs on top of skiboot firmware. The firmware identifies
hotpluggable slots and marked their device tree node with proper
"ibm,slot-pluggable" and "ibm,reset-by-firmware". The driver scans
device tree nodes to create/register PCI hotplug slot accordingly.
The PCI slots are organized in fashion of tree, which means one
PCI slot might have parent PCI slot and parent PCI slot possibly
contains multiple child PCI slots. At the plugging time, the parent
PCI slot is populated before its children. The child PCI slots are
removed before their parent PCI slot can be removed from the system.
If the skiboot firmware doesn't support slot status retrieval, the PCI
slot device node shouldn't have property "ibm,reset-by-firmware". In
that case, none of valid PCI slots will be detected from device tree.
The skiboot firmware doesn't export the capability to access attention
LEDs yet and it's something for TBD.
Signed-off-by: Gavin Shan <redacted>
Acked-by: Bjorn Helgaas <bhelgaas@google.com>
---
MAINTAINERS | 1 +
drivers/pci/hotplug/Kconfig | 13 +
drivers/pci/hotplug/Makefile | 3 +
drivers/pci/hotplug/pnv_php.c | 733 ++++++++++++++++++++++++++++++++++++++++++
4 files changed, 750 insertions(+)
create mode 100644 drivers/pci/hotplug/pnv_php.c
@@ -0,0 +1,733 @@+/*+*PCIHotplugDriverforPowerPCPowerNVplatform.+*+*CopyrightGavinShan,IBMCorporation2016.+*+*Thisprogramisfreesoftware;youcanredistributeitand/ormodify+*itunderthetermsoftheGNUGeneralPublicLicenseaspublishedby+*theFreeSoftwareFoundation;eitherversion2oftheLicense,or+*(atyouroption)anylaterversion.+*/++#include<linux/libfdt.h>+#include<linux/module.h>+#include<linux/pci.h>+#include<linux/pci_hotplug.h>++#include<asm/opal.h>+#include<asm/pnv-pci.h>+#include<asm/ppc-pci.h>++#define DRIVER_VERSION "0.1"+#define DRIVER_AUTHOR "Gavin Shan, IBM Corporation"+#define DRIVER_DESC "PowerPC PowerNV PCI Hotplug Driver"++structpnv_php_slot{+structhotplug_slotslot;+structhotplug_slot_infoslot_info;+uint64_tid;+char*name;+intslot_no;+structkrefkref;+#define PNV_PHP_STATE_INITIALIZED 0+#define PNV_PHP_STATE_REGISTERED 1+#define PNV_PHP_STATE_POPULATED 2+#define PNV_PHP_STATE_OFFLINE 3+intstate;+structdevice_node*dn;+structpci_dev*pdev;+structpci_bus*bus;+boolpower_state_check;+void*fdt;+void*dt;+structof_changesetocs;+structpnv_php_slot*parent;+structlist_headchildren;+structlist_headlink;+};++staticLIST_HEAD(pnv_php_slot_list);+staticDEFINE_SPINLOCK(pnv_php_lock);++staticvoidpnv_php_register(structdevice_node*dn);+staticvoidpnv_php_unregister_one(structdevice_node*dn);+staticvoidpnv_php_unregister(structdevice_node*dn);++staticvoidpnv_php_free_slot(structkref*kref)+{+structpnv_php_slot*php_slot=container_of(kref,+structpnv_php_slot,kref);++WARN_ON(!list_empty(&php_slot->children));+kfree(php_slot->name);+kfree(php_slot);+}++staticinlinevoidpnv_php_put_slot(structpnv_php_slot*php_slot)+{++if(WARN_ON(!php_slot))+return;++kref_put(&php_slot->kref,pnv_php_free_slot);+}++staticstructpnv_php_slot*pnv_php_match(structdevice_node*dn,+structpnv_php_slot*php_slot)+{+structpnv_php_slot*target,*tmp;++if(php_slot->dn==dn){+kref_get(&php_slot->kref);+returnphp_slot;+}++list_for_each_entry(tmp,&php_slot->children,link){+target=pnv_php_match(dn,tmp);+if(target)+returntarget;+}++returnNULL;+}++staticstructpnv_php_slot*pnv_php_find_slot(structdevice_node*dn)+{+structpnv_php_slot*php_slot,*tmp;+unsignedlongflags;++spin_lock_irqsave(&pnv_php_lock,flags);+list_for_each_entry(tmp,&pnv_php_slot_list,link){+php_slot=pnv_php_match(dn,tmp);+if(php_slot){+spin_unlock_irqrestore(&pnv_php_lock,flags);+returnphp_slot;+}+}+spin_unlock_irqrestore(&pnv_php_lock,flags);++returnNULL;+}++/*+*Removepdnforallchildrenoftheindicateddevicenode.+*Thefunctionshouldremovepdninadepth-firstmanner.+*/+staticvoidpnv_php_rmv_pdns(structdevice_node*dn)+{+structdevice_node*child;++for_each_child_of_node(dn,child){+pnv_php_rmv_pdns(child);++pci_remove_device_node_info(child);+}+}++/*+*Detachallchildnodesoftheindicateddevicenodes.The+*functionshouldhandledevicenodesindepth-firstmanner.+*+*Weshouldnotinvokeof_node_release()asthememoryfor+*individualdevicenodeispartoflargememoryblock.The+*largeblockisallocatedfrommemblock(systembootup)or+*kmalloc()whenunflatteningthedevicetreebyOFchangeset.+*Wecannotfreethelargeblockallocatedfrommemblock.For+*latercase,itshouldbereleasedatonce.+*/+staticvoidpnv_php_detach_device_nodes(structdevice_node*parent)+{+structdevice_node*dn;+intrefcount;++for_each_child_of_node(parent,dn){+pnv_php_detach_device_nodes(dn);++of_node_put(dn);+refcount=atomic_read(&dn->kobj.kref.refcount);+if(unlikely(refcount!=1))+pr_warn("Invalid refcount %d on <%s>\n",+refcount,of_node_full_name(dn));++of_detach_node(dn);+}+}++staticvoidpnv_php_rmv_devtree(structpnv_php_slot*php_slot)+{+pnv_php_rmv_pdns(php_slot->dn);++/*+*Decreasetherefcountifthedevicenodeswerecreated+*throughOFchangesetbeforedetachingthem.+*/+if(php_slot->fdt)+of_changeset_destroy(&php_slot->ocs);+pnv_php_detach_device_nodes(php_slot->dn);++if(php_slot->fdt){+kfree(php_slot->dt);+kfree(php_slot->fdt);+php_slot->dt=NULL;+php_slot->dn->child=NULL;+php_slot->fdt=NULL;+}+}++/*+*AsthenodesinOFchangesetareappliedinreverseorder,we+*needrevertthenodesinadvancesothatwehavecorrectnode+*orderafterthechangesetisapplied.+*/+staticvoidpnv_php_reverse_nodes(structdevice_node*parent)+{+structdevice_node*child,*next;++/* In-depth first */+for_each_child_of_node(parent,child)+pnv_php_reverse_nodes(child);++/* Reverse the nodes in the child list */+child=parent->child;+parent->child=NULL;+while(child){+next=child->sibling;++child->sibling=parent->child;+parent->child=child;+child=next;+}+}++staticintpnv_php_populate_changeset(structof_changeset*ocs,+structdevice_node*dn)+{+structdevice_node*child;+intret=0;++for_each_child_of_node(dn,child){+ret=of_changeset_attach_node(ocs,child);+if(unlikely(ret))+break;++ret=pnv_php_populate_changeset(ocs,child);+if(unlikely(ret))+break;+}++returnret;+}++staticvoid*pnv_php_add_one_pdn(structdevice_node*dn,void*data)+{+structpci_controller*hose=(structpci_controller*)data;+structpci_dn*pdn;++pdn=pci_add_device_node_info(hose,dn);+if(unlikely(!pdn))+returnERR_PTR(-ENOMEM);++returnNULL;+}++staticvoidpnv_php_add_pdns(structpnv_php_slot*slot)+{+structpci_controller*hose=pci_bus_to_host(slot->bus);++pci_traverse_device_nodes(slot->dn,pnv_php_add_one_pdn,hose);+}++staticintpnv_php_add_devtree(structpnv_php_slot*php_slot)+{+void*fdt,*fdt1,*dt;+intret;++/* We don't know the FDT blob size. We try to get it through+*maximalmemorychunkandthencopyittoanotherchunkthat+*fitstherealsize.+*/+fdt1=kzalloc(0x10000,GFP_KERNEL);+if(unlikely(!fdt1)){+ret=-ENOMEM;+dev_warn(&php_slot->pdev->dev,"Cannot alloc FDT blob\n");+gotoout;+}++ret=pnv_pci_get_device_tree(php_slot->dn->phandle,fdt1,0x10000);+if(unlikely(ret)){+dev_warn(&php_slot->pdev->dev,"Error %d getting FDT blob\n",+ret);+gotofree_fdt1;+}++fdt=kzalloc(fdt_totalsize(fdt1),GFP_KERNEL);+if(unlikely(!fdt)){+ret=-ENOMEM;+dev_warn(&php_slot->pdev->dev,"Cannot %d bytes memory\n",+fdt_totalsize(fdt1));+gotofree_fdt1;+}++/* Unflatten device tree blob */+memcpy(fdt,fdt1,fdt_totalsize(fdt1));+dt=of_fdt_unflatten_tree(fdt,php_slot->dn,NULL);+if(unlikely(!dt)){+ret=-EINVAL;+dev_warn(&php_slot->pdev->dev,"Cannot unflatten FDT\n");+gotofree_fdt;+}++/* Initialize and apply the changeset */+of_changeset_init(&php_slot->ocs);+pnv_php_reverse_nodes(php_slot->dn);+ret=pnv_php_populate_changeset(&php_slot->ocs,php_slot->dn);+if(unlikely(ret)){+pnv_php_reverse_nodes(php_slot->dn);+dev_warn(&php_slot->pdev->dev,"Error %d populating changeset\n",+ret);+gotofree_dt;+}++php_slot->dn->child=NULL;+ret=of_changeset_apply(&php_slot->ocs);+if(unlikely(ret)){+dev_warn(&php_slot->pdev->dev,"Error %d applying changeset\n",+ret);+gotodestroy_changeset;+}++/* Add device node firmware data */+pnv_php_add_pdns(php_slot);+php_slot->fdt=fdt;+php_slot->dt=dt;+kfree(fdt1);+gotoout;++destroy_changeset:+of_changeset_destroy(&php_slot->ocs);+free_dt:+kfree(dt);+php_slot->dn->child=NULL;+free_fdt:+kfree(fdt);+free_fdt1:+kfree(fdt1);+out:+returnret;+}++staticintpnv_php_set_slot_power_state(structhotplug_slot*slot,+uint8_tstate)+{+structpnv_php_slot*php_slot=slot->private;+structopal_msgmsg;+intret;++ret=pnv_pci_set_power_state(php_slot->id,state,&msg);+if(likely(ret>0)){+if(be64_to_cpu(msg.params[1])!=php_slot->dn->phandle||+be64_to_cpu(msg.params[2])!=state||+be64_to_cpu(msg.params[3])!=OPAL_SUCCESS){+dev_warn(&php_slot->pdev->dev,"Wrong msg (%lld, %lld, %lld)\n",+be64_to_cpu(msg.params[1]),+be64_to_cpu(msg.params[2]),+be64_to_cpu(msg.params[3]));+return-ENOMSG;+}+}elseif(unlikely(ret<0)){+dev_warn(&php_slot->pdev->dev,"Error %d powering %s\n",+ret,(state==OPAL_PCI_SLOT_POWER_ON)?"on":"off");+returnret;+}++if(state==OPAL_PCI_SLOT_POWER_OFF)+pnv_php_rmv_devtree(php_slot);+else+ret=pnv_php_add_devtree(php_slot);++returnret;+}++staticintpnv_php_get_power_state(structhotplug_slot*slot,u8*state)+{+structpnv_php_slot*php_slot=slot->private;+uint8_tpower_state=OPAL_PCI_SLOT_POWER_ON;+intret;++/*+*Retrievepowerstatusfromfirmware.Ifwefail+*gettingthat,thepowerstatusfailsbackto+*beon.+*/+ret=pnv_pci_get_power_state(php_slot->id,&power_state);+if(unlikely(ret)){+dev_warn(&php_slot->pdev->dev,"Error %d getting power status\n",+ret);+}else{+*state=power_state;+slot->info->power_status=power_state;+}++return0;+}++staticintpnv_php_get_adapter_state(structhotplug_slot*slot,u8*state)+{+structpnv_php_slot*php_slot=slot->private;+uint8_tpresence=OPAL_PCI_SLOT_EMPTY;+intret;++/*+*Retrievepresencestatusfromfirmware.Ifwecan't+*getthat,itwillfailbacktobeempty.+*/+ret=pnv_pci_get_presence_state(php_slot->id,&presence);+if(likely(ret>=0)){+*state=presence;+slot->info->adapter_status=presence;+ret=0;+}else{+dev_warn(&php_slot->pdev->dev,"Error %d getting presence\n",+ret);+}++returnret;+}++staticintpnv_php_set_attention_state(structhotplug_slot*slot,u8state)+{+/* FIXME: Make it real once firmware supports it */+slot->info->attention_status=state;++return0;+}++staticintpnv_php_enable(structpnv_php_slot*php_slot,boolrescan)+{+structhotplug_slot*slot=&php_slot->slot;+uint8_tpresence=OPAL_PCI_SLOT_EMPTY;+uint8_tpower_status=OPAL_PCI_SLOT_POWER_ON;+intret;++/* Check if the slot has been configured */+if(php_slot->state!=PNV_PHP_STATE_REGISTERED)+return0;++/* Retrieve slot presence status */+ret=pnv_php_get_adapter_state(slot,&presence);+if(unlikely(ret))+returnret;++/* Proceed if there have nothing behind the slot */+if(presence==OPAL_PCI_SLOT_EMPTY)+gotoscan;++/*+*Ifthepowersupplytotheslotisoff,wecan'tdetect+*adapterpresencestate.Thatmeanswehavetoturnthe+*slotonbeforegoingtoprobeslot'spresencestate.+*+*Onthefirsttime,wedon'tchangethepowerstatusto+*boostsystembootwithassumptionthatthefirmware+*suppliesconsistentslotpowerstatus:emptyslotalways+*hasitspoweroffandnon-emptyslothasitspoweron.+*/+if(!php_slot->power_state_check){+php_slot->power_state_check=true;++ret=pnv_php_get_power_state(slot,&power_status);+if(unlikely(ret))+returnret;++if(power_status!=OPAL_PCI_SLOT_POWER_ON)+return0;+}++/* Check the power status. Scan the slot if it is already on */+ret=pnv_php_get_power_state(slot,&power_status);+if(unlikely(ret))+returnret;++if(power_status==OPAL_PCI_SLOT_POWER_ON)+gotoscan;++/* Power is off, turn it on and then scan the slot */+ret=pnv_php_set_slot_power_state(slot,OPAL_PCI_SLOT_POWER_ON);+if(unlikely(ret))+returnret;++scan:+if(presence==OPAL_PCI_SLOT_PRESENT){+if(rescan){+pci_lock_rescan_remove();+pci_hp_add_devices(php_slot->bus);+pci_unlock_rescan_remove();+}++/* Rescan for child hotpluggable slots */+php_slot->state=PNV_PHP_STATE_POPULATED;+if(rescan)+pnv_php_register(php_slot->dn);+}else{+php_slot->state=PNV_PHP_STATE_POPULATED;+}++return0;+}++staticintpnv_php_enable_slot(structhotplug_slot*slot)+{+structpnv_php_slot*php_slot=container_of(slot,+structpnv_php_slot,slot);++returnpnv_php_enable(php_slot,true);+}++staticintpnv_php_disable_slot(structhotplug_slot*slot)+{+structpnv_php_slot*php_slot=slot->private;+intret;++if(php_slot->state!=PNV_PHP_STATE_POPULATED)+return0;++/* Remove all devices behind the slot */+pci_lock_rescan_remove();+pci_hp_remove_devices(php_slot->bus);+pci_unlock_rescan_remove();++/* Detach the child hotpluggable slots */+pnv_php_unregister(php_slot->dn);++/* Notify firmware and remove device nodes */+ret=pnv_php_set_slot_power_state(slot,OPAL_PCI_SLOT_POWER_OFF);++php_slot->state=PNV_PHP_STATE_REGISTERED;+returnret;+}++staticstructhotplug_slot_opsphp_slot_ops={+.get_power_status=pnv_php_get_power_state,+.get_adapter_status=pnv_php_get_adapter_state,+.set_attention_status=pnv_php_set_attention_state,+.enable_slot=pnv_php_enable_slot,+.disable_slot=pnv_php_disable_slot,+};++staticvoidpnv_php_release(structhotplug_slot*slot)+{+structpnv_php_slot*php_slot=slot->private;+unsignedlongflags;++/* Remove from global or child list */+spin_lock_irqsave(&pnv_php_lock,flags);+list_del(&php_slot->link);+spin_unlock_irqrestore(&pnv_php_lock,flags);++/* Detach from parent */+pnv_php_put_slot(php_slot);+pnv_php_put_slot(php_slot->parent);+}++staticstructpnv_php_slot*pnv_php_alloc_slot(structdevice_node*dn)+{+structpnv_php_slot*php_slot;+structpci_bus*bus;+constchar*label;+uint64_tid;++label=of_get_property(dn,"ibm,slot-label",NULL);+if(unlikely(!label))+returnNULL;++if(pnv_pci_get_slot_id(dn,&id))+returnNULL;++bus=pci_find_bus_by_node(dn);+if(unlikely(!bus))+returnNULL;++php_slot=kzalloc(sizeof(*php_slot),GFP_KERNEL);+if(unlikely(!php_slot))+returnNULL;++php_slot->name=kstrdup(label,GFP_KERNEL);+if(unlikely(!php_slot->name)){+kfree(php_slot);+returnNULL;+}++if(likely(dn->child&&PCI_DN(dn->child)))+php_slot->slot_no=PCI_SLOT(PCI_DN(dn->child)->devfn);+else+php_slot->slot_no=-1;/* Placeholder slot */++kref_init(&php_slot->kref);+php_slot->state=PNV_PHP_STATE_INITIALIZED;+php_slot->dn=dn;+php_slot->pdev=bus->self;+php_slot->bus=bus;+php_slot->id=id;+php_slot->power_state_check=false;+php_slot->slot.ops=&php_slot_ops;+php_slot->slot.info=&php_slot->slot_info;+php_slot->slot.release=pnv_php_release;+php_slot->slot.private=php_slot;++INIT_LIST_HEAD(&php_slot->children);+INIT_LIST_HEAD(&php_slot->link);++returnphp_slot;+}++staticintpnv_php_register_slot(structpnv_php_slot*php_slot)+{+structpnv_php_slot*parent;+structdevice_node*dn=php_slot->dn;+unsignedlongflags;+intret;++/* Check if the slot is registered or not */+parent=pnv_php_find_slot(php_slot->dn);+if(unlikely(parent)){+pnv_php_put_slot(parent);+return-EEXIST;+}++/* Register PCI slot */+ret=pci_hp_register(&php_slot->slot,php_slot->bus,+php_slot->slot_no,php_slot->name);+if(unlikely(ret)){+dev_warn(&php_slot->pdev->dev,"Error %d registering slot\n",+ret);+returnret;+}++/* Attach to the parent's child list or global list */+while((dn=of_get_parent(dn))){+if(!PCI_DN(dn)){+of_node_put(dn);+break;+}++parent=pnv_php_find_slot(dn);+if(parent){+of_node_put(dn);+break;+}++of_node_put(dn);+}++spin_lock_irqsave(&pnv_php_lock,flags);+php_slot->parent=parent;+if(parent)+list_add_tail(&php_slot->link,&parent->children);+else+list_add_tail(&php_slot->link,&pnv_php_slot_list);+spin_unlock_irqrestore(&pnv_php_lock,flags);++php_slot->state=PNV_PHP_STATE_REGISTERED;+return0;+}++staticintpnv_php_register_one(structdevice_node*dn)+{+structpnv_php_slot*php_slot;+const__be32*prop32;+intret;++/* Check if it's hotpluggable slot */+prop32=of_get_property(dn,"ibm,slot-pluggable",NULL);+if(!prop32||!of_read_number(prop32,1))+return-ENXIO;++prop32=of_get_property(dn,"ibm,reset-by-firmware",NULL);+if(!prop32||!of_read_number(prop32,1))+return-ENXIO;++php_slot=pnv_php_alloc_slot(dn);+if(unlikely(!php_slot))+return-ENODEV;++ret=pnv_php_register_slot(php_slot);+if(unlikely(ret))+gotofree_slot;++ret=pnv_php_enable(php_slot,false);+if(unlikely(ret))+gotounregister_slot;++return0;++unregister_slot:+pnv_php_unregister_one(php_slot->dn);+free_slot:+pnv_php_put_slot(php_slot);+returnret;+}++staticvoidpnv_php_register(structdevice_node*dn)+{+structdevice_node*child;++/*+*Theparentslotsshouldberegisteredbeforetheir+*childslots.+*/+for_each_child_of_node(dn,child){+pnv_php_register_one(child);+pnv_php_register(child);+}+}++staticvoidpnv_php_unregister_one(structdevice_node*dn)+{+structpnv_php_slot*php_slot;++php_slot=pnv_php_find_slot(dn);+if(!php_slot)+return;++php_slot->state=PNV_PHP_STATE_OFFLINE;+pnv_php_put_slot(php_slot);+pci_hp_deregister(&php_slot->slot);+}++staticvoidpnv_php_unregister(structdevice_node*dn)+{+structdevice_node*child;++/* The child slots should go before their parent slots */+for_each_child_of_node(dn,child){+pnv_php_unregister(child);+pnv_php_unregister_one(child);+}+}++staticint__initpnv_php_init(void)+{+structdevice_node*dn;++pr_info(DRIVER_DESC" version: "DRIVER_VERSION"\n");+for_each_compatible_node(dn,NULL,"ibm,ioda2-phb")+pnv_php_register(dn);++return0;+}++staticvoid__exitpnv_php_exit(void)+{+structdevice_node*dn;++for_each_compatible_node(dn,NULL,"ibm,ioda2-phb")+pnv_php_unregister(dn);+}++module_init(pnv_php_init);+module_exit(pnv_php_exit);++MODULE_VERSION(DRIVER_VERSION);+MODULE_LICENSE("GPL v2");+MODULE_AUTHOR(DRIVER_AUTHOR);+MODULE_DESCRIPTION(DRIVER_DESC);
The pdn (struct pci_dn) instances are allocated from memblock or
bootmem when creating PCI controller (hoses) in setup_arch(). PCI
hotplug, which will be supported by proceeding patches, releases
PCI device nodes and their corresponding pdn on unplugging event.
The memory chunks for pdn instances allocated from memblock or
bootmem are hard to reused after being released.
This delays creating pdn by pci_devs_phb_init() from setup_arch()
to core_initcall() so that they are allocated from slab. The memory
consumed by pdn can be released to system without problem during
PCI unplugging time. It indicates that pci_dn is unavailable in
setup_arch() and the the fixup on pdn (like AGP's) can't be carried
out that time. We have to do that in pcibios_root_bridge_prepare()
on maple/pasemi/powermac platforms where/when the pdn is available.
pcibios_root_bridge_prepare is called from subsys_initcall() which
is executed after core_initcall() so the code flow does not change.
At the mean while, the EEH device is created when pdn is populated,
meaning pdn and EEH device have same life cycle. In turn, we needn't
call eeh_dev_init() to create EEH device explicitly.
Signed-off-by: Gavin Shan <redacted>
Reviewed-by: Alexey Kardashevskiy <redacted>
---
arch/powerpc/include/asm/eeh.h | 2 +-
arch/powerpc/include/asm/ppc-pci.h | 2 --
arch/powerpc/kernel/eeh_dev.c | 17 +++------------
arch/powerpc/kernel/pci_dn.c | 23 ++++++++++++++++----
arch/powerpc/platforms/maple/pci.c | 34 ++++++++++++++++++------------
arch/powerpc/platforms/pasemi/pci.c | 3 ---
arch/powerpc/platforms/powermac/pci.c | 38 +++++++++++++++++++++-------------
arch/powerpc/platforms/powernv/pci.c | 3 ---
arch/powerpc/platforms/pseries/setup.c | 6 +-----
9 files changed, 69 insertions(+), 59 deletions(-)
@@ -81,16 +80,8 @@ void *eeh_dev_init(struct pci_dn *pdn, void *data)*/voideeh_dev_phb_init_dynamic(structpci_controller*phb){-structpci_dn*root=phb->pci_data;-/* EEH PE for PHB */eeh_phb_pe_create(phb);--/* EEH device for PHB */-eeh_dev_init(root,phb);--/* EEH devices for children OF nodes */-traverse_pci_dn(root,eeh_dev_init,phb);}/**
@@ -106,8 +97,6 @@ static int __init eeh_dev_phb_init(void)list_for_each_entry_safe(phb,tmp,&hose_list,list_node)eeh_dev_phb_init_dynamic(phb);-pr_info("EEH: devices created\n");-return0;}
@@ -504,15 +515,19 @@ void pci_devs_phb_init_dynamic(struct pci_controller *phb)*pcidevicefoundunderneath.Thisroutinerunsonce,*earlyinthebootsequence.*/-void__initpci_devs_phb_init(void)+staticint__initpci_devs_phb_init(void){structpci_controller*phb,*tmp;/* This must be done first so the device nodes have valid pci info! */list_for_each_entry_safe(phb,tmp,&hose_list,list_node)pci_devs_phb_init_dynamic(phb);++return0;}+core_initcall(pci_devs_phb_init);+staticvoidpci_dev_pdn_setup(structpci_dev*pdev){structpci_dn*pdn;
@@ -568,6 +568,26 @@ void maple_pci_irq_fixup(struct pci_dev *dev)DBG(" <- maple_pci_irq_fixup\n");}+staticintmaple_pci_root_bridge_prepare(structpci_host_bridge*bridge)+{+structpci_controller*hose=pci_bus_to_host(bridge->bus);+structdevice_node*np,*child;++if(hose!=u3_agp)+return0;++/* Fixup the PCI<->OF mapping for U3 AGP due to bus renumbering. We+*assumethereisnoP2PbridgeontheAGPbus,whichshouldbea+*safeassumptionshopefully.+*/+np=hose->dn;+PCI_DN(np)->busno=0xf0;+for_each_child_of_node(np,child)+PCI_DN(child)->busno=0xf0;++return0;+}+void__initmaple_pci_init(void){structdevice_node*np,*root;
@@ -605,19 +625,7 @@ void __init maple_pci_init(void)if(ht&&maple_add_bridge(ht)!=0)of_node_put(ht);-/* Setup the linkage between OF nodes and PHBs */-pci_devs_phb_init();--/* Fixup the PCI<->OF mapping for U3 AGP due to bus renumbering. We-*assumethereisnoP2PbridgeontheAGPbus,whichshouldbea-*safeassumptionshopefully.-*/-if(u3_agp){-structdevice_node*np=u3_agp->dn;-PCI_DN(np)->busno=0xf0;-for(np=np->child;np;np=np->sibling)-PCI_DN(np)->busno=0xf0;-}+ppc_md.pcibios_root_bridge_prepare=maple_pci_root_bridge_prepare;/* Tell pci.c to not change any resource allocations. */pci_add_flags(PCI_PROBE_ONLY);
@@ -229,9 +229,6 @@ void __init pas_pci_init(void)of_node_get(np);of_node_put(root);--/* Setup the linkage between OF nodes and PHBs */-pci_devs_phb_init();}void__iomem*pasemi_pci_getcfgaddr(structpci_dev*dev,intoffset)
@@ -878,6 +878,29 @@ void pmac_pci_irq_fixup(struct pci_dev *dev)#endif /* CONFIG_PPC32 */}+#ifdef CONFIG_PPC64+staticintpmac_pci_root_bridge_prepare(structpci_host_bridge*bridge)+{+structpci_controller*hose=pci_bus_to_host(bridge->bus);+structdevice_node*np,*child;++if(hose!=u3_agp)+return0;++/* Fixup the PCI<->OF mapping for U3 AGP due to bus renumbering. We+*assumethereisnoP2PbridgeontheAGPbus,whichshouldbea+*safeassumptionsfornow.Weshoulddosomethingbetterinthe+*futurethough+*/+np=hose->dn;+PCI_DN(np)->busno=0xf0;+for_each_child_of_node(np,child)+PCI_DN(child)->busno=0xf0;++return0;+}+#endif /* CONFIG_PPC64 */+void__initpmac_pci_init(void){structdevice_node*np,*root;
@@ -914,20 +937,7 @@ void __init pmac_pci_init(void)if(ht&&pmac_add_bridge(ht)!=0)of_node_put(ht);-/* Setup the linkage between OF nodes and PHBs */-pci_devs_phb_init();--/* Fixup the PCI<->OF mapping for U3 AGP due to bus renumbering. We-*assumethereisnoP2PbridgeontheAGPbus,whichshouldbea-*safeassumptionsfornow.Weshoulddosomethingbetterinthe-*futurethough-*/-if(u3_agp){-structdevice_node*np=u3_agp->dn;-PCI_DN(np)->busno=0xf0;-for(np=np->child;np;np=np->sibling)-PCI_DN(np)->busno=0xf0;-}+ppc_md.pcibios_root_bridge_prepare=pmac_pci_root_bridge_prepare;/* pmac_check_ht_link(); */#else /* CONFIG_PPC64 */
On the PCI plugging event, PCI slot's subordinate devices are
scanned and their (IO and MMIO) resources are assigned. Platform
dependent resources (PE#, IO/MMIO/DMA windows) are allocated or
created on updating windows of the slot's upstream bridge.
This updates the windows of the hot plugged slot's upstream bridge
in pcibios_finish_adding_to_bus() so that the platform resources
(PE#, IO/MMIO/DMA segments) are allocated or created accordingly.
Signed-off-by: Gavin Shan <redacted>
Reviewed-by: Alexey Kardashevskiy <redacted>
---
arch/powerpc/kernel/pci-common.c | 8 ++++++--
1 file changed, 6 insertions(+), 2 deletions(-)
The PCI slots are associated with root port or downstream ports
of the PCIe switch connected to root port. When adapter is hot
added to the PCI slot, it usually requests more IO or memory
resource from the directly connected parent bridge (port) and
update the bridge's windows accordingly. The resource windows
of upstream bridges can't be updated automatically. It possibly
leads to unbalanced resource across the bridges: The window of
downstream bridge is overruning that of upstream bridge. The
IO or MMIO path won't work.
This resolves the above issue by extending bridge windows of
root port and upstream port of the PCIe switch connected to
the root port to PHB's windows.
Signed-off-by: Gavin Shan <redacted>
---
arch/powerpc/platforms/powernv/pci-ioda.c | 46 +++++++++++++++++++++++++++++++
1 file changed, 46 insertions(+)
@@ -3221,6 +3221,49 @@ static resource_size_t pnv_pci_window_alignment(struct pci_bus *bus,returnphb->ioda.io_segsize;}+/*+*Weareupdatingrootportortheupstreamportofthe+*bridgebehindtherootportwithPHB'swindowsinorder+*toaccommodatethechangesonrequiredresourcesduring+*PCI(slot)hotplug,whichisconnectedtoeitherroot+*portorthedownstreamportsofPCIeswitchbehindthe+*rootport.+*/+staticvoidpnv_pci_fixup_bridge_resources(structpci_bus*bus,+unsignedlongtype)+{+structpci_controller*hose=pci_bus_to_host(bus);+structpnv_phb*phb=hose->private_data;+structpci_dev*bridge=bus->self;+structresource*r,*w;+inti;++/* Check if we need apply fixup to the bridge's windows */+if(!pci_is_root_bus(bridge->bus)&&+!pci_is_root_bus(bridge->bus->self->bus))+return;++/* Fixup the resources */+for(i=0;i<PCI_BRIDGE_RESOURCE_NUM;i++){+r=&bridge->resource[PCI_BRIDGE_RESOURCES+i];+if(!r->flags||!r->parent)+continue;++w=NULL;+if(r->flags&type&IORESOURCE_IO)+w=&hose->io_resource;+elseif(pnv_pci_is_mem_pref_64(r->flags)&&+(type&IORESOURCE_PREFETCH)&&+phb->ioda.m64_segsize)+w=&hose->mem_resources[1];+elseif(r->flags&type&IORESOURCE_MEM)+w=&hose->mem_resources[0];++r->start=w->start;+r->end=w->end;+}+}+staticvoidpnv_pci_setup_bridge(structpci_bus*bus,unsignedlongtype){structpci_controller*hose=pci_bus_to_host(bus);
@@ -3229,6 +3272,9 @@ static void pnv_pci_setup_bridge(struct pci_bus *bus, unsigned long type)structpnv_ioda_pe*pe;boolall=(pci_pcie_type(bridge)==PCI_EXP_TYPE_PCI_BRIDGE);+/* Extend bridge's windows if necessary */+pnv_pci_fixup_bridge_resources(bus,type);+/* The PE for root bus should be realized before any one else */if(!phb->ioda.root_pe_populated){pe=pnv_ioda_setup_bus_PE(phb->hose->bus,false);
This introduces pnv_pci_get_slot_id() to get the hotpluggable PCI
slot ID from the corresponding device node. It will be used by
hotplug driver.
Requested-by: Andrew Donnellan [off-list ref]
Signed-off-by: Gavin Shan <redacted>
---
arch/powerpc/include/asm/pnv-pci.h | 2 ++
arch/powerpc/platforms/powernv/pci.c | 38 ++++++++++++++++++++++++++++++++++++
2 files changed, 40 insertions(+)
pnv_pci_ioda_setup_opal_tce_kill() called by pnv_ioda_setup_dma()
to remap the TCE kill regiter. What's done in pnv_ioda_setup_dma()
will be covered in pcibios_setup_bridge() which is invoked on each
PCI bridge. It means we will possibly remap the TCE kill register
for multiple times and it's unnecessary.
This moves pnv_pci_ioda_setup_opal_tce_kill() to where the PHB is
initialized (pnv_pci_init_ioda_phb()) to avoid above issue.
Signed-off-by: Gavin Shan <redacted>
Reviewed-by: Alexey Kardashevskiy <redacted>
---
arch/powerpc/platforms/powernv/pci-ioda.c | 5 +++--
1 file changed, 3 insertions(+), 2 deletions(-)
@@ -2702,8 +2702,6 @@ static void pnv_ioda_setup_dma(struct pnv_phb *phb)pr_info("PCI: Domain %04x has %d available 32-bit DMA segments\n",hose->global_number,phb->ioda.dma32_count);-pnv_pci_ioda_setup_opal_tce_kill(phb);-/* Walk our PE list and configure their DMA segments */list_for_each_entry(pe,&phb->ioda.pe_list,list){weight=pnv_pci_ioda_pe_dma_weight(pe);
pnv_ioda_deconfigure_pe() is visible only when CONFIG_PCI_IOV is
enabled. The function will be used to tear down PE's associated
mapping in PCI hotplug path that doesn't depend on CONFIG_PCI_IOV.
This makes pnv_ioda_deconfigure_pe() visible and not depend on
CONFIG_PCI_IOV.
Signed-off-by: Gavin Shan <redacted>
---
arch/powerpc/platforms/powernv/pci-ioda.c | 6 ++++--
1 file changed, 4 insertions(+), 2 deletions(-)
The reset and poll functionality from (OPAL) firmware supports
PHB and PCI slot at same time. They are identified by ID. This
supports PCI slot ID by:
* Rename the argument name for opal_pci_reset() and opal_pci_poll()
accordingly
* Rename pnv_eeh_phb_poll() to pnv_eeh_poll() and adjust its argument
name.
* One macro is added to produce PCI slot ID.
Signed-off-by: Gavin Shan <redacted>
---
arch/powerpc/include/asm/opal.h | 4 ++--
arch/powerpc/include/asm/pnv-pci.h | 4 ++++
arch/powerpc/platforms/powernv/eeh-powernv.c | 8 ++++----
3 files changed, 10 insertions(+), 6 deletions(-)
@@ -717,12 +717,12 @@ static int pnv_eeh_get_state(struct eeh_pe *pe, int *delay)returnret;}-statics64pnv_eeh_phb_poll(structpnv_phb*phb)+statics64pnv_eeh_poll(unsignedlongid){s64rc=OPAL_HARDWARE;while(1){-rc=opal_pci_poll(phb->opal_id);+rc=opal_pci_poll(id);if(rc<=0)break;
@@ -762,7 +762,7 @@ int pnv_eeh_phb_reset(struct pci_controller *hose, int option)*resetfollowedbyhotresetonrootbus.Sowealso*needthePCIbussettlementdelay.*/-rc=pnv_eeh_phb_poll(phb);+rc=pnv_eeh_poll(phb->opal_id);if(option==EEH_RESET_DEACTIVATE){if(system_state<SYSTEM_RUNNING)udelay(1000*EEH_PE_RST_SETTLE_TIME);
@@ -805,7 +805,7 @@ static int pnv_eeh_root_reset(struct pci_controller *hose, int option)gotoout;/* Poll state of the PHB until the request is done */-rc=pnv_eeh_phb_poll(phb);+rc=pnv_eeh_poll(phb->opal_id);if(option==EEH_RESET_DEACTIVATE)msleep(EEH_PE_RST_SETTLE_TIME);out:
From: Andrew Donnellan <hidden> Date: 2016-06-01 02:45:48
On 20/05/16 16:41, Gavin Shan wrote:
The macro defined in arch/powerpc/platforms/powernv/pci.c isn't
used by anyone. Just remove it.
Signed-off-by: Gavin Shan <redacted>
Looks like the only user of it disappeared in late 2014...
Reviewed-by: Andrew Donnellan <redacted>
--
Andrew Donnellan OzLabs, ADL Canberra
andrew.donnellan@au1.ibm.com IBM Australia Limited
The PCI slots are associated with root port or downstream ports
of the PCIe switch connected to root port. When adapter is hot
added to the PCI slot, it usually requests more IO or memory
resource from the directly connected parent bridge (port) and
update the bridge's windows accordingly. The resource windows
of upstream bridges can't be updated automatically. It possibly
leads to unbalanced resource across the bridges: The window of
downstream bridge is overruning that of upstream bridge. The
IO or MMIO path won't work.
This resolves the above issue by extending bridge windows of
root port and upstream port of the PCIe switch connected to
the root port to PHB's windows.
Signed-off-by: Gavin Shan <redacted>
This breaks Garrison machine (g86l):
EEH: Frozen PE#f9 on PHB#5 detected
EEH: PE location: Backplane PLX, PHB location: N/A
EEH: This PCI device has failed 1 times in the last hour
EEH: Notify device drivers to shutdown
EEH: Collect temporary log
PHB#5 has a boot device so we end up in initramdisk.
│0005:03:00.0 USB controller: Texas Instruments TUSB73x0 SuperSpeed USB 3.0
xHCI Host Controller (rev 02)
│0005:04:00.0 SATA controller: Marvell Technology Group Ltd. 88SE9235 PCIe
2.0 x2 4-port SATA 6 Gb/s Controller (rev 11)
│0005:05:00.0 PCI bridge: ASPEED Technology, Inc. AST1150 PCI-to-PCI Bridge
(rev 03)
│0005:06:00.0 VGA compatible controller: ASPEED Technology, Inc. ASPEED
Graphics Family (rev 30)
@@ -3221,6 +3221,49 @@ static resource_size_t pnv_pci_window_alignment(struct pci_bus *bus,returnphb->ioda.io_segsize;}+/*+*Weareupdatingrootportortheupstreamportofthe+*bridgebehindtherootportwithPHB'swindowsinorder+*toaccommodatethechangesonrequiredresourcesduring+*PCI(slot)hotplug,whichisconnectedtoeitherroot+*portorthedownstreamportsofPCIeswitchbehindthe+*rootport.+*/+staticvoidpnv_pci_fixup_bridge_resources(structpci_bus*bus,+unsignedlongtype)+{+structpci_controller*hose=pci_bus_to_host(bus);+structpnv_phb*phb=hose->private_data;+structpci_dev*bridge=bus->self;+structresource*r,*w;+inti;++/* Check if we need apply fixup to the bridge's windows */+if(!pci_is_root_bus(bridge->bus)&&+!pci_is_root_bus(bridge->bus->self->bus))+return;++/* Fixup the resources */+for(i=0;i<PCI_BRIDGE_RESOURCE_NUM;i++){+r=&bridge->resource[PCI_BRIDGE_RESOURCES+i];+if(!r->flags||!r->parent)+continue;++w=NULL;+if(r->flags&type&IORESOURCE_IO)+w=&hose->io_resource;+elseif(pnv_pci_is_mem_pref_64(r->flags)&&+(type&IORESOURCE_PREFETCH)&&+phb->ioda.m64_segsize)+w=&hose->mem_resources[1];+elseif(r->flags&type&IORESOURCE_MEM)+w=&hose->mem_resources[0];++r->start=w->start;+r->end=w->end;+}+}+staticvoidpnv_pci_setup_bridge(structpci_bus*bus,unsignedlongtype){structpci_controller*hose=pci_bus_to_host(bus);
@@ -3229,6 +3272,9 @@ static void pnv_pci_setup_bridge(struct pci_bus *bus, unsigned long type)structpnv_ioda_pe*pe;boolall=(pci_pcie_type(bridge)==PCI_EXP_TYPE_PCI_BRIDGE);+/* Extend bridge's windows if necessary */+pnv_pci_fixup_bridge_resources(bus,type);+/* The PE for root bus should be realized before any one else */if(!phb->ioda.root_pe_populated){pe=pnv_ioda_setup_bus_PE(phb->hose->bus,false);
On Wed, Jun 08, 2016 at 01:47:16PM +1000, Alexey Kardashevskiy wrote:
On 20/05/16 16:41, Gavin Shan wrote:
quoted
The PCI slots are associated with root port or downstream ports
of the PCIe switch connected to root port. When adapter is hot
added to the PCI slot, it usually requests more IO or memory
resource from the directly connected parent bridge (port) and
update the bridge's windows accordingly. The resource windows
of upstream bridges can't be updated automatically. It possibly
leads to unbalanced resource across the bridges: The window of
downstream bridge is overruning that of upstream bridge. The
IO or MMIO path won't work.
This resolves the above issue by extending bridge windows of
root port and upstream port of the PCIe switch connected to
the root port to PHB's windows.
Signed-off-by: Gavin Shan <redacted>
This breaks Garrison machine (g86l):
EEH: Frozen PE#f9 on PHB#5 detected
EEH: PE location: Backplane PLX, PHB location: N/A
EEH: This PCI device has failed 1 times in the last hour
EEH: Notify device drivers to shutdown
EEH: Collect temporary log
Thanks for reporting the issue. I don't think the issue was caused by
the code in this patch. Actually, it's likely caused by hardware defect
- we can't set 2GB (0x80000000 - 0xffffffff) to RC's memory window.
Otherwise, it *seems* the window is disabled. I tried updating the
window with (0x80000000 - 0xffefffff) or (0x80000000 - 0xffdffff), no
EEH error was seen. I already got 0x00001000 on read despite whatever
I wrote to 0x20 reg.
The hardware is broken. In order to fix this, I intend to include a
bitmap for every PHB device node in skiboot. Kernel uses this to apply
fixup accordingly. One bit is reserved on Garrison platform to avoid
this issue. The fix can be a patch inserted before this patch in next
revision or as a followup patch after this series of patches.
PHB#5 has a boot device so we end up in initramdisk.
│0005:03:00.0 USB controller: Texas Instruments TUSB73x0 SuperSpeed USB 3.0
xHCI Host Controller (rev 02)
│0005:04:00.0 SATA controller: Marvell Technology Group Ltd. 88SE9235 PCIe
2.0 x2 4-port SATA 6 Gb/s Controller (rev 11)
│0005:05:00.0 PCI bridge: ASPEED Technology, Inc. AST1150 PCI-to-PCI Bridge
(rev 03)
│0005:06:00.0 VGA compatible controller: ASPEED Technology, Inc. ASPEED
Graphics Family (rev 30)
@@ -3221,6 +3221,49 @@ static resource_size_t pnv_pci_window_alignment(struct pci_bus *bus,returnphb->ioda.io_segsize;}+/*+*Weareupdatingrootportortheupstreamportofthe+*bridgebehindtherootportwithPHB'swindowsinorder+*toaccommodatethechangesonrequiredresourcesduring+*PCI(slot)hotplug,whichisconnectedtoeitherroot+*portorthedownstreamportsofPCIeswitchbehindthe+*rootport.+*/+staticvoidpnv_pci_fixup_bridge_resources(structpci_bus*bus,+unsignedlongtype)+{+structpci_controller*hose=pci_bus_to_host(bus);+structpnv_phb*phb=hose->private_data;+structpci_dev*bridge=bus->self;+structresource*r,*w;+inti;++/* Check if we need apply fixup to the bridge's windows */+if(!pci_is_root_bus(bridge->bus)&&+!pci_is_root_bus(bridge->bus->self->bus))+return;++/* Fixup the resources */+for(i=0;i<PCI_BRIDGE_RESOURCE_NUM;i++){+r=&bridge->resource[PCI_BRIDGE_RESOURCES+i];+if(!r->flags||!r->parent)+continue;++w=NULL;+if(r->flags&type&IORESOURCE_IO)+w=&hose->io_resource;+elseif(pnv_pci_is_mem_pref_64(r->flags)&&+(type&IORESOURCE_PREFETCH)&&+phb->ioda.m64_segsize)+w=&hose->mem_resources[1];+elseif(r->flags&type&IORESOURCE_MEM)+w=&hose->mem_resources[0];++r->start=w->start;+r->end=w->end;+}+}+staticvoidpnv_pci_setup_bridge(structpci_bus*bus,unsignedlongtype){structpci_controller*hose=pci_bus_to_host(bus);
@@ -3229,6 +3272,9 @@ static void pnv_pci_setup_bridge(struct pci_bus *bus, unsigned long type)structpnv_ioda_pe*pe;boolall=(pci_pcie_type(bridge)==PCI_EXP_TYPE_PCI_BRIDGE);+/* Extend bridge's windows if necessary */+pnv_pci_fixup_bridge_resources(bus,type);+/* The PE for root bus should be realized before any one else */if(!phb->ioda.root_pe_populated){pe=pnv_ioda_setup_bus_PE(phb->hose->bus,false);
On Wed, Jun 08, 2016 at 01:47:16PM +1000, Alexey Kardashevskiy wrote:
quoted
On 20/05/16 16:41, Gavin Shan wrote:
quoted
The PCI slots are associated with root port or downstream ports
of the PCIe switch connected to root port. When adapter is hot
added to the PCI slot, it usually requests more IO or memory
resource from the directly connected parent bridge (port) and
update the bridge's windows accordingly. The resource windows
of upstream bridges can't be updated automatically. It possibly
leads to unbalanced resource across the bridges: The window of
downstream bridge is overruning that of upstream bridge. The
IO or MMIO path won't work.
This resolves the above issue by extending bridge windows of
root port and upstream port of the PCIe switch connected to
the root port to PHB's windows.
Signed-off-by: Gavin Shan <redacted>
This breaks Garrison machine (g86l):
EEH: Frozen PE#f9 on PHB#5 detected
EEH: PE location: Backplane PLX, PHB location: N/A
EEH: This PCI device has failed 1 times in the last hour
EEH: Notify device drivers to shutdown
EEH: Collect temporary log
Thanks for reporting the issue. I don't think the issue was caused by
the code in this patch.
If you say so :) I am just saying that the code in this patch did trigger
the bug, I bisected the series to this patch to find this out.
Actually, it's likely caused by hardware defect
- we can't set 2GB (0x80000000 - 0xffffffff) to RC's memory window.
Otherwise, it *seems* the window is disabled. I tried updating the
window with (0x80000000 - 0xffefffff) or (0x80000000 - 0xffdffff), no
EEH error was seen. I already got 0x00001000 on read despite whatever
I wrote to 0x20 reg.
The hardware is broken. In order to fix this, I intend to include a
bitmap for every PHB device node in skiboot. Kernel uses this to apply
fixup accordingly. One bit is reserved on Garrison platform to avoid
this issue. The fix can be a patch inserted before this patch in next
revision
This sounds better as preserves bisectability. Thanks.
or as a followup patch after this series of patches.
From: Benjamin Herrenschmidt <benh@kernel.crashing.org> Date: 2016-06-10 05:45:44
On Fri, 2016-06-10 at 15:28 +1000, Alexey Kardashevskiy wrote:
quoted
Actually, it's likely caused by hardware defect
- we can't set 2GB (0x80000000 - 0xffffffff) to RC's memory window.
Otherwise, it *seems* the window is disabled. I tried updating the
window with (0x80000000 - 0xffefffff) or (0x80000000 - 0xffdffff), no
EEH error was seen. I already got 0x00001000 on read despite whatever
I wrote to 0x20 reg.
The hardware is broken. In order to fix this, I intend to include a
bitmap for every PHB device node in skiboot. Kernel uses this to apply
fixup accordingly. One bit is reserved on Garrison platform to avoid
this issue. The fix can be a patch inserted before this patch in next
revision
This sounds better as preserves bisectability. Thanks.
Ah yes they made those registers read-only. Look at my PHB4 code, I
implement a cache for them in SW.
Cheers,
Ben.
On Fri, Jun 10, 2016 at 03:45:30PM +1000, Benjamin Herrenschmidt wrote:
On Fri, 2016-06-10 at 15:28 +1000, Alexey Kardashevskiy wrote:
quoted
quoted
Actually, it's likely caused by hardware defect
- we can't set 2GB (0x80000000 - 0xffffffff) to RC's memory window.
Otherwise, it *seems* the window is disabled. I tried updating the
window with (0x80000000 - 0xffefffff) or (0x80000000 - 0xffdffff), no
EEH error was seen. I already got 0x00001000 on read despite whatever
I wrote to 0x20 reg.
The hardware is broken. In order to fix this, I intend to include a
bitmap for every PHB device node in skiboot. Kernel uses this to apply
fixup accordingly. One bit is reserved on Garrison platform to avoid
this issue. The fix can be a patch inserted before this patch in next
revision
This sounds better as preserves bisectability. Thanks.
Ah yes they made those registers read-only. Look at my PHB4 code, I
implement a cache for them in SW.
Ben, thanks for your confirm. Could you please share the link to
your PHB4 code? I think writing to SW cache, not going to hardware
will fix the issue.
Currently, skiboot supports emulated config regiters with help of
(struct pci_cfg_reg_filter) that was introduced for CAPI M64 BAR
issue on Garrison platform. Potentially, I can have similar thing
for 0x20 (memory window) to avoid writing to the register. However,
I need take a look on your PHB4 code to see if there is anything I
can lend. Otherwise, I will reuse the struct pci_cfg_reg_filter.
At same time, I guess the bitmap (mentioned as above) is still
needed to ensure (new kernel + old skiboot) works well, but it
depends on how much Garrison boxes have been deployed.
I don't see this in skiboot?
It also doesn't seem to be used, so I've dropped it.
Thanks, Michael. All the changes are correct. The enum name was changed
in the last skiboot patchset that was merged couple days ago. At same
time, OPAL_MSG_PCI_HOTPLUG isn't needed as PCI hotplug won't have one
dedicated message type, an asychornous message is used instead.
Thanks,
Gavin
From: Michael Ellerman <mpe@ellerman.id.au> Date: 2016-06-21 12:27:15
On Fri, 2016-20-05 at 06:41:25 UTC, Gavin Shan wrote:
Currently, PowerPC PowerNV platform utilizes ppc_md.pcibios_fixup(),
which is called for once after PCI probing and resource assignment
are completed, to allocate platform required resources for PCI devices:
PE#, IO and MMIO mapping, DMA address translation (TCE) table etc.
Obviously, it's not hotplug friendly.
This adds weak function pcibios_setup_bridge(), which is called by
pci_setup_bridge(). PowerPC PowerNV platform will reuse the function
to assign above platform required resources to newly plugged PCI devices
during PCI hotplug in subsequent patches.
Signed-off-by: Gavin Shan <redacted>
Acked-by: Bjorn Helgaas <bhelgaas@google.com>
From: Daniel Axtens <hidden> Date: 2016-06-23 01:00:12
Hi,
mpe merged this to his next yesterday, and my nightly scripts picked up
the following cppcheck warning:
[arch/powerpc/kernel/pci_dn.c:486]: (error) Memory leak: pdn
That goes to the following function:
static void *add_pdn(struct device_node *dn, void *data)
{
struct pci_controller *hose = data;
struct pci_dn *pdn;
pdn = pci_add_device_node_info(hose, dn);
if (!pdn)
return ERR_PTR(-ENOMEM);
return NULL;
}
It looks like this is just called in pci_devs_phb_init_dynamic:
/* Update dn->phb ptrs for new phb and children devices */
pci_traverse_device_nodes(dn, add_pdn, phb);
So my understanding is that this isn't a real memory leak but just
cppcheck getting confused.
Gavin, can you confirm that?
Regards,
Daniel
Gavin Shan [off-list ref] writes:
quoted hunk
The pdn (struct pci_dn) instances are allocated from memblock or
bootmem when creating PCI controller (hoses) in setup_arch(). PCI
hotplug, which will be supported by proceeding patches, releases
PCI device nodes and their corresponding pdn on unplugging event.
The memory chunks for pdn instances allocated from memblock or
bootmem are hard to reused after being released.
This delays creating pdn by pci_devs_phb_init() from setup_arch()
to core_initcall() so that they are allocated from slab. The memory
consumed by pdn can be released to system without problem during
PCI unplugging time. It indicates that pci_dn is unavailable in
setup_arch() and the the fixup on pdn (like AGP's) can't be carried
out that time. We have to do that in pcibios_root_bridge_prepare()
on maple/pasemi/powermac platforms where/when the pdn is available.
pcibios_root_bridge_prepare is called from subsys_initcall() which
is executed after core_initcall() so the code flow does not change.
At the mean while, the EEH device is created when pdn is populated,
meaning pdn and EEH device have same life cycle. In turn, we needn't
call eeh_dev_init() to create EEH device explicitly.
Signed-off-by: Gavin Shan <redacted>
Reviewed-by: Alexey Kardashevskiy <redacted>
---
arch/powerpc/include/asm/eeh.h | 2 +-
arch/powerpc/include/asm/ppc-pci.h | 2 --
arch/powerpc/kernel/eeh_dev.c | 17 +++------------
arch/powerpc/kernel/pci_dn.c | 23 ++++++++++++++++----
arch/powerpc/platforms/maple/pci.c | 34 ++++++++++++++++++------------
arch/powerpc/platforms/pasemi/pci.c | 3 ---
arch/powerpc/platforms/powermac/pci.c | 38 +++++++++++++++++++++-------------
arch/powerpc/platforms/powernv/pci.c | 3 ---
arch/powerpc/platforms/pseries/setup.c | 6 +-----
9 files changed, 69 insertions(+), 59 deletions(-)
@@ -81,16 +80,8 @@ void *eeh_dev_init(struct pci_dn *pdn, void *data)*/voideeh_dev_phb_init_dynamic(structpci_controller*phb){-structpci_dn*root=phb->pci_data;-/* EEH PE for PHB */eeh_phb_pe_create(phb);--/* EEH device for PHB */-eeh_dev_init(root,phb);--/* EEH devices for children OF nodes */-traverse_pci_dn(root,eeh_dev_init,phb);}/**
@@ -106,8 +97,6 @@ static int __init eeh_dev_phb_init(void)list_for_each_entry_safe(phb,tmp,&hose_list,list_node)eeh_dev_phb_init_dynamic(phb);-pr_info("EEH: devices created\n");-return0;}
@@ -504,15 +515,19 @@ void pci_devs_phb_init_dynamic(struct pci_controller *phb)*pcidevicefoundunderneath.Thisroutinerunsonce,*earlyinthebootsequence.*/-void__initpci_devs_phb_init(void)+staticint__initpci_devs_phb_init(void){structpci_controller*phb,*tmp;/* This must be done first so the device nodes have valid pci info! */list_for_each_entry_safe(phb,tmp,&hose_list,list_node)pci_devs_phb_init_dynamic(phb);++return0;}+core_initcall(pci_devs_phb_init);+staticvoidpci_dev_pdn_setup(structpci_dev*pdev){structpci_dn*pdn;
@@ -568,6 +568,26 @@ void maple_pci_irq_fixup(struct pci_dev *dev)DBG(" <- maple_pci_irq_fixup\n");}+staticintmaple_pci_root_bridge_prepare(structpci_host_bridge*bridge)+{+structpci_controller*hose=pci_bus_to_host(bridge->bus);+structdevice_node*np,*child;++if(hose!=u3_agp)+return0;++/* Fixup the PCI<->OF mapping for U3 AGP due to bus renumbering. We+*assumethereisnoP2PbridgeontheAGPbus,whichshouldbea+*safeassumptionshopefully.+*/+np=hose->dn;+PCI_DN(np)->busno=0xf0;+for_each_child_of_node(np,child)+PCI_DN(child)->busno=0xf0;++return0;+}+void__initmaple_pci_init(void){structdevice_node*np,*root;
@@ -605,19 +625,7 @@ void __init maple_pci_init(void)if(ht&&maple_add_bridge(ht)!=0)of_node_put(ht);-/* Setup the linkage between OF nodes and PHBs */-pci_devs_phb_init();--/* Fixup the PCI<->OF mapping for U3 AGP due to bus renumbering. We-*assumethereisnoP2PbridgeontheAGPbus,whichshouldbea-*safeassumptionshopefully.-*/-if(u3_agp){-structdevice_node*np=u3_agp->dn;-PCI_DN(np)->busno=0xf0;-for(np=np->child;np;np=np->sibling)-PCI_DN(np)->busno=0xf0;-}+ppc_md.pcibios_root_bridge_prepare=maple_pci_root_bridge_prepare;/* Tell pci.c to not change any resource allocations. */pci_add_flags(PCI_PROBE_ONLY);
@@ -229,9 +229,6 @@ void __init pas_pci_init(void)of_node_get(np);of_node_put(root);--/* Setup the linkage between OF nodes and PHBs */-pci_devs_phb_init();}void__iomem*pasemi_pci_getcfgaddr(structpci_dev*dev,intoffset)
@@ -878,6 +878,29 @@ void pmac_pci_irq_fixup(struct pci_dev *dev)#endif /* CONFIG_PPC32 */}+#ifdef CONFIG_PPC64+staticintpmac_pci_root_bridge_prepare(structpci_host_bridge*bridge)+{+structpci_controller*hose=pci_bus_to_host(bridge->bus);+structdevice_node*np,*child;++if(hose!=u3_agp)+return0;++/* Fixup the PCI<->OF mapping for U3 AGP due to bus renumbering. We+*assumethereisnoP2PbridgeontheAGPbus,whichshouldbea+*safeassumptionsfornow.Weshoulddosomethingbetterinthe+*futurethough+*/+np=hose->dn;+PCI_DN(np)->busno=0xf0;+for_each_child_of_node(np,child)+PCI_DN(child)->busno=0xf0;++return0;+}+#endif /* CONFIG_PPC64 */+void__initpmac_pci_init(void){structdevice_node*np,*root;
@@ -914,20 +937,7 @@ void __init pmac_pci_init(void)if(ht&&pmac_add_bridge(ht)!=0)of_node_put(ht);-/* Setup the linkage between OF nodes and PHBs */-pci_devs_phb_init();--/* Fixup the PCI<->OF mapping for U3 AGP due to bus renumbering. We-*assumethereisnoP2PbridgeontheAGPbus,whichshouldbea-*safeassumptionsfornow.Weshoulddosomethingbetterinthe-*futurethough-*/-if(u3_agp){-structdevice_node*np=u3_agp->dn;-PCI_DN(np)->busno=0xf0;-for(np=np->child;np;np=np->sibling)-PCI_DN(np)->busno=0xf0;-}+ppc_md.pcibios_root_bridge_prepare=pmac_pci_root_bridge_prepare;/* pmac_check_ht_link(); */#else /* CONFIG_PPC64 */
--
2.1.0
--
To unsubscribe from this list: send the line "unsubscribe linux-pci" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
On Thu, Jun 23, 2016 at 10:59:57AM +1000, Daniel Axtens wrote:
Hi,
mpe merged this to his next yesterday, and my nightly scripts picked up
the following cppcheck warning:
[arch/powerpc/kernel/pci_dn.c:486]: (error) Memory leak: pdn
That goes to the following function:
static void *add_pdn(struct device_node *dn, void *data)
{
struct pci_controller *hose = data;
struct pci_dn *pdn;
pdn = pci_add_device_node_info(hose, dn);
if (!pdn)
return ERR_PTR(-ENOMEM);
return NULL;
}
It looks like this is just called in pci_devs_phb_init_dynamic:
/* Update dn->phb ptrs for new phb and children devices */
pci_traverse_device_nodes(dn, add_pdn, phb);
So my understanding is that this isn't a real memory leak but just
cppcheck getting confused.
Gavin, can you confirm that?
Daniel, thanks for the report. It's a false alarm: pci_traverse_device_nodes()
will be terminated immediate on non-NULL return value from add_pdn(). So the
function (add_pdn()) returns NULL intentionally. The newly created @pdn has
been attached to @dn. The @pdn is release when the @dn is released in PCI
hot remove time.
Regards,
Daniel
Thanks,
Gavin
Gavin Shan [off-list ref] writes:
quoted
The pdn (struct pci_dn) instances are allocated from memblock or
bootmem when creating PCI controller (hoses) in setup_arch(). PCI
hotplug, which will be supported by proceeding patches, releases
PCI device nodes and their corresponding pdn on unplugging event.
The memory chunks for pdn instances allocated from memblock or
bootmem are hard to reused after being released.
This delays creating pdn by pci_devs_phb_init() from setup_arch()
to core_initcall() so that they are allocated from slab. The memory
consumed by pdn can be released to system without problem during
PCI unplugging time. It indicates that pci_dn is unavailable in
setup_arch() and the the fixup on pdn (like AGP's) can't be carried
out that time. We have to do that in pcibios_root_bridge_prepare()
on maple/pasemi/powermac platforms where/when the pdn is available.
pcibios_root_bridge_prepare is called from subsys_initcall() which
is executed after core_initcall() so the code flow does not change.
At the mean while, the EEH device is created when pdn is populated,
meaning pdn and EEH device have same life cycle. In turn, we needn't
call eeh_dev_init() to create EEH device explicitly.
Signed-off-by: Gavin Shan <redacted>
Reviewed-by: Alexey Kardashevskiy <redacted>
---
arch/powerpc/include/asm/eeh.h | 2 +-
arch/powerpc/include/asm/ppc-pci.h | 2 --
arch/powerpc/kernel/eeh_dev.c | 17 +++------------
arch/powerpc/kernel/pci_dn.c | 23 ++++++++++++++++----
arch/powerpc/platforms/maple/pci.c | 34 ++++++++++++++++++------------
arch/powerpc/platforms/pasemi/pci.c | 3 ---
arch/powerpc/platforms/powermac/pci.c | 38 +++++++++++++++++++++-------------
arch/powerpc/platforms/powernv/pci.c | 3 ---
arch/powerpc/platforms/pseries/setup.c | 6 +-----
9 files changed, 69 insertions(+), 59 deletions(-)
@@ -81,16 +80,8 @@ void *eeh_dev_init(struct pci_dn *pdn, void *data)*/voideeh_dev_phb_init_dynamic(structpci_controller*phb){-structpci_dn*root=phb->pci_data;-/* EEH PE for PHB */eeh_phb_pe_create(phb);--/* EEH device for PHB */-eeh_dev_init(root,phb);--/* EEH devices for children OF nodes */-traverse_pci_dn(root,eeh_dev_init,phb);}/**
@@ -106,8 +97,6 @@ static int __init eeh_dev_phb_init(void)list_for_each_entry_safe(phb,tmp,&hose_list,list_node)eeh_dev_phb_init_dynamic(phb);-pr_info("EEH: devices created\n");-return0;}
@@ -504,15 +515,19 @@ void pci_devs_phb_init_dynamic(struct pci_controller *phb)*pcidevicefoundunderneath.Thisroutinerunsonce,*earlyinthebootsequence.*/-void__initpci_devs_phb_init(void)+staticint__initpci_devs_phb_init(void){structpci_controller*phb,*tmp;/* This must be done first so the device nodes have valid pci info! */list_for_each_entry_safe(phb,tmp,&hose_list,list_node)pci_devs_phb_init_dynamic(phb);++return0;}+core_initcall(pci_devs_phb_init);+staticvoidpci_dev_pdn_setup(structpci_dev*pdev){structpci_dn*pdn;
@@ -568,6 +568,26 @@ void maple_pci_irq_fixup(struct pci_dev *dev)DBG(" <- maple_pci_irq_fixup\n");}+staticintmaple_pci_root_bridge_prepare(structpci_host_bridge*bridge)+{+structpci_controller*hose=pci_bus_to_host(bridge->bus);+structdevice_node*np,*child;++if(hose!=u3_agp)+return0;++/* Fixup the PCI<->OF mapping for U3 AGP due to bus renumbering. We+*assumethereisnoP2PbridgeontheAGPbus,whichshouldbea+*safeassumptionshopefully.+*/+np=hose->dn;+PCI_DN(np)->busno=0xf0;+for_each_child_of_node(np,child)+PCI_DN(child)->busno=0xf0;++return0;+}+void__initmaple_pci_init(void){structdevice_node*np,*root;
@@ -605,19 +625,7 @@ void __init maple_pci_init(void)if(ht&&maple_add_bridge(ht)!=0)of_node_put(ht);-/* Setup the linkage between OF nodes and PHBs */-pci_devs_phb_init();--/* Fixup the PCI<->OF mapping for U3 AGP due to bus renumbering. We-*assumethereisnoP2PbridgeontheAGPbus,whichshouldbea-*safeassumptionshopefully.-*/-if(u3_agp){-structdevice_node*np=u3_agp->dn;-PCI_DN(np)->busno=0xf0;-for(np=np->child;np;np=np->sibling)-PCI_DN(np)->busno=0xf0;-}+ppc_md.pcibios_root_bridge_prepare=maple_pci_root_bridge_prepare;/* Tell pci.c to not change any resource allocations. */pci_add_flags(PCI_PROBE_ONLY);
@@ -229,9 +229,6 @@ void __init pas_pci_init(void)of_node_get(np);of_node_put(root);--/* Setup the linkage between OF nodes and PHBs */-pci_devs_phb_init();}void__iomem*pasemi_pci_getcfgaddr(structpci_dev*dev,intoffset)
@@ -878,6 +878,29 @@ void pmac_pci_irq_fixup(struct pci_dev *dev)#endif /* CONFIG_PPC32 */}+#ifdef CONFIG_PPC64+staticintpmac_pci_root_bridge_prepare(structpci_host_bridge*bridge)+{+structpci_controller*hose=pci_bus_to_host(bridge->bus);+structdevice_node*np,*child;++if(hose!=u3_agp)+return0;++/* Fixup the PCI<->OF mapping for U3 AGP due to bus renumbering. We+*assumethereisnoP2PbridgeontheAGPbus,whichshouldbea+*safeassumptionsfornow.Weshoulddosomethingbetterinthe+*futurethough+*/+np=hose->dn;+PCI_DN(np)->busno=0xf0;+for_each_child_of_node(np,child)+PCI_DN(child)->busno=0xf0;++return0;+}+#endif /* CONFIG_PPC64 */+void__initpmac_pci_init(void){structdevice_node*np,*root;
@@ -914,20 +937,7 @@ void __init pmac_pci_init(void)if(ht&&pmac_add_bridge(ht)!=0)of_node_put(ht);-/* Setup the linkage between OF nodes and PHBs */-pci_devs_phb_init();--/* Fixup the PCI<->OF mapping for U3 AGP due to bus renumbering. We-*assumethereisnoP2PbridgeontheAGPbus,whichshouldbea-*safeassumptionsfornow.Weshoulddosomethingbetterinthe-*futurethough-*/-if(u3_agp){-structdevice_node*np=u3_agp->dn;-PCI_DN(np)->busno=0xf0;-for(np=np->child;np;np=np->sibling)-PCI_DN(np)->busno=0xf0;-}+ppc_md.pcibios_root_bridge_prepare=pmac_pci_root_bridge_prepare;/* pmac_check_ht_link(); */#else /* CONFIG_PPC64 */
--
2.1.0
--
To unsubscribe from this list: send the line "unsubscribe linux-pci" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html