* Re: [PATCH v2 08/11] irqdomain: Refactor irq_domain_associate_many()
From: Grant Likely @ 2013-06-18 9:05 UTC (permalink / raw)
To: Michael Neuling
Cc: Stephen Rothwell, Linux Kernel Mailing List, Linux PPC dev,
linux-next, Thomas Gleixner
In-Reply-To: <5220.1371518734@ale.ozlabs.ibm.com>
On Tue, Jun 18, 2013 at 2:25 AM, Michael Neuling <mikey@neuling.org> wrote:
> Michael Neuling <mikey@neuling.org> wrote:
>
>> Grant,
>>
>> In next-20130617 we are getting the below crash on POWER7. Bisecting,
>> points to this patch (d39046ec72 in next)
>
> Also, reverting just d39046ec72 fixes the crash in next-20130617.
Odd. Of all the changes in that series, I would not have expected that
one to cause any problems. I'm digging into it now...
g.
^ permalink raw reply
* Re: [PATCH v2 08/11] irqdomain: Refactor irq_domain_associate_many()
From: Grant Likely @ 2013-06-18 9:46 UTC (permalink / raw)
To: Michael Neuling
Cc: Stephen Rothwell, Linux Kernel Mailing List, Linux PPC dev,
linux-next, Thomas Gleixner
In-Reply-To: <CACxGe6s+K0xF0pnLjKoKXtz_YNoeM2+erhr_g2dX4ONpddRNGA@mail.gmail.com>
On Tue, 18 Jun 2013 10:05:31 +0100, Grant Likely <grant.likely@linaro.org> wrote:
> On Tue, Jun 18, 2013 at 2:25 AM, Michael Neuling <mikey@neuling.org> wrote:
> > Michael Neuling <mikey@neuling.org> wrote:
> >
> >> Grant,
> >>
> >> In next-20130617 we are getting the below crash on POWER7. Bisecting,
> >> points to this patch (d39046ec72 in next)
> >
> > Also, reverting just d39046ec72 fixes the crash in next-20130617.
>
> Odd. Of all the changes in that series, I would not have expected that
> one to cause any problems. I'm digging into it now...
Ugh. I flubbed the commit. Try this patch. It should solve the problem:
>From b7ba09c29ed36eda8e16453646b55faea7f9c25f Mon Sep 17 00:00:00 2001
From: Grant Likely <grant.likely@linaro.org>
Date: Tue, 18 Jun 2013 10:15:19 +0100
Subject: [PATCH] irqdomain: Fix flubbed irq_domain_associate_many refactoring
commit d39046ec72, "irqdomain: Refactor irq_domain_associate_many()" was
missing the following hunk which causes a boot failure on anything using
irq_domain_add_tree() to allocate an irq domain.
Signed-off-by: Grant Likely <grant.likely@linaro.org>
Cc: Michael Neuling <mikey@neuling.org>
Cc: Benjamin Herrenschmidt <benh@kernel.crashing.org>,
Cc: Thomas Gleixner <tglx@linutronix.de>,
Cc: Stephen Rothwell <sfr@canb.auug.org.au>
---
include/linux/irqdomain.h | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/include/linux/irqdomain.h b/include/linux/irqdomain.h
index 02f7658..c983ed1 100644
--- a/include/linux/irqdomain.h
+++ b/include/linux/irqdomain.h
@@ -162,7 +162,7 @@ static inline struct irq_domain *irq_domain_add_tree(struct device_node *of_node
const struct irq_domain_ops *ops,
void *host_data)
{
- return irq_domain_add_linear(of_node, 0, ops, host_data);
+ return __irq_domain_add(of_node, 0, ~0, 0, ops, host_data);
}
extern void irq_domain_remove(struct irq_domain *host);
--
1.8.1.2
^ permalink raw reply related
* Re: [PATCH v2 08/11] irqdomain: Refactor irq_domain_associate_many()
From: Michael Neuling @ 2013-06-18 11:04 UTC (permalink / raw)
To: Grant Likely
Cc: Stephen Rothwell, Linux Kernel Mailing List, Linux PPC dev,
linux-next, Thomas Gleixner
In-Reply-To: <20130618094617.9BFA93E0D88@localhost>
Grant Likely <grant.likely@linaro.org> wrote:
> On Tue, 18 Jun 2013 10:05:31 +0100, Grant Likely <grant.likely@linaro.org> wrote:
> > On Tue, Jun 18, 2013 at 2:25 AM, Michael Neuling <mikey@neuling.org> wrote:
> > > Michael Neuling <mikey@neuling.org> wrote:
> > >
> > >> Grant,
> > >>
> > >> In next-20130617 we are getting the below crash on POWER7. Bisecting,
> > >> points to this patch (d39046ec72 in next)
> > >
> > > Also, reverting just d39046ec72 fixes the crash in next-20130617.
> >
> > Odd. Of all the changes in that series, I would not have expected that
> > one to cause any problems. I'm digging into it now...
>
> Ugh. I flubbed the commit. Try this patch. It should solve the problem:
>
Yep, it fixes it. Thanks.
Mikey
> From b7ba09c29ed36eda8e16453646b55faea7f9c25f Mon Sep 17 00:00:00 2001
> From: Grant Likely <grant.likely@linaro.org>
> Date: Tue, 18 Jun 2013 10:15:19 +0100
> Subject: [PATCH] irqdomain: Fix flubbed irq_domain_associate_many refactoring
>
> commit d39046ec72, "irqdomain: Refactor irq_domain_associate_many()" was
> missing the following hunk which causes a boot failure on anything using
> irq_domain_add_tree() to allocate an irq domain.
>
> Signed-off-by: Grant Likely <grant.likely@linaro.org>
> Cc: Michael Neuling <mikey@neuling.org>
> Cc: Benjamin Herrenschmidt <benh@kernel.crashing.org>,
> Cc: Thomas Gleixner <tglx@linutronix.de>,
> Cc: Stephen Rothwell <sfr@canb.auug.org.au>
> ---
> include/linux/irqdomain.h | 2 +-
> 1 file changed, 1 insertion(+), 1 deletion(-)
>
> diff --git a/include/linux/irqdomain.h b/include/linux/irqdomain.h
> index 02f7658..c983ed1 100644
> --- a/include/linux/irqdomain.h
> +++ b/include/linux/irqdomain.h
> @@ -162,7 +162,7 @@ static inline struct irq_domain *irq_domain_add_tree(struct device_node *of_node
> const struct irq_domain_ops *ops,
> void *host_data)
> {
> - return irq_domain_add_linear(of_node, 0, ops, host_data);
> + return __irq_domain_add(of_node, 0, ~0, 0, ops, host_data);
> }
>
> extern void irq_domain_remove(struct irq_domain *host);
> --
> 1.8.1.2
>
^ permalink raw reply
* Re: [PATCH v2 08/11] irqdomain: Refactor irq_domain_associate_many()
From: Grant Likely @ 2013-06-18 12:13 UTC (permalink / raw)
To: Michael Neuling
Cc: Stephen Rothwell, Linux Kernel Mailing List, Linux PPC dev,
linux-next, Thomas Gleixner
In-Reply-To: <32351.1371553495@ale.ozlabs.ibm.com>
On Tue, Jun 18, 2013 at 12:04 PM, Michael Neuling <mikey@neuling.org> wrote:
> Grant Likely <grant.likely@linaro.org> wrote:
>
>> On Tue, 18 Jun 2013 10:05:31 +0100, Grant Likely <grant.likely@linaro.org> wrote:
>> > On Tue, Jun 18, 2013 at 2:25 AM, Michael Neuling <mikey@neuling.org> wrote:
>> > > Michael Neuling <mikey@neuling.org> wrote:
>> > >
>> > >> Grant,
>> > >>
>> > >> In next-20130617 we are getting the below crash on POWER7. Bisecting,
>> > >> points to this patch (d39046ec72 in next)
>> > >
>> > > Also, reverting just d39046ec72 fixes the crash in next-20130617.
>> >
>> > Odd. Of all the changes in that series, I would not have expected that
>> > one to cause any problems. I'm digging into it now...
>>
>> Ugh. I flubbed the commit. Try this patch. It should solve the problem:
>>
>
> Yep, it fixes it. Thanks.
Excellent, thanks for testing.
g.
^ permalink raw reply
* Re: [PATCH] powerpc: Fix bad pmd error with book3E config
From: Aneesh Kumar K.V @ 2013-06-18 13:02 UTC (permalink / raw)
To: benh, scottwood; +Cc: linuxppc-dev
In-Reply-To: <1371033004-15864-1-git-send-email-aneesh.kumar@linux.vnet.ibm.com>
"Aneesh Kumar K.V" <aneesh.kumar@linux.vnet.ibm.com> writes:
> From: "Aneesh Kumar K.V" <aneesh.kumar@linux.vnet.ibm.com>
>
> Book3E uses the hugepd at PMD level and don't encode pte directly
> at the pmd level. So it will find the lower bits of pmd set
> and the pmd_bad check throws error. Infact the current code
> will never take the free_hugepd_range call at all because it will
> clear the pmd if it find a hugepd pointer.
>
> Reported-by: Scott Wood <scottwood@freescale.com>
> Signed-off-by: Aneesh Kumar K.V <aneesh.kumar@linux.vnet.ibm.com>
Ben,
This is regression introduced by
e2b3d202d1dba8f3546ed28224ce485bc50010be
"powerpc: Switch 16GB and 16MB explicit hugepages to a different page table format"
and should go upstream in 3.10
$git describe --contains e2b3d202d1dba8f3546ed28224ce485bc50010be
v3.10-rc1~121^2~15
Without this path, we leak hugepd with all subarchs using old huge page
directory format.
-aneesh
^ permalink raw reply
* Re: [PATCH 3/4] KVM: PPC: Add support for IOMMU in-kernel handling
From: Alex Williamson @ 2013-06-18 14:48 UTC (permalink / raw)
To: Benjamin Herrenschmidt
Cc: kvm, Alexey Kardashevskiy, Rusty Russell, Alexander Graf, kvm-ppc,
linux-kernel, Paul Mackerras, linuxppc-dev, David Gibson
In-Reply-To: <1371530335.21896.169.camel@pasglop>
On Tue, 2013-06-18 at 14:38 +1000, Benjamin Herrenschmidt wrote:
> On Mon, 2013-06-17 at 20:32 -0600, Alex Williamson wrote:
>
> > Right, we don't want to create dependencies across modules. I don't
> > have a vision for how this should work. This is effectively a complete
> > side-band to vfio, so we're really just dealing in the iommu group
> > space. Maybe there needs to be some kind of registration of ownership
> > for the group using some kind of token. It would need to include some
> > kind of notification when that ownership ends. That might also be a
> > convenient tag to toggle driver probing off for devices in the group.
> > Other ideas? Thanks,
>
> All of that smells nasty like it will need a pile of bloody
> infrastructure.... which makes me think it's too complicated and not the
> right approach.
>
> How does access control work today on x86/VFIO ? Can you give me a bit
> more details ? I didn't get a good grasp in your previous email....
The current model is not x86 specific, but it only covers doing iommu
and device access through vfio. The kink here is that we're trying to
do device access and setup through vfio, but iommu manipulation through
kvm. We may want to revisit whether we can do the in-kernel iommu
manipulation through vfio rather than kvm.
For vfio in general, the group is the unit of ownership. A user is
granted access to /dev/vfio/$GROUP through file permissions. The user
opens the group and a container (/dev/vfio/vfio) and calls SET_CONTAINER
on the group. If supported by the platform, multiple groups can be set
to the same container, which allows for iommu domain sharing. Once a
group is associated with a container, an iommu backend can be
initialized for the container. Only then can a device be accessed
through the group.
So even if we were to pass a vfio group file descriptor into kvm and it
matched as some kind of ownership token on the iommu group, it's not
clear that's sufficient to assume we can start programming the iommu.
Thanks,
Alex
> From the look of it, the VFIO file descriptor is what has the "access
> control" to the underlying iommu, is this right ? So we somewhat need to
> transfer (or copy) that ownership from the VFIO fd to the KVM VM.
>
> I don't see a way to do that without some cross-layering here...
>
> Rusty, are you aware of some kernel mechanism we can use for that ?
>
> Cheers,
> Ben.
>
>
^ permalink raw reply
* Re: [PATCH 3/5] powerpc/dts: update MSI bindings doc for MPIC v4.3
From: Scott Wood @ 2013-06-18 16:21 UTC (permalink / raw)
To: Lian Minghuan-b31939; +Cc: Minghuan Lian, linuxppc-dev, Zang Roy-R61911
In-Reply-To: <51BFCAAF.1070400@freescale.com>
On 06/17/2013 09:49:19 PM, Lian Minghuan-b31939 wrote:
> On 06/18/2013 08:42 AM, Scott Wood wrote:
>> On 06/17/2013 07:28:07 PM, Scott Wood wrote:
>>> On 06/17/2013 12:07:41 AM, Lian Minghuan-b31939 wrote:
>>>>>> + compatible =3D "fsl,mpic-msi";
>>>>>> + reg =3D <0x41600 0x200 0x44140 4>;
>>>>>=20
>>>>> Why 0x200?
>>>>>=20
>>>> [Minghuan] The offsets of the MSIA registers are from 0x41600 to =20
>>>> 0x417ff, and the size is 0x200.
>>>> offset 0x41600-0x4170 are MSIIRA1-7.
>>>> 0x41720 is MSISRA,
>>>> 0x41750 is MSIIR.
>>>> The others are reserved.
>>>=20
>>> There is no MSIIRA on fsl,mpic-msi.
>>=20
>> Sigh, I was thinking of MSIIR1A -- which of course is distinct from =20
>> both MSIIRA1 and MSIIRA. :-P
>>=20
>> So it's just a bug that pq3-mpic.dtsi has a length of 0x80?
> [Minghuan] I am sorry, there is a typo.
> offset 0x41600-0x4170 should be MSIRA0-7.
> The MSI bank size is 0x200.
> The MSIR 0-7 size is 0x80.
> So the first region of 'reg' should indicate bank size or MSIR size?
> I think it should be a bank size. So MSI driver can access MSISR and =20
> MSIIR, and provide some new features in feature.
It should be the bank size. There's already inconsistency between =20
pq3-mpic.dtsi and qoriq-mpic.dtsi (the latter has 0x200). I think =20
pq3-mpic.dtsi is just wrong and should be fixed.
-Scott=
^ permalink raw reply
* Re: [PATCH 5/5] powerpc/fsl_msi: add 'msiregs' kernel parameter
From: Scott Wood @ 2013-06-18 16:22 UTC (permalink / raw)
To: Lian Minghuan-b31939; +Cc: Minghuan Lian, linuxppc-dev, Zang Roy-R61911
In-Reply-To: <51BFCF99.1010608@freescale.com>
On 06/17/2013 10:10:17 PM, Lian Minghuan-b31939 wrote:
> Hi Scott,
>=20
> please see my comments inline.
>=20
> On 06/18/2013 08:18 AM, Scott Wood wrote:
>> On 06/17/2013 12:36:50 AM, Lian Minghuan-b31939 wrote:
>>> Hi Scott,
>>>=20
>>> please see my comments inline.
>>>=20
>>> On 06/15/2013 06:13 AM, Scott Wood wrote:
>>>> On 06/14/2013 02:15:59 AM, Minghuan Lian wrote:
>>>>> 1. Only MSIIR1 can index 16 MSI registers, but when using MSIIR1
>>>>> the IRQs of a register are not continuous. for example, the first
>>>>> register irq values are 0x0, 0x10, 0x20, 0x30 ... 0x1f0. So it
>>>>> is hard to use 'msi-available-ranges' property to indicate the
>>>>> available ranges and 'msi-available-ranges' property has been
>>>>> removed from dts node, so this patch removes the related code.
>>>>>=20
>>>>> 2. Add 'msiregs' kernel parameter instead of =20
>>>>> 'msi-available-ranges'
>>>>> functionality.
>>>>=20
>>>> The reason we used a device tree property was because this is for =20
>>>> virtualization and AMP scenarios where this instance of Linux does =20
>>>> not own all of the MSI registers.
>>>>=20
>>>> I don't see any reasonable way to partition an MPIC v4.3 MSI group =20
>>>> -- but there are more groups, so it's not that bad. What's the use =20
>>>> case for this patch?
>>>>=20
>>> [Minghuan] I do not known any case about this patch. I add =20
>>> 'msiregs' just for achieving "msi-available-ranges" functionality. =20
>>> I do not want to remove partition functionality when updating to =20
>>> mpic4.3, although I do not see virtualization and AMP cases on =20
>>> T4(KVM does not need this functionality).
>>=20
>> Such functionality does not work on mpic v4.3. There are =20
>> conflicting requirements of contiguous MSIs (because PCI devices can =20
>> use them that way) and the inability to partition a single register =20
>> (because they all go to the same MPIC interrupt).
>>=20
>> Keep msi-available-ranges as is for older hardware, and just ignore =20
>> it (with a warning printed) if it's present on MPIC v4.3.
>>=20
> [Minghuan] Thanks for your guidance.
> But 'msireg' should be remained or removed?
Removed.
-Scott=
^ permalink raw reply
* Re: [PATCH v2] powerpc/pci: Fix setup of Freescale PCI / PCIe controllers
From: Michael Guntsche @ 2013-06-18 17:31 UTC (permalink / raw)
To: Scott Wood; +Cc: linuxppc-dev, Rojhalat Ibrahim, linux-kernel
In-Reply-To: <1371514253.9073.8@snotra>
On Tue, Jun 18, 2013 at 2:10 AM, Scott Wood <scottwood@freescale.com> wrote:
> On 06/17/2013 08:15:33 AM, Rojhalat Ibrahim wrote:
>>
>> On Friday 14 June 2013 15:18:03 Scott Wood wrote:
>> > On 83xx:
>> > cc1: warnings being treated as errors
>> > /home/scott/fsl/git/linux/upstream/arch/powerpc/sysdev/fsl_pci.c:100:23:
>> > error: 'fsl_indirect_pcie_ops' defined but not used
>> > make[2]: *** [arch/powerpc/sysdev/fsl_pci.o] Error 1
>> > make[2]: *** Waiting for unfinished jobs....
>> >
>> > I can fix this when applying, but this makes me wonder how you tested
>> > it, given that the whole point is to fix 83xx... Did you fix this and
>> > then accidentally sent a stale version?
>> >
>> > Also, please be careful that the patch doesn't get line wrapped -- I
>> > had to manually unwrap a couple places. Use git send-email if you
>> > can't get KMail to cooperate.
>> >
>> > -Scott
>>
>> Sorry about the mess. I'll send a v3.
>> Please note: I don't have an 83xx system. So I can only test if it
>> compiles,
>> which I obviously did not do with the right config.
>
>
> OK, so it looks like it was Michael who ran into the problem on 83xx.
> Michael, could you test the v3 patch that Rojhalat posted?
>
> -Scott
I did a quick check against -rc6 and the system compiles and boots up fine.
/Mike
^ permalink raw reply
* Re: [PATCH -V10 00/15] THP support for PPC64
From: Aneesh Kumar K.V @ 2013-06-18 17:54 UTC (permalink / raw)
To: Benjamin Herrenschmidt; +Cc: Andrea Arcangeli, linuxppc-dev, paulus
In-Reply-To: <1371348007.21896.62.camel@pasglop>
Benjamin Herrenschmidt <benh@au1.ibm.com> writes:
> On Wed, 2013-06-05 at 20:58 +0530, Aneesh Kumar K.V wrote:
>> This is the second patchset needed to support THP on ppc64. Some of
>> the changes
>> included in this series are tricky in that it changes the powerpc
>> linux page table
>> walk subtly. We also overload few of the pte flags for ptes at PMD
>> level (huge
>> page PTEs).
>>
>> The related mm/ changes are already merged to Andrew's -mm tree.
>
> [Andrea, question for you near the end ]
>
> So I'm trying to understand how you handle races between hash_page
> and collapse.
>
> The generic collapse code does:
>
> _pmd = pmdp_clear_flush(vma, address, pmd);
>
> Which expects the architecture to essentially have stopped any
> concurrent walk by the time it returns.
>
> Your implementation of the above does this:
>
> pmd = *pmdp;
> pmd_clear(pmdp);
> /*
> * Now invalidate the hpte entries in the range
> * covered by pmd. This make sure we take a
> * fault and will find the pmd as none, which will
> * result in a major fault which takes mmap_sem and
> * hence wait for collapse to complete. Without this
> * the __collapse_huge_page_copy can result in copying
> * the old content.
> */
> flush_tlb_pmd_range(vma->vm_mm, &pmd, address);
>
> So we clear the pmd after making a copy of it. This will eventually
> prevent a tablewalk but only eventually, once that store becomes visible
> to other processors, which may take a while. Then you proceed to flush
> the hash table for all the underlying PTEs.
>
> So at this point, hash_page might *still* see the old pmd. Unless I
> missed something, you did nothing that will prevent that (the only way
> to lock against hash_page is really an IPI & wait or to take the PTE's
> busy and make them !present or something). So as far as I can tell,
> a concurrent hash_page can still sneak into the hash some "small"
> entries after you have supposedly flushed them.
>
We are depending on the pmd being none. But as you said that doesn't
take care of an already undergoing hash_page. As per the discussion I
am listing below the option that use synchronize_sched. The other option
that we have is to clear _PAGE_USER.
commit f69f11ba6a957aac81ea8096b244005c450a2059
Author: Aneesh Kumar K.V <aneesh.kumar@linux.vnet.ibm.com>
Date: Tue Jun 18 19:17:17 2013 +0530
powerpc/THP: Wait for all hash_page calls to finish before invalidating HPTE entries
When we collapse normal pages to hugepage, we first clear the pmd, then invalidate all
the PTE entries. The assumption here is that any low level page fault will see pmd as
none and take the slow path that will wait on mmap_sem. But we could very well be in
a hash_page with local ptep pointer value. Such a hash page can result in adding new
HPTE entries for normal subpages/small page. That means we could be modifying the
page content as we copy them to a huge page. Fix this by waiting on hash_page to finish
after marking the pmd none and bfore invalidating HPTE entries. We use the heavy
synchronize_sched(). This should be ok as we do this in the background khugepaged thread
and not in application context. Also if we find collapse slow we can ideally increase
the scan rate.
Signed-off-by: Aneesh Kumar K.V <aneesh.kumar@linux.vnet.ibm.com>
diff --git a/arch/powerpc/mm/pgtable_64.c b/arch/powerpc/mm/pgtable_64.c
index bbecac4..92b733e 100644
--- a/arch/powerpc/mm/pgtable_64.c
+++ b/arch/powerpc/mm/pgtable_64.c
@@ -532,6 +532,7 @@ pmd_t pmdp_clear_flush(struct vm_area_struct *vma, unsigned long address,
pmd_t *pmdp)
{
pmd_t pmd;
+ struct mm_struct *mm = vma->vm_mm;
VM_BUG_ON(address & ~HPAGE_PMD_MASK);
if (pmd_trans_huge(*pmdp)) {
@@ -542,6 +543,16 @@ pmd_t pmdp_clear_flush(struct vm_area_struct *vma, unsigned long address,
*/
pmd = *pmdp;
pmd_clear(pmdp);
+ spin_unlock(&mm->page_table_lock);
+ /*
+ * Wait for all pending hash_page to finish
+ * We can do this by waiting for a context switch to happen on
+ * the cpus. Any new hash_page after this will see pmd none
+ * and fallback to code that takes mmap_sem and hence will block
+ * for collapse to finish.
+ */
+ synchronize_sched();
+ spin_lock(&mm->page_table_lock);
/*
* Now invalidate the hpte entries in the range
* covered by pmd. This make sure we take a
^ permalink raw reply related
* Re: [PATCH 2/5] powerpc/fsl_msi: add MSIIR1 support for MPIC v4.3
From: Scott Wood @ 2013-06-18 18:08 UTC (permalink / raw)
To: Lian Minghuan-b31939; +Cc: Minghuan Lian, linuxppc-dev, Zang Roy-R61911
In-Reply-To: <51BFC749.3080502@freescale.com>
On 06/17/2013 09:34:49 PM, Lian Minghuan-b31939 wrote:
> Hi Soctt,
>=20
> please see my comments inline.
>=20
> On 06/18/2013 08:15 AM, Scott Wood wrote:
>> On 06/16/2013 10:00:01 PM, Lian Minghuan-b31939 wrote:
>>> Hi Scott,
>>>=20
>>> please see my comments inline.
>>>=20
>>> On 06/15/2013 06:09 AM, Scott Wood wrote:
>>>> On 06/14/2013 02:15:56 AM, Minghuan Lian wrote:
>>>>> diff --git a/arch/powerpc/sysdev/fsl_msi.h =20
>>>>> b/arch/powerpc/sysdev/fsl_msi.h
>>>>> index 8225f86..43a9d99 100644
>>>>> --- a/arch/powerpc/sysdev/fsl_msi.h
>>>>> +++ b/arch/powerpc/sysdev/fsl_msi.h
>>>>> @@ -16,7 +16,7 @@
>>>>> #include <linux/of.h>
>>>>> #include <asm/msi_bitmap.h>
>>>>>=20
>>>>> -#define NR_MSI_REG 8
>>>>> +#define NR_MSI_REG 16
>>>>> #define IRQS_PER_MSI_REG 32
>>>>> #define NR_MSI_IRQS (NR_MSI_REG * IRQS_PER_MSI_REG)
>>>>=20
>>>> I don't see where you update all_avail in fsl_of_msi_probe.
>>>>=20
>>>> We should also be bounds-checking the contents of =20
>>>> msi-available-ranges.
>>>> Currently it looks like we just silently overrun the bitmap if we =20
>>>> get bad
>>>> input from the device tree.
>>>>=20
>>> [Minghuan] all_avail definition: static const u32 all_avail[] =3D { =20
>>> 0, NR_MSI_IRQS };
>>> When changing NR_MSI_REG to 16, NR_MSI_IRQS has been changed to =20
>>> 16*32, and all_avail also is updated.
>>=20
>> That's my point. It shouldn't change for older hardware.
> [Minghaun] the older hardware has 8 registers, mipcv4.3 has 16 =20
> registers. If we do not use 16*32 bitmap to indicate 8*32 irqs.(this =20
> way just only wastes some memory and has no other harm)
Using the larger bitmap unconditionally is fine. What is not fine is, =20
on older hardware, acting as if all 16 irqs are present.
In other words, I'm talking about the contents of the bitmap, not its =20
size.
> we have two choice I think.
> 1. Use a variable assigned value 8 or 16 based on compatible, then =20
> dynamically create bitmap
If we have the mpic4.3 compatible, then we don't even support =20
msi-available-ranges, so we'd skip this code and free everything in the =20
bitmap.
> 2. Add a new file for mpic v4.3.
No. :-)
-Scott=
^ permalink raw reply
* Re: [PATCH -V10 00/15] THP support for PPC64
From: Aneesh Kumar K.V @ 2013-06-18 18:46 UTC (permalink / raw)
To: Benjamin Herrenschmidt; +Cc: Alexey Kardashevskiy, linuxppc-dev, paulus
In-Reply-To: <1371355567.21896.101.camel@pasglop>
Benjamin Herrenschmidt <benh@kernel.crashing.org> writes:
> On Sun, 2013-06-16 at 13:37 +1000, Benjamin Herrenschmidt wrote:
>> On Sun, 2013-06-16 at 12:00 +1000, Benjamin Herrenschmidt wrote:
>> > So at this point, hash_page might *still* see the old pmd. Unless I
>> > missed something, you did nothing that will prevent that (the only way
>> > to lock against hash_page is really an IPI & wait or to take the PTE's
>> > busy and make them !present or something). So as far as I can tell,
>> > a concurrent hash_page can still sneak into the hash some "small"
>> > entries after you have supposedly flushed them.
>>
>> Note that the _PAGE_PRESENT bit is removed eventually ... but much
>> later, in __collapse_huge_page_copy() which will also flush the hash, so
>> at least we will remove a stale hash entry that would have been added by
>> the race above I suppose... but:
>>
>> - _PAGE_ACCESSED can still potentially be set after it was supposed to
>> be stable
>>
>> - The clearing happens *after* copy_user_highpage(), ie, unless I
>> missed something here, we potentially still have something writing to
>> the 4k page while it's being copied, which is BAD.
>>
>> Now, let me know if I did miss something here :-)
>
> An additional issue is that this all collides a bit with Alexey's work
> to support TCEs in real mode in KVM, which is necessary to have "usable"
> PCI pass-through.
>
> Look at patches http://patchwork.ozlabs.org/patch/248920/ and followup,
> he basically walks the page tables here in a slightly different way than
> Paul does in H_ENTER. It's more like gup_fast. It will need to handle
> concurrent split/collapse etc... as well which it doesn't right now.
>
> I'm considering merging Alexei stuff first (provided I don't find major
> problems with it), then you can provide a new THP series on top of it.
>
> While at it, also fix:
>
> - Some of your patches are bug fixes (like the one about subpage
> protection). They need to be either merged in the main patch or put
> before the patch that enables THP.
We can move them before. One of the reason I would like to keep them as
seperate patches is to document the changes better in commit messages.
>
> - I haven't completely yet considered the impact of the demotion of
> segments, but neither do you :-) IE. Under some circumstances, we can
> demote entire segments from 64K HW pages to 4K HW pages in the SLB. For
> example if a driver (such as HCA) sets the 4K_PFN bit in a PTE, this
> will happen at hashing time. I don't think your code deals with that at
> all, am I correct ? It *might* be that the right approach with those is:
>
But will that by anonymous memory ? ie, will we find them suitable for
THP allocation ?
> * If you find a THP in hash_page and the segment size is 4k, fault
>
> * In do_page_fault, re-check for that condition (or maybe we can make
> hash_page return a specific bit that gets ORed into the error_code into
> do_page_fault ?) and split huge pages there.
>
> But that's just an idea off the top of my mind, there might be a better
> way. Of course this needs to be tested.
>
> BTW. For the subpage protection, similarily, you need to make sure you
> properly map the entire segment as "no THP", not just the range
> passed-in by the user.
Can you explain that more, why should the entire segment be marked no THP ?
The segment can work with 4K base page size and we still be able to
allocate a hugepage in that segment.
-aneesh
^ permalink raw reply
* Pull request: scottwood/linux.git for-3.10
From: Scott Wood @ 2013-06-18 20:14 UTC (permalink / raw)
To: galak; +Cc: linuxppc-dev
This fixes a regression that causes 83xx to oops on boot if a
non-express PCI bus is present.
The following changes since commit 17858ca65eef148d335ffd4cfc09228a1c1cbfb5:
Merge tag 'please-pull-fixia64' of git://git.kernel.org/pub/scm/linux/kernel/git/aegl/linux (2013-06-18 06:29:19 -1000)
are available in the git repository at:
git://git.kernel.org/pub/scm/linux/kernel/git/scottwood/linux.git for-3.10
for you to fetch changes up to 2383ea94854bcf5a0df3c6803b980868cef95418:
powerpc/pci: Fix setup of Freescale PCI / PCIe controllers (2013-06-18 14:44:57 -0500)
----------------------------------------------------------------
Rojhalat Ibrahim (1):
powerpc/pci: Fix setup of Freescale PCI / PCIe controllers
arch/powerpc/sysdev/fsl_pci.c | 24 +++++++++---------------
1 file changed, 9 insertions(+), 15 deletions(-)
^ permalink raw reply
* Re: [PATCH] powerpc: delete __cpuinit usage from all users
From: Paul Gortmaker @ 2013-06-18 20:23 UTC (permalink / raw)
To: Benjamin Herrenschmidt; +Cc: Paul Gortmaker, linuxppc-dev, Paul Mackerras
In-Reply-To: <1371499802-26262-1-git-send-email-paul.gortmaker@windriver.com>
On 13-06-17 04:10 PM, Paul Gortmaker wrote:
> The __cpuinit type of throwaway sections might have made sense
> some time ago when RAM was more constrained, but now the savings
> do not offset the cost and complications. For example, the fix in
> commit 5e427ec2d0 ("x86: Fix bit corruption at CPU resume time")
> is a good example of the nasty type of bugs that can be created
> with improper use of the various __init prefixes.
>
> After a discussion on LKML[1] it was decided that cpuinit should go
> the way of devinit and be phased out. Once all the users are gone,
> we can then finally remove the macros themselves from linux/init.h.
>
> This removes all the powerpc uses of the __cpuinit macros.
I see I missed a couple with a too limited regex. I'll resend a v2
shortly. It is up to you guys whether you want to carry this in the
git://git.kernel.org/pub/scm/linux/kernel/git/benh/powerpc.git#next
repo, or have me keep it with the arch independent __cpuinit removal
changes. In mips for example, Ralf expected some significant tree
churn still pending and wanted to handle the conflicts himself, so
he took the mips chunk in his tree.
Thanks,
P.
--
>
> [1] https://lkml.org/lkml/2013/5/20/589
>
> Signed-off-by: Paul Gortmaker <paul.gortmaker@windriver.com>
> ---
>
> [This was generated against today's linux-next tree ; I'm assuming all
> pending powerpc changes are in there currently.]
>
> arch/powerpc/include/asm/rtas.h | 4 ++--
> arch/powerpc/include/asm/vdso.h | 2 +-
> arch/powerpc/kernel/cacheinfo.c | 19 +++++++++++--------
> arch/powerpc/kernel/rtas.c | 4 ++--
> arch/powerpc/kernel/smp.c | 2 +-
> arch/powerpc/kernel/sysfs.c | 6 +++---
> arch/powerpc/kernel/time.c | 1 -
> arch/powerpc/kernel/vdso.c | 2 +-
> arch/powerpc/mm/44x_mmu.c | 6 +++---
> arch/powerpc/mm/hash_utils_64.c | 2 +-
> arch/powerpc/mm/mmu_context_nohash.c | 6 +++---
> arch/powerpc/mm/numa.c | 7 +++----
> arch/powerpc/mm/tlb_nohash.c | 2 +-
> arch/powerpc/perf/core-book3s.c | 4 ++--
> arch/powerpc/platforms/44x/currituck.c | 4 ++--
> arch/powerpc/platforms/44x/iss4xx.c | 4 ++--
> arch/powerpc/platforms/85xx/smp.c | 6 +++---
> arch/powerpc/platforms/powermac/smp.c | 2 +-
> arch/powerpc/platforms/powernv/smp.c | 2 +-
> 19 files changed, 43 insertions(+), 42 deletions(-)
>
> diff --git a/arch/powerpc/include/asm/rtas.h b/arch/powerpc/include/asm/rtas.h
> index 34fd704..c7a8bfc 100644
> --- a/arch/powerpc/include/asm/rtas.h
> +++ b/arch/powerpc/include/asm/rtas.h
> @@ -350,8 +350,8 @@ static inline u32 rtas_config_addr(int busno, int devfn, int reg)
> (devfn << 8) | (reg & 0xff);
> }
>
> -extern void __cpuinit rtas_give_timebase(void);
> -extern void __cpuinit rtas_take_timebase(void);
> +extern void rtas_give_timebase(void);
> +extern void rtas_take_timebase(void);
>
> #ifdef CONFIG_PPC_RTAS
> static inline int page_is_rtas_user_buf(unsigned long pfn)
> diff --git a/arch/powerpc/include/asm/vdso.h b/arch/powerpc/include/asm/vdso.h
> index 50f261b..0d9cecd 100644
> --- a/arch/powerpc/include/asm/vdso.h
> +++ b/arch/powerpc/include/asm/vdso.h
> @@ -22,7 +22,7 @@ extern unsigned long vdso64_rt_sigtramp;
> extern unsigned long vdso32_sigtramp;
> extern unsigned long vdso32_rt_sigtramp;
>
> -int __cpuinit vdso_getcpu_init(void);
> +int vdso_getcpu_init(void);
>
> #else /* __ASSEMBLY__ */
>
> diff --git a/arch/powerpc/kernel/cacheinfo.c b/arch/powerpc/kernel/cacheinfo.c
> index 92c6b00..ddaecb1 100644
> --- a/arch/powerpc/kernel/cacheinfo.c
> +++ b/arch/powerpc/kernel/cacheinfo.c
> @@ -131,7 +131,8 @@ static const char *cache_type_string(const struct cache *cache)
> return cache_type_info[cache->type].name;
> }
>
> -static void __cpuinit cache_init(struct cache *cache, int type, int level, struct device_node *ofnode)
> +static void cache_init(struct cache *cache, int type, int level,
> + struct device_node *ofnode)
> {
> cache->type = type;
> cache->level = level;
> @@ -385,7 +386,7 @@ static struct cache *__cpuinit cache_lookup_or_instantiate(struct device_node *n
> return cache;
> }
>
> -static void __cpuinit link_cache_lists(struct cache *smaller, struct cache *bigger)
> +static void link_cache_lists(struct cache *smaller, struct cache *bigger)
> {
> while (smaller->next_local) {
> if (smaller->next_local == bigger)
> @@ -396,13 +397,13 @@ static void __cpuinit link_cache_lists(struct cache *smaller, struct cache *bigg
> smaller->next_local = bigger;
> }
>
> -static void __cpuinit do_subsidiary_caches_debugcheck(struct cache *cache)
> +static void do_subsidiary_caches_debugcheck(struct cache *cache)
> {
> WARN_ON_ONCE(cache->level != 1);
> WARN_ON_ONCE(strcmp(cache->ofnode->type, "cpu"));
> }
>
> -static void __cpuinit do_subsidiary_caches(struct cache *cache)
> +static void do_subsidiary_caches(struct cache *cache)
> {
> struct device_node *subcache_node;
> int level = cache->level;
> @@ -653,7 +654,7 @@ static struct kobj_type cache_index_type = {
> .default_attrs = cache_index_default_attrs,
> };
>
> -static void __cpuinit cacheinfo_create_index_opt_attrs(struct cache_index_dir *dir)
> +static void cacheinfo_create_index_opt_attrs(struct cache_index_dir *dir)
> {
> const char *cache_name;
> const char *cache_type;
> @@ -696,7 +697,8 @@ static void __cpuinit cacheinfo_create_index_opt_attrs(struct cache_index_dir *d
> kfree(buf);
> }
>
> -static void __cpuinit cacheinfo_create_index_dir(struct cache *cache, int index, struct cache_dir *cache_dir)
> +static void cacheinfo_create_index_dir(struct cache *cache, int index,
> + struct cache_dir *cache_dir)
> {
> struct cache_index_dir *index_dir;
> int rc;
> @@ -722,7 +724,8 @@ err:
> kfree(index_dir);
> }
>
> -static void __cpuinit cacheinfo_sysfs_populate(unsigned int cpu_id, struct cache *cache_list)
> +static void cacheinfo_sysfs_populate(unsigned int cpu_id,
> + struct cache *cache_list)
> {
> struct cache_dir *cache_dir;
> struct cache *cache;
> @@ -740,7 +743,7 @@ static void __cpuinit cacheinfo_sysfs_populate(unsigned int cpu_id, struct cache
> }
> }
>
> -void __cpuinit cacheinfo_cpu_online(unsigned int cpu_id)
> +void cacheinfo_cpu_online(unsigned int cpu_id)
> {
> struct cache *cache;
>
> diff --git a/arch/powerpc/kernel/rtas.c b/arch/powerpc/kernel/rtas.c
> index 52add6f..80b5ef4 100644
> --- a/arch/powerpc/kernel/rtas.c
> +++ b/arch/powerpc/kernel/rtas.c
> @@ -1172,7 +1172,7 @@ int __init early_init_dt_scan_rtas(unsigned long node,
> static arch_spinlock_t timebase_lock;
> static u64 timebase = 0;
>
> -void __cpuinit rtas_give_timebase(void)
> +void rtas_give_timebase(void)
> {
> unsigned long flags;
>
> @@ -1189,7 +1189,7 @@ void __cpuinit rtas_give_timebase(void)
> local_irq_restore(flags);
> }
>
> -void __cpuinit rtas_take_timebase(void)
> +void rtas_take_timebase(void)
> {
> while (!timebase)
> barrier();
> diff --git a/arch/powerpc/kernel/smp.c b/arch/powerpc/kernel/smp.c
> index ee7ac5e..d6f42dc 100644
> --- a/arch/powerpc/kernel/smp.c
> +++ b/arch/powerpc/kernel/smp.c
> @@ -480,7 +480,7 @@ static void cpu_idle_thread_init(unsigned int cpu, struct task_struct *idle)
> secondary_ti = current_set[cpu] = ti;
> }
>
> -int __cpuinit __cpu_up(unsigned int cpu, struct task_struct *tidle)
> +int __cpu_up(unsigned int cpu, struct task_struct *tidle)
> {
> int rc, c;
>
> diff --git a/arch/powerpc/kernel/sysfs.c b/arch/powerpc/kernel/sysfs.c
> index e68a845..27a90b9 100644
> --- a/arch/powerpc/kernel/sysfs.c
> +++ b/arch/powerpc/kernel/sysfs.c
> @@ -341,7 +341,7 @@ static struct device_attribute pa6t_attrs[] = {
> #endif /* HAS_PPC_PMC_PA6T */
> #endif /* HAS_PPC_PMC_CLASSIC */
>
> -static void __cpuinit register_cpu_online(unsigned int cpu)
> +static void register_cpu_online(unsigned int cpu)
> {
> struct cpu *c = &per_cpu(cpu_devices, cpu);
> struct device *s = &c->dev;
> @@ -502,7 +502,7 @@ ssize_t arch_cpu_release(const char *buf, size_t count)
>
> #endif /* CONFIG_HOTPLUG_CPU */
>
> -static int __cpuinit sysfs_cpu_notify(struct notifier_block *self,
> +static int sysfs_cpu_notify(struct notifier_block *self,
> unsigned long action, void *hcpu)
> {
> unsigned int cpu = (unsigned int)(long)hcpu;
> @@ -522,7 +522,7 @@ static int __cpuinit sysfs_cpu_notify(struct notifier_block *self,
> return NOTIFY_OK;
> }
>
> -static struct notifier_block __cpuinitdata sysfs_cpu_nb = {
> +static struct notifier_block sysfs_cpu_nb = {
> .notifier_call = sysfs_cpu_notify,
> };
>
> diff --git a/arch/powerpc/kernel/time.c b/arch/powerpc/kernel/time.c
> index 5fc29ad..65ab9e9 100644
> --- a/arch/powerpc/kernel/time.c
> +++ b/arch/powerpc/kernel/time.c
> @@ -631,7 +631,6 @@ static int __init get_freq(char *name, int cells, unsigned long *val)
> return found;
> }
>
> -/* should become __cpuinit when secondary_cpu_time_init also is */
> void start_cpu_decrementer(void)
> {
> #if defined(CONFIG_BOOKE) || defined(CONFIG_40x)
> diff --git a/arch/powerpc/kernel/vdso.c b/arch/powerpc/kernel/vdso.c
> index d4f463a..1d9c926 100644
> --- a/arch/powerpc/kernel/vdso.c
> +++ b/arch/powerpc/kernel/vdso.c
> @@ -711,7 +711,7 @@ static void __init vdso_setup_syscall_map(void)
> }
>
> #ifdef CONFIG_PPC64
> -int __cpuinit vdso_getcpu_init(void)
> +int vdso_getcpu_init(void)
> {
> unsigned long cpu, node, val;
>
> diff --git a/arch/powerpc/mm/44x_mmu.c b/arch/powerpc/mm/44x_mmu.c
> index 2c9441e..82b1ff7 100644
> --- a/arch/powerpc/mm/44x_mmu.c
> +++ b/arch/powerpc/mm/44x_mmu.c
> @@ -41,7 +41,7 @@ int icache_44x_need_flush;
>
> unsigned long tlb_47x_boltmap[1024/8];
>
> -static void __cpuinit ppc44x_update_tlb_hwater(void)
> +static void ppc44x_update_tlb_hwater(void)
> {
> extern unsigned int tlb_44x_patch_hwater_D[];
> extern unsigned int tlb_44x_patch_hwater_I[];
> @@ -134,7 +134,7 @@ static void __init ppc47x_update_boltmap(void)
> /*
> * "Pins" a 256MB TLB entry in AS0 for kernel lowmem for 47x type MMU
> */
> -static void __cpuinit ppc47x_pin_tlb(unsigned int virt, unsigned int phys)
> +static void ppc47x_pin_tlb(unsigned int virt, unsigned int phys)
> {
> unsigned int rA;
> int bolted;
> @@ -229,7 +229,7 @@ void setup_initial_memory_limit(phys_addr_t first_memblock_base,
> }
>
> #ifdef CONFIG_SMP
> -void __cpuinit mmu_init_secondary(int cpu)
> +void mmu_init_secondary(int cpu)
> {
> unsigned long addr;
> unsigned long memstart = memstart_addr & ~(PPC_PIN_SIZE - 1);
> diff --git a/arch/powerpc/mm/hash_utils_64.c b/arch/powerpc/mm/hash_utils_64.c
> index e303a6d..4481172 100644
> --- a/arch/powerpc/mm/hash_utils_64.c
> +++ b/arch/powerpc/mm/hash_utils_64.c
> @@ -807,7 +807,7 @@ void __init early_init_mmu(void)
> }
>
> #ifdef CONFIG_SMP
> -void __cpuinit early_init_mmu_secondary(void)
> +void early_init_mmu_secondary(void)
> {
> /* Initialize hash table for that CPU */
> if (!firmware_has_feature(FW_FEATURE_LPAR))
> diff --git a/arch/powerpc/mm/mmu_context_nohash.c b/arch/powerpc/mm/mmu_context_nohash.c
> index e779642..0f149b9 100644
> --- a/arch/powerpc/mm/mmu_context_nohash.c
> +++ b/arch/powerpc/mm/mmu_context_nohash.c
> @@ -329,8 +329,8 @@ void destroy_context(struct mm_struct *mm)
>
> #ifdef CONFIG_SMP
>
> -static int __cpuinit mmu_context_cpu_notify(struct notifier_block *self,
> - unsigned long action, void *hcpu)
> +static int mmu_context_cpu_notify(struct notifier_block *self,
> + unsigned long action, void *hcpu)
> {
> unsigned int cpu = (unsigned int)(long)hcpu;
>
> @@ -363,7 +363,7 @@ static int __cpuinit mmu_context_cpu_notify(struct notifier_block *self,
> return NOTIFY_OK;
> }
>
> -static struct notifier_block __cpuinitdata mmu_context_cpu_nb = {
> +static struct notifier_block mmu_context_cpu_nb = {
> .notifier_call = mmu_context_cpu_notify,
> };
>
> diff --git a/arch/powerpc/mm/numa.c b/arch/powerpc/mm/numa.c
> index 88c0425..c792cd9 100644
> --- a/arch/powerpc/mm/numa.c
> +++ b/arch/powerpc/mm/numa.c
> @@ -516,7 +516,7 @@ static int of_drconf_to_nid_single(struct of_drconf_cell *drmem,
> * Figure out to which domain a cpu belongs and stick it there.
> * Return the id of the domain used.
> */
> -static int __cpuinit numa_setup_cpu(unsigned long lcpu)
> +static int numa_setup_cpu(unsigned long lcpu)
> {
> int nid = 0;
> struct device_node *cpu = of_get_cpu_node(lcpu, NULL);
> @@ -538,8 +538,7 @@ out:
> return nid;
> }
>
> -static int __cpuinit cpu_numa_callback(struct notifier_block *nfb,
> - unsigned long action,
> +static int cpu_numa_callback(struct notifier_block *nfb, unsigned long action,
> void *hcpu)
> {
> unsigned long lcpu = (unsigned long)hcpu;
> @@ -919,7 +918,7 @@ static void __init *careful_zallocation(int nid, unsigned long size,
> return ret;
> }
>
> -static struct notifier_block __cpuinitdata ppc64_numa_nb = {
> +static struct notifier_block ppc64_numa_nb = {
> .notifier_call = cpu_numa_callback,
> .priority = 1 /* Must run before sched domains notifier. */
> };
> diff --git a/arch/powerpc/mm/tlb_nohash.c b/arch/powerpc/mm/tlb_nohash.c
> index 6888cad..41cd68d 100644
> --- a/arch/powerpc/mm/tlb_nohash.c
> +++ b/arch/powerpc/mm/tlb_nohash.c
> @@ -648,7 +648,7 @@ void __init early_init_mmu(void)
> __early_init_mmu(1);
> }
>
> -void __cpuinit early_init_mmu_secondary(void)
> +void early_init_mmu_secondary(void)
> {
> __early_init_mmu(0);
> }
> diff --git a/arch/powerpc/perf/core-book3s.c b/arch/powerpc/perf/core-book3s.c
> index 29c6482..af94a71 100644
> --- a/arch/powerpc/perf/core-book3s.c
> +++ b/arch/powerpc/perf/core-book3s.c
> @@ -1786,7 +1786,7 @@ static void power_pmu_setup(int cpu)
> cpuhw->mmcr[0] = MMCR0_FC;
> }
>
> -static int __cpuinit
> +static int
> power_pmu_notifier(struct notifier_block *self, unsigned long action, void *hcpu)
> {
> unsigned int cpu = (long)hcpu;
> @@ -1803,7 +1803,7 @@ power_pmu_notifier(struct notifier_block *self, unsigned long action, void *hcpu
> return NOTIFY_OK;
> }
>
> -int __cpuinit register_power_pmu(struct power_pmu *pmu)
> +int register_power_pmu(struct power_pmu *pmu)
> {
> if (ppmu)
> return -EBUSY; /* something's already registered */
> diff --git a/arch/powerpc/platforms/44x/currituck.c b/arch/powerpc/platforms/44x/currituck.c
> index ecd3890..9cef9d3 100644
> --- a/arch/powerpc/platforms/44x/currituck.c
> +++ b/arch/powerpc/platforms/44x/currituck.c
> @@ -91,12 +91,12 @@ static void __init ppc47x_init_irq(void)
> }
>
> #ifdef CONFIG_SMP
> -static void __cpuinit smp_ppc47x_setup_cpu(int cpu)
> +static void smp_ppc47x_setup_cpu(int cpu)
> {
> mpic_setup_this_cpu();
> }
>
> -static int __cpuinit smp_ppc47x_kick_cpu(int cpu)
> +static int smp_ppc47x_kick_cpu(int cpu)
> {
> struct device_node *cpunode = of_get_cpu_node(cpu, NULL);
> const u64 *spin_table_addr_prop;
> diff --git a/arch/powerpc/platforms/44x/iss4xx.c b/arch/powerpc/platforms/44x/iss4xx.c
> index a28a862..4241bc8 100644
> --- a/arch/powerpc/platforms/44x/iss4xx.c
> +++ b/arch/powerpc/platforms/44x/iss4xx.c
> @@ -81,12 +81,12 @@ static void __init iss4xx_init_irq(void)
> }
>
> #ifdef CONFIG_SMP
> -static void __cpuinit smp_iss4xx_setup_cpu(int cpu)
> +static void smp_iss4xx_setup_cpu(int cpu)
> {
> mpic_setup_this_cpu();
> }
>
> -static int __cpuinit smp_iss4xx_kick_cpu(int cpu)
> +static int smp_iss4xx_kick_cpu(int cpu)
> {
> struct device_node *cpunode = of_get_cpu_node(cpu, NULL);
> const u64 *spin_table_addr_prop;
> diff --git a/arch/powerpc/platforms/85xx/smp.c b/arch/powerpc/platforms/85xx/smp.c
> index 6a17599..5ced4f5 100644
> --- a/arch/powerpc/platforms/85xx/smp.c
> +++ b/arch/powerpc/platforms/85xx/smp.c
> @@ -99,7 +99,7 @@ static void mpc85xx_take_timebase(void)
> }
>
> #ifdef CONFIG_HOTPLUG_CPU
> -static void __cpuinit smp_85xx_mach_cpu_die(void)
> +static void smp_85xx_mach_cpu_die(void)
> {
> unsigned int cpu = smp_processor_id();
> u32 tmp;
> @@ -141,7 +141,7 @@ static inline u32 read_spin_table_addr_l(void *spin_table)
> return in_be32(&((struct epapr_spin_table *)spin_table)->addr_l);
> }
>
> -static int __cpuinit smp_85xx_kick_cpu(int nr)
> +static int smp_85xx_kick_cpu(int nr)
> {
> unsigned long flags;
> const u64 *cpu_rel_addr;
> @@ -362,7 +362,7 @@ static void mpc85xx_smp_machine_kexec(struct kimage *image)
> }
> #endif /* CONFIG_KEXEC */
>
> -static void __cpuinit smp_85xx_setup_cpu(int cpu_nr)
> +static void smp_85xx_setup_cpu(int cpu_nr)
> {
> if (smp_85xx_ops.probe == smp_mpic_probe)
> mpic_setup_this_cpu();
> diff --git a/arch/powerpc/platforms/powermac/smp.c b/arch/powerpc/platforms/powermac/smp.c
> index f921067..5cbd4d6 100644
> --- a/arch/powerpc/platforms/powermac/smp.c
> +++ b/arch/powerpc/platforms/powermac/smp.c
> @@ -885,7 +885,7 @@ static int smp_core99_cpu_notify(struct notifier_block *self,
> return NOTIFY_OK;
> }
>
> -static struct notifier_block __cpuinitdata smp_core99_cpu_nb = {
> +static struct notifier_block smp_core99_cpu_nb = {
> .notifier_call = smp_core99_cpu_notify,
> };
> #endif /* CONFIG_HOTPLUG_CPU */
> diff --git a/arch/powerpc/platforms/powernv/smp.c b/arch/powerpc/platforms/powernv/smp.c
> index 88c9459..c22b2b3 100644
> --- a/arch/powerpc/platforms/powernv/smp.c
> +++ b/arch/powerpc/platforms/powernv/smp.c
> @@ -40,7 +40,7 @@
> #define DBG(fmt...)
> #endif
>
> -static void __cpuinit pnv_smp_setup_cpu(int cpu)
> +static void pnv_smp_setup_cpu(int cpu)
> {
> if (cpu != boot_cpuid)
> xics_setup_cpu();
>
^ permalink raw reply
* Re: [PATCH 3/4] KVM: PPC: Add support for IOMMU in-kernel handling
From: Benjamin Herrenschmidt @ 2013-06-18 21:58 UTC (permalink / raw)
To: Alex Williamson
Cc: kvm, Alexey Kardashevskiy, Rusty Russell, Alexander Graf, kvm-ppc,
linux-kernel, Paul Mackerras, linuxppc-dev, David Gibson
In-Reply-To: <1371566935.22681.169.camel@ul30vt.home>
On Tue, 2013-06-18 at 08:48 -0600, Alex Williamson wrote:
> On Tue, 2013-06-18 at 14:38 +1000, Benjamin Herrenschmidt wrote:
> > On Mon, 2013-06-17 at 20:32 -0600, Alex Williamson wrote:
> >
> > > Right, we don't want to create dependencies across modules. I don't
> > > have a vision for how this should work. This is effectively a complete
> > > side-band to vfio, so we're really just dealing in the iommu group
> > > space. Maybe there needs to be some kind of registration of ownership
> > > for the group using some kind of token. It would need to include some
> > > kind of notification when that ownership ends. That might also be a
> > > convenient tag to toggle driver probing off for devices in the group.
> > > Other ideas? Thanks,
> >
> > All of that smells nasty like it will need a pile of bloody
> > infrastructure.... which makes me think it's too complicated and not the
> > right approach.
> >
> > How does access control work today on x86/VFIO ? Can you give me a bit
> > more details ? I didn't get a good grasp in your previous email....
>
> The current model is not x86 specific, but it only covers doing iommu
> and device access through vfio. The kink here is that we're trying to
> do device access and setup through vfio, but iommu manipulation through
> kvm. We may want to revisit whether we can do the in-kernel iommu
> manipulation through vfio rather than kvm.
How would that be possible ?
The hypercalls from the guest arrive in KVM... in a very very specific &
restricted environment which we call real mode (MMU off but still
running in guest context), where we try to do as much as possible, or in
virtual mode, where they get handled as normal KVM exits.
The only way we could handle them "in VFIO" would be if somewhat VFIO
registered callbacks with KVM... if we have that sort of
cross-dependency, then we may as well have a simpler one where VFIO
tells KVM what iommu is available for the VM
> For vfio in general, the group is the unit of ownership. A user is
> granted access to /dev/vfio/$GROUP through file permissions. The user
> opens the group and a container (/dev/vfio/vfio) and calls SET_CONTAINER
> on the group. If supported by the platform, multiple groups can be set
> to the same container, which allows for iommu domain sharing. Once a
> group is associated with a container, an iommu backend can be
> initialized for the container. Only then can a device be accessed
> through the group.
>
> So even if we were to pass a vfio group file descriptor into kvm and it
> matched as some kind of ownership token on the iommu group, it's not
> clear that's sufficient to assume we can start programming the iommu.
> Thanks,
Your scheme seems to me that it would have the same problem if you
wanted to do virtualized iommu....
In any case, this is a big deal. We have a requirement for pass-through.
It cannot work with any remotely usable performance level if we don't
implement the calls in KVM, so it needs to be sorted one way or another
and I'm at a loss how here...
Ben.
> Alex
>
> > From the look of it, the VFIO file descriptor is what has the "access
> > control" to the underlying iommu, is this right ? So we somewhat need to
> > transfer (or copy) that ownership from the VFIO fd to the KVM VM.
> >
> > I don't see a way to do that without some cross-layering here...
> >
> > Rusty, are you aware of some kernel mechanism we can use for that ?
> >
> > Cheers,
> > Ben.
> >
> >
>
>
>
> --
> To unsubscribe from this list: send the line "unsubscribe kvm-ppc" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply
* Re: [PATCH -V10 00/15] THP support for PPC64
From: Benjamin Herrenschmidt @ 2013-06-18 22:03 UTC (permalink / raw)
To: Aneesh Kumar K.V; +Cc: Alexey Kardashevskiy, linuxppc-dev, paulus
In-Reply-To: <8738sfi7er.fsf@linux.vnet.ibm.com>
On Wed, 2013-06-19 at 00:16 +0530, Aneesh Kumar K.V wrote:
> But will that by anonymous memory ? ie, will we find them suitable for
> THP allocation ?
The 4k pages themselves with 4k_PFN no, but the segment yes. A single of
these will demote the whole segment, ie 256M or 1T.
> > * If you find a THP in hash_page and the segment size is 4k, fault
> >
> > * In do_page_fault, re-check for that condition (or maybe we can make
> > hash_page return a specific bit that gets ORed into the error_code into
> > do_page_fault ?) and split huge pages there.
> >
> > But that's just an idea off the top of my mind, there might be a better
> > way. Of course this needs to be tested.
> >
> > BTW. For the subpage protection, similarily, you need to make sure you
> > properly map the entire segment as "no THP", not just the range
> > passed-in by the user.
>
> Can you explain that more, why should the entire segment be marked no THP ?
> The segment can work with 4K base page size and we still be able to
> allocate a hugepage in that segment.
Will we be able to track all the possible hashings of the huge page on a
4k segment ?
Ben.
^ permalink raw reply
* Re: [PATCH 3/4] KVM: PPC: Add support for IOMMU in-kernel handling
From: Alexey Kardashevskiy @ 2013-06-19 3:17 UTC (permalink / raw)
To: Benjamin Herrenschmidt
Cc: kvm, linux-kernel, kvm-ppc, Alexander Graf, Paul Mackerras,
linuxppc-dev, David Gibson
In-Reply-To: <1371357560.21896.120.camel@pasglop>
On 06/16/2013 02:39 PM, Benjamin Herrenschmidt wrote:
>> static pte_t kvmppc_lookup_pte(pgd_t *pgdir, unsigned long hva, bool writing,
>> - unsigned long *pte_sizep)
>> + unsigned long *pte_sizep, bool do_get_page)
>> {
>> pte_t *ptep;
>> unsigned int shift = 0;
>> @@ -135,6 +136,14 @@ static pte_t kvmppc_lookup_pte(pgd_t *pgdir, unsigned long hva, bool writing,
>> if (!pte_present(*ptep))
>> return __pte(0);
>>
>> + /*
>> + * Put huge pages handling to the virtual mode.
>> + * The only exception is for TCE list pages which we
>> + * do need to call get_page() for.
>> + */
>> + if ((*pte_sizep > PAGE_SIZE) && do_get_page)
>> + return __pte(0);
>> +
>> /* wait until _PAGE_BUSY is clear then set it atomically */
>> __asm__ __volatile__ (
>> "1: ldarx %0,0,%3\n"
>> @@ -148,6 +157,18 @@ static pte_t kvmppc_lookup_pte(pgd_t *pgdir, unsigned long hva, bool writing,
>> : "cc");
>>
>> ret = pte;
>> + if (do_get_page && pte_present(pte) && (!writing || pte_write(pte))) {
>> + struct page *pg = NULL;
>> + pg = realmode_pfn_to_page(pte_pfn(pte));
>> + if (realmode_get_page(pg)) {
>> + ret = __pte(0);
>> + } else {
>> + pte = pte_mkyoung(pte);
>> + if (writing)
>> + pte = pte_mkdirty(pte);
>> + }
>> + }
>> + *ptep = pte; /* clears _PAGE_BUSY */
>>
>> return ret;
>> }
>
> So now you are adding the clearing of _PAGE_BUSY that was missing for
> your first patch, except that this is not enough since that means that
> in the "emulated" case (ie, !do_get_page) you will in essence return
> and then use a PTE that is not locked without any synchronization to
> ensure that the underlying page doesn't go away... then you'll
> dereference that page.
>
> So either make everything use speculative get_page, or make the emulated
> case use the MMU notifier to drop the operation in case of collision.
>
> The former looks easier.
>
> Also, any specific reason why you do:
>
> - Lock the PTE
> - get_page()
> - Unlock the PTE
>
> Instead of
>
> - Read the PTE
> - get_page_unless_zero
> - re-check PTE
>
> Like get_user_pages_fast() does ?
>
> The former will be two atomic ops, the latter only one (faster), but
> maybe you have a good reason why that can't work...
If we want to set "dirty" and "young" bits for pte then I do not know how
to avoid _PAGE_BUSY.
--
Alexey
^ permalink raw reply
* Re: [PATCH -V10 00/15] THP support for PPC64
From: Aneesh Kumar K.V @ 2013-06-19 3:30 UTC (permalink / raw)
To: Benjamin Herrenschmidt; +Cc: Alexey Kardashevskiy, linuxppc-dev, paulus
In-Reply-To: <1371593004.21896.199.camel@pasglop>
Benjamin Herrenschmidt <benh@kernel.crashing.org> writes:
> On Wed, 2013-06-19 at 00:16 +0530, Aneesh Kumar K.V wrote:
>
>> But will that by anonymous memory ? ie, will we find them suitable for
>> THP allocation ?
>
> The 4k pages themselves with 4k_PFN no, but the segment yes. A single of
> these will demote the whole segment, ie 256M or 1T.
>
>> > * If you find a THP in hash_page and the segment size is 4k, fault
>> >
>> > * In do_page_fault, re-check for that condition (or maybe we can make
>> > hash_page return a specific bit that gets ORed into the error_code into
>> > do_page_fault ?) and split huge pages there.
>> >
>> > But that's just an idea off the top of my mind, there might be a better
>> > way. Of course this needs to be tested.
>> >
>> > BTW. For the subpage protection, similarily, you need to make sure you
>> > properly map the entire segment as "no THP", not just the range
>> > passed-in by the user.
>>
>> Can you explain that more, why should the entire segment be marked no THP ?
>> The segment can work with 4K base page size and we still be able to
>> allocate a hugepage in that segment.
>
> Will we be able to track all the possible hashings of the huge page on a
> 4k segment ?
Yes. The comment above hpte_valid explains that
* The linux hugepage PMD now include the pmd entries followed by the address
* to the stashed pgtable_t. The stashed pgtable_t contains the hpte bits.
* [ 1 bit secondary | 3 bit hidx | 1 bit valid | 000]. We use one byte per
* each HPTE entry. With 16MB hugepage and 64K HPTE we need 256 entries and
* with 4K HPTE we need 4096 entries. Both will fit in a 4K pgtable_t.
-aneesh
^ permalink raw reply
* Re: [PATCH 3/4] KVM: PPC: Add support for IOMMU in-kernel handling
From: Rusty Russell @ 2013-06-19 3:35 UTC (permalink / raw)
To: Alex Williamson, Benjamin Herrenschmidt
Cc: kvm, Alexey Kardashevskiy, Alexander Graf, kvm-ppc, linux-kernel,
Paul Mackerras, linuxppc-dev, David Gibson
In-Reply-To: <1371522772.22681.140.camel@ul30vt.home>
Alex Williamson <alex.williamson@redhat.com> writes:
> On Mon, 2013-06-17 at 13:56 +1000, Benjamin Herrenschmidt wrote:
>> On Sun, 2013-06-16 at 21:13 -0600, Alex Williamson wrote:
>>
>> > IOMMU groups themselves don't provide security, they're accessed by
>> > interfaces like VFIO, which provide the security. Given a brief look, I
>> > agree, this looks like a possible backdoor. The typical VFIO way to
>> > handle this would be to pass a VFIO file descriptor here to prove that
>> > the process has access to the IOMMU group. This is how /dev/vfio/vfio
>> > gains the ability to setup an IOMMU domain an do mappings with the
>> > SET_CONTAINER ioctl using a group fd. Thanks,
>>
>> How do you envision that in the kernel ? IE. I'm in KVM code, gets that
>> vfio fd, what do I do with it ?
>>
>> Basically, KVM needs to know that the user is allowed to use that iommu
>> group. I don't think we want KVM however to call into VFIO directly
>> right ?
>
> Right, we don't want to create dependencies across modules. I don't
> have a vision for how this should work. This is effectively a complete
> side-band to vfio, so we're really just dealing in the iommu group
> space. Maybe there needs to be some kind of registration of ownership
> for the group using some kind of token. It would need to include some
> kind of notification when that ownership ends. That might also be a
> convenient tag to toggle driver probing off for devices in the group.
> Other ideas? Thanks,
It's actually not that bad.
eg.
struct vfio_container *vfio_container_from_file(struct file *filp)
{
if (filp->f_op != &vfio_device_fops)
return ERR_PTR(-EINVAL);
/* OK it really is a vfio fd, return the data. */
....
}
EXPORT_SYMBOL_GPL(vfio_container_from_file);
...
inside KVM_CREATE_SPAPR_TCE_IOMMU:
struct file *vfio_filp;
struct vfio_container *(lookup)(struct file *filp);
vfio_filp = fget(create_tce_iommu.fd);
if (!vfio)
ret = -EBADF;
lookup = symbol_get(vfio_container_from_file);
if (!lookup)
ret = -EINVAL;
else {
container = lookup(vfio_filp);
if (IS_ERR(container))
ret = PTR_ERR(container);
else
...
symbol_put(vfio_container_from_file);
}
symbol_get() won't try to load a module; it'll just fail. This is what
you want, since they must have vfio in the kernel to get a valid fd...
Hope that helps,
Rusty.
^ permalink raw reply
* Re: [PATCH 01/31] powerpc/eeh: Move common part to kernel directory
From: Michael Neuling @ 2013-06-19 3:58 UTC (permalink / raw)
To: Gavin Shan; +Cc: linuxppc-dev
In-Reply-To: <1371544435-4943-2-git-send-email-shangw@linux.vnet.ibm.com>
Bunch of whitespace issues here:
% git am ~/Mail/linuxppc/31202
Applying: powerpc/eeh: Move common part to kernel directory
/home/mikey/src/powerpc-test/.git/rebase-apply/patch:437: trailing whitespace.
/home/mikey/src/powerpc-test/.git/rebase-apply/patch:594: space before tab in indent.
*/
/home/mikey/src/powerpc-test/.git/rebase-apply/patch:607: trailing whitespace.
/home/mikey/src/powerpc-test/.git/rebase-apply/patch:608: trailing whitespace.
/* We might get hit with another EEH freeze as soon as the
/home/mikey/src/powerpc-test/.git/rebase-apply/patch:673: trailing whitespace.
error: patch failed: arch/powerpc/platforms/pseries/eeh_pe.c:1
error: arch/powerpc/platforms/pseries/eeh_pe.c: patch does not apply
Patch failed at 0001 powerpc/eeh: Move common part to kernel directory
When you have resolved this problem run "git am --resolved".
If you would prefer to skip this patch, instead run "git am --skip".
To restore the original branch and stop patching run "git am --abort".
Mikey
Gavin Shan <shangw@linux.vnet.ibm.com> wrote:
> The patch moves the common part of EEH core into arch/powerpc/kernel
> directory so that we needn't PPC_PSERIES while compiling POWERNV
> platform:
>
> * Move the EEH common part into arch/powerpc/kernel
> * Move the functions for PCI hotplug from pSeries platform to
> arch/powerpc/kernel/pci_hotplug.c
> * Move CONFIG_EEH from arch/powerpc/platforms/pseries/Kconfig to
> arch/powerpc/platforms/Kconfig
> * Adjust makefile accordingly
>
> Signed-off-by: Gavin Shan <shangw@linux.vnet.ibm.com>
> ---
> arch/powerpc/kernel/Makefile | 4 +-
> arch/powerpc/kernel/eeh.c | 942 +++++++++++++++++++++++++++
> arch/powerpc/kernel/eeh_cache.c | 319 +++++++++
> arch/powerpc/kernel/eeh_dev.c | 112 ++++
> arch/powerpc/kernel/eeh_driver.c | 552 ++++++++++++++++
> arch/powerpc/kernel/eeh_event.c | 142 ++++
> arch/powerpc/kernel/eeh_pe.c | 653 +++++++++++++++++++
> arch/powerpc/kernel/eeh_sysfs.c | 75 +++
> arch/powerpc/kernel/pci_hotplug.c | 111 ++++
> arch/powerpc/platforms/Kconfig | 5 +
> arch/powerpc/platforms/pseries/Kconfig | 5 -
> arch/powerpc/platforms/pseries/Makefile | 4 +-
> arch/powerpc/platforms/pseries/eeh.c | 942 ---------------------------
> arch/powerpc/platforms/pseries/eeh_cache.c | 319 ---------
> arch/powerpc/platforms/pseries/eeh_dev.c | 112 ----
> arch/powerpc/platforms/pseries/eeh_driver.c | 552 ----------------
> arch/powerpc/platforms/pseries/eeh_event.c | 142 ----
> arch/powerpc/platforms/pseries/eeh_pe.c | 653 -------------------
> arch/powerpc/platforms/pseries/eeh_sysfs.c | 75 ---
> arch/powerpc/platforms/pseries/pci_dlpar.c | 85 ---
> 20 files changed, 2915 insertions(+), 2889 deletions(-)
> create mode 100644 arch/powerpc/kernel/eeh.c
> create mode 100644 arch/powerpc/kernel/eeh_cache.c
> create mode 100644 arch/powerpc/kernel/eeh_dev.c
> create mode 100644 arch/powerpc/kernel/eeh_driver.c
> create mode 100644 arch/powerpc/kernel/eeh_event.c
> create mode 100644 arch/powerpc/kernel/eeh_pe.c
> create mode 100644 arch/powerpc/kernel/eeh_sysfs.c
> create mode 100644 arch/powerpc/kernel/pci_hotplug.c
> delete mode 100644 arch/powerpc/platforms/pseries/eeh.c
> delete mode 100644 arch/powerpc/platforms/pseries/eeh_cache.c
> delete mode 100644 arch/powerpc/platforms/pseries/eeh_dev.c
> delete mode 100644 arch/powerpc/platforms/pseries/eeh_driver.c
> delete mode 100644 arch/powerpc/platforms/pseries/eeh_event.c
> delete mode 100644 arch/powerpc/platforms/pseries/eeh_pe.c
> delete mode 100644 arch/powerpc/platforms/pseries/eeh_sysfs.c
>
> diff --git a/arch/powerpc/kernel/Makefile b/arch/powerpc/kernel/Makefile
> index f960a79..5826906 100644
> --- a/arch/powerpc/kernel/Makefile
> +++ b/arch/powerpc/kernel/Makefile
> @@ -58,6 +58,8 @@ obj-$(CONFIG_RTAS_PROC) += rtas-proc.o
> obj-$(CONFIG_LPARCFG) += lparcfg.o
> obj-$(CONFIG_IBMVIO) += vio.o
> obj-$(CONFIG_IBMEBUS) += ibmebus.o
> +obj-$(CONFIG_EEH) += eeh.o eeh_pe.o eeh_dev.o eeh_cache.o \
> + eeh_driver.o eeh_event.o eeh_sysfs.o
> obj-$(CONFIG_GENERIC_TBSYNC) += smp-tbsync.o
> obj-$(CONFIG_CRASH_DUMP) += crash_dump.o
> obj-$(CONFIG_FA_DUMP) += fadump.o
> @@ -100,7 +102,7 @@ obj-$(CONFIG_PPC_UDBG_16550) += legacy_serial.o udbg_16550.o
> obj-$(CONFIG_STACKTRACE) += stacktrace.o
> obj-$(CONFIG_SWIOTLB) += dma-swiotlb.o
>
> -pci64-$(CONFIG_PPC64) += pci_dn.o isa-bridge.o
> +pci64-$(CONFIG_PPC64) += pci_hotplug.o pci_dn.o isa-bridge.o
> obj-$(CONFIG_PCI) += pci_$(CONFIG_WORD_SIZE).o $(pci64-y) \
> pci-common.o pci_of_scan.o
> obj-$(CONFIG_PCI_MSI) += msi.o
> diff --git a/arch/powerpc/kernel/eeh.c b/arch/powerpc/kernel/eeh.c
> new file mode 100644
> index 0000000..6b73d6c
> --- /dev/null
> +++ b/arch/powerpc/kernel/eeh.c
> @@ -0,0 +1,942 @@
> +/*
> + * Copyright IBM Corporation 2001, 2005, 2006
> + * Copyright Dave Engebretsen & Todd Inglett 2001
> + * Copyright Linas Vepstas 2005, 2006
> + * Copyright 2001-2012 IBM Corporation.
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> + * the Free Software Foundation; either version 2 of the License, or
> + * (at your option) any later version.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> + * GNU General Public License for more details.
> + *
> + * You should have received a copy of the GNU General Public License
> + * along with this program; if not, write to the Free Software
> + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
> + *
> + * Please address comments and feedback to Linas Vepstas <linas@austin.ibm.com>
> + */
> +
> +#include <linux/delay.h>
> +#include <linux/sched.h>
> +#include <linux/init.h>
> +#include <linux/list.h>
> +#include <linux/pci.h>
> +#include <linux/proc_fs.h>
> +#include <linux/rbtree.h>
> +#include <linux/seq_file.h>
> +#include <linux/spinlock.h>
> +#include <linux/export.h>
> +#include <linux/of.h>
> +
> +#include <linux/atomic.h>
> +#include <asm/eeh.h>
> +#include <asm/eeh_event.h>
> +#include <asm/io.h>
> +#include <asm/machdep.h>
> +#include <asm/ppc-pci.h>
> +#include <asm/rtas.h>
> +
> +
> +/** Overview:
> + * EEH, or "Extended Error Handling" is a PCI bridge technology for
> + * dealing with PCI bus errors that can't be dealt with within the
> + * usual PCI framework, except by check-stopping the CPU. Systems
> + * that are designed for high-availability/reliability cannot afford
> + * to crash due to a "mere" PCI error, thus the need for EEH.
> + * An EEH-capable bridge operates by converting a detected error
> + * into a "slot freeze", taking the PCI adapter off-line, making
> + * the slot behave, from the OS'es point of view, as if the slot
> + * were "empty": all reads return 0xff's and all writes are silently
> + * ignored. EEH slot isolation events can be triggered by parity
> + * errors on the address or data busses (e.g. during posted writes),
> + * which in turn might be caused by low voltage on the bus, dust,
> + * vibration, humidity, radioactivity or plain-old failed hardware.
> + *
> + * Note, however, that one of the leading causes of EEH slot
> + * freeze events are buggy device drivers, buggy device microcode,
> + * or buggy device hardware. This is because any attempt by the
> + * device to bus-master data to a memory address that is not
> + * assigned to the device will trigger a slot freeze. (The idea
> + * is to prevent devices-gone-wild from corrupting system memory).
> + * Buggy hardware/drivers will have a miserable time co-existing
> + * with EEH.
> + *
> + * Ideally, a PCI device driver, when suspecting that an isolation
> + * event has occurred (e.g. by reading 0xff's), will then ask EEH
> + * whether this is the case, and then take appropriate steps to
> + * reset the PCI slot, the PCI device, and then resume operations.
> + * However, until that day, the checking is done here, with the
> + * eeh_check_failure() routine embedded in the MMIO macros. If
> + * the slot is found to be isolated, an "EEH Event" is synthesized
> + * and sent out for processing.
> + */
> +
> +/* If a device driver keeps reading an MMIO register in an interrupt
> + * handler after a slot isolation event, it might be broken.
> + * This sets the threshold for how many read attempts we allow
> + * before printing an error message.
> + */
> +#define EEH_MAX_FAILS 2100000
> +
> +/* Time to wait for a PCI slot to report status, in milliseconds */
> +#define PCI_BUS_RESET_WAIT_MSEC (60*1000)
> +
> +/* Platform dependent EEH operations */
> +struct eeh_ops *eeh_ops = NULL;
> +
> +int eeh_subsystem_enabled;
> +EXPORT_SYMBOL(eeh_subsystem_enabled);
> +
> +/*
> + * EEH probe mode support. The intention is to support multiple
> + * platforms for EEH. Some platforms like pSeries do PCI emunation
> + * based on device tree. However, other platforms like powernv probe
> + * PCI devices from hardware. The flag is used to distinguish that.
> + * In addition, struct eeh_ops::probe would be invoked for particular
> + * OF node or PCI device so that the corresponding PE would be created
> + * there.
> + */
> +int eeh_probe_mode;
> +
> +/* Global EEH mutex */
> +DEFINE_MUTEX(eeh_mutex);
> +
> +/* Lock to avoid races due to multiple reports of an error */
> +static DEFINE_RAW_SPINLOCK(confirm_error_lock);
> +
> +/* Buffer for reporting pci register dumps. Its here in BSS, and
> + * not dynamically alloced, so that it ends up in RMO where RTAS
> + * can access it.
> + */
> +#define EEH_PCI_REGS_LOG_LEN 4096
> +static unsigned char pci_regs_buf[EEH_PCI_REGS_LOG_LEN];
> +
> +/*
> + * The struct is used to maintain the EEH global statistic
> + * information. Besides, the EEH global statistics will be
> + * exported to user space through procfs
> + */
> +struct eeh_stats {
> + u64 no_device; /* PCI device not found */
> + u64 no_dn; /* OF node not found */
> + u64 no_cfg_addr; /* Config address not found */
> + u64 ignored_check; /* EEH check skipped */
> + u64 total_mmio_ffs; /* Total EEH checks */
> + u64 false_positives; /* Unnecessary EEH checks */
> + u64 slot_resets; /* PE reset */
> +};
> +
> +static struct eeh_stats eeh_stats;
> +
> +#define IS_BRIDGE(class_code) (((class_code)<<16) == PCI_BASE_CLASS_BRIDGE)
> +
> +/**
> + * eeh_gather_pci_data - Copy assorted PCI config space registers to buff
> + * @edev: device to report data for
> + * @buf: point to buffer in which to log
> + * @len: amount of room in buffer
> + *
> + * This routine captures assorted PCI configuration space data,
> + * and puts them into a buffer for RTAS error logging.
> + */
> +static size_t eeh_gather_pci_data(struct eeh_dev *edev, char * buf, size_t len)
> +{
> + struct device_node *dn = eeh_dev_to_of_node(edev);
> + struct pci_dev *dev = eeh_dev_to_pci_dev(edev);
> + u32 cfg;
> + int cap, i;
> + int n = 0;
> +
> + n += scnprintf(buf+n, len-n, "%s\n", dn->full_name);
> + printk(KERN_WARNING "EEH: of node=%s\n", dn->full_name);
> +
> + eeh_ops->read_config(dn, PCI_VENDOR_ID, 4, &cfg);
> + n += scnprintf(buf+n, len-n, "dev/vend:%08x\n", cfg);
> + printk(KERN_WARNING "EEH: PCI device/vendor: %08x\n", cfg);
> +
> + eeh_ops->read_config(dn, PCI_COMMAND, 4, &cfg);
> + n += scnprintf(buf+n, len-n, "cmd/stat:%x\n", cfg);
> + printk(KERN_WARNING "EEH: PCI cmd/status register: %08x\n", cfg);
> +
> + if (!dev) {
> + printk(KERN_WARNING "EEH: no PCI device for this of node\n");
> + return n;
> + }
> +
> + /* Gather bridge-specific registers */
> + if (dev->class >> 16 == PCI_BASE_CLASS_BRIDGE) {
> + eeh_ops->read_config(dn, PCI_SEC_STATUS, 2, &cfg);
> + n += scnprintf(buf+n, len-n, "sec stat:%x\n", cfg);
> + printk(KERN_WARNING "EEH: Bridge secondary status: %04x\n", cfg);
> +
> + eeh_ops->read_config(dn, PCI_BRIDGE_CONTROL, 2, &cfg);
> + n += scnprintf(buf+n, len-n, "brdg ctl:%x\n", cfg);
> + printk(KERN_WARNING "EEH: Bridge control: %04x\n", cfg);
> + }
> +
> + /* Dump out the PCI-X command and status regs */
> + cap = pci_find_capability(dev, PCI_CAP_ID_PCIX);
> + if (cap) {
> + eeh_ops->read_config(dn, cap, 4, &cfg);
> + n += scnprintf(buf+n, len-n, "pcix-cmd:%x\n", cfg);
> + printk(KERN_WARNING "EEH: PCI-X cmd: %08x\n", cfg);
> +
> + eeh_ops->read_config(dn, cap+4, 4, &cfg);
> + n += scnprintf(buf+n, len-n, "pcix-stat:%x\n", cfg);
> + printk(KERN_WARNING "EEH: PCI-X status: %08x\n", cfg);
> + }
> +
> + /* If PCI-E capable, dump PCI-E cap 10, and the AER */
> + cap = pci_find_capability(dev, PCI_CAP_ID_EXP);
> + if (cap) {
> + n += scnprintf(buf+n, len-n, "pci-e cap10:\n");
> + printk(KERN_WARNING
> + "EEH: PCI-E capabilities and status follow:\n");
> +
> + for (i=0; i<=8; i++) {
> + eeh_ops->read_config(dn, cap+4*i, 4, &cfg);
> + n += scnprintf(buf+n, len-n, "%02x:%x\n", 4*i, cfg);
> + printk(KERN_WARNING "EEH: PCI-E %02x: %08x\n", i, cfg);
> + }
> +
> + cap = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_ERR);
> + if (cap) {
> + n += scnprintf(buf+n, len-n, "pci-e AER:\n");
> + printk(KERN_WARNING
> + "EEH: PCI-E AER capability register set follows:\n");
> +
> + for (i=0; i<14; i++) {
> + eeh_ops->read_config(dn, cap+4*i, 4, &cfg);
> + n += scnprintf(buf+n, len-n, "%02x:%x\n", 4*i, cfg);
> + printk(KERN_WARNING "EEH: PCI-E AER %02x: %08x\n", i, cfg);
> + }
> + }
> + }
> +
> + return n;
> +}
> +
> +/**
> + * eeh_slot_error_detail - Generate combined log including driver log and error log
> + * @pe: EEH PE
> + * @severity: temporary or permanent error log
> + *
> + * This routine should be called to generate the combined log, which
> + * is comprised of driver log and error log. The driver log is figured
> + * out from the config space of the corresponding PCI device, while
> + * the error log is fetched through platform dependent function call.
> + */
> +void eeh_slot_error_detail(struct eeh_pe *pe, int severity)
> +{
> + size_t loglen = 0;
> + struct eeh_dev *edev;
> +
> + eeh_pci_enable(pe, EEH_OPT_THAW_MMIO);
> + eeh_ops->configure_bridge(pe);
> + eeh_pe_restore_bars(pe);
> +
> + pci_regs_buf[0] = 0;
> + eeh_pe_for_each_dev(pe, edev) {
> + loglen += eeh_gather_pci_data(edev, pci_regs_buf,
> + EEH_PCI_REGS_LOG_LEN);
> + }
> +
> + eeh_ops->get_log(pe, severity, pci_regs_buf, loglen);
> +}
> +
> +/**
> + * eeh_token_to_phys - Convert EEH address token to phys address
> + * @token: I/O token, should be address in the form 0xA....
> + *
> + * This routine should be called to convert virtual I/O address
> + * to physical one.
> + */
> +static inline unsigned long eeh_token_to_phys(unsigned long token)
> +{
> + pte_t *ptep;
> + unsigned long pa;
> +
> + ptep = find_linux_pte(init_mm.pgd, token);
> + if (!ptep)
> + return token;
> + pa = pte_pfn(*ptep) << PAGE_SHIFT;
> +
> + return pa | (token & (PAGE_SIZE-1));
> +}
> +
> +/**
> + * eeh_dev_check_failure - Check if all 1's data is due to EEH slot freeze
> + * @edev: eeh device
> + *
> + * Check for an EEH failure for the given device node. Call this
> + * routine if the result of a read was all 0xff's and you want to
> + * find out if this is due to an EEH slot freeze. This routine
> + * will query firmware for the EEH status.
> + *
> + * Returns 0 if there has not been an EEH error; otherwise returns
> + * a non-zero value and queues up a slot isolation event notification.
> + *
> + * It is safe to call this routine in an interrupt context.
> + */
> +int eeh_dev_check_failure(struct eeh_dev *edev)
> +{
> + int ret;
> + unsigned long flags;
> + struct device_node *dn;
> + struct pci_dev *dev;
> + struct eeh_pe *pe;
> + int rc = 0;
> + const char *location;
> +
> + eeh_stats.total_mmio_ffs++;
> +
> + if (!eeh_subsystem_enabled)
> + return 0;
> +
> + if (!edev) {
> + eeh_stats.no_dn++;
> + return 0;
> + }
> + dn = eeh_dev_to_of_node(edev);
> + dev = eeh_dev_to_pci_dev(edev);
> + pe = edev->pe;
> +
> + /* Access to IO BARs might get this far and still not want checking. */
> + if (!pe) {
> + eeh_stats.ignored_check++;
> + pr_debug("EEH: Ignored check for %s %s\n",
> + eeh_pci_name(dev), dn->full_name);
> + return 0;
> + }
> +
> + if (!pe->addr && !pe->config_addr) {
> + eeh_stats.no_cfg_addr++;
> + return 0;
> + }
> +
> + /* If we already have a pending isolation event for this
> + * slot, we know it's bad already, we don't need to check.
> + * Do this checking under a lock; as multiple PCI devices
> + * in one slot might report errors simultaneously, and we
> + * only want one error recovery routine running.
> + */
> + raw_spin_lock_irqsave(&confirm_error_lock, flags);
> + rc = 1;
> + if (pe->state & EEH_PE_ISOLATED) {
> + pe->check_count++;
> + if (pe->check_count % EEH_MAX_FAILS == 0) {
> + location = of_get_property(dn, "ibm,loc-code", NULL);
> + printk(KERN_ERR "EEH: %d reads ignored for recovering device at "
> + "location=%s driver=%s pci addr=%s\n",
> + pe->check_count, location,
> + eeh_driver_name(dev), eeh_pci_name(dev));
> + printk(KERN_ERR "EEH: Might be infinite loop in %s driver\n",
> + eeh_driver_name(dev));
> + dump_stack();
> + }
> + goto dn_unlock;
> + }
> +
> + /*
> + * Now test for an EEH failure. This is VERY expensive.
> + * Note that the eeh_config_addr may be a parent device
> + * in the case of a device behind a bridge, or it may be
> + * function zero of a multi-function device.
> + * In any case they must share a common PHB.
> + */
> + ret = eeh_ops->get_state(pe, NULL);
> +
> + /* Note that config-io to empty slots may fail;
> + * they are empty when they don't have children.
> + * We will punt with the following conditions: Failure to get
> + * PE's state, EEH not support and Permanently unavailable
> + * state, PE is in good state.
> + */
> + if ((ret < 0) ||
> + (ret == EEH_STATE_NOT_SUPPORT) ||
> + (ret & (EEH_STATE_MMIO_ACTIVE | EEH_STATE_DMA_ACTIVE)) ==
> + (EEH_STATE_MMIO_ACTIVE | EEH_STATE_DMA_ACTIVE)) {
> + eeh_stats.false_positives++;
> + pe->false_positives++;
> + rc = 0;
> + goto dn_unlock;
> + }
> +
> + eeh_stats.slot_resets++;
> +
> + /* Avoid repeated reports of this failure, including problems
> + * with other functions on this device, and functions under
> + * bridges.
> + */
> + eeh_pe_state_mark(pe, EEH_PE_ISOLATED);
> + raw_spin_unlock_irqrestore(&confirm_error_lock, flags);
> +
> + eeh_send_failure_event(pe);
> +
> + /* Most EEH events are due to device driver bugs. Having
> + * a stack trace will help the device-driver authors figure
> + * out what happened. So print that out.
> + */
> + WARN(1, "EEH: failure detected\n");
> + return 1;
> +
> +dn_unlock:
> + raw_spin_unlock_irqrestore(&confirm_error_lock, flags);
> + return rc;
> +}
> +
> +EXPORT_SYMBOL_GPL(eeh_dev_check_failure);
> +
> +/**
> + * eeh_check_failure - Check if all 1's data is due to EEH slot freeze
> + * @token: I/O token, should be address in the form 0xA....
> + * @val: value, should be all 1's (XXX why do we need this arg??)
> + *
> + * Check for an EEH failure at the given token address. Call this
> + * routine if the result of a read was all 0xff's and you want to
> + * find out if this is due to an EEH slot freeze event. This routine
> + * will query firmware for the EEH status.
> + *
> + * Note this routine is safe to call in an interrupt context.
> + */
> +unsigned long eeh_check_failure(const volatile void __iomem *token, unsigned long val)
> +{
> + unsigned long addr;
> + struct eeh_dev *edev;
> +
> + /* Finding the phys addr + pci device; this is pretty quick. */
> + addr = eeh_token_to_phys((unsigned long __force) token);
> + edev = eeh_addr_cache_get_dev(addr);
> + if (!edev) {
> + eeh_stats.no_device++;
> + return val;
> + }
> +
> + eeh_dev_check_failure(edev);
> +
> + pci_dev_put(eeh_dev_to_pci_dev(edev));
> + return val;
> +}
> +
> +EXPORT_SYMBOL(eeh_check_failure);
> +
> +
> +/**
> + * eeh_pci_enable - Enable MMIO or DMA transfers for this slot
> + * @pe: EEH PE
> + *
> + * This routine should be called to reenable frozen MMIO or DMA
> + * so that it would work correctly again. It's useful while doing
> + * recovery or log collection on the indicated device.
> + */
> +int eeh_pci_enable(struct eeh_pe *pe, int function)
> +{
> + int rc;
> +
> + rc = eeh_ops->set_option(pe, function);
> + if (rc)
> + pr_warning("%s: Unexpected state change %d on PHB#%d-PE#%x, err=%d\n",
> + __func__, function, pe->phb->global_number, pe->addr, rc);
> +
> + rc = eeh_ops->wait_state(pe, PCI_BUS_RESET_WAIT_MSEC);
> + if (rc > 0 && (rc & EEH_STATE_MMIO_ENABLED) &&
> + (function == EEH_OPT_THAW_MMIO))
> + return 0;
> +
> + return rc;
> +}
> +
> +/**
> + * pcibios_set_pcie_slot_reset - Set PCI-E reset state
> + * @dev: pci device struct
> + * @state: reset state to enter
> + *
> + * Return value:
> + * 0 if success
> + */
> +int pcibios_set_pcie_reset_state(struct pci_dev *dev, enum pcie_reset_state state)
> +{
> + struct eeh_dev *edev = pci_dev_to_eeh_dev(dev);
> + struct eeh_pe *pe = edev->pe;
> +
> + if (!pe) {
> + pr_err("%s: No PE found on PCI device %s\n",
> + __func__, pci_name(dev));
> + return -EINVAL;
> + }
> +
> + switch (state) {
> + case pcie_deassert_reset:
> + eeh_ops->reset(pe, EEH_RESET_DEACTIVATE);
> + break;
> + case pcie_hot_reset:
> + eeh_ops->reset(pe, EEH_RESET_HOT);
> + break;
> + case pcie_warm_reset:
> + eeh_ops->reset(pe, EEH_RESET_FUNDAMENTAL);
> + break;
> + default:
> + return -EINVAL;
> + };
> +
> + return 0;
> +}
> +
> +/**
> + * eeh_set_pe_freset - Check the required reset for the indicated device
> + * @data: EEH device
> + * @flag: return value
> + *
> + * Each device might have its preferred reset type: fundamental or
> + * hot reset. The routine is used to collected the information for
> + * the indicated device and its children so that the bunch of the
> + * devices could be reset properly.
> + */
> +static void *eeh_set_dev_freset(void *data, void *flag)
> +{
> + struct pci_dev *dev;
> + unsigned int *freset = (unsigned int *)flag;
> + struct eeh_dev *edev = (struct eeh_dev *)data;
> +
> + dev = eeh_dev_to_pci_dev(edev);
> + if (dev)
> + *freset |= dev->needs_freset;
> +
> + return NULL;
> +}
> +
> +/**
> + * eeh_reset_pe_once - Assert the pci #RST line for 1/4 second
> + * @pe: EEH PE
> + *
> + * Assert the PCI #RST line for 1/4 second.
> + */
> +static void eeh_reset_pe_once(struct eeh_pe *pe)
> +{
> + unsigned int freset = 0;
> +
> + /* Determine type of EEH reset required for
> + * Partitionable Endpoint, a hot-reset (1)
> + * or a fundamental reset (3).
> + * A fundamental reset required by any device under
> + * Partitionable Endpoint trumps hot-reset.
> + */
> + eeh_pe_dev_traverse(pe, eeh_set_dev_freset, &freset);
> +
> + if (freset)
> + eeh_ops->reset(pe, EEH_RESET_FUNDAMENTAL);
> + else
> + eeh_ops->reset(pe, EEH_RESET_HOT);
> +
> + /* The PCI bus requires that the reset be held high for at least
> + * a 100 milliseconds. We wait a bit longer 'just in case'.
> + */
> +#define PCI_BUS_RST_HOLD_TIME_MSEC 250
> + msleep(PCI_BUS_RST_HOLD_TIME_MSEC);
> +
> + /* We might get hit with another EEH freeze as soon as the
> + * pci slot reset line is dropped. Make sure we don't miss
> + * these, and clear the flag now.
> + */
> + eeh_pe_state_clear(pe, EEH_PE_ISOLATED);
> +
> + eeh_ops->reset(pe, EEH_RESET_DEACTIVATE);
> +
> + /* After a PCI slot has been reset, the PCI Express spec requires
> + * a 1.5 second idle time for the bus to stabilize, before starting
> + * up traffic.
> + */
> +#define PCI_BUS_SETTLE_TIME_MSEC 1800
> + msleep(PCI_BUS_SETTLE_TIME_MSEC);
> +}
> +
> +/**
> + * eeh_reset_pe - Reset the indicated PE
> + * @pe: EEH PE
> + *
> + * This routine should be called to reset indicated device, including
> + * PE. A PE might include multiple PCI devices and sometimes PCI bridges
> + * might be involved as well.
> + */
> +int eeh_reset_pe(struct eeh_pe *pe)
> +{
> + int i, rc;
> +
> + /* Take three shots at resetting the bus */
> + for (i=0; i<3; i++) {
> + eeh_reset_pe_once(pe);
> +
> + rc = eeh_ops->wait_state(pe, PCI_BUS_RESET_WAIT_MSEC);
> + if (rc == (EEH_STATE_MMIO_ACTIVE | EEH_STATE_DMA_ACTIVE))
> + return 0;
> +
> + if (rc < 0) {
> + pr_err("%s: Unrecoverable slot failure on PHB#%d-PE#%x",
> + __func__, pe->phb->global_number, pe->addr);
> + return -1;
> + }
> + pr_err("EEH: bus reset %d failed on PHB#%d-PE#%x, rc=%d\n",
> + i+1, pe->phb->global_number, pe->addr, rc);
> + }
> +
> + return -1;
> +}
> +
> +/**
> + * eeh_save_bars - Save device bars
> + * @edev: PCI device associated EEH device
> + *
> + * Save the values of the device bars. Unlike the restore
> + * routine, this routine is *not* recursive. This is because
> + * PCI devices are added individually; but, for the restore,
> + * an entire slot is reset at a time.
> + */
> +void eeh_save_bars(struct eeh_dev *edev)
> +{
> + int i;
> + struct device_node *dn;
> +
> + if (!edev)
> + return;
> + dn = eeh_dev_to_of_node(edev);
> +
> + for (i = 0; i < 16; i++)
> + eeh_ops->read_config(dn, i * 4, 4, &edev->config_space[i]);
> +}
> +
> +/**
> + * eeh_ops_register - Register platform dependent EEH operations
> + * @ops: platform dependent EEH operations
> + *
> + * Register the platform dependent EEH operation callback
> + * functions. The platform should call this function before
> + * any other EEH operations.
> + */
> +int __init eeh_ops_register(struct eeh_ops *ops)
> +{
> + if (!ops->name) {
> + pr_warning("%s: Invalid EEH ops name for %p\n",
> + __func__, ops);
> + return -EINVAL;
> + }
> +
> + if (eeh_ops && eeh_ops != ops) {
> + pr_warning("%s: EEH ops of platform %s already existing (%s)\n",
> + __func__, eeh_ops->name, ops->name);
> + return -EEXIST;
> + }
> +
> + eeh_ops = ops;
> +
> + return 0;
> +}
> +
> +/**
> + * eeh_ops_unregister - Unreigster platform dependent EEH operations
> + * @name: name of EEH platform operations
> + *
> + * Unregister the platform dependent EEH operation callback
> + * functions.
> + */
> +int __exit eeh_ops_unregister(const char *name)
> +{
> + if (!name || !strlen(name)) {
> + pr_warning("%s: Invalid EEH ops name\n",
> + __func__);
> + return -EINVAL;
> + }
> +
> + if (eeh_ops && !strcmp(eeh_ops->name, name)) {
> + eeh_ops = NULL;
> + return 0;
> + }
> +
> + return -EEXIST;
> +}
> +
> +/**
> + * eeh_init - EEH initialization
> + *
> + * Initialize EEH by trying to enable it for all of the adapters in the system.
> + * As a side effect we can determine here if eeh is supported at all.
> + * Note that we leave EEH on so failed config cycles won't cause a machine
> + * check. If a user turns off EEH for a particular adapter they are really
> + * telling Linux to ignore errors. Some hardware (e.g. POWER5) won't
> + * grant access to a slot if EEH isn't enabled, and so we always enable
> + * EEH for all slots/all devices.
> + *
> + * The eeh-force-off option disables EEH checking globally, for all slots.
> + * Even if force-off is set, the EEH hardware is still enabled, so that
> + * newer systems can boot.
> + */
> +static int __init eeh_init(void)
> +{
> + struct pci_controller *hose, *tmp;
> + struct device_node *phb;
> + int ret;
> +
> + /* call platform initialization function */
> + if (!eeh_ops) {
> + pr_warning("%s: Platform EEH operation not found\n",
> + __func__);
> + return -EEXIST;
> + } else if ((ret = eeh_ops->init())) {
> + pr_warning("%s: Failed to call platform init function (%d)\n",
> + __func__, ret);
> + return ret;
> + }
> +
> + raw_spin_lock_init(&confirm_error_lock);
> +
> + /* Enable EEH for all adapters */
> + if (eeh_probe_mode_devtree()) {
> + list_for_each_entry_safe(hose, tmp,
> + &hose_list, list_node) {
> + phb = hose->dn;
> + traverse_pci_devices(phb, eeh_ops->of_probe, NULL);
> + }
> + }
> +
> + if (eeh_subsystem_enabled)
> + pr_info("EEH: PCI Enhanced I/O Error Handling Enabled\n");
> + else
> + pr_warning("EEH: No capable adapters found\n");
> +
> + return ret;
> +}
> +
> +core_initcall_sync(eeh_init);
> +
> +/**
> + * eeh_add_device_early - Enable EEH for the indicated device_node
> + * @dn: device node for which to set up EEH
> + *
> + * This routine must be used to perform EEH initialization for PCI
> + * devices that were added after system boot (e.g. hotplug, dlpar).
> + * This routine must be called before any i/o is performed to the
> + * adapter (inluding any config-space i/o).
> + * Whether this actually enables EEH or not for this device depends
> + * on the CEC architecture, type of the device, on earlier boot
> + * command-line arguments & etc.
> + */
> +static void eeh_add_device_early(struct device_node *dn)
> +{
> + struct pci_controller *phb;
> +
> + if (!of_node_to_eeh_dev(dn))
> + return;
> + phb = of_node_to_eeh_dev(dn)->phb;
> +
> + /* USB Bus children of PCI devices will not have BUID's */
> + if (NULL == phb || 0 == phb->buid)
> + return;
> +
> + /* FIXME: hotplug support on POWERNV */
> + eeh_ops->of_probe(dn, NULL);
> +}
> +
> +/**
> + * eeh_add_device_tree_early - Enable EEH for the indicated device
> + * @dn: device node
> + *
> + * This routine must be used to perform EEH initialization for the
> + * indicated PCI device that was added after system boot (e.g.
> + * hotplug, dlpar).
> + */
> +void eeh_add_device_tree_early(struct device_node *dn)
> +{
> + struct device_node *sib;
> +
> + for_each_child_of_node(dn, sib)
> + eeh_add_device_tree_early(sib);
> + eeh_add_device_early(dn);
> +}
> +EXPORT_SYMBOL_GPL(eeh_add_device_tree_early);
> +
> +/**
> + * eeh_add_device_late - Perform EEH initialization for the indicated pci device
> + * @dev: pci device for which to set up EEH
> + *
> + * This routine must be used to complete EEH initialization for PCI
> + * devices that were added after system boot (e.g. hotplug, dlpar).
> + */
> +static void eeh_add_device_late(struct pci_dev *dev)
> +{
> + struct device_node *dn;
> + struct eeh_dev *edev;
> +
> + if (!dev || !eeh_subsystem_enabled)
> + return;
> +
> + pr_debug("EEH: Adding device %s\n", pci_name(dev));
> +
> + dn = pci_device_to_OF_node(dev);
> + edev = of_node_to_eeh_dev(dn);
> + if (edev->pdev == dev) {
> + pr_debug("EEH: Already referenced !\n");
> + return;
> + }
> + WARN_ON(edev->pdev);
> +
> + pci_dev_get(dev);
> + edev->pdev = dev;
> + dev->dev.archdata.edev = edev;
> +
> + eeh_addr_cache_insert_dev(dev);
> +}
> +
> +/**
> + * eeh_add_device_tree_late - Perform EEH initialization for the indicated PCI bus
> + * @bus: PCI bus
> + *
> + * This routine must be used to perform EEH initialization for PCI
> + * devices which are attached to the indicated PCI bus. The PCI bus
> + * is added after system boot through hotplug or dlpar.
> + */
> +void eeh_add_device_tree_late(struct pci_bus *bus)
> +{
> + struct pci_dev *dev;
> +
> + list_for_each_entry(dev, &bus->devices, bus_list) {
> + eeh_add_device_late(dev);
> + if (dev->hdr_type == PCI_HEADER_TYPE_BRIDGE) {
> + struct pci_bus *subbus = dev->subordinate;
> + if (subbus)
> + eeh_add_device_tree_late(subbus);
> + }
> + }
> +}
> +EXPORT_SYMBOL_GPL(eeh_add_device_tree_late);
> +
> +/**
> + * eeh_add_sysfs_files - Add EEH sysfs files for the indicated PCI bus
> + * @bus: PCI bus
> + *
> + * This routine must be used to add EEH sysfs files for PCI
> + * devices which are attached to the indicated PCI bus. The PCI bus
> + * is added after system boot through hotplug or dlpar.
> + */
> +void eeh_add_sysfs_files(struct pci_bus *bus)
> +{
> + struct pci_dev *dev;
> +
> + list_for_each_entry(dev, &bus->devices, bus_list) {
> + eeh_sysfs_add_device(dev);
> + if (dev->hdr_type == PCI_HEADER_TYPE_BRIDGE) {
> + struct pci_bus *subbus = dev->subordinate;
> + if (subbus)
> + eeh_add_sysfs_files(subbus);
> + }
> + }
> +}
> +EXPORT_SYMBOL_GPL(eeh_add_sysfs_files);
> +
> +/**
> + * eeh_remove_device - Undo EEH setup for the indicated pci device
> + * @dev: pci device to be removed
> + * @purge_pe: remove the PE or not
> + *
> + * This routine should be called when a device is removed from
> + * a running system (e.g. by hotplug or dlpar). It unregisters
> + * the PCI device from the EEH subsystem. I/O errors affecting
> + * this device will no longer be detected after this call; thus,
> + * i/o errors affecting this slot may leave this device unusable.
> + */
> +static void eeh_remove_device(struct pci_dev *dev, int purge_pe)
> +{
> + struct eeh_dev *edev;
> +
> + if (!dev || !eeh_subsystem_enabled)
> + return;
> + edev = pci_dev_to_eeh_dev(dev);
> +
> + /* Unregister the device with the EEH/PCI address search system */
> + pr_debug("EEH: Removing device %s\n", pci_name(dev));
> +
> + if (!edev || !edev->pdev) {
> + pr_debug("EEH: Not referenced !\n");
> + return;
> + }
> + edev->pdev = NULL;
> + dev->dev.archdata.edev = NULL;
> + pci_dev_put(dev);
> +
> + eeh_rmv_from_parent_pe(edev, purge_pe);
> + eeh_addr_cache_rmv_dev(dev);
> + eeh_sysfs_remove_device(dev);
> +}
> +
> +/**
> + * eeh_remove_bus_device - Undo EEH setup for the indicated PCI device
> + * @dev: PCI device
> + * @purge_pe: remove the corresponding PE or not
> + *
> + * This routine must be called when a device is removed from the
> + * running system through hotplug or dlpar. The corresponding
> + * PCI address cache will be removed.
> + */
> +void eeh_remove_bus_device(struct pci_dev *dev, int purge_pe)
> +{
> + struct pci_bus *bus = dev->subordinate;
> + struct pci_dev *child, *tmp;
> +
> + eeh_remove_device(dev, purge_pe);
> +
> + if (bus && dev->hdr_type == PCI_HEADER_TYPE_BRIDGE) {
> + list_for_each_entry_safe(child, tmp, &bus->devices, bus_list)
> + eeh_remove_bus_device(child, purge_pe);
> + }
> +}
> +EXPORT_SYMBOL_GPL(eeh_remove_bus_device);
> +
> +static int proc_eeh_show(struct seq_file *m, void *v)
> +{
> + if (0 == eeh_subsystem_enabled) {
> + seq_printf(m, "EEH Subsystem is globally disabled\n");
> + seq_printf(m, "eeh_total_mmio_ffs=%llu\n", eeh_stats.total_mmio_ffs);
> + } else {
> + seq_printf(m, "EEH Subsystem is enabled\n");
> + seq_printf(m,
> + "no device=%llu\n"
> + "no device node=%llu\n"
> + "no config address=%llu\n"
> + "check not wanted=%llu\n"
> + "eeh_total_mmio_ffs=%llu\n"
> + "eeh_false_positives=%llu\n"
> + "eeh_slot_resets=%llu\n",
> + eeh_stats.no_device,
> + eeh_stats.no_dn,
> + eeh_stats.no_cfg_addr,
> + eeh_stats.ignored_check,
> + eeh_stats.total_mmio_ffs,
> + eeh_stats.false_positives,
> + eeh_stats.slot_resets);
> + }
> +
> + return 0;
> +}
> +
> +static int proc_eeh_open(struct inode *inode, struct file *file)
> +{
> + return single_open(file, proc_eeh_show, NULL);
> +}
> +
> +static const struct file_operations proc_eeh_operations = {
> + .open = proc_eeh_open,
> + .read = seq_read,
> + .llseek = seq_lseek,
> + .release = single_release,
> +};
> +
> +static int __init eeh_init_proc(void)
> +{
> + if (machine_is(pseries))
> + proc_create("powerpc/eeh", 0, NULL, &proc_eeh_operations);
> + return 0;
> +}
> +__initcall(eeh_init_proc);
> diff --git a/arch/powerpc/kernel/eeh_cache.c b/arch/powerpc/kernel/eeh_cache.c
> new file mode 100644
> index 0000000..5a4c879
> --- /dev/null
> +++ b/arch/powerpc/kernel/eeh_cache.c
> @@ -0,0 +1,319 @@
> +/*
> + * PCI address cache; allows the lookup of PCI devices based on I/O address
> + *
> + * Copyright IBM Corporation 2004
> + * Copyright Linas Vepstas <linas@austin.ibm.com> 2004
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> + * the Free Software Foundation; either version 2 of the License, or
> + * (at your option) any later version.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> + * GNU General Public License for more details.
> + *
> + * You should have received a copy of the GNU General Public License
> + * along with this program; if not, write to the Free Software
> + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
> + */
> +
> +#include <linux/list.h>
> +#include <linux/pci.h>
> +#include <linux/rbtree.h>
> +#include <linux/slab.h>
> +#include <linux/spinlock.h>
> +#include <linux/atomic.h>
> +#include <asm/pci-bridge.h>
> +#include <asm/ppc-pci.h>
> +
> +
> +/**
> + * The pci address cache subsystem. This subsystem places
> + * PCI device address resources into a red-black tree, sorted
> + * according to the address range, so that given only an i/o
> + * address, the corresponding PCI device can be **quickly**
> + * found. It is safe to perform an address lookup in an interrupt
> + * context; this ability is an important feature.
> + *
> + * Currently, the only customer of this code is the EEH subsystem;
> + * thus, this code has been somewhat tailored to suit EEH better.
> + * In particular, the cache does *not* hold the addresses of devices
> + * for which EEH is not enabled.
> + *
> + * (Implementation Note: The RB tree seems to be better/faster
> + * than any hash algo I could think of for this problem, even
> + * with the penalty of slow pointer chases for d-cache misses).
> + */
> +struct pci_io_addr_range {
> + struct rb_node rb_node;
> + unsigned long addr_lo;
> + unsigned long addr_hi;
> + struct eeh_dev *edev;
> + struct pci_dev *pcidev;
> + unsigned int flags;
> +};
> +
> +static struct pci_io_addr_cache {
> + struct rb_root rb_root;
> + spinlock_t piar_lock;
> +} pci_io_addr_cache_root;
> +
> +static inline struct eeh_dev *__eeh_addr_cache_get_device(unsigned long addr)
> +{
> + struct rb_node *n = pci_io_addr_cache_root.rb_root.rb_node;
> +
> + while (n) {
> + struct pci_io_addr_range *piar;
> + piar = rb_entry(n, struct pci_io_addr_range, rb_node);
> +
> + if (addr < piar->addr_lo) {
> + n = n->rb_left;
> + } else {
> + if (addr > piar->addr_hi) {
> + n = n->rb_right;
> + } else {
> + pci_dev_get(piar->pcidev);
> + return piar->edev;
> + }
> + }
> + }
> +
> + return NULL;
> +}
> +
> +/**
> + * eeh_addr_cache_get_dev - Get device, given only address
> + * @addr: mmio (PIO) phys address or i/o port number
> + *
> + * Given an mmio phys address, or a port number, find a pci device
> + * that implements this address. Be sure to pci_dev_put the device
> + * when finished. I/O port numbers are assumed to be offset
> + * from zero (that is, they do *not* have pci_io_addr added in).
> + * It is safe to call this function within an interrupt.
> + */
> +struct eeh_dev *eeh_addr_cache_get_dev(unsigned long addr)
> +{
> + struct eeh_dev *edev;
> + unsigned long flags;
> +
> + spin_lock_irqsave(&pci_io_addr_cache_root.piar_lock, flags);
> + edev = __eeh_addr_cache_get_device(addr);
> + spin_unlock_irqrestore(&pci_io_addr_cache_root.piar_lock, flags);
> + return edev;
> +}
> +
> +#ifdef DEBUG
> +/*
> + * Handy-dandy debug print routine, does nothing more
> + * than print out the contents of our addr cache.
> + */
> +static void eeh_addr_cache_print(struct pci_io_addr_cache *cache)
> +{
> + struct rb_node *n;
> + int cnt = 0;
> +
> + n = rb_first(&cache->rb_root);
> + while (n) {
> + struct pci_io_addr_range *piar;
> + piar = rb_entry(n, struct pci_io_addr_range, rb_node);
> + pr_debug("PCI: %s addr range %d [%lx-%lx]: %s\n",
> + (piar->flags & IORESOURCE_IO) ? "i/o" : "mem", cnt,
> + piar->addr_lo, piar->addr_hi, pci_name(piar->pcidev));
> + cnt++;
> + n = rb_next(n);
> + }
> +}
> +#endif
> +
> +/* Insert address range into the rb tree. */
> +static struct pci_io_addr_range *
> +eeh_addr_cache_insert(struct pci_dev *dev, unsigned long alo,
> + unsigned long ahi, unsigned int flags)
> +{
> + struct rb_node **p = &pci_io_addr_cache_root.rb_root.rb_node;
> + struct rb_node *parent = NULL;
> + struct pci_io_addr_range *piar;
> +
> + /* Walk tree, find a place to insert into tree */
> + while (*p) {
> + parent = *p;
> + piar = rb_entry(parent, struct pci_io_addr_range, rb_node);
> + if (ahi < piar->addr_lo) {
> + p = &parent->rb_left;
> + } else if (alo > piar->addr_hi) {
> + p = &parent->rb_right;
> + } else {
> + if (dev != piar->pcidev ||
> + alo != piar->addr_lo || ahi != piar->addr_hi) {
> + pr_warning("PIAR: overlapping address range\n");
> + }
> + return piar;
> + }
> + }
> + piar = kzalloc(sizeof(struct pci_io_addr_range), GFP_ATOMIC);
> + if (!piar)
> + return NULL;
> +
> + pci_dev_get(dev);
> + piar->addr_lo = alo;
> + piar->addr_hi = ahi;
> + piar->edev = pci_dev_to_eeh_dev(dev);
> + piar->pcidev = dev;
> + piar->flags = flags;
> +
> +#ifdef DEBUG
> + pr_debug("PIAR: insert range=[%lx:%lx] dev=%s\n",
> + alo, ahi, pci_name(dev));
> +#endif
> +
> + rb_link_node(&piar->rb_node, parent, p);
> + rb_insert_color(&piar->rb_node, &pci_io_addr_cache_root.rb_root);
> +
> + return piar;
> +}
> +
> +static void __eeh_addr_cache_insert_dev(struct pci_dev *dev)
> +{
> + struct device_node *dn;
> + struct eeh_dev *edev;
> + int i;
> +
> + dn = pci_device_to_OF_node(dev);
> + if (!dn) {
> + pr_warning("PCI: no pci dn found for dev=%s\n", pci_name(dev));
> + return;
> + }
> +
> + edev = of_node_to_eeh_dev(dn);
> + if (!edev) {
> + pr_warning("PCI: no EEH dev found for dn=%s\n",
> + dn->full_name);
> + return;
> + }
> +
> + /* Skip any devices for which EEH is not enabled. */
> + if (!edev->pe) {
> +#ifdef DEBUG
> + pr_info("PCI: skip building address cache for=%s - %s\n",
> + pci_name(dev), dn->full_name);
> +#endif
> + return;
> + }
> +
> + /* Walk resources on this device, poke them into the tree */
> + for (i = 0; i < DEVICE_COUNT_RESOURCE; i++) {
> + unsigned long start = pci_resource_start(dev,i);
> + unsigned long end = pci_resource_end(dev,i);
> + unsigned int flags = pci_resource_flags(dev,i);
> +
> + /* We are interested only bus addresses, not dma or other stuff */
> + if (0 == (flags & (IORESOURCE_IO | IORESOURCE_MEM)))
> + continue;
> + if (start == 0 || ~start == 0 || end == 0 || ~end == 0)
> + continue;
> + eeh_addr_cache_insert(dev, start, end, flags);
> + }
> +}
> +
> +/**
> + * eeh_addr_cache_insert_dev - Add a device to the address cache
> + * @dev: PCI device whose I/O addresses we are interested in.
> + *
> + * In order to support the fast lookup of devices based on addresses,
> + * we maintain a cache of devices that can be quickly searched.
> + * This routine adds a device to that cache.
> + */
> +void eeh_addr_cache_insert_dev(struct pci_dev *dev)
> +{
> + unsigned long flags;
> +
> + /* Ignore PCI bridges */
> + if ((dev->class >> 16) == PCI_BASE_CLASS_BRIDGE)
> + return;
> +
> + spin_lock_irqsave(&pci_io_addr_cache_root.piar_lock, flags);
> + __eeh_addr_cache_insert_dev(dev);
> + spin_unlock_irqrestore(&pci_io_addr_cache_root.piar_lock, flags);
> +}
> +
> +static inline void __eeh_addr_cache_rmv_dev(struct pci_dev *dev)
> +{
> + struct rb_node *n;
> +
> +restart:
> + n = rb_first(&pci_io_addr_cache_root.rb_root);
> + while (n) {
> + struct pci_io_addr_range *piar;
> + piar = rb_entry(n, struct pci_io_addr_range, rb_node);
> +
> + if (piar->pcidev == dev) {
> + rb_erase(n, &pci_io_addr_cache_root.rb_root);
> + pci_dev_put(piar->pcidev);
> + kfree(piar);
> + goto restart;
> + }
> + n = rb_next(n);
> + }
> +}
> +
> +/**
> + * eeh_addr_cache_rmv_dev - remove pci device from addr cache
> + * @dev: device to remove
> + *
> + * Remove a device from the addr-cache tree.
> + * This is potentially expensive, since it will walk
> + * the tree multiple times (once per resource).
> + * But so what; device removal doesn't need to be that fast.
> + */
> +void eeh_addr_cache_rmv_dev(struct pci_dev *dev)
> +{
> + unsigned long flags;
> +
> + spin_lock_irqsave(&pci_io_addr_cache_root.piar_lock, flags);
> + __eeh_addr_cache_rmv_dev(dev);
> + spin_unlock_irqrestore(&pci_io_addr_cache_root.piar_lock, flags);
> +}
> +
> +/**
> + * eeh_addr_cache_build - Build a cache of I/O addresses
> + *
> + * Build a cache of pci i/o addresses. This cache will be used to
> + * find the pci device that corresponds to a given address.
> + * This routine scans all pci busses to build the cache.
> + * Must be run late in boot process, after the pci controllers
> + * have been scanned for devices (after all device resources are known).
> + */
> +void __init eeh_addr_cache_build(void)
> +{
> + struct device_node *dn;
> + struct eeh_dev *edev;
> + struct pci_dev *dev = NULL;
> +
> + spin_lock_init(&pci_io_addr_cache_root.piar_lock);
> +
> + for_each_pci_dev(dev) {
> + eeh_addr_cache_insert_dev(dev);
> +
> + dn = pci_device_to_OF_node(dev);
> + if (!dn)
> + continue;
> +
> + edev = of_node_to_eeh_dev(dn);
> + if (!edev)
> + continue;
> +
> + pci_dev_get(dev); /* matching put is in eeh_remove_device() */
> + dev->dev.archdata.edev = edev;
> + edev->pdev = dev;
> +
> + eeh_sysfs_add_device(dev);
> + }
> +
> +#ifdef DEBUG
> + /* Verify tree built up above, echo back the list of addrs. */
> + eeh_addr_cache_print(&pci_io_addr_cache_root);
> +#endif
> +}
> +
> diff --git a/arch/powerpc/kernel/eeh_dev.c b/arch/powerpc/kernel/eeh_dev.c
> new file mode 100644
> index 0000000..1efa28f
> --- /dev/null
> +++ b/arch/powerpc/kernel/eeh_dev.c
> @@ -0,0 +1,112 @@
> +/*
> + * The file intends to implement dynamic creation of EEH device, which will
> + * be bound with OF node and PCI device simutaneously. The EEH devices would
> + * be foundamental information for EEH core components to work proerly. Besides,
> + * We have to support multiple situations where dynamic creation of EEH device
> + * is required:
> + *
> + * 1) Before PCI emunation starts, we need create EEH devices according to the
> + * PCI sensitive OF nodes.
> + * 2) When PCI emunation is done, we need do the binding between PCI device and
> + * the associated EEH device.
> + * 3) DR (Dynamic Reconfiguration) would create PCI sensitive OF node. EEH device
> + * will be created while PCI sensitive OF node is detected from DR.
> + * 4) PCI hotplug needs redoing the binding between PCI device and EEH device. If
> + * PHB is newly inserted, we also need create EEH devices accordingly.
> + *
> + * Copyright Benjamin Herrenschmidt & Gavin Shan, IBM Corporation 2012.
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> + * the Free Software Foundation; either version 2 of the License, or
> + * (at your option) any later version.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> + * GNU General Public License for more details.
> + *
> + * You should have received a copy of the GNU General Public License
> + * along with this program; if not, write to the Free Software
> + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
> + */
> +
> +#include <linux/export.h>
> +#include <linux/gfp.h>
> +#include <linux/init.h>
> +#include <linux/kernel.h>
> +#include <linux/pci.h>
> +#include <linux/string.h>
> +
> +#include <asm/pci-bridge.h>
> +#include <asm/ppc-pci.h>
> +
> +/**
> + * eeh_dev_init - Create EEH device according to OF node
> + * @dn: device node
> + * @data: PHB
> + *
> + * It will create EEH device according to the given OF node. The function
> + * might be called by PCI emunation, DR, PHB hotplug.
> + */
> +void *eeh_dev_init(struct device_node *dn, void *data)
> +{
> + struct pci_controller *phb = data;
> + struct eeh_dev *edev;
> +
> + /* Allocate EEH device */
> + edev = kzalloc(sizeof(*edev), GFP_KERNEL);
> + if (!edev) {
> + pr_warning("%s: out of memory\n", __func__);
> + return NULL;
> + }
> +
> + /* Associate EEH device with OF node */
> + PCI_DN(dn)->edev = edev;
> + edev->dn = dn;
> + edev->phb = phb;
> + INIT_LIST_HEAD(&edev->list);
> +
> + return NULL;
> +}
> +
> +/**
> + * eeh_dev_phb_init_dynamic - Create EEH devices for devices included in PHB
> + * @phb: PHB
> + *
> + * Scan the PHB OF node and its child association, then create the
> + * EEH devices accordingly
> + */
> +void eeh_dev_phb_init_dynamic(struct pci_controller *phb)
> +{
> + struct device_node *dn = phb->dn;
> +
> + /* EEH PE for PHB */
> + eeh_phb_pe_create(phb);
> +
> + /* EEH device for PHB */
> + eeh_dev_init(dn, phb);
> +
> + /* EEH devices for children OF nodes */
> + traverse_pci_devices(dn, eeh_dev_init, phb);
> +}
> +
> +/**
> + * eeh_dev_phb_init - Create EEH devices for devices included in existing PHBs
> + *
> + * Scan all the existing PHBs and create EEH devices for their OF
> + * nodes and their children OF nodes
> + */
> +static int __init eeh_dev_phb_init(void)
> +{
> + struct pci_controller *phb, *tmp;
> +
> + list_for_each_entry_safe(phb, tmp, &hose_list, list_node)
> + eeh_dev_phb_init_dynamic(phb);
> +
> + pr_info("EEH: devices created\n");
> +
> + return 0;
> +}
> +
> +core_initcall(eeh_dev_phb_init);
> diff --git a/arch/powerpc/kernel/eeh_driver.c b/arch/powerpc/kernel/eeh_driver.c
> new file mode 100644
> index 0000000..a3fefb6
> --- /dev/null
> +++ b/arch/powerpc/kernel/eeh_driver.c
> @@ -0,0 +1,552 @@
> +/*
> + * PCI Error Recovery Driver for RPA-compliant PPC64 platform.
> + * Copyright IBM Corp. 2004 2005
> + * Copyright Linas Vepstas <linas@linas.org> 2004, 2005
> + *
> + * All rights reserved.
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> + * the Free Software Foundation; either version 2 of the License, or (at
> + * your option) any later version.
> + *
> + * This program is distributed in the hope that it will be useful, but
> + * WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or
> + * NON INFRINGEMENT. See the GNU General Public License for more
> + * details.
> + *
> + * You should have received a copy of the GNU General Public License
> + * along with this program; if not, write to the Free Software
> + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
> + *
> + * Send comments and feedback to Linas Vepstas <linas@austin.ibm.com>
> + */
> +#include <linux/delay.h>
> +#include <linux/interrupt.h>
> +#include <linux/irq.h>
> +#include <linux/module.h>
> +#include <linux/pci.h>
> +#include <asm/eeh.h>
> +#include <asm/eeh_event.h>
> +#include <asm/ppc-pci.h>
> +#include <asm/pci-bridge.h>
> +#include <asm/prom.h>
> +#include <asm/rtas.h>
> +
> +/**
> + * eeh_pcid_name - Retrieve name of PCI device driver
> + * @pdev: PCI device
> + *
> + * This routine is used to retrieve the name of PCI device driver
> + * if that's valid.
> + */
> +static inline const char *eeh_pcid_name(struct pci_dev *pdev)
> +{
> + if (pdev && pdev->dev.driver)
> + return pdev->dev.driver->name;
> + return "";
> +}
> +
> +/**
> + * eeh_pcid_get - Get the PCI device driver
> + * @pdev: PCI device
> + *
> + * The function is used to retrieve the PCI device driver for
> + * the indicated PCI device. Besides, we will increase the reference
> + * of the PCI device driver to prevent that being unloaded on
> + * the fly. Otherwise, kernel crash would be seen.
> + */
> +static inline struct pci_driver *eeh_pcid_get(struct pci_dev *pdev)
> +{
> + if (!pdev || !pdev->driver)
> + return NULL;
> +
> + if (!try_module_get(pdev->driver->driver.owner))
> + return NULL;
> +
> + return pdev->driver;
> +}
> +
> +/**
> + * eeh_pcid_put - Dereference on the PCI device driver
> + * @pdev: PCI device
> + *
> + * The function is called to do dereference on the PCI device
> + * driver of the indicated PCI device.
> + */
> +static inline void eeh_pcid_put(struct pci_dev *pdev)
> +{
> + if (!pdev || !pdev->driver)
> + return;
> +
> + module_put(pdev->driver->driver.owner);
> +}
> +
> +#if 0
> +static void print_device_node_tree(struct pci_dn *pdn, int dent)
> +{
> + int i;
> + struct device_node *pc;
> +
> + if (!pdn)
> + return;
> + for (i = 0; i < dent; i++)
> + printk(" ");
> + printk("dn=%s mode=%x \tcfg_addr=%x pe_addr=%x \tfull=%s\n",
> + pdn->node->name, pdn->eeh_mode, pdn->eeh_config_addr,
> + pdn->eeh_pe_config_addr, pdn->node->full_name);
> + dent += 3;
> + pc = pdn->node->child;
> + while (pc) {
> + print_device_node_tree(PCI_DN(pc), dent);
> + pc = pc->sibling;
> + }
> +}
> +#endif
> +
> +/**
> + * eeh_disable_irq - Disable interrupt for the recovering device
> + * @dev: PCI device
> + *
> + * This routine must be called when reporting temporary or permanent
> + * error to the particular PCI device to disable interrupt of that
> + * device. If the device has enabled MSI or MSI-X interrupt, we needn't
> + * do real work because EEH should freeze DMA transfers for those PCI
> + * devices encountering EEH errors, which includes MSI or MSI-X.
> + */
> +static void eeh_disable_irq(struct pci_dev *dev)
> +{
> + struct eeh_dev *edev = pci_dev_to_eeh_dev(dev);
> +
> + /* Don't disable MSI and MSI-X interrupts. They are
> + * effectively disabled by the DMA Stopped state
> + * when an EEH error occurs.
> + */
> + if (dev->msi_enabled || dev->msix_enabled)
> + return;
> +
> + if (!irq_has_action(dev->irq))
> + return;
> +
> + edev->mode |= EEH_DEV_IRQ_DISABLED;
> + disable_irq_nosync(dev->irq);
> +}
> +
> +/**
> + * eeh_enable_irq - Enable interrupt for the recovering device
> + * @dev: PCI device
> + *
> + * This routine must be called to enable interrupt while failed
> + * device could be resumed.
> + */
> +static void eeh_enable_irq(struct pci_dev *dev)
> +{
> + struct eeh_dev *edev = pci_dev_to_eeh_dev(dev);
> +
> + if ((edev->mode) & EEH_DEV_IRQ_DISABLED) {
> + edev->mode &= ~EEH_DEV_IRQ_DISABLED;
> + enable_irq(dev->irq);
> + }
> +}
> +
> +/**
> + * eeh_report_error - Report pci error to each device driver
> + * @data: eeh device
> + * @userdata: return value
> + *
> + * Report an EEH error to each device driver, collect up and
> + * merge the device driver responses. Cumulative response
> + * passed back in "userdata".
> + */
> +static void *eeh_report_error(void *data, void *userdata)
> +{
> + struct eeh_dev *edev = (struct eeh_dev *)data;
> + struct pci_dev *dev = eeh_dev_to_pci_dev(edev);
> + enum pci_ers_result rc, *res = userdata;
> + struct pci_driver *driver;
> +
> + /* We might not have the associated PCI device,
> + * then we should continue for next one.
> + */
> + if (!dev) return NULL;
> + dev->error_state = pci_channel_io_frozen;
> +
> + driver = eeh_pcid_get(dev);
> + if (!driver) return NULL;
> +
> + eeh_disable_irq(dev);
> +
> + if (!driver->err_handler ||
> + !driver->err_handler->error_detected) {
> + eeh_pcid_put(dev);
> + return NULL;
> + }
> +
> + rc = driver->err_handler->error_detected(dev, pci_channel_io_frozen);
> +
> + /* A driver that needs a reset trumps all others */
> + if (rc == PCI_ERS_RESULT_NEED_RESET) *res = rc;
> + if (*res == PCI_ERS_RESULT_NONE) *res = rc;
> +
> + eeh_pcid_put(dev);
> + return NULL;
> +}
> +
> +/**
> + * eeh_report_mmio_enabled - Tell drivers that MMIO has been enabled
> + * @data: eeh device
> + * @userdata: return value
> + *
> + * Tells each device driver that IO ports, MMIO and config space I/O
> + * are now enabled. Collects up and merges the device driver responses.
> + * Cumulative response passed back in "userdata".
> + */
> +static void *eeh_report_mmio_enabled(void *data, void *userdata)
> +{
> + struct eeh_dev *edev = (struct eeh_dev *)data;
> + struct pci_dev *dev = eeh_dev_to_pci_dev(edev);
> + enum pci_ers_result rc, *res = userdata;
> + struct pci_driver *driver;
> +
> + driver = eeh_pcid_get(dev);
> + if (!driver) return NULL;
> +
> + if (!driver->err_handler ||
> + !driver->err_handler->mmio_enabled) {
> + eeh_pcid_put(dev);
> + return NULL;
> + }
> +
> + rc = driver->err_handler->mmio_enabled(dev);
> +
> + /* A driver that needs a reset trumps all others */
> + if (rc == PCI_ERS_RESULT_NEED_RESET) *res = rc;
> + if (*res == PCI_ERS_RESULT_NONE) *res = rc;
> +
> + eeh_pcid_put(dev);
> + return NULL;
> +}
> +
> +/**
> + * eeh_report_reset - Tell device that slot has been reset
> + * @data: eeh device
> + * @userdata: return value
> + *
> + * This routine must be called while EEH tries to reset particular
> + * PCI device so that the associated PCI device driver could take
> + * some actions, usually to save data the driver needs so that the
> + * driver can work again while the device is recovered.
> + */
> +static void *eeh_report_reset(void *data, void *userdata)
> +{
> + struct eeh_dev *edev = (struct eeh_dev *)data;
> + struct pci_dev *dev = eeh_dev_to_pci_dev(edev);
> + enum pci_ers_result rc, *res = userdata;
> + struct pci_driver *driver;
> +
> + if (!dev) return NULL;
> + dev->error_state = pci_channel_io_normal;
> +
> + driver = eeh_pcid_get(dev);
> + if (!driver) return NULL;
> +
> + eeh_enable_irq(dev);
> +
> + if (!driver->err_handler ||
> + !driver->err_handler->slot_reset) {
> + eeh_pcid_put(dev);
> + return NULL;
> + }
> +
> + rc = driver->err_handler->slot_reset(dev);
> + if ((*res == PCI_ERS_RESULT_NONE) ||
> + (*res == PCI_ERS_RESULT_RECOVERED)) *res = rc;
> + if (*res == PCI_ERS_RESULT_DISCONNECT &&
> + rc == PCI_ERS_RESULT_NEED_RESET) *res = rc;
> +
> + eeh_pcid_put(dev);
> + return NULL;
> +}
> +
> +/**
> + * eeh_report_resume - Tell device to resume normal operations
> + * @data: eeh device
> + * @userdata: return value
> + *
> + * This routine must be called to notify the device driver that it
> + * could resume so that the device driver can do some initialization
> + * to make the recovered device work again.
> + */
> +static void *eeh_report_resume(void *data, void *userdata)
> +{
> + struct eeh_dev *edev = (struct eeh_dev *)data;
> + struct pci_dev *dev = eeh_dev_to_pci_dev(edev);
> + struct pci_driver *driver;
> +
> + if (!dev) return NULL;
> + dev->error_state = pci_channel_io_normal;
> +
> + driver = eeh_pcid_get(dev);
> + if (!driver) return NULL;
> +
> + eeh_enable_irq(dev);
> +
> + if (!driver->err_handler ||
> + !driver->err_handler->resume) {
> + eeh_pcid_put(dev);
> + return NULL;
> + }
> +
> + driver->err_handler->resume(dev);
> +
> + eeh_pcid_put(dev);
> + return NULL;
> +}
> +
> +/**
> + * eeh_report_failure - Tell device driver that device is dead.
> + * @data: eeh device
> + * @userdata: return value
> + *
> + * This informs the device driver that the device is permanently
> + * dead, and that no further recovery attempts will be made on it.
> + */
> +static void *eeh_report_failure(void *data, void *userdata)
> +{
> + struct eeh_dev *edev = (struct eeh_dev *)data;
> + struct pci_dev *dev = eeh_dev_to_pci_dev(edev);
> + struct pci_driver *driver;
> +
> + if (!dev) return NULL;
> + dev->error_state = pci_channel_io_perm_failure;
> +
> + driver = eeh_pcid_get(dev);
> + if (!driver) return NULL;
> +
> + eeh_disable_irq(dev);
> +
> + if (!driver->err_handler ||
> + !driver->err_handler->error_detected) {
> + eeh_pcid_put(dev);
> + return NULL;
> + }
> +
> + driver->err_handler->error_detected(dev, pci_channel_io_perm_failure);
> +
> + eeh_pcid_put(dev);
> + return NULL;
> +}
> +
> +/**
> + * eeh_reset_device - Perform actual reset of a pci slot
> + * @pe: EEH PE
> + * @bus: PCI bus corresponding to the isolcated slot
> + *
> + * This routine must be called to do reset on the indicated PE.
> + * During the reset, udev might be invoked because those affected
> + * PCI devices will be removed and then added.
> + */
> +static int eeh_reset_device(struct eeh_pe *pe, struct pci_bus *bus)
> +{
> + int cnt, rc;
> +
> + /* pcibios will clear the counter; save the value */
> + cnt = pe->freeze_count;
> +
> + /*
> + * We don't remove the corresponding PE instances because
> + * we need the information afterwords. The attached EEH
> + * devices are expected to be attached soon when calling
> + * into pcibios_add_pci_devices().
> + */
> + if (bus)
> + __pcibios_remove_pci_devices(bus, 0);
> +
> + /* Reset the pci controller. (Asserts RST#; resets config space).
> + * Reconfigure bridges and devices. Don't try to bring the system
> + * up if the reset failed for some reason.
> + */
> + rc = eeh_reset_pe(pe);
> + if (rc)
> + return rc;
> +
> + /* Restore PE */
> + eeh_ops->configure_bridge(pe);
> + eeh_pe_restore_bars(pe);
> +
> + /* Give the system 5 seconds to finish running the user-space
> + * hotplug shutdown scripts, e.g. ifdown for ethernet. Yes,
> + * this is a hack, but if we don't do this, and try to bring
> + * the device up before the scripts have taken it down,
> + * potentially weird things happen.
> + */
> + if (bus) {
> + ssleep(5);
> + pcibios_add_pci_devices(bus);
> + }
> + pe->freeze_count = cnt;
> +
> + return 0;
> +}
> +
> +/* The longest amount of time to wait for a pci device
> + * to come back on line, in seconds.
> + */
> +#define MAX_WAIT_FOR_RECOVERY 150
> +
> +/**
> + * eeh_handle_event - Reset a PCI device after hard lockup.
> + * @pe: EEH PE
> + *
> + * While PHB detects address or data parity errors on particular PCI
> + * slot, the associated PE will be frozen. Besides, DMA's occurring
> + * to wild addresses (which usually happen due to bugs in device
> + * drivers or in PCI adapter firmware) can cause EEH error. #SERR,
> + * #PERR or other misc PCI-related errors also can trigger EEH errors.
> + *
> + * Recovery process consists of unplugging the device driver (which
> + * generated hotplug events to userspace), then issuing a PCI #RST to
> + * the device, then reconfiguring the PCI config space for all bridges
> + * & devices under this slot, and then finally restarting the device
> + * drivers (which cause a second set of hotplug events to go out to
> + * userspace).
> + */
> +void eeh_handle_event(struct eeh_pe *pe)
> +{
> + struct pci_bus *frozen_bus;
> + int rc = 0;
> + enum pci_ers_result result = PCI_ERS_RESULT_NONE;
> +
> + frozen_bus = eeh_pe_bus_get(pe);
> + if (!frozen_bus) {
> + pr_err("%s: Cannot find PCI bus for PHB#%d-PE#%x\n",
> + __func__, pe->phb->global_number, pe->addr);
> + return;
> + }
> +
> + pe->freeze_count++;
> + if (pe->freeze_count > EEH_MAX_ALLOWED_FREEZES)
> + goto excess_failures;
> + pr_warning("EEH: This PCI device has failed %d times in the last hour\n",
> + pe->freeze_count);
> +
> + /* Walk the various device drivers attached to this slot through
> + * a reset sequence, giving each an opportunity to do what it needs
> + * to accomplish the reset. Each child gets a report of the
> + * status ... if any child can't handle the reset, then the entire
> + * slot is dlpar removed and added.
> + */
> + eeh_pe_dev_traverse(pe, eeh_report_error, &result);
> +
> + /* Get the current PCI slot state. This can take a long time,
> + * sometimes over 3 seconds for certain systems.
> + */
> + rc = eeh_ops->wait_state(pe, MAX_WAIT_FOR_RECOVERY*1000);
> + if (rc < 0 || rc == EEH_STATE_NOT_SUPPORT) {
> + printk(KERN_WARNING "EEH: Permanent failure\n");
> + goto hard_fail;
> + }
> +
> + /* Since rtas may enable MMIO when posting the error log,
> + * don't post the error log until after all dev drivers
> + * have been informed.
> + */
> + eeh_slot_error_detail(pe, EEH_LOG_TEMP);
> +
> + /* If all device drivers were EEH-unaware, then shut
> + * down all of the device drivers, and hope they
> + * go down willingly, without panicing the system.
> + */
> + if (result == PCI_ERS_RESULT_NONE) {
> + rc = eeh_reset_device(pe, frozen_bus);
> + if (rc) {
> + printk(KERN_WARNING "EEH: Unable to reset, rc=%d\n", rc);
> + goto hard_fail;
> + }
> + }
> +
> + /* If all devices reported they can proceed, then re-enable MMIO */
> + if (result == PCI_ERS_RESULT_CAN_RECOVER) {
> + rc = eeh_pci_enable(pe, EEH_OPT_THAW_MMIO);
> +
> + if (rc < 0)
> + goto hard_fail;
> + if (rc) {
> + result = PCI_ERS_RESULT_NEED_RESET;
> + } else {
> + result = PCI_ERS_RESULT_NONE;
> + eeh_pe_dev_traverse(pe, eeh_report_mmio_enabled, &result);
> + }
> + }
> +
> + /* If all devices reported they can proceed, then re-enable DMA */
> + if (result == PCI_ERS_RESULT_CAN_RECOVER) {
> + rc = eeh_pci_enable(pe, EEH_OPT_THAW_DMA);
> +
> + if (rc < 0)
> + goto hard_fail;
> + if (rc)
> + result = PCI_ERS_RESULT_NEED_RESET;
> + else
> + result = PCI_ERS_RESULT_RECOVERED;
> + }
> +
> + /* If any device has a hard failure, then shut off everything. */
> + if (result == PCI_ERS_RESULT_DISCONNECT) {
> + printk(KERN_WARNING "EEH: Device driver gave up\n");
> + goto hard_fail;
> + }
> +
> + /* If any device called out for a reset, then reset the slot */
> + if (result == PCI_ERS_RESULT_NEED_RESET) {
> + rc = eeh_reset_device(pe, NULL);
> + if (rc) {
> + printk(KERN_WARNING "EEH: Cannot reset, rc=%d\n", rc);
> + goto hard_fail;
> + }
> + result = PCI_ERS_RESULT_NONE;
> + eeh_pe_dev_traverse(pe, eeh_report_reset, &result);
> + }
> +
> + /* All devices should claim they have recovered by now. */
> + if ((result != PCI_ERS_RESULT_RECOVERED) &&
> + (result != PCI_ERS_RESULT_NONE)) {
> + printk(KERN_WARNING "EEH: Not recovered\n");
> + goto hard_fail;
> + }
> +
> + /* Tell all device drivers that they can resume operations */
> + eeh_pe_dev_traverse(pe, eeh_report_resume, NULL);
> +
> + return;
> +
> +excess_failures:
> + /*
> + * About 90% of all real-life EEH failures in the field
> + * are due to poorly seated PCI cards. Only 10% or so are
> + * due to actual, failed cards.
> + */
> + pr_err("EEH: PHB#%d-PE#%x has failed %d times in the\n"
> + "last hour and has been permanently disabled.\n"
> + "Please try reseating or replacing it.\n",
> + pe->phb->global_number, pe->addr,
> + pe->freeze_count);
> + goto perm_error;
> +
> +hard_fail:
> + pr_err("EEH: Unable to recover from failure from PHB#%d-PE#%x.\n"
> + "Please try reseating or replacing it\n",
> + pe->phb->global_number, pe->addr);
> +
> +perm_error:
> + eeh_slot_error_detail(pe, EEH_LOG_PERM);
> +
> + /* Notify all devices that they're about to go down. */
> + eeh_pe_dev_traverse(pe, eeh_report_failure, NULL);
> +
> + /* Shut down the device drivers for good. */
> + if (frozen_bus)
> + pcibios_remove_pci_devices(frozen_bus);
> +}
> +
> diff --git a/arch/powerpc/kernel/eeh_event.c b/arch/powerpc/kernel/eeh_event.c
> new file mode 100644
> index 0000000..185bedd
> --- /dev/null
> +++ b/arch/powerpc/kernel/eeh_event.c
> @@ -0,0 +1,142 @@
> +/*
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> + * the Free Software Foundation; either version 2 of the License, or
> + * (at your option) any later version.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> + * GNU General Public License for more details.
> + *
> + * You should have received a copy of the GNU General Public License
> + * along with this program; if not, write to the Free Software
> + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
> + *
> + * Copyright (c) 2005 Linas Vepstas <linas@linas.org>
> + */
> +
> +#include <linux/delay.h>
> +#include <linux/list.h>
> +#include <linux/mutex.h>
> +#include <linux/sched.h>
> +#include <linux/pci.h>
> +#include <linux/slab.h>
> +#include <linux/workqueue.h>
> +#include <linux/kthread.h>
> +#include <asm/eeh_event.h>
> +#include <asm/ppc-pci.h>
> +
> +/** Overview:
> + * EEH error states may be detected within exception handlers;
> + * however, the recovery processing needs to occur asynchronously
> + * in a normal kernel context and not an interrupt context.
> + * This pair of routines creates an event and queues it onto a
> + * work-queue, where a worker thread can drive recovery.
> + */
> +
> +/* EEH event workqueue setup. */
> +static DEFINE_SPINLOCK(eeh_eventlist_lock);
> +LIST_HEAD(eeh_eventlist);
> +static void eeh_thread_launcher(struct work_struct *);
> +DECLARE_WORK(eeh_event_wq, eeh_thread_launcher);
> +
> +/* Serialize reset sequences for a given pci device */
> +DEFINE_MUTEX(eeh_event_mutex);
> +
> +/**
> + * eeh_event_handler - Dispatch EEH events.
> + * @dummy - unused
> + *
> + * The detection of a frozen slot can occur inside an interrupt,
> + * where it can be hard to do anything about it. The goal of this
> + * routine is to pull these detection events out of the context
> + * of the interrupt handler, and re-dispatch them for processing
> + * at a later time in a normal context.
> + */
> +static int eeh_event_handler(void * dummy)
> +{
> + unsigned long flags;
> + struct eeh_event *event;
> + struct eeh_pe *pe;
> +
> + spin_lock_irqsave(&eeh_eventlist_lock, flags);
> + event = NULL;
> +
> + /* Unqueue the event, get ready to process. */
> + if (!list_empty(&eeh_eventlist)) {
> + event = list_entry(eeh_eventlist.next, struct eeh_event, list);
> + list_del(&event->list);
> + }
> + spin_unlock_irqrestore(&eeh_eventlist_lock, flags);
> +
> + if (event == NULL)
> + return 0;
> +
> + /* Serialize processing of EEH events */
> + mutex_lock(&eeh_event_mutex);
> + pe = event->pe;
> + eeh_pe_state_mark(pe, EEH_PE_RECOVERING);
> + pr_info("EEH: Detected PCI bus error on PHB#%d-PE#%x\n",
> + pe->phb->global_number, pe->addr);
> +
> + set_current_state(TASK_INTERRUPTIBLE); /* Don't add to load average */
> + eeh_handle_event(pe);
> + eeh_pe_state_clear(pe, EEH_PE_RECOVERING);
> +
> + kfree(event);
> + mutex_unlock(&eeh_event_mutex);
> +
> + /* If there are no new errors after an hour, clear the counter. */
> + if (pe && pe->freeze_count > 0) {
> + msleep_interruptible(3600*1000);
> + if (pe->freeze_count > 0)
> + pe->freeze_count--;
> +
> + }
> +
> + return 0;
> +}
> +
> +/**
> + * eeh_thread_launcher - Start kernel thread to handle EEH events
> + * @dummy - unused
> + *
> + * This routine is called to start the kernel thread for processing
> + * EEH event.
> + */
> +static void eeh_thread_launcher(struct work_struct *dummy)
> +{
> + if (IS_ERR(kthread_run(eeh_event_handler, NULL, "eehd")))
> + printk(KERN_ERR "Failed to start EEH daemon\n");
> +}
> +
> +/**
> + * eeh_send_failure_event - Generate a PCI error event
> + * @pe: EEH PE
> + *
> + * This routine can be called within an interrupt context;
> + * the actual event will be delivered in a normal context
> + * (from a workqueue).
> + */
> +int eeh_send_failure_event(struct eeh_pe *pe)
> +{
> + unsigned long flags;
> + struct eeh_event *event;
> +
> + event = kzalloc(sizeof(*event), GFP_ATOMIC);
> + if (!event) {
> + pr_err("EEH: out of memory, event not handled\n");
> + return -ENOMEM;
> + }
> + event->pe = pe;
> +
> + /* We may or may not be called in an interrupt context */
> + spin_lock_irqsave(&eeh_eventlist_lock, flags);
> + list_add(&event->list, &eeh_eventlist);
> + spin_unlock_irqrestore(&eeh_eventlist_lock, flags);
> +
> + schedule_work(&eeh_event_wq);
> +
> + return 0;
> +}
> diff --git a/arch/powerpc/kernel/eeh_pe.c b/arch/powerpc/kernel/eeh_pe.c
> new file mode 100644
> index 0000000..9d4a9e8
> --- /dev/null
> +++ b/arch/powerpc/kernel/eeh_pe.c
> @@ -0,0 +1,653 @@
> +/*
> + * The file intends to implement PE based on the information from
> + * platforms. Basically, there have 3 types of PEs: PHB/Bus/Device.
> + * All the PEs should be organized as hierarchy tree. The first level
> + * of the tree will be associated to existing PHBs since the particular
> + * PE is only meaningful in one PHB domain.
> + *
> + * Copyright Benjamin Herrenschmidt & Gavin Shan, IBM Corporation 2012.
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> + * the Free Software Foundation; either version 2 of the License, or
> + * (at your option) any later version.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> + * GNU General Public License for more details.
> + *
> + * You should have received a copy of the GNU General Public License
> + * along with this program; if not, write to the Free Software
> + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
> + */
> +
> +#include <linux/export.h>
> +#include <linux/gfp.h>
> +#include <linux/init.h>
> +#include <linux/kernel.h>
> +#include <linux/pci.h>
> +#include <linux/string.h>
> +
> +#include <asm/pci-bridge.h>
> +#include <asm/ppc-pci.h>
> +
> +static LIST_HEAD(eeh_phb_pe);
> +
> +/**
> + * eeh_pe_alloc - Allocate PE
> + * @phb: PCI controller
> + * @type: PE type
> + *
> + * Allocate PE instance dynamically.
> + */
> +static struct eeh_pe *eeh_pe_alloc(struct pci_controller *phb, int type)
> +{
> + struct eeh_pe *pe;
> +
> + /* Allocate PHB PE */
> + pe = kzalloc(sizeof(struct eeh_pe), GFP_KERNEL);
> + if (!pe) return NULL;
> +
> + /* Initialize PHB PE */
> + pe->type = type;
> + pe->phb = phb;
> + INIT_LIST_HEAD(&pe->child_list);
> + INIT_LIST_HEAD(&pe->child);
> + INIT_LIST_HEAD(&pe->edevs);
> +
> + return pe;
> +}
> +
> +/**
> + * eeh_phb_pe_create - Create PHB PE
> + * @phb: PCI controller
> + *
> + * The function should be called while the PHB is detected during
> + * system boot or PCI hotplug in order to create PHB PE.
> + */
> +int eeh_phb_pe_create(struct pci_controller *phb)
> +{
> + struct eeh_pe *pe;
> +
> + /* Allocate PHB PE */
> + pe = eeh_pe_alloc(phb, EEH_PE_PHB);
> + if (!pe) {
> + pr_err("%s: out of memory!\n", __func__);
> + return -ENOMEM;
> + }
> +
> + /* Put it into the list */
> + eeh_lock();
> + list_add_tail(&pe->child, &eeh_phb_pe);
> + eeh_unlock();
> +
> + pr_debug("EEH: Add PE for PHB#%d\n", phb->global_number);
> +
> + return 0;
> +}
> +
> +/**
> + * eeh_phb_pe_get - Retrieve PHB PE based on the given PHB
> + * @phb: PCI controller
> + *
> + * The overall PEs form hierarchy tree. The first layer of the
> + * hierarchy tree is composed of PHB PEs. The function is used
> + * to retrieve the corresponding PHB PE according to the given PHB.
> + */
> +static struct eeh_pe *eeh_phb_pe_get(struct pci_controller *phb)
> +{
> + struct eeh_pe *pe;
> +
> + list_for_each_entry(pe, &eeh_phb_pe, child) {
> + /*
> + * Actually, we needn't check the type since
> + * the PE for PHB has been determined when that
> + * was created.
> + */
> + if ((pe->type & EEH_PE_PHB) && pe->phb == phb)
> + return pe;
> + }
> +
> + return NULL;
> +}
> +
> +/**
> + * eeh_pe_next - Retrieve the next PE in the tree
> + * @pe: current PE
> + * @root: root PE
> + *
> + * The function is used to retrieve the next PE in the
> + * hierarchy PE tree.
> + */
> +static struct eeh_pe *eeh_pe_next(struct eeh_pe *pe,
> + struct eeh_pe *root)
> +{
> + struct list_head *next = pe->child_list.next;
> +
> + if (next == &pe->child_list) {
> + while (1) {
> + if (pe == root)
> + return NULL;
> + next = pe->child.next;
> + if (next != &pe->parent->child_list)
> + break;
> + pe = pe->parent;
> + }
> + }
> +
> + return list_entry(next, struct eeh_pe, child);
> +}
> +
> +/**
> + * eeh_pe_traverse - Traverse PEs in the specified PHB
> + * @root: root PE
> + * @fn: callback
> + * @flag: extra parameter to callback
> + *
> + * The function is used to traverse the specified PE and its
> + * child PEs. The traversing is to be terminated once the
> + * callback returns something other than NULL, or no more PEs
> + * to be traversed.
> + */
> +static void *eeh_pe_traverse(struct eeh_pe *root,
> + eeh_traverse_func fn, void *flag)
> +{
> + struct eeh_pe *pe;
> + void *ret;
> +
> + for (pe = root; pe; pe = eeh_pe_next(pe, root)) {
> + ret = fn(pe, flag);
> + if (ret) return ret;
> + }
> +
> + return NULL;
> +}
> +
> +/**
> + * eeh_pe_dev_traverse - Traverse the devices from the PE
> + * @root: EEH PE
> + * @fn: function callback
> + * @flag: extra parameter to callback
> + *
> + * The function is used to traverse the devices of the specified
> + * PE and its child PEs.
> + */
> +void *eeh_pe_dev_traverse(struct eeh_pe *root,
> + eeh_traverse_func fn, void *flag)
> +{
> + struct eeh_pe *pe;
> + struct eeh_dev *edev;
> + void *ret;
> +
> + if (!root) {
> + pr_warning("%s: Invalid PE %p\n", __func__, root);
> + return NULL;
> + }
> +
> + eeh_lock();
> +
> + /* Traverse root PE */
> + for (pe = root; pe; pe = eeh_pe_next(pe, root)) {
> + eeh_pe_for_each_dev(pe, edev) {
> + ret = fn(edev, flag);
> + if (ret) {
> + eeh_unlock();
> + return ret;
> + }
> + }
> + }
> +
> + eeh_unlock();
> +
> + return NULL;
> +}
> +
> +/**
> + * __eeh_pe_get - Check the PE address
> + * @data: EEH PE
> + * @flag: EEH device
> + *
> + * For one particular PE, it can be identified by PE address
> + * or tranditional BDF address. BDF address is composed of
> + * Bus/Device/Function number. The extra data referred by flag
> + * indicates which type of address should be used.
> + */
> +static void *__eeh_pe_get(void *data, void *flag)
> +{
> + struct eeh_pe *pe = (struct eeh_pe *)data;
> + struct eeh_dev *edev = (struct eeh_dev *)flag;
> +
> + /* Unexpected PHB PE */
> + if (pe->type & EEH_PE_PHB)
> + return NULL;
> +
> + /* We prefer PE address */
> + if (edev->pe_config_addr &&
> + (edev->pe_config_addr == pe->addr))
> + return pe;
> +
> + /* Try BDF address */
> + if (edev->pe_config_addr &&
> + (edev->config_addr == pe->config_addr))
> + return pe;
> +
> + return NULL;
> +}
> +
> +/**
> + * eeh_pe_get - Search PE based on the given address
> + * @edev: EEH device
> + *
> + * Search the corresponding PE based on the specified address which
> + * is included in the eeh device. The function is used to check if
> + * the associated PE has been created against the PE address. It's
> + * notable that the PE address has 2 format: traditional PE address
> + * which is composed of PCI bus/device/function number, or unified
> + * PE address.
> + */
> +static struct eeh_pe *eeh_pe_get(struct eeh_dev *edev)
> +{
> + struct eeh_pe *root = eeh_phb_pe_get(edev->phb);
> + struct eeh_pe *pe;
> +
> + pe = eeh_pe_traverse(root, __eeh_pe_get, edev);
> +
> + return pe;
> +}
> +
> +/**
> + * eeh_pe_get_parent - Retrieve the parent PE
> + * @edev: EEH device
> + *
> + * The whole PEs existing in the system are organized as hierarchy
> + * tree. The function is used to retrieve the parent PE according
> + * to the parent EEH device.
> + */
> +static struct eeh_pe *eeh_pe_get_parent(struct eeh_dev *edev)
> +{
> + struct device_node *dn;
> + struct eeh_dev *parent;
> +
> + /*
> + * It might have the case for the indirect parent
> + * EEH device already having associated PE, but
> + * the direct parent EEH device doesn't have yet.
> + */
> + dn = edev->dn->parent;
> + while (dn) {
> + /* We're poking out of PCI territory */
> + if (!PCI_DN(dn)) return NULL;
> +
> + parent = of_node_to_eeh_dev(dn);
> + /* We're poking out of PCI territory */
> + if (!parent) return NULL;
> +
> + if (parent->pe)
> + return parent->pe;
> +
> + dn = dn->parent;
> + }
> +
> + return NULL;
> +}
> +
> +/**
> + * eeh_add_to_parent_pe - Add EEH device to parent PE
> + * @edev: EEH device
> + *
> + * Add EEH device to the parent PE. If the parent PE already
> + * exists, the PE type will be changed to EEH_PE_BUS. Otherwise,
> + * we have to create new PE to hold the EEH device and the new
> + * PE will be linked to its parent PE as well.
> + */
> +int eeh_add_to_parent_pe(struct eeh_dev *edev)
> +{
> + struct eeh_pe *pe, *parent;
> +
> + eeh_lock();
> +
> + /*
> + * Search the PE has been existing or not according
> + * to the PE address. If that has been existing, the
> + * PE should be composed of PCI bus and its subordinate
> + * components.
> + */
> + pe = eeh_pe_get(edev);
> + if (pe && !(pe->type & EEH_PE_INVALID)) {
> + if (!edev->pe_config_addr) {
> + eeh_unlock();
> + pr_err("%s: PE with addr 0x%x already exists\n",
> + __func__, edev->config_addr);
> + return -EEXIST;
> + }
> +
> + /* Mark the PE as type of PCI bus */
> + pe->type = EEH_PE_BUS;
> + edev->pe = pe;
> +
> + /* Put the edev to PE */
> + list_add_tail(&edev->list, &pe->edevs);
> + eeh_unlock();
> + pr_debug("EEH: Add %s to Bus PE#%x\n",
> + edev->dn->full_name, pe->addr);
> +
> + return 0;
> + } else if (pe && (pe->type & EEH_PE_INVALID)) {
> + list_add_tail(&edev->list, &pe->edevs);
> + edev->pe = pe;
> + /*
> + * We're running to here because of PCI hotplug caused by
> + * EEH recovery. We need clear EEH_PE_INVALID until the top.
> + */
> + parent = pe;
> + while (parent) {
> + if (!(parent->type & EEH_PE_INVALID))
> + break;
> + parent->type &= ~EEH_PE_INVALID;
> + parent = parent->parent;
> + }
> + eeh_unlock();
> + pr_debug("EEH: Add %s to Device PE#%x, Parent PE#%x\n",
> + edev->dn->full_name, pe->addr, pe->parent->addr);
> +
> + return 0;
> + }
> +
> + /* Create a new EEH PE */
> + pe = eeh_pe_alloc(edev->phb, EEH_PE_DEVICE);
> + if (!pe) {
> + eeh_unlock();
> + pr_err("%s: out of memory!\n", __func__);
> + return -ENOMEM;
> + }
> + pe->addr = edev->pe_config_addr;
> + pe->config_addr = edev->config_addr;
> +
> + /*
> + * Put the new EEH PE into hierarchy tree. If the parent
> + * can't be found, the newly created PE will be attached
> + * to PHB directly. Otherwise, we have to associate the
> + * PE with its parent.
> + */
> + parent = eeh_pe_get_parent(edev);
> + if (!parent) {
> + parent = eeh_phb_pe_get(edev->phb);
> + if (!parent) {
> + eeh_unlock();
> + pr_err("%s: No PHB PE is found (PHB Domain=%d)\n",
> + __func__, edev->phb->global_number);
> + edev->pe = NULL;
> + kfree(pe);
> + return -EEXIST;
> + }
> + }
> + pe->parent = parent;
> +
> + /*
> + * Put the newly created PE into the child list and
> + * link the EEH device accordingly.
> + */
> + list_add_tail(&pe->child, &parent->child_list);
> + list_add_tail(&edev->list, &pe->edevs);
> + edev->pe = pe;
> + eeh_unlock();
> + pr_debug("EEH: Add %s to Device PE#%x, Parent PE#%x\n",
> + edev->dn->full_name, pe->addr, pe->parent->addr);
> +
> + return 0;
> +}
> +
> +/**
> + * eeh_rmv_from_parent_pe - Remove one EEH device from the associated PE
> + * @edev: EEH device
> + * @purge_pe: remove PE or not
> + *
> + * The PE hierarchy tree might be changed when doing PCI hotplug.
> + * Also, the PCI devices or buses could be removed from the system
> + * during EEH recovery. So we have to call the function remove the
> + * corresponding PE accordingly if necessary.
> + */
> +int eeh_rmv_from_parent_pe(struct eeh_dev *edev, int purge_pe)
> +{
> + struct eeh_pe *pe, *parent, *child;
> + int cnt;
> +
> + if (!edev->pe) {
> + pr_warning("%s: No PE found for EEH device %s\n",
> + __func__, edev->dn->full_name);
> + return -EEXIST;
> + }
> +
> + eeh_lock();
> +
> + /* Remove the EEH device */
> + pe = edev->pe;
> + edev->pe = NULL;
> + list_del(&edev->list);
> +
> + /*
> + * Check if the parent PE includes any EEH devices.
> + * If not, we should delete that. Also, we should
> + * delete the parent PE if it doesn't have associated
> + * child PEs and EEH devices.
> + */
> + while (1) {
> + parent = pe->parent;
> + if (pe->type & EEH_PE_PHB)
> + break;
> +
> + if (purge_pe) {
> + if (list_empty(&pe->edevs) &&
> + list_empty(&pe->child_list)) {
> + list_del(&pe->child);
> + kfree(pe);
> + } else {
> + break;
> + }
> + } else {
> + if (list_empty(&pe->edevs)) {
> + cnt = 0;
> + list_for_each_entry(child, &pe->child_list, child) {
> + if (!(child->type & EEH_PE_INVALID)) {
> + cnt++;
> + break;
> + }
> + }
> +
> + if (!cnt)
> + pe->type |= EEH_PE_INVALID;
> + else
> + break;
> + }
> + }
> +
> + pe = parent;
> + }
> +
> + eeh_unlock();
> +
> + return 0;
> +}
> +
> +/**
> + * __eeh_pe_state_mark - Mark the state for the PE
> + * @data: EEH PE
> + * @flag: state
> + *
> + * The function is used to mark the indicated state for the given
> + * PE. Also, the associated PCI devices will be put into IO frozen
> + * state as well.
> + */
> +static void *__eeh_pe_state_mark(void *data, void *flag)
> +{
> + struct eeh_pe *pe = (struct eeh_pe *)data;
> + int state = *((int *)flag);
> + struct eeh_dev *tmp;
> + struct pci_dev *pdev;
> +
> + /*
> + * Mark the PE with the indicated state. Also,
> + * the associated PCI device will be put into
> + * I/O frozen state to avoid I/O accesses from
> + * the PCI device driver.
> + */
> + pe->state |= state;
> + eeh_pe_for_each_dev(pe, tmp) {
> + pdev = eeh_dev_to_pci_dev(tmp);
> + if (pdev)
> + pdev->error_state = pci_channel_io_frozen;
> + }
> +
> + return NULL;
> +}
> +
> +/**
> + * eeh_pe_state_mark - Mark specified state for PE and its associated device
> + * @pe: EEH PE
> + *
> + * EEH error affects the current PE and its child PEs. The function
> + * is used to mark appropriate state for the affected PEs and the
> + * associated devices.
> + */
> +void eeh_pe_state_mark(struct eeh_pe *pe, int state)
> +{
> + eeh_lock();
> + eeh_pe_traverse(pe, __eeh_pe_state_mark, &state);
> + eeh_unlock();
> +}
> +
> +/**
> + * __eeh_pe_state_clear - Clear state for the PE
> + * @data: EEH PE
> + * @flag: state
> + *
> + * The function is used to clear the indicated state from the
> + * given PE. Besides, we also clear the check count of the PE
> + * as well.
> + */
> +static void *__eeh_pe_state_clear(void *data, void *flag)
> +{
> + struct eeh_pe *pe = (struct eeh_pe *)data;
> + int state = *((int *)flag);
> +
> + pe->state &= ~state;
> + pe->check_count = 0;
> +
> + return NULL;
> +}
> +
> +/**
> + * eeh_pe_state_clear - Clear state for the PE and its children
> + * @pe: PE
> + * @state: state to be cleared
> + *
> + * When the PE and its children has been recovered from error,
> + * we need clear the error state for that. The function is used
> + * for the purpose.
> + */
> +void eeh_pe_state_clear(struct eeh_pe *pe, int state)
> +{
> + eeh_lock();
> + eeh_pe_traverse(pe, __eeh_pe_state_clear, &state);
> + eeh_unlock();
> +}
> +
> +/**
> + * eeh_restore_one_device_bars - Restore the Base Address Registers for one device
> + * @data: EEH device
> + * @flag: Unused
> + *
> + * Loads the PCI configuration space base address registers,
> + * the expansion ROM base address, the latency timer, and etc.
> + * from the saved values in the device node.
> + */
> +static void *eeh_restore_one_device_bars(void *data, void *flag)
> +{
> + int i;
> + u32 cmd;
> + struct eeh_dev *edev = (struct eeh_dev *)data;
> + struct device_node *dn = eeh_dev_to_of_node(edev);
> +
> + for (i = 4; i < 10; i++)
> + eeh_ops->write_config(dn, i*4, 4, edev->config_space[i]);
> + /* 12 == Expansion ROM Address */
> + eeh_ops->write_config(dn, 12*4, 4, edev->config_space[12]);
> +
> +#define BYTE_SWAP(OFF) (8*((OFF)/4)+3-(OFF))
> +#define SAVED_BYTE(OFF) (((u8 *)(edev->config_space))[BYTE_SWAP(OFF)])
> +
> + eeh_ops->write_config(dn, PCI_CACHE_LINE_SIZE, 1,
> + SAVED_BYTE(PCI_CACHE_LINE_SIZE));
> + eeh_ops->write_config(dn, PCI_LATENCY_TIMER, 1,
> + SAVED_BYTE(PCI_LATENCY_TIMER));
> +
> + /* max latency, min grant, interrupt pin and line */
> + eeh_ops->write_config(dn, 15*4, 4, edev->config_space[15]);
> +
> + /*
> + * Restore PERR & SERR bits, some devices require it,
> + * don't touch the other command bits
> + */
> + eeh_ops->read_config(dn, PCI_COMMAND, 4, &cmd);
> + if (edev->config_space[1] & PCI_COMMAND_PARITY)
> + cmd |= PCI_COMMAND_PARITY;
> + else
> + cmd &= ~PCI_COMMAND_PARITY;
> + if (edev->config_space[1] & PCI_COMMAND_SERR)
> + cmd |= PCI_COMMAND_SERR;
> + else
> + cmd &= ~PCI_COMMAND_SERR;
> + eeh_ops->write_config(dn, PCI_COMMAND, 4, cmd);
> +
> + return NULL;
> +}
> +
> +/**
> + * eeh_pe_restore_bars - Restore the PCI config space info
> + * @pe: EEH PE
> + *
> + * This routine performs a recursive walk to the children
> + * of this device as well.
> + */
> +void eeh_pe_restore_bars(struct eeh_pe *pe)
> +{
> + /*
> + * We needn't take the EEH lock since eeh_pe_dev_traverse()
> + * will take that.
> + */
> + eeh_pe_dev_traverse(pe, eeh_restore_one_device_bars, NULL);
> +}
> +
> +/**
> + * eeh_pe_bus_get - Retrieve PCI bus according to the given PE
> + * @pe: EEH PE
> + *
> + * Retrieve the PCI bus according to the given PE. Basically,
> + * there're 3 types of PEs: PHB/Bus/Device. For PHB PE, the
> + * primary PCI bus will be retrieved. The parent bus will be
> + * returned for BUS PE. However, we don't have associated PCI
> + * bus for DEVICE PE.
> + */
> +struct pci_bus *eeh_pe_bus_get(struct eeh_pe *pe)
> +{
> + struct pci_bus *bus = NULL;
> + struct eeh_dev *edev;
> + struct pci_dev *pdev;
> +
> + eeh_lock();
> +
> + if (pe->type & EEH_PE_PHB) {
> + bus = pe->phb->bus;
> + } else if (pe->type & EEH_PE_BUS ||
> + pe->type & EEH_PE_DEVICE) {
> + edev = list_first_entry(&pe->edevs, struct eeh_dev, list);
> + pdev = eeh_dev_to_pci_dev(edev);
> + if (pdev)
> + bus = pdev->bus;
> + }
> +
> + eeh_unlock();
> +
> + return bus;
> +}
> diff --git a/arch/powerpc/kernel/eeh_sysfs.c b/arch/powerpc/kernel/eeh_sysfs.c
> new file mode 100644
> index 0000000..d377083
> --- /dev/null
> +++ b/arch/powerpc/kernel/eeh_sysfs.c
> @@ -0,0 +1,75 @@
> +/*
> + * Sysfs entries for PCI Error Recovery for PAPR-compliant platform.
> + * Copyright IBM Corporation 2007
> + * Copyright Linas Vepstas <linas@austin.ibm.com> 2007
> + *
> + * All rights reserved.
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> + * the Free Software Foundation; either version 2 of the License, or (at
> + * your option) any later version.
> + *
> + * This program is distributed in the hope that it will be useful, but
> + * WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or
> + * NON INFRINGEMENT. See the GNU General Public License for more
> + * details.
> + *
> + * You should have received a copy of the GNU General Public License
> + * along with this program; if not, write to the Free Software
> + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
> + *
> + * Send comments and feedback to Linas Vepstas <linas@austin.ibm.com>
> + */
> +#include <linux/pci.h>
> +#include <linux/stat.h>
> +#include <asm/ppc-pci.h>
> +#include <asm/pci-bridge.h>
> +
> +/**
> + * EEH_SHOW_ATTR -- Create sysfs entry for eeh statistic
> + * @_name: name of file in sysfs directory
> + * @_memb: name of member in struct pci_dn to access
> + * @_format: printf format for display
> + *
> + * All of the attributes look very similar, so just
> + * auto-gen a cut-n-paste routine to display them.
> + */
> +#define EEH_SHOW_ATTR(_name,_memb,_format) \
> +static ssize_t eeh_show_##_name(struct device *dev, \
> + struct device_attribute *attr, char *buf) \
> +{ \
> + struct pci_dev *pdev = to_pci_dev(dev); \
> + struct eeh_dev *edev = pci_dev_to_eeh_dev(pdev); \
> + \
> + if (!edev) \
> + return 0; \
> + \
> + return sprintf(buf, _format "\n", edev->_memb); \
> +} \
> +static DEVICE_ATTR(_name, S_IRUGO, eeh_show_##_name, NULL);
> +
> +EEH_SHOW_ATTR(eeh_mode, mode, "0x%x");
> +EEH_SHOW_ATTR(eeh_config_addr, config_addr, "0x%x");
> +EEH_SHOW_ATTR(eeh_pe_config_addr, pe_config_addr, "0x%x");
> +
> +void eeh_sysfs_add_device(struct pci_dev *pdev)
> +{
> + int rc=0;
> +
> + rc += device_create_file(&pdev->dev, &dev_attr_eeh_mode);
> + rc += device_create_file(&pdev->dev, &dev_attr_eeh_config_addr);
> + rc += device_create_file(&pdev->dev, &dev_attr_eeh_pe_config_addr);
> +
> + if (rc)
> + printk(KERN_WARNING "EEH: Unable to create sysfs entries\n");
> +}
> +
> +void eeh_sysfs_remove_device(struct pci_dev *pdev)
> +{
> + device_remove_file(&pdev->dev, &dev_attr_eeh_mode);
> + device_remove_file(&pdev->dev, &dev_attr_eeh_config_addr);
> + device_remove_file(&pdev->dev, &dev_attr_eeh_pe_config_addr);
> +}
> +
> diff --git a/arch/powerpc/kernel/pci_hotplug.c b/arch/powerpc/kernel/pci_hotplug.c
> new file mode 100644
> index 0000000..3f60880
> --- /dev/null
> +++ b/arch/powerpc/kernel/pci_hotplug.c
> @@ -0,0 +1,111 @@
> +/*
> + * Derived from "arch/powerpc/platforms/pseries/pci_dlpar.c"
> + *
> + * Copyright (C) 2003 Linda Xie <lxie@us.ibm.com>
> + * Copyright (C) 2005 International Business Machines
> + *
> + * Updates, 2005, John Rose <johnrose@austin.ibm.com>
> + * Updates, 2005, Linas Vepstas <linas@austin.ibm.com>
> + * Updates, 2013, Gavin Shan <shangw@linux.vnet.ibm.com>
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> + * the Free Software Foundation; either version 2 of the License, or
> + * (at your option) any later version.
> + */
> +
> +#include <linux/pci.h>
> +#include <linux/export.h>
> +#include <asm/pci-bridge.h>
> +#include <asm/ppc-pci.h>
> +#include <asm/firmware.h>
> +#include <asm/eeh.h>
> +
> +/**
> + * __pcibios_remove_pci_devices - remove all devices under this bus
> + * @bus: the indicated PCI bus
> + * @purge_pe: destroy the PE on removal of PCI devices
> + *
> + * Remove all of the PCI devices under this bus both from the
> + * linux pci device tree, and from the powerpc EEH address cache.
> + * By default, the corresponding PE will be destroied during the
> + * normal PCI hotplug path. For PCI hotplug during EEH recovery,
> + * the corresponding PE won't be destroied and deallocated.
> + */
> +void __pcibios_remove_pci_devices(struct pci_bus *bus, int purge_pe)
> +{
> + struct pci_dev *dev, *tmp;
> + struct pci_bus *child_bus;
> +
> + /* First go down child busses */
> + list_for_each_entry(child_bus, &bus->children, node)
> + __pcibios_remove_pci_devices(child_bus, purge_pe);
> +
> + pr_debug("PCI: Removing devices on bus %04x:%02x\n",
> + pci_domain_nr(bus), bus->number);
> + list_for_each_entry_safe(dev, tmp, &bus->devices, bus_list) {
> + pr_debug(" * Removing %s...\n", pci_name(dev));
> + eeh_remove_bus_device(dev, purge_pe);
> + pci_stop_and_remove_bus_device(dev);
> + }
> +}
> +
> +/**
> + * pcibios_remove_pci_devices - remove all devices under this bus
> + * @bus: the indicated PCI bus
> + *
> + * Remove all of the PCI devices under this bus both from the
> + * linux pci device tree, and from the powerpc EEH address cache.
> + */
> +void pcibios_remove_pci_devices(struct pci_bus *bus)
> +{
> + __pcibios_remove_pci_devices(bus, 1);
> +}
> +EXPORT_SYMBOL_GPL(pcibios_remove_pci_devices);
> +
> +/**
> + * pcibios_add_pci_devices - adds new pci devices to bus
> + * @bus: the indicated PCI bus
> + *
> + * This routine will find and fixup new pci devices under
> + * the indicated bus. This routine presumes that there
> + * might already be some devices under this bridge, so
> + * it carefully tries to add only new devices. (And that
> + * is how this routine differs from other, similar pcibios
> + * routines.)
> + */
> +void pcibios_add_pci_devices(struct pci_bus * bus)
> +{
> + int slotno, num, mode, pass, max;
> + struct pci_dev *dev;
> + struct device_node *dn = pci_bus_to_OF_node(bus);
> +
> + eeh_add_device_tree_early(dn);
> +
> + mode = PCI_PROBE_NORMAL;
> + if (ppc_md.pci_probe_mode)
> + mode = ppc_md.pci_probe_mode(bus);
> +
> + if (mode == PCI_PROBE_DEVTREE) {
> + /* use ofdt-based probe */
> + of_rescan_bus(dn, bus);
> + } else if (mode == PCI_PROBE_NORMAL) {
> + /* use legacy probe */
> + slotno = PCI_SLOT(PCI_DN(dn->child)->devfn);
> + num = pci_scan_slot(bus, PCI_DEVFN(slotno, 0));
> + if (!num)
> + return;
> + pcibios_setup_bus_devices(bus);
> + max = bus->busn_res.start;
> + for (pass = 0; pass < 2; pass++) {
> + list_for_each_entry(dev, &bus->devices, bus_list) {
> + if (dev->hdr_type == PCI_HEADER_TYPE_BRIDGE ||
> + dev->hdr_type == PCI_HEADER_TYPE_CARDBUS)
> + max = pci_scan_bridge(bus, dev,
> + max, pass);
> + }
> + }
> + }
> + pcibios_finish_adding_to_bus(bus);
> +}
> +EXPORT_SYMBOL_GPL(pcibios_add_pci_devices);
> diff --git a/arch/powerpc/platforms/Kconfig b/arch/powerpc/platforms/Kconfig
> index b62aab3..bed8c60 100644
> --- a/arch/powerpc/platforms/Kconfig
> +++ b/arch/powerpc/platforms/Kconfig
> @@ -164,6 +164,11 @@ config IBMEBUS
> help
> Bus device driver for GX bus based adapters.
>
> +config EEH
> + bool
> + depends on (PPC_POWERNV || PPC_PSERIES) && PCI
> + default y
> +
> config PPC_MPC106
> bool
> default n
> diff --git a/arch/powerpc/platforms/pseries/Kconfig b/arch/powerpc/platforms/pseries/Kconfig
> index 4459eff..1bd3399 100644
> --- a/arch/powerpc/platforms/pseries/Kconfig
> +++ b/arch/powerpc/platforms/pseries/Kconfig
> @@ -33,11 +33,6 @@ config PPC_SPLPAR
> processors, that is, which share physical processors between
> two or more partitions.
>
> -config EEH
> - bool
> - depends on PPC_PSERIES && PCI
> - default y
> -
> config PSERIES_MSI
> bool
> depends on PCI_MSI && EEH
> diff --git a/arch/powerpc/platforms/pseries/Makefile b/arch/powerpc/platforms/pseries/Makefile
> index 53866e5..8ae0103 100644
> --- a/arch/powerpc/platforms/pseries/Makefile
> +++ b/arch/powerpc/platforms/pseries/Makefile
> @@ -6,9 +6,7 @@ obj-y := lpar.o hvCall.o nvram.o reconfig.o \
> firmware.o power.o dlpar.o mobility.o
> obj-$(CONFIG_SMP) += smp.o
> obj-$(CONFIG_SCANLOG) += scanlog.o
> -obj-$(CONFIG_EEH) += eeh.o eeh_pe.o eeh_dev.o eeh_cache.o \
> - eeh_driver.o eeh_event.o eeh_sysfs.o \
> - eeh_pseries.o
> +obj-$(CONFIG_EEH) += eeh_pseries.o
> obj-$(CONFIG_KEXEC) += kexec.o
> obj-$(CONFIG_PCI) += pci.o pci_dlpar.o
> obj-$(CONFIG_PSERIES_MSI) += msi.o
> diff --git a/arch/powerpc/platforms/pseries/eeh.c b/arch/powerpc/platforms/pseries/eeh.c
> deleted file mode 100644
> index 6b73d6c..0000000
> --- a/arch/powerpc/platforms/pseries/eeh.c
> +++ /dev/null
> @@ -1,942 +0,0 @@
> -/*
> - * Copyright IBM Corporation 2001, 2005, 2006
> - * Copyright Dave Engebretsen & Todd Inglett 2001
> - * Copyright Linas Vepstas 2005, 2006
> - * Copyright 2001-2012 IBM Corporation.
> - *
> - * This program is free software; you can redistribute it and/or modify
> - * it under the terms of the GNU General Public License as published by
> - * the Free Software Foundation; either version 2 of the License, or
> - * (at your option) any later version.
> - *
> - * This program is distributed in the hope that it will be useful,
> - * but WITHOUT ANY WARRANTY; without even the implied warranty of
> - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> - * GNU General Public License for more details.
> - *
> - * You should have received a copy of the GNU General Public License
> - * along with this program; if not, write to the Free Software
> - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
> - *
> - * Please address comments and feedback to Linas Vepstas <linas@austin.ibm.com>
> - */
> -
> -#include <linux/delay.h>
> -#include <linux/sched.h>
> -#include <linux/init.h>
> -#include <linux/list.h>
> -#include <linux/pci.h>
> -#include <linux/proc_fs.h>
> -#include <linux/rbtree.h>
> -#include <linux/seq_file.h>
> -#include <linux/spinlock.h>
> -#include <linux/export.h>
> -#include <linux/of.h>
> -
> -#include <linux/atomic.h>
> -#include <asm/eeh.h>
> -#include <asm/eeh_event.h>
> -#include <asm/io.h>
> -#include <asm/machdep.h>
> -#include <asm/ppc-pci.h>
> -#include <asm/rtas.h>
> -
> -
> -/** Overview:
> - * EEH, or "Extended Error Handling" is a PCI bridge technology for
> - * dealing with PCI bus errors that can't be dealt with within the
> - * usual PCI framework, except by check-stopping the CPU. Systems
> - * that are designed for high-availability/reliability cannot afford
> - * to crash due to a "mere" PCI error, thus the need for EEH.
> - * An EEH-capable bridge operates by converting a detected error
> - * into a "slot freeze", taking the PCI adapter off-line, making
> - * the slot behave, from the OS'es point of view, as if the slot
> - * were "empty": all reads return 0xff's and all writes are silently
> - * ignored. EEH slot isolation events can be triggered by parity
> - * errors on the address or data busses (e.g. during posted writes),
> - * which in turn might be caused by low voltage on the bus, dust,
> - * vibration, humidity, radioactivity or plain-old failed hardware.
> - *
> - * Note, however, that one of the leading causes of EEH slot
> - * freeze events are buggy device drivers, buggy device microcode,
> - * or buggy device hardware. This is because any attempt by the
> - * device to bus-master data to a memory address that is not
> - * assigned to the device will trigger a slot freeze. (The idea
> - * is to prevent devices-gone-wild from corrupting system memory).
> - * Buggy hardware/drivers will have a miserable time co-existing
> - * with EEH.
> - *
> - * Ideally, a PCI device driver, when suspecting that an isolation
> - * event has occurred (e.g. by reading 0xff's), will then ask EEH
> - * whether this is the case, and then take appropriate steps to
> - * reset the PCI slot, the PCI device, and then resume operations.
> - * However, until that day, the checking is done here, with the
> - * eeh_check_failure() routine embedded in the MMIO macros. If
> - * the slot is found to be isolated, an "EEH Event" is synthesized
> - * and sent out for processing.
> - */
> -
> -/* If a device driver keeps reading an MMIO register in an interrupt
> - * handler after a slot isolation event, it might be broken.
> - * This sets the threshold for how many read attempts we allow
> - * before printing an error message.
> - */
> -#define EEH_MAX_FAILS 2100000
> -
> -/* Time to wait for a PCI slot to report status, in milliseconds */
> -#define PCI_BUS_RESET_WAIT_MSEC (60*1000)
> -
> -/* Platform dependent EEH operations */
> -struct eeh_ops *eeh_ops = NULL;
> -
> -int eeh_subsystem_enabled;
> -EXPORT_SYMBOL(eeh_subsystem_enabled);
> -
> -/*
> - * EEH probe mode support. The intention is to support multiple
> - * platforms for EEH. Some platforms like pSeries do PCI emunation
> - * based on device tree. However, other platforms like powernv probe
> - * PCI devices from hardware. The flag is used to distinguish that.
> - * In addition, struct eeh_ops::probe would be invoked for particular
> - * OF node or PCI device so that the corresponding PE would be created
> - * there.
> - */
> -int eeh_probe_mode;
> -
> -/* Global EEH mutex */
> -DEFINE_MUTEX(eeh_mutex);
> -
> -/* Lock to avoid races due to multiple reports of an error */
> -static DEFINE_RAW_SPINLOCK(confirm_error_lock);
> -
> -/* Buffer for reporting pci register dumps. Its here in BSS, and
> - * not dynamically alloced, so that it ends up in RMO where RTAS
> - * can access it.
> - */
> -#define EEH_PCI_REGS_LOG_LEN 4096
> -static unsigned char pci_regs_buf[EEH_PCI_REGS_LOG_LEN];
> -
> -/*
> - * The struct is used to maintain the EEH global statistic
> - * information. Besides, the EEH global statistics will be
> - * exported to user space through procfs
> - */
> -struct eeh_stats {
> - u64 no_device; /* PCI device not found */
> - u64 no_dn; /* OF node not found */
> - u64 no_cfg_addr; /* Config address not found */
> - u64 ignored_check; /* EEH check skipped */
> - u64 total_mmio_ffs; /* Total EEH checks */
> - u64 false_positives; /* Unnecessary EEH checks */
> - u64 slot_resets; /* PE reset */
> -};
> -
> -static struct eeh_stats eeh_stats;
> -
> -#define IS_BRIDGE(class_code) (((class_code)<<16) == PCI_BASE_CLASS_BRIDGE)
> -
> -/**
> - * eeh_gather_pci_data - Copy assorted PCI config space registers to buff
> - * @edev: device to report data for
> - * @buf: point to buffer in which to log
> - * @len: amount of room in buffer
> - *
> - * This routine captures assorted PCI configuration space data,
> - * and puts them into a buffer for RTAS error logging.
> - */
> -static size_t eeh_gather_pci_data(struct eeh_dev *edev, char * buf, size_t len)
> -{
> - struct device_node *dn = eeh_dev_to_of_node(edev);
> - struct pci_dev *dev = eeh_dev_to_pci_dev(edev);
> - u32 cfg;
> - int cap, i;
> - int n = 0;
> -
> - n += scnprintf(buf+n, len-n, "%s\n", dn->full_name);
> - printk(KERN_WARNING "EEH: of node=%s\n", dn->full_name);
> -
> - eeh_ops->read_config(dn, PCI_VENDOR_ID, 4, &cfg);
> - n += scnprintf(buf+n, len-n, "dev/vend:%08x\n", cfg);
> - printk(KERN_WARNING "EEH: PCI device/vendor: %08x\n", cfg);
> -
> - eeh_ops->read_config(dn, PCI_COMMAND, 4, &cfg);
> - n += scnprintf(buf+n, len-n, "cmd/stat:%x\n", cfg);
> - printk(KERN_WARNING "EEH: PCI cmd/status register: %08x\n", cfg);
> -
> - if (!dev) {
> - printk(KERN_WARNING "EEH: no PCI device for this of node\n");
> - return n;
> - }
> -
> - /* Gather bridge-specific registers */
> - if (dev->class >> 16 == PCI_BASE_CLASS_BRIDGE) {
> - eeh_ops->read_config(dn, PCI_SEC_STATUS, 2, &cfg);
> - n += scnprintf(buf+n, len-n, "sec stat:%x\n", cfg);
> - printk(KERN_WARNING "EEH: Bridge secondary status: %04x\n", cfg);
> -
> - eeh_ops->read_config(dn, PCI_BRIDGE_CONTROL, 2, &cfg);
> - n += scnprintf(buf+n, len-n, "brdg ctl:%x\n", cfg);
> - printk(KERN_WARNING "EEH: Bridge control: %04x\n", cfg);
> - }
> -
> - /* Dump out the PCI-X command and status regs */
> - cap = pci_find_capability(dev, PCI_CAP_ID_PCIX);
> - if (cap) {
> - eeh_ops->read_config(dn, cap, 4, &cfg);
> - n += scnprintf(buf+n, len-n, "pcix-cmd:%x\n", cfg);
> - printk(KERN_WARNING "EEH: PCI-X cmd: %08x\n", cfg);
> -
> - eeh_ops->read_config(dn, cap+4, 4, &cfg);
> - n += scnprintf(buf+n, len-n, "pcix-stat:%x\n", cfg);
> - printk(KERN_WARNING "EEH: PCI-X status: %08x\n", cfg);
> - }
> -
> - /* If PCI-E capable, dump PCI-E cap 10, and the AER */
> - cap = pci_find_capability(dev, PCI_CAP_ID_EXP);
> - if (cap) {
> - n += scnprintf(buf+n, len-n, "pci-e cap10:\n");
> - printk(KERN_WARNING
> - "EEH: PCI-E capabilities and status follow:\n");
> -
> - for (i=0; i<=8; i++) {
> - eeh_ops->read_config(dn, cap+4*i, 4, &cfg);
> - n += scnprintf(buf+n, len-n, "%02x:%x\n", 4*i, cfg);
> - printk(KERN_WARNING "EEH: PCI-E %02x: %08x\n", i, cfg);
> - }
> -
> - cap = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_ERR);
> - if (cap) {
> - n += scnprintf(buf+n, len-n, "pci-e AER:\n");
> - printk(KERN_WARNING
> - "EEH: PCI-E AER capability register set follows:\n");
> -
> - for (i=0; i<14; i++) {
> - eeh_ops->read_config(dn, cap+4*i, 4, &cfg);
> - n += scnprintf(buf+n, len-n, "%02x:%x\n", 4*i, cfg);
> - printk(KERN_WARNING "EEH: PCI-E AER %02x: %08x\n", i, cfg);
> - }
> - }
> - }
> -
> - return n;
> -}
> -
> -/**
> - * eeh_slot_error_detail - Generate combined log including driver log and error log
> - * @pe: EEH PE
> - * @severity: temporary or permanent error log
> - *
> - * This routine should be called to generate the combined log, which
> - * is comprised of driver log and error log. The driver log is figured
> - * out from the config space of the corresponding PCI device, while
> - * the error log is fetched through platform dependent function call.
> - */
> -void eeh_slot_error_detail(struct eeh_pe *pe, int severity)
> -{
> - size_t loglen = 0;
> - struct eeh_dev *edev;
> -
> - eeh_pci_enable(pe, EEH_OPT_THAW_MMIO);
> - eeh_ops->configure_bridge(pe);
> - eeh_pe_restore_bars(pe);
> -
> - pci_regs_buf[0] = 0;
> - eeh_pe_for_each_dev(pe, edev) {
> - loglen += eeh_gather_pci_data(edev, pci_regs_buf,
> - EEH_PCI_REGS_LOG_LEN);
> - }
> -
> - eeh_ops->get_log(pe, severity, pci_regs_buf, loglen);
> -}
> -
> -/**
> - * eeh_token_to_phys - Convert EEH address token to phys address
> - * @token: I/O token, should be address in the form 0xA....
> - *
> - * This routine should be called to convert virtual I/O address
> - * to physical one.
> - */
> -static inline unsigned long eeh_token_to_phys(unsigned long token)
> -{
> - pte_t *ptep;
> - unsigned long pa;
> -
> - ptep = find_linux_pte(init_mm.pgd, token);
> - if (!ptep)
> - return token;
> - pa = pte_pfn(*ptep) << PAGE_SHIFT;
> -
> - return pa | (token & (PAGE_SIZE-1));
> -}
> -
> -/**
> - * eeh_dev_check_failure - Check if all 1's data is due to EEH slot freeze
> - * @edev: eeh device
> - *
> - * Check for an EEH failure for the given device node. Call this
> - * routine if the result of a read was all 0xff's and you want to
> - * find out if this is due to an EEH slot freeze. This routine
> - * will query firmware for the EEH status.
> - *
> - * Returns 0 if there has not been an EEH error; otherwise returns
> - * a non-zero value and queues up a slot isolation event notification.
> - *
> - * It is safe to call this routine in an interrupt context.
> - */
> -int eeh_dev_check_failure(struct eeh_dev *edev)
> -{
> - int ret;
> - unsigned long flags;
> - struct device_node *dn;
> - struct pci_dev *dev;
> - struct eeh_pe *pe;
> - int rc = 0;
> - const char *location;
> -
> - eeh_stats.total_mmio_ffs++;
> -
> - if (!eeh_subsystem_enabled)
> - return 0;
> -
> - if (!edev) {
> - eeh_stats.no_dn++;
> - return 0;
> - }
> - dn = eeh_dev_to_of_node(edev);
> - dev = eeh_dev_to_pci_dev(edev);
> - pe = edev->pe;
> -
> - /* Access to IO BARs might get this far and still not want checking. */
> - if (!pe) {
> - eeh_stats.ignored_check++;
> - pr_debug("EEH: Ignored check for %s %s\n",
> - eeh_pci_name(dev), dn->full_name);
> - return 0;
> - }
> -
> - if (!pe->addr && !pe->config_addr) {
> - eeh_stats.no_cfg_addr++;
> - return 0;
> - }
> -
> - /* If we already have a pending isolation event for this
> - * slot, we know it's bad already, we don't need to check.
> - * Do this checking under a lock; as multiple PCI devices
> - * in one slot might report errors simultaneously, and we
> - * only want one error recovery routine running.
> - */
> - raw_spin_lock_irqsave(&confirm_error_lock, flags);
> - rc = 1;
> - if (pe->state & EEH_PE_ISOLATED) {
> - pe->check_count++;
> - if (pe->check_count % EEH_MAX_FAILS == 0) {
> - location = of_get_property(dn, "ibm,loc-code", NULL);
> - printk(KERN_ERR "EEH: %d reads ignored for recovering device at "
> - "location=%s driver=%s pci addr=%s\n",
> - pe->check_count, location,
> - eeh_driver_name(dev), eeh_pci_name(dev));
> - printk(KERN_ERR "EEH: Might be infinite loop in %s driver\n",
> - eeh_driver_name(dev));
> - dump_stack();
> - }
> - goto dn_unlock;
> - }
> -
> - /*
> - * Now test for an EEH failure. This is VERY expensive.
> - * Note that the eeh_config_addr may be a parent device
> - * in the case of a device behind a bridge, or it may be
> - * function zero of a multi-function device.
> - * In any case they must share a common PHB.
> - */
> - ret = eeh_ops->get_state(pe, NULL);
> -
> - /* Note that config-io to empty slots may fail;
> - * they are empty when they don't have children.
> - * We will punt with the following conditions: Failure to get
> - * PE's state, EEH not support and Permanently unavailable
> - * state, PE is in good state.
> - */
> - if ((ret < 0) ||
> - (ret == EEH_STATE_NOT_SUPPORT) ||
> - (ret & (EEH_STATE_MMIO_ACTIVE | EEH_STATE_DMA_ACTIVE)) ==
> - (EEH_STATE_MMIO_ACTIVE | EEH_STATE_DMA_ACTIVE)) {
> - eeh_stats.false_positives++;
> - pe->false_positives++;
> - rc = 0;
> - goto dn_unlock;
> - }
> -
> - eeh_stats.slot_resets++;
> -
> - /* Avoid repeated reports of this failure, including problems
> - * with other functions on this device, and functions under
> - * bridges.
> - */
> - eeh_pe_state_mark(pe, EEH_PE_ISOLATED);
> - raw_spin_unlock_irqrestore(&confirm_error_lock, flags);
> -
> - eeh_send_failure_event(pe);
> -
> - /* Most EEH events are due to device driver bugs. Having
> - * a stack trace will help the device-driver authors figure
> - * out what happened. So print that out.
> - */
> - WARN(1, "EEH: failure detected\n");
> - return 1;
> -
> -dn_unlock:
> - raw_spin_unlock_irqrestore(&confirm_error_lock, flags);
> - return rc;
> -}
> -
> -EXPORT_SYMBOL_GPL(eeh_dev_check_failure);
> -
> -/**
> - * eeh_check_failure - Check if all 1's data is due to EEH slot freeze
> - * @token: I/O token, should be address in the form 0xA....
> - * @val: value, should be all 1's (XXX why do we need this arg??)
> - *
> - * Check for an EEH failure at the given token address. Call this
> - * routine if the result of a read was all 0xff's and you want to
> - * find out if this is due to an EEH slot freeze event. This routine
> - * will query firmware for the EEH status.
> - *
> - * Note this routine is safe to call in an interrupt context.
> - */
> -unsigned long eeh_check_failure(const volatile void __iomem *token, unsigned long val)
> -{
> - unsigned long addr;
> - struct eeh_dev *edev;
> -
> - /* Finding the phys addr + pci device; this is pretty quick. */
> - addr = eeh_token_to_phys((unsigned long __force) token);
> - edev = eeh_addr_cache_get_dev(addr);
> - if (!edev) {
> - eeh_stats.no_device++;
> - return val;
> - }
> -
> - eeh_dev_check_failure(edev);
> -
> - pci_dev_put(eeh_dev_to_pci_dev(edev));
> - return val;
> -}
> -
> -EXPORT_SYMBOL(eeh_check_failure);
> -
> -
> -/**
> - * eeh_pci_enable - Enable MMIO or DMA transfers for this slot
> - * @pe: EEH PE
> - *
> - * This routine should be called to reenable frozen MMIO or DMA
> - * so that it would work correctly again. It's useful while doing
> - * recovery or log collection on the indicated device.
> - */
> -int eeh_pci_enable(struct eeh_pe *pe, int function)
> -{
> - int rc;
> -
> - rc = eeh_ops->set_option(pe, function);
> - if (rc)
> - pr_warning("%s: Unexpected state change %d on PHB#%d-PE#%x, err=%d\n",
> - __func__, function, pe->phb->global_number, pe->addr, rc);
> -
> - rc = eeh_ops->wait_state(pe, PCI_BUS_RESET_WAIT_MSEC);
> - if (rc > 0 && (rc & EEH_STATE_MMIO_ENABLED) &&
> - (function == EEH_OPT_THAW_MMIO))
> - return 0;
> -
> - return rc;
> -}
> -
> -/**
> - * pcibios_set_pcie_slot_reset - Set PCI-E reset state
> - * @dev: pci device struct
> - * @state: reset state to enter
> - *
> - * Return value:
> - * 0 if success
> - */
> -int pcibios_set_pcie_reset_state(struct pci_dev *dev, enum pcie_reset_state state)
> -{
> - struct eeh_dev *edev = pci_dev_to_eeh_dev(dev);
> - struct eeh_pe *pe = edev->pe;
> -
> - if (!pe) {
> - pr_err("%s: No PE found on PCI device %s\n",
> - __func__, pci_name(dev));
> - return -EINVAL;
> - }
> -
> - switch (state) {
> - case pcie_deassert_reset:
> - eeh_ops->reset(pe, EEH_RESET_DEACTIVATE);
> - break;
> - case pcie_hot_reset:
> - eeh_ops->reset(pe, EEH_RESET_HOT);
> - break;
> - case pcie_warm_reset:
> - eeh_ops->reset(pe, EEH_RESET_FUNDAMENTAL);
> - break;
> - default:
> - return -EINVAL;
> - };
> -
> - return 0;
> -}
> -
> -/**
> - * eeh_set_pe_freset - Check the required reset for the indicated device
> - * @data: EEH device
> - * @flag: return value
> - *
> - * Each device might have its preferred reset type: fundamental or
> - * hot reset. The routine is used to collected the information for
> - * the indicated device and its children so that the bunch of the
> - * devices could be reset properly.
> - */
> -static void *eeh_set_dev_freset(void *data, void *flag)
> -{
> - struct pci_dev *dev;
> - unsigned int *freset = (unsigned int *)flag;
> - struct eeh_dev *edev = (struct eeh_dev *)data;
> -
> - dev = eeh_dev_to_pci_dev(edev);
> - if (dev)
> - *freset |= dev->needs_freset;
> -
> - return NULL;
> -}
> -
> -/**
> - * eeh_reset_pe_once - Assert the pci #RST line for 1/4 second
> - * @pe: EEH PE
> - *
> - * Assert the PCI #RST line for 1/4 second.
> - */
> -static void eeh_reset_pe_once(struct eeh_pe *pe)
> -{
> - unsigned int freset = 0;
> -
> - /* Determine type of EEH reset required for
> - * Partitionable Endpoint, a hot-reset (1)
> - * or a fundamental reset (3).
> - * A fundamental reset required by any device under
> - * Partitionable Endpoint trumps hot-reset.
> - */
> - eeh_pe_dev_traverse(pe, eeh_set_dev_freset, &freset);
> -
> - if (freset)
> - eeh_ops->reset(pe, EEH_RESET_FUNDAMENTAL);
> - else
> - eeh_ops->reset(pe, EEH_RESET_HOT);
> -
> - /* The PCI bus requires that the reset be held high for at least
> - * a 100 milliseconds. We wait a bit longer 'just in case'.
> - */
> -#define PCI_BUS_RST_HOLD_TIME_MSEC 250
> - msleep(PCI_BUS_RST_HOLD_TIME_MSEC);
> -
> - /* We might get hit with another EEH freeze as soon as the
> - * pci slot reset line is dropped. Make sure we don't miss
> - * these, and clear the flag now.
> - */
> - eeh_pe_state_clear(pe, EEH_PE_ISOLATED);
> -
> - eeh_ops->reset(pe, EEH_RESET_DEACTIVATE);
> -
> - /* After a PCI slot has been reset, the PCI Express spec requires
> - * a 1.5 second idle time for the bus to stabilize, before starting
> - * up traffic.
> - */
> -#define PCI_BUS_SETTLE_TIME_MSEC 1800
> - msleep(PCI_BUS_SETTLE_TIME_MSEC);
> -}
> -
> -/**
> - * eeh_reset_pe - Reset the indicated PE
> - * @pe: EEH PE
> - *
> - * This routine should be called to reset indicated device, including
> - * PE. A PE might include multiple PCI devices and sometimes PCI bridges
> - * might be involved as well.
> - */
> -int eeh_reset_pe(struct eeh_pe *pe)
> -{
> - int i, rc;
> -
> - /* Take three shots at resetting the bus */
> - for (i=0; i<3; i++) {
> - eeh_reset_pe_once(pe);
> -
> - rc = eeh_ops->wait_state(pe, PCI_BUS_RESET_WAIT_MSEC);
> - if (rc == (EEH_STATE_MMIO_ACTIVE | EEH_STATE_DMA_ACTIVE))
> - return 0;
> -
> - if (rc < 0) {
> - pr_err("%s: Unrecoverable slot failure on PHB#%d-PE#%x",
> - __func__, pe->phb->global_number, pe->addr);
> - return -1;
> - }
> - pr_err("EEH: bus reset %d failed on PHB#%d-PE#%x, rc=%d\n",
> - i+1, pe->phb->global_number, pe->addr, rc);
> - }
> -
> - return -1;
> -}
> -
> -/**
> - * eeh_save_bars - Save device bars
> - * @edev: PCI device associated EEH device
> - *
> - * Save the values of the device bars. Unlike the restore
> - * routine, this routine is *not* recursive. This is because
> - * PCI devices are added individually; but, for the restore,
> - * an entire slot is reset at a time.
> - */
> -void eeh_save_bars(struct eeh_dev *edev)
> -{
> - int i;
> - struct device_node *dn;
> -
> - if (!edev)
> - return;
> - dn = eeh_dev_to_of_node(edev);
> -
> - for (i = 0; i < 16; i++)
> - eeh_ops->read_config(dn, i * 4, 4, &edev->config_space[i]);
> -}
> -
> -/**
> - * eeh_ops_register - Register platform dependent EEH operations
> - * @ops: platform dependent EEH operations
> - *
> - * Register the platform dependent EEH operation callback
> - * functions. The platform should call this function before
> - * any other EEH operations.
> - */
> -int __init eeh_ops_register(struct eeh_ops *ops)
> -{
> - if (!ops->name) {
> - pr_warning("%s: Invalid EEH ops name for %p\n",
> - __func__, ops);
> - return -EINVAL;
> - }
> -
> - if (eeh_ops && eeh_ops != ops) {
> - pr_warning("%s: EEH ops of platform %s already existing (%s)\n",
> - __func__, eeh_ops->name, ops->name);
> - return -EEXIST;
> - }
> -
> - eeh_ops = ops;
> -
> - return 0;
> -}
> -
> -/**
> - * eeh_ops_unregister - Unreigster platform dependent EEH operations
> - * @name: name of EEH platform operations
> - *
> - * Unregister the platform dependent EEH operation callback
> - * functions.
> - */
> -int __exit eeh_ops_unregister(const char *name)
> -{
> - if (!name || !strlen(name)) {
> - pr_warning("%s: Invalid EEH ops name\n",
> - __func__);
> - return -EINVAL;
> - }
> -
> - if (eeh_ops && !strcmp(eeh_ops->name, name)) {
> - eeh_ops = NULL;
> - return 0;
> - }
> -
> - return -EEXIST;
> -}
> -
> -/**
> - * eeh_init - EEH initialization
> - *
> - * Initialize EEH by trying to enable it for all of the adapters in the system.
> - * As a side effect we can determine here if eeh is supported at all.
> - * Note that we leave EEH on so failed config cycles won't cause a machine
> - * check. If a user turns off EEH for a particular adapter they are really
> - * telling Linux to ignore errors. Some hardware (e.g. POWER5) won't
> - * grant access to a slot if EEH isn't enabled, and so we always enable
> - * EEH for all slots/all devices.
> - *
> - * The eeh-force-off option disables EEH checking globally, for all slots.
> - * Even if force-off is set, the EEH hardware is still enabled, so that
> - * newer systems can boot.
> - */
> -static int __init eeh_init(void)
> -{
> - struct pci_controller *hose, *tmp;
> - struct device_node *phb;
> - int ret;
> -
> - /* call platform initialization function */
> - if (!eeh_ops) {
> - pr_warning("%s: Platform EEH operation not found\n",
> - __func__);
> - return -EEXIST;
> - } else if ((ret = eeh_ops->init())) {
> - pr_warning("%s: Failed to call platform init function (%d)\n",
> - __func__, ret);
> - return ret;
> - }
> -
> - raw_spin_lock_init(&confirm_error_lock);
> -
> - /* Enable EEH for all adapters */
> - if (eeh_probe_mode_devtree()) {
> - list_for_each_entry_safe(hose, tmp,
> - &hose_list, list_node) {
> - phb = hose->dn;
> - traverse_pci_devices(phb, eeh_ops->of_probe, NULL);
> - }
> - }
> -
> - if (eeh_subsystem_enabled)
> - pr_info("EEH: PCI Enhanced I/O Error Handling Enabled\n");
> - else
> - pr_warning("EEH: No capable adapters found\n");
> -
> - return ret;
> -}
> -
> -core_initcall_sync(eeh_init);
> -
> -/**
> - * eeh_add_device_early - Enable EEH for the indicated device_node
> - * @dn: device node for which to set up EEH
> - *
> - * This routine must be used to perform EEH initialization for PCI
> - * devices that were added after system boot (e.g. hotplug, dlpar).
> - * This routine must be called before any i/o is performed to the
> - * adapter (inluding any config-space i/o).
> - * Whether this actually enables EEH or not for this device depends
> - * on the CEC architecture, type of the device, on earlier boot
> - * command-line arguments & etc.
> - */
> -static void eeh_add_device_early(struct device_node *dn)
> -{
> - struct pci_controller *phb;
> -
> - if (!of_node_to_eeh_dev(dn))
> - return;
> - phb = of_node_to_eeh_dev(dn)->phb;
> -
> - /* USB Bus children of PCI devices will not have BUID's */
> - if (NULL == phb || 0 == phb->buid)
> - return;
> -
> - /* FIXME: hotplug support on POWERNV */
> - eeh_ops->of_probe(dn, NULL);
> -}
> -
> -/**
> - * eeh_add_device_tree_early - Enable EEH for the indicated device
> - * @dn: device node
> - *
> - * This routine must be used to perform EEH initialization for the
> - * indicated PCI device that was added after system boot (e.g.
> - * hotplug, dlpar).
> - */
> -void eeh_add_device_tree_early(struct device_node *dn)
> -{
> - struct device_node *sib;
> -
> - for_each_child_of_node(dn, sib)
> - eeh_add_device_tree_early(sib);
> - eeh_add_device_early(dn);
> -}
> -EXPORT_SYMBOL_GPL(eeh_add_device_tree_early);
> -
> -/**
> - * eeh_add_device_late - Perform EEH initialization for the indicated pci device
> - * @dev: pci device for which to set up EEH
> - *
> - * This routine must be used to complete EEH initialization for PCI
> - * devices that were added after system boot (e.g. hotplug, dlpar).
> - */
> -static void eeh_add_device_late(struct pci_dev *dev)
> -{
> - struct device_node *dn;
> - struct eeh_dev *edev;
> -
> - if (!dev || !eeh_subsystem_enabled)
> - return;
> -
> - pr_debug("EEH: Adding device %s\n", pci_name(dev));
> -
> - dn = pci_device_to_OF_node(dev);
> - edev = of_node_to_eeh_dev(dn);
> - if (edev->pdev == dev) {
> - pr_debug("EEH: Already referenced !\n");
> - return;
> - }
> - WARN_ON(edev->pdev);
> -
> - pci_dev_get(dev);
> - edev->pdev = dev;
> - dev->dev.archdata.edev = edev;
> -
> - eeh_addr_cache_insert_dev(dev);
> -}
> -
> -/**
> - * eeh_add_device_tree_late - Perform EEH initialization for the indicated PCI bus
> - * @bus: PCI bus
> - *
> - * This routine must be used to perform EEH initialization for PCI
> - * devices which are attached to the indicated PCI bus. The PCI bus
> - * is added after system boot through hotplug or dlpar.
> - */
> -void eeh_add_device_tree_late(struct pci_bus *bus)
> -{
> - struct pci_dev *dev;
> -
> - list_for_each_entry(dev, &bus->devices, bus_list) {
> - eeh_add_device_late(dev);
> - if (dev->hdr_type == PCI_HEADER_TYPE_BRIDGE) {
> - struct pci_bus *subbus = dev->subordinate;
> - if (subbus)
> - eeh_add_device_tree_late(subbus);
> - }
> - }
> -}
> -EXPORT_SYMBOL_GPL(eeh_add_device_tree_late);
> -
> -/**
> - * eeh_add_sysfs_files - Add EEH sysfs files for the indicated PCI bus
> - * @bus: PCI bus
> - *
> - * This routine must be used to add EEH sysfs files for PCI
> - * devices which are attached to the indicated PCI bus. The PCI bus
> - * is added after system boot through hotplug or dlpar.
> - */
> -void eeh_add_sysfs_files(struct pci_bus *bus)
> -{
> - struct pci_dev *dev;
> -
> - list_for_each_entry(dev, &bus->devices, bus_list) {
> - eeh_sysfs_add_device(dev);
> - if (dev->hdr_type == PCI_HEADER_TYPE_BRIDGE) {
> - struct pci_bus *subbus = dev->subordinate;
> - if (subbus)
> - eeh_add_sysfs_files(subbus);
> - }
> - }
> -}
> -EXPORT_SYMBOL_GPL(eeh_add_sysfs_files);
> -
> -/**
> - * eeh_remove_device - Undo EEH setup for the indicated pci device
> - * @dev: pci device to be removed
> - * @purge_pe: remove the PE or not
> - *
> - * This routine should be called when a device is removed from
> - * a running system (e.g. by hotplug or dlpar). It unregisters
> - * the PCI device from the EEH subsystem. I/O errors affecting
> - * this device will no longer be detected after this call; thus,
> - * i/o errors affecting this slot may leave this device unusable.
> - */
> -static void eeh_remove_device(struct pci_dev *dev, int purge_pe)
> -{
> - struct eeh_dev *edev;
> -
> - if (!dev || !eeh_subsystem_enabled)
> - return;
> - edev = pci_dev_to_eeh_dev(dev);
> -
> - /* Unregister the device with the EEH/PCI address search system */
> - pr_debug("EEH: Removing device %s\n", pci_name(dev));
> -
> - if (!edev || !edev->pdev) {
> - pr_debug("EEH: Not referenced !\n");
> - return;
> - }
> - edev->pdev = NULL;
> - dev->dev.archdata.edev = NULL;
> - pci_dev_put(dev);
> -
> - eeh_rmv_from_parent_pe(edev, purge_pe);
> - eeh_addr_cache_rmv_dev(dev);
> - eeh_sysfs_remove_device(dev);
> -}
> -
> -/**
> - * eeh_remove_bus_device - Undo EEH setup for the indicated PCI device
> - * @dev: PCI device
> - * @purge_pe: remove the corresponding PE or not
> - *
> - * This routine must be called when a device is removed from the
> - * running system through hotplug or dlpar. The corresponding
> - * PCI address cache will be removed.
> - */
> -void eeh_remove_bus_device(struct pci_dev *dev, int purge_pe)
> -{
> - struct pci_bus *bus = dev->subordinate;
> - struct pci_dev *child, *tmp;
> -
> - eeh_remove_device(dev, purge_pe);
> -
> - if (bus && dev->hdr_type == PCI_HEADER_TYPE_BRIDGE) {
> - list_for_each_entry_safe(child, tmp, &bus->devices, bus_list)
> - eeh_remove_bus_device(child, purge_pe);
> - }
> -}
> -EXPORT_SYMBOL_GPL(eeh_remove_bus_device);
> -
> -static int proc_eeh_show(struct seq_file *m, void *v)
> -{
> - if (0 == eeh_subsystem_enabled) {
> - seq_printf(m, "EEH Subsystem is globally disabled\n");
> - seq_printf(m, "eeh_total_mmio_ffs=%llu\n", eeh_stats.total_mmio_ffs);
> - } else {
> - seq_printf(m, "EEH Subsystem is enabled\n");
> - seq_printf(m,
> - "no device=%llu\n"
> - "no device node=%llu\n"
> - "no config address=%llu\n"
> - "check not wanted=%llu\n"
> - "eeh_total_mmio_ffs=%llu\n"
> - "eeh_false_positives=%llu\n"
> - "eeh_slot_resets=%llu\n",
> - eeh_stats.no_device,
> - eeh_stats.no_dn,
> - eeh_stats.no_cfg_addr,
> - eeh_stats.ignored_check,
> - eeh_stats.total_mmio_ffs,
> - eeh_stats.false_positives,
> - eeh_stats.slot_resets);
> - }
> -
> - return 0;
> -}
> -
> -static int proc_eeh_open(struct inode *inode, struct file *file)
> -{
> - return single_open(file, proc_eeh_show, NULL);
> -}
> -
> -static const struct file_operations proc_eeh_operations = {
> - .open = proc_eeh_open,
> - .read = seq_read,
> - .llseek = seq_lseek,
> - .release = single_release,
> -};
> -
> -static int __init eeh_init_proc(void)
> -{
> - if (machine_is(pseries))
> - proc_create("powerpc/eeh", 0, NULL, &proc_eeh_operations);
> - return 0;
> -}
> -__initcall(eeh_init_proc);
> diff --git a/arch/powerpc/platforms/pseries/eeh_cache.c b/arch/powerpc/platforms/pseries/eeh_cache.c
> deleted file mode 100644
> index 5a4c879..0000000
> --- a/arch/powerpc/platforms/pseries/eeh_cache.c
> +++ /dev/null
> @@ -1,319 +0,0 @@
> -/*
> - * PCI address cache; allows the lookup of PCI devices based on I/O address
> - *
> - * Copyright IBM Corporation 2004
> - * Copyright Linas Vepstas <linas@austin.ibm.com> 2004
> - *
> - * This program is free software; you can redistribute it and/or modify
> - * it under the terms of the GNU General Public License as published by
> - * the Free Software Foundation; either version 2 of the License, or
> - * (at your option) any later version.
> - *
> - * This program is distributed in the hope that it will be useful,
> - * but WITHOUT ANY WARRANTY; without even the implied warranty of
> - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> - * GNU General Public License for more details.
> - *
> - * You should have received a copy of the GNU General Public License
> - * along with this program; if not, write to the Free Software
> - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
> - */
> -
> -#include <linux/list.h>
> -#include <linux/pci.h>
> -#include <linux/rbtree.h>
> -#include <linux/slab.h>
> -#include <linux/spinlock.h>
> -#include <linux/atomic.h>
> -#include <asm/pci-bridge.h>
> -#include <asm/ppc-pci.h>
> -
> -
> -/**
> - * The pci address cache subsystem. This subsystem places
> - * PCI device address resources into a red-black tree, sorted
> - * according to the address range, so that given only an i/o
> - * address, the corresponding PCI device can be **quickly**
> - * found. It is safe to perform an address lookup in an interrupt
> - * context; this ability is an important feature.
> - *
> - * Currently, the only customer of this code is the EEH subsystem;
> - * thus, this code has been somewhat tailored to suit EEH better.
> - * In particular, the cache does *not* hold the addresses of devices
> - * for which EEH is not enabled.
> - *
> - * (Implementation Note: The RB tree seems to be better/faster
> - * than any hash algo I could think of for this problem, even
> - * with the penalty of slow pointer chases for d-cache misses).
> - */
> -struct pci_io_addr_range {
> - struct rb_node rb_node;
> - unsigned long addr_lo;
> - unsigned long addr_hi;
> - struct eeh_dev *edev;
> - struct pci_dev *pcidev;
> - unsigned int flags;
> -};
> -
> -static struct pci_io_addr_cache {
> - struct rb_root rb_root;
> - spinlock_t piar_lock;
> -} pci_io_addr_cache_root;
> -
> -static inline struct eeh_dev *__eeh_addr_cache_get_device(unsigned long addr)
> -{
> - struct rb_node *n = pci_io_addr_cache_root.rb_root.rb_node;
> -
> - while (n) {
> - struct pci_io_addr_range *piar;
> - piar = rb_entry(n, struct pci_io_addr_range, rb_node);
> -
> - if (addr < piar->addr_lo) {
> - n = n->rb_left;
> - } else {
> - if (addr > piar->addr_hi) {
> - n = n->rb_right;
> - } else {
> - pci_dev_get(piar->pcidev);
> - return piar->edev;
> - }
> - }
> - }
> -
> - return NULL;
> -}
> -
> -/**
> - * eeh_addr_cache_get_dev - Get device, given only address
> - * @addr: mmio (PIO) phys address or i/o port number
> - *
> - * Given an mmio phys address, or a port number, find a pci device
> - * that implements this address. Be sure to pci_dev_put the device
> - * when finished. I/O port numbers are assumed to be offset
> - * from zero (that is, they do *not* have pci_io_addr added in).
> - * It is safe to call this function within an interrupt.
> - */
> -struct eeh_dev *eeh_addr_cache_get_dev(unsigned long addr)
> -{
> - struct eeh_dev *edev;
> - unsigned long flags;
> -
> - spin_lock_irqsave(&pci_io_addr_cache_root.piar_lock, flags);
> - edev = __eeh_addr_cache_get_device(addr);
> - spin_unlock_irqrestore(&pci_io_addr_cache_root.piar_lock, flags);
> - return edev;
> -}
> -
> -#ifdef DEBUG
> -/*
> - * Handy-dandy debug print routine, does nothing more
> - * than print out the contents of our addr cache.
> - */
> -static void eeh_addr_cache_print(struct pci_io_addr_cache *cache)
> -{
> - struct rb_node *n;
> - int cnt = 0;
> -
> - n = rb_first(&cache->rb_root);
> - while (n) {
> - struct pci_io_addr_range *piar;
> - piar = rb_entry(n, struct pci_io_addr_range, rb_node);
> - pr_debug("PCI: %s addr range %d [%lx-%lx]: %s\n",
> - (piar->flags & IORESOURCE_IO) ? "i/o" : "mem", cnt,
> - piar->addr_lo, piar->addr_hi, pci_name(piar->pcidev));
> - cnt++;
> - n = rb_next(n);
> - }
> -}
> -#endif
> -
> -/* Insert address range into the rb tree. */
> -static struct pci_io_addr_range *
> -eeh_addr_cache_insert(struct pci_dev *dev, unsigned long alo,
> - unsigned long ahi, unsigned int flags)
> -{
> - struct rb_node **p = &pci_io_addr_cache_root.rb_root.rb_node;
> - struct rb_node *parent = NULL;
> - struct pci_io_addr_range *piar;
> -
> - /* Walk tree, find a place to insert into tree */
> - while (*p) {
> - parent = *p;
> - piar = rb_entry(parent, struct pci_io_addr_range, rb_node);
> - if (ahi < piar->addr_lo) {
> - p = &parent->rb_left;
> - } else if (alo > piar->addr_hi) {
> - p = &parent->rb_right;
> - } else {
> - if (dev != piar->pcidev ||
> - alo != piar->addr_lo || ahi != piar->addr_hi) {
> - pr_warning("PIAR: overlapping address range\n");
> - }
> - return piar;
> - }
> - }
> - piar = kzalloc(sizeof(struct pci_io_addr_range), GFP_ATOMIC);
> - if (!piar)
> - return NULL;
> -
> - pci_dev_get(dev);
> - piar->addr_lo = alo;
> - piar->addr_hi = ahi;
> - piar->edev = pci_dev_to_eeh_dev(dev);
> - piar->pcidev = dev;
> - piar->flags = flags;
> -
> -#ifdef DEBUG
> - pr_debug("PIAR: insert range=[%lx:%lx] dev=%s\n",
> - alo, ahi, pci_name(dev));
> -#endif
> -
> - rb_link_node(&piar->rb_node, parent, p);
> - rb_insert_color(&piar->rb_node, &pci_io_addr_cache_root.rb_root);
> -
> - return piar;
> -}
> -
> -static void __eeh_addr_cache_insert_dev(struct pci_dev *dev)
> -{
> - struct device_node *dn;
> - struct eeh_dev *edev;
> - int i;
> -
> - dn = pci_device_to_OF_node(dev);
> - if (!dn) {
> - pr_warning("PCI: no pci dn found for dev=%s\n", pci_name(dev));
> - return;
> - }
> -
> - edev = of_node_to_eeh_dev(dn);
> - if (!edev) {
> - pr_warning("PCI: no EEH dev found for dn=%s\n",
> - dn->full_name);
> - return;
> - }
> -
> - /* Skip any devices for which EEH is not enabled. */
> - if (!edev->pe) {
> -#ifdef DEBUG
> - pr_info("PCI: skip building address cache for=%s - %s\n",
> - pci_name(dev), dn->full_name);
> -#endif
> - return;
> - }
> -
> - /* Walk resources on this device, poke them into the tree */
> - for (i = 0; i < DEVICE_COUNT_RESOURCE; i++) {
> - unsigned long start = pci_resource_start(dev,i);
> - unsigned long end = pci_resource_end(dev,i);
> - unsigned int flags = pci_resource_flags(dev,i);
> -
> - /* We are interested only bus addresses, not dma or other stuff */
> - if (0 == (flags & (IORESOURCE_IO | IORESOURCE_MEM)))
> - continue;
> - if (start == 0 || ~start == 0 || end == 0 || ~end == 0)
> - continue;
> - eeh_addr_cache_insert(dev, start, end, flags);
> - }
> -}
> -
> -/**
> - * eeh_addr_cache_insert_dev - Add a device to the address cache
> - * @dev: PCI device whose I/O addresses we are interested in.
> - *
> - * In order to support the fast lookup of devices based on addresses,
> - * we maintain a cache of devices that can be quickly searched.
> - * This routine adds a device to that cache.
> - */
> -void eeh_addr_cache_insert_dev(struct pci_dev *dev)
> -{
> - unsigned long flags;
> -
> - /* Ignore PCI bridges */
> - if ((dev->class >> 16) == PCI_BASE_CLASS_BRIDGE)
> - return;
> -
> - spin_lock_irqsave(&pci_io_addr_cache_root.piar_lock, flags);
> - __eeh_addr_cache_insert_dev(dev);
> - spin_unlock_irqrestore(&pci_io_addr_cache_root.piar_lock, flags);
> -}
> -
> -static inline void __eeh_addr_cache_rmv_dev(struct pci_dev *dev)
> -{
> - struct rb_node *n;
> -
> -restart:
> - n = rb_first(&pci_io_addr_cache_root.rb_root);
> - while (n) {
> - struct pci_io_addr_range *piar;
> - piar = rb_entry(n, struct pci_io_addr_range, rb_node);
> -
> - if (piar->pcidev == dev) {
> - rb_erase(n, &pci_io_addr_cache_root.rb_root);
> - pci_dev_put(piar->pcidev);
> - kfree(piar);
> - goto restart;
> - }
> - n = rb_next(n);
> - }
> -}
> -
> -/**
> - * eeh_addr_cache_rmv_dev - remove pci device from addr cache
> - * @dev: device to remove
> - *
> - * Remove a device from the addr-cache tree.
> - * This is potentially expensive, since it will walk
> - * the tree multiple times (once per resource).
> - * But so what; device removal doesn't need to be that fast.
> - */
> -void eeh_addr_cache_rmv_dev(struct pci_dev *dev)
> -{
> - unsigned long flags;
> -
> - spin_lock_irqsave(&pci_io_addr_cache_root.piar_lock, flags);
> - __eeh_addr_cache_rmv_dev(dev);
> - spin_unlock_irqrestore(&pci_io_addr_cache_root.piar_lock, flags);
> -}
> -
> -/**
> - * eeh_addr_cache_build - Build a cache of I/O addresses
> - *
> - * Build a cache of pci i/o addresses. This cache will be used to
> - * find the pci device that corresponds to a given address.
> - * This routine scans all pci busses to build the cache.
> - * Must be run late in boot process, after the pci controllers
> - * have been scanned for devices (after all device resources are known).
> - */
> -void __init eeh_addr_cache_build(void)
> -{
> - struct device_node *dn;
> - struct eeh_dev *edev;
> - struct pci_dev *dev = NULL;
> -
> - spin_lock_init(&pci_io_addr_cache_root.piar_lock);
> -
> - for_each_pci_dev(dev) {
> - eeh_addr_cache_insert_dev(dev);
> -
> - dn = pci_device_to_OF_node(dev);
> - if (!dn)
> - continue;
> -
> - edev = of_node_to_eeh_dev(dn);
> - if (!edev)
> - continue;
> -
> - pci_dev_get(dev); /* matching put is in eeh_remove_device() */
> - dev->dev.archdata.edev = edev;
> - edev->pdev = dev;
> -
> - eeh_sysfs_add_device(dev);
> - }
> -
> -#ifdef DEBUG
> - /* Verify tree built up above, echo back the list of addrs. */
> - eeh_addr_cache_print(&pci_io_addr_cache_root);
> -#endif
> -}
> -
> diff --git a/arch/powerpc/platforms/pseries/eeh_dev.c b/arch/powerpc/platforms/pseries/eeh_dev.c
> deleted file mode 100644
> index 1efa28f..0000000
> --- a/arch/powerpc/platforms/pseries/eeh_dev.c
> +++ /dev/null
> @@ -1,112 +0,0 @@
> -/*
> - * The file intends to implement dynamic creation of EEH device, which will
> - * be bound with OF node and PCI device simutaneously. The EEH devices would
> - * be foundamental information for EEH core components to work proerly. Besides,
> - * We have to support multiple situations where dynamic creation of EEH device
> - * is required:
> - *
> - * 1) Before PCI emunation starts, we need create EEH devices according to the
> - * PCI sensitive OF nodes.
> - * 2) When PCI emunation is done, we need do the binding between PCI device and
> - * the associated EEH device.
> - * 3) DR (Dynamic Reconfiguration) would create PCI sensitive OF node. EEH device
> - * will be created while PCI sensitive OF node is detected from DR.
> - * 4) PCI hotplug needs redoing the binding between PCI device and EEH device. If
> - * PHB is newly inserted, we also need create EEH devices accordingly.
> - *
> - * Copyright Benjamin Herrenschmidt & Gavin Shan, IBM Corporation 2012.
> - *
> - * This program is free software; you can redistribute it and/or modify
> - * it under the terms of the GNU General Public License as published by
> - * the Free Software Foundation; either version 2 of the License, or
> - * (at your option) any later version.
> - *
> - * This program is distributed in the hope that it will be useful,
> - * but WITHOUT ANY WARRANTY; without even the implied warranty of
> - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> - * GNU General Public License for more details.
> - *
> - * You should have received a copy of the GNU General Public License
> - * along with this program; if not, write to the Free Software
> - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
> - */
> -
> -#include <linux/export.h>
> -#include <linux/gfp.h>
> -#include <linux/init.h>
> -#include <linux/kernel.h>
> -#include <linux/pci.h>
> -#include <linux/string.h>
> -
> -#include <asm/pci-bridge.h>
> -#include <asm/ppc-pci.h>
> -
> -/**
> - * eeh_dev_init - Create EEH device according to OF node
> - * @dn: device node
> - * @data: PHB
> - *
> - * It will create EEH device according to the given OF node. The function
> - * might be called by PCI emunation, DR, PHB hotplug.
> - */
> -void *eeh_dev_init(struct device_node *dn, void *data)
> -{
> - struct pci_controller *phb = data;
> - struct eeh_dev *edev;
> -
> - /* Allocate EEH device */
> - edev = kzalloc(sizeof(*edev), GFP_KERNEL);
> - if (!edev) {
> - pr_warning("%s: out of memory\n", __func__);
> - return NULL;
> - }
> -
> - /* Associate EEH device with OF node */
> - PCI_DN(dn)->edev = edev;
> - edev->dn = dn;
> - edev->phb = phb;
> - INIT_LIST_HEAD(&edev->list);
> -
> - return NULL;
> -}
> -
> -/**
> - * eeh_dev_phb_init_dynamic - Create EEH devices for devices included in PHB
> - * @phb: PHB
> - *
> - * Scan the PHB OF node and its child association, then create the
> - * EEH devices accordingly
> - */
> -void eeh_dev_phb_init_dynamic(struct pci_controller *phb)
> -{
> - struct device_node *dn = phb->dn;
> -
> - /* EEH PE for PHB */
> - eeh_phb_pe_create(phb);
> -
> - /* EEH device for PHB */
> - eeh_dev_init(dn, phb);
> -
> - /* EEH devices for children OF nodes */
> - traverse_pci_devices(dn, eeh_dev_init, phb);
> -}
> -
> -/**
> - * eeh_dev_phb_init - Create EEH devices for devices included in existing PHBs
> - *
> - * Scan all the existing PHBs and create EEH devices for their OF
> - * nodes and their children OF nodes
> - */
> -static int __init eeh_dev_phb_init(void)
> -{
> - struct pci_controller *phb, *tmp;
> -
> - list_for_each_entry_safe(phb, tmp, &hose_list, list_node)
> - eeh_dev_phb_init_dynamic(phb);
> -
> - pr_info("EEH: devices created\n");
> -
> - return 0;
> -}
> -
> -core_initcall(eeh_dev_phb_init);
> diff --git a/arch/powerpc/platforms/pseries/eeh_driver.c b/arch/powerpc/platforms/pseries/eeh_driver.c
> deleted file mode 100644
> index a3fefb6..0000000
> --- a/arch/powerpc/platforms/pseries/eeh_driver.c
> +++ /dev/null
> @@ -1,552 +0,0 @@
> -/*
> - * PCI Error Recovery Driver for RPA-compliant PPC64 platform.
> - * Copyright IBM Corp. 2004 2005
> - * Copyright Linas Vepstas <linas@linas.org> 2004, 2005
> - *
> - * All rights reserved.
> - *
> - * This program is free software; you can redistribute it and/or modify
> - * it under the terms of the GNU General Public License as published by
> - * the Free Software Foundation; either version 2 of the License, or (at
> - * your option) any later version.
> - *
> - * This program is distributed in the hope that it will be useful, but
> - * WITHOUT ANY WARRANTY; without even the implied warranty of
> - * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or
> - * NON INFRINGEMENT. See the GNU General Public License for more
> - * details.
> - *
> - * You should have received a copy of the GNU General Public License
> - * along with this program; if not, write to the Free Software
> - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
> - *
> - * Send comments and feedback to Linas Vepstas <linas@austin.ibm.com>
> - */
> -#include <linux/delay.h>
> -#include <linux/interrupt.h>
> -#include <linux/irq.h>
> -#include <linux/module.h>
> -#include <linux/pci.h>
> -#include <asm/eeh.h>
> -#include <asm/eeh_event.h>
> -#include <asm/ppc-pci.h>
> -#include <asm/pci-bridge.h>
> -#include <asm/prom.h>
> -#include <asm/rtas.h>
> -
> -/**
> - * eeh_pcid_name - Retrieve name of PCI device driver
> - * @pdev: PCI device
> - *
> - * This routine is used to retrieve the name of PCI device driver
> - * if that's valid.
> - */
> -static inline const char *eeh_pcid_name(struct pci_dev *pdev)
> -{
> - if (pdev && pdev->dev.driver)
> - return pdev->dev.driver->name;
> - return "";
> -}
> -
> -/**
> - * eeh_pcid_get - Get the PCI device driver
> - * @pdev: PCI device
> - *
> - * The function is used to retrieve the PCI device driver for
> - * the indicated PCI device. Besides, we will increase the reference
> - * of the PCI device driver to prevent that being unloaded on
> - * the fly. Otherwise, kernel crash would be seen.
> - */
> -static inline struct pci_driver *eeh_pcid_get(struct pci_dev *pdev)
> -{
> - if (!pdev || !pdev->driver)
> - return NULL;
> -
> - if (!try_module_get(pdev->driver->driver.owner))
> - return NULL;
> -
> - return pdev->driver;
> -}
> -
> -/**
> - * eeh_pcid_put - Dereference on the PCI device driver
> - * @pdev: PCI device
> - *
> - * The function is called to do dereference on the PCI device
> - * driver of the indicated PCI device.
> - */
> -static inline void eeh_pcid_put(struct pci_dev *pdev)
> -{
> - if (!pdev || !pdev->driver)
> - return;
> -
> - module_put(pdev->driver->driver.owner);
> -}
> -
> -#if 0
> -static void print_device_node_tree(struct pci_dn *pdn, int dent)
> -{
> - int i;
> - struct device_node *pc;
> -
> - if (!pdn)
> - return;
> - for (i = 0; i < dent; i++)
> - printk(" ");
> - printk("dn=%s mode=%x \tcfg_addr=%x pe_addr=%x \tfull=%s\n",
> - pdn->node->name, pdn->eeh_mode, pdn->eeh_config_addr,
> - pdn->eeh_pe_config_addr, pdn->node->full_name);
> - dent += 3;
> - pc = pdn->node->child;
> - while (pc) {
> - print_device_node_tree(PCI_DN(pc), dent);
> - pc = pc->sibling;
> - }
> -}
> -#endif
> -
> -/**
> - * eeh_disable_irq - Disable interrupt for the recovering device
> - * @dev: PCI device
> - *
> - * This routine must be called when reporting temporary or permanent
> - * error to the particular PCI device to disable interrupt of that
> - * device. If the device has enabled MSI or MSI-X interrupt, we needn't
> - * do real work because EEH should freeze DMA transfers for those PCI
> - * devices encountering EEH errors, which includes MSI or MSI-X.
> - */
> -static void eeh_disable_irq(struct pci_dev *dev)
> -{
> - struct eeh_dev *edev = pci_dev_to_eeh_dev(dev);
> -
> - /* Don't disable MSI and MSI-X interrupts. They are
> - * effectively disabled by the DMA Stopped state
> - * when an EEH error occurs.
> - */
> - if (dev->msi_enabled || dev->msix_enabled)
> - return;
> -
> - if (!irq_has_action(dev->irq))
> - return;
> -
> - edev->mode |= EEH_DEV_IRQ_DISABLED;
> - disable_irq_nosync(dev->irq);
> -}
> -
> -/**
> - * eeh_enable_irq - Enable interrupt for the recovering device
> - * @dev: PCI device
> - *
> - * This routine must be called to enable interrupt while failed
> - * device could be resumed.
> - */
> -static void eeh_enable_irq(struct pci_dev *dev)
> -{
> - struct eeh_dev *edev = pci_dev_to_eeh_dev(dev);
> -
> - if ((edev->mode) & EEH_DEV_IRQ_DISABLED) {
> - edev->mode &= ~EEH_DEV_IRQ_DISABLED;
> - enable_irq(dev->irq);
> - }
> -}
> -
> -/**
> - * eeh_report_error - Report pci error to each device driver
> - * @data: eeh device
> - * @userdata: return value
> - *
> - * Report an EEH error to each device driver, collect up and
> - * merge the device driver responses. Cumulative response
> - * passed back in "userdata".
> - */
> -static void *eeh_report_error(void *data, void *userdata)
> -{
> - struct eeh_dev *edev = (struct eeh_dev *)data;
> - struct pci_dev *dev = eeh_dev_to_pci_dev(edev);
> - enum pci_ers_result rc, *res = userdata;
> - struct pci_driver *driver;
> -
> - /* We might not have the associated PCI device,
> - * then we should continue for next one.
> - */
> - if (!dev) return NULL;
> - dev->error_state = pci_channel_io_frozen;
> -
> - driver = eeh_pcid_get(dev);
> - if (!driver) return NULL;
> -
> - eeh_disable_irq(dev);
> -
> - if (!driver->err_handler ||
> - !driver->err_handler->error_detected) {
> - eeh_pcid_put(dev);
> - return NULL;
> - }
> -
> - rc = driver->err_handler->error_detected(dev, pci_channel_io_frozen);
> -
> - /* A driver that needs a reset trumps all others */
> - if (rc == PCI_ERS_RESULT_NEED_RESET) *res = rc;
> - if (*res == PCI_ERS_RESULT_NONE) *res = rc;
> -
> - eeh_pcid_put(dev);
> - return NULL;
> -}
> -
> -/**
> - * eeh_report_mmio_enabled - Tell drivers that MMIO has been enabled
> - * @data: eeh device
> - * @userdata: return value
> - *
> - * Tells each device driver that IO ports, MMIO and config space I/O
> - * are now enabled. Collects up and merges the device driver responses.
> - * Cumulative response passed back in "userdata".
> - */
> -static void *eeh_report_mmio_enabled(void *data, void *userdata)
> -{
> - struct eeh_dev *edev = (struct eeh_dev *)data;
> - struct pci_dev *dev = eeh_dev_to_pci_dev(edev);
> - enum pci_ers_result rc, *res = userdata;
> - struct pci_driver *driver;
> -
> - driver = eeh_pcid_get(dev);
> - if (!driver) return NULL;
> -
> - if (!driver->err_handler ||
> - !driver->err_handler->mmio_enabled) {
> - eeh_pcid_put(dev);
> - return NULL;
> - }
> -
> - rc = driver->err_handler->mmio_enabled(dev);
> -
> - /* A driver that needs a reset trumps all others */
> - if (rc == PCI_ERS_RESULT_NEED_RESET) *res = rc;
> - if (*res == PCI_ERS_RESULT_NONE) *res = rc;
> -
> - eeh_pcid_put(dev);
> - return NULL;
> -}
> -
> -/**
> - * eeh_report_reset - Tell device that slot has been reset
> - * @data: eeh device
> - * @userdata: return value
> - *
> - * This routine must be called while EEH tries to reset particular
> - * PCI device so that the associated PCI device driver could take
> - * some actions, usually to save data the driver needs so that the
> - * driver can work again while the device is recovered.
> - */
> -static void *eeh_report_reset(void *data, void *userdata)
> -{
> - struct eeh_dev *edev = (struct eeh_dev *)data;
> - struct pci_dev *dev = eeh_dev_to_pci_dev(edev);
> - enum pci_ers_result rc, *res = userdata;
> - struct pci_driver *driver;
> -
> - if (!dev) return NULL;
> - dev->error_state = pci_channel_io_normal;
> -
> - driver = eeh_pcid_get(dev);
> - if (!driver) return NULL;
> -
> - eeh_enable_irq(dev);
> -
> - if (!driver->err_handler ||
> - !driver->err_handler->slot_reset) {
> - eeh_pcid_put(dev);
> - return NULL;
> - }
> -
> - rc = driver->err_handler->slot_reset(dev);
> - if ((*res == PCI_ERS_RESULT_NONE) ||
> - (*res == PCI_ERS_RESULT_RECOVERED)) *res = rc;
> - if (*res == PCI_ERS_RESULT_DISCONNECT &&
> - rc == PCI_ERS_RESULT_NEED_RESET) *res = rc;
> -
> - eeh_pcid_put(dev);
> - return NULL;
> -}
> -
> -/**
> - * eeh_report_resume - Tell device to resume normal operations
> - * @data: eeh device
> - * @userdata: return value
> - *
> - * This routine must be called to notify the device driver that it
> - * could resume so that the device driver can do some initialization
> - * to make the recovered device work again.
> - */
> -static void *eeh_report_resume(void *data, void *userdata)
> -{
> - struct eeh_dev *edev = (struct eeh_dev *)data;
> - struct pci_dev *dev = eeh_dev_to_pci_dev(edev);
> - struct pci_driver *driver;
> -
> - if (!dev) return NULL;
> - dev->error_state = pci_channel_io_normal;
> -
> - driver = eeh_pcid_get(dev);
> - if (!driver) return NULL;
> -
> - eeh_enable_irq(dev);
> -
> - if (!driver->err_handler ||
> - !driver->err_handler->resume) {
> - eeh_pcid_put(dev);
> - return NULL;
> - }
> -
> - driver->err_handler->resume(dev);
> -
> - eeh_pcid_put(dev);
> - return NULL;
> -}
> -
> -/**
> - * eeh_report_failure - Tell device driver that device is dead.
> - * @data: eeh device
> - * @userdata: return value
> - *
> - * This informs the device driver that the device is permanently
> - * dead, and that no further recovery attempts will be made on it.
> - */
> -static void *eeh_report_failure(void *data, void *userdata)
> -{
> - struct eeh_dev *edev = (struct eeh_dev *)data;
> - struct pci_dev *dev = eeh_dev_to_pci_dev(edev);
> - struct pci_driver *driver;
> -
> - if (!dev) return NULL;
> - dev->error_state = pci_channel_io_perm_failure;
> -
> - driver = eeh_pcid_get(dev);
> - if (!driver) return NULL;
> -
> - eeh_disable_irq(dev);
> -
> - if (!driver->err_handler ||
> - !driver->err_handler->error_detected) {
> - eeh_pcid_put(dev);
> - return NULL;
> - }
> -
> - driver->err_handler->error_detected(dev, pci_channel_io_perm_failure);
> -
> - eeh_pcid_put(dev);
> - return NULL;
> -}
> -
> -/**
> - * eeh_reset_device - Perform actual reset of a pci slot
> - * @pe: EEH PE
> - * @bus: PCI bus corresponding to the isolcated slot
> - *
> - * This routine must be called to do reset on the indicated PE.
> - * During the reset, udev might be invoked because those affected
> - * PCI devices will be removed and then added.
> - */
> -static int eeh_reset_device(struct eeh_pe *pe, struct pci_bus *bus)
> -{
> - int cnt, rc;
> -
> - /* pcibios will clear the counter; save the value */
> - cnt = pe->freeze_count;
> -
> - /*
> - * We don't remove the corresponding PE instances because
> - * we need the information afterwords. The attached EEH
> - * devices are expected to be attached soon when calling
> - * into pcibios_add_pci_devices().
> - */
> - if (bus)
> - __pcibios_remove_pci_devices(bus, 0);
> -
> - /* Reset the pci controller. (Asserts RST#; resets config space).
> - * Reconfigure bridges and devices. Don't try to bring the system
> - * up if the reset failed for some reason.
> - */
> - rc = eeh_reset_pe(pe);
> - if (rc)
> - return rc;
> -
> - /* Restore PE */
> - eeh_ops->configure_bridge(pe);
> - eeh_pe_restore_bars(pe);
> -
> - /* Give the system 5 seconds to finish running the user-space
> - * hotplug shutdown scripts, e.g. ifdown for ethernet. Yes,
> - * this is a hack, but if we don't do this, and try to bring
> - * the device up before the scripts have taken it down,
> - * potentially weird things happen.
> - */
> - if (bus) {
> - ssleep(5);
> - pcibios_add_pci_devices(bus);
> - }
> - pe->freeze_count = cnt;
> -
> - return 0;
> -}
> -
> -/* The longest amount of time to wait for a pci device
> - * to come back on line, in seconds.
> - */
> -#define MAX_WAIT_FOR_RECOVERY 150
> -
> -/**
> - * eeh_handle_event - Reset a PCI device after hard lockup.
> - * @pe: EEH PE
> - *
> - * While PHB detects address or data parity errors on particular PCI
> - * slot, the associated PE will be frozen. Besides, DMA's occurring
> - * to wild addresses (which usually happen due to bugs in device
> - * drivers or in PCI adapter firmware) can cause EEH error. #SERR,
> - * #PERR or other misc PCI-related errors also can trigger EEH errors.
> - *
> - * Recovery process consists of unplugging the device driver (which
> - * generated hotplug events to userspace), then issuing a PCI #RST to
> - * the device, then reconfiguring the PCI config space for all bridges
> - * & devices under this slot, and then finally restarting the device
> - * drivers (which cause a second set of hotplug events to go out to
> - * userspace).
> - */
> -void eeh_handle_event(struct eeh_pe *pe)
> -{
> - struct pci_bus *frozen_bus;
> - int rc = 0;
> - enum pci_ers_result result = PCI_ERS_RESULT_NONE;
> -
> - frozen_bus = eeh_pe_bus_get(pe);
> - if (!frozen_bus) {
> - pr_err("%s: Cannot find PCI bus for PHB#%d-PE#%x\n",
> - __func__, pe->phb->global_number, pe->addr);
> - return;
> - }
> -
> - pe->freeze_count++;
> - if (pe->freeze_count > EEH_MAX_ALLOWED_FREEZES)
> - goto excess_failures;
> - pr_warning("EEH: This PCI device has failed %d times in the last hour\n",
> - pe->freeze_count);
> -
> - /* Walk the various device drivers attached to this slot through
> - * a reset sequence, giving each an opportunity to do what it needs
> - * to accomplish the reset. Each child gets a report of the
> - * status ... if any child can't handle the reset, then the entire
> - * slot is dlpar removed and added.
> - */
> - eeh_pe_dev_traverse(pe, eeh_report_error, &result);
> -
> - /* Get the current PCI slot state. This can take a long time,
> - * sometimes over 3 seconds for certain systems.
> - */
> - rc = eeh_ops->wait_state(pe, MAX_WAIT_FOR_RECOVERY*1000);
> - if (rc < 0 || rc == EEH_STATE_NOT_SUPPORT) {
> - printk(KERN_WARNING "EEH: Permanent failure\n");
> - goto hard_fail;
> - }
> -
> - /* Since rtas may enable MMIO when posting the error log,
> - * don't post the error log until after all dev drivers
> - * have been informed.
> - */
> - eeh_slot_error_detail(pe, EEH_LOG_TEMP);
> -
> - /* If all device drivers were EEH-unaware, then shut
> - * down all of the device drivers, and hope they
> - * go down willingly, without panicing the system.
> - */
> - if (result == PCI_ERS_RESULT_NONE) {
> - rc = eeh_reset_device(pe, frozen_bus);
> - if (rc) {
> - printk(KERN_WARNING "EEH: Unable to reset, rc=%d\n", rc);
> - goto hard_fail;
> - }
> - }
> -
> - /* If all devices reported they can proceed, then re-enable MMIO */
> - if (result == PCI_ERS_RESULT_CAN_RECOVER) {
> - rc = eeh_pci_enable(pe, EEH_OPT_THAW_MMIO);
> -
> - if (rc < 0)
> - goto hard_fail;
> - if (rc) {
> - result = PCI_ERS_RESULT_NEED_RESET;
> - } else {
> - result = PCI_ERS_RESULT_NONE;
> - eeh_pe_dev_traverse(pe, eeh_report_mmio_enabled, &result);
> - }
> - }
> -
> - /* If all devices reported they can proceed, then re-enable DMA */
> - if (result == PCI_ERS_RESULT_CAN_RECOVER) {
> - rc = eeh_pci_enable(pe, EEH_OPT_THAW_DMA);
> -
> - if (rc < 0)
> - goto hard_fail;
> - if (rc)
> - result = PCI_ERS_RESULT_NEED_RESET;
> - else
> - result = PCI_ERS_RESULT_RECOVERED;
> - }
> -
> - /* If any device has a hard failure, then shut off everything. */
> - if (result == PCI_ERS_RESULT_DISCONNECT) {
> - printk(KERN_WARNING "EEH: Device driver gave up\n");
> - goto hard_fail;
> - }
> -
> - /* If any device called out for a reset, then reset the slot */
> - if (result == PCI_ERS_RESULT_NEED_RESET) {
> - rc = eeh_reset_device(pe, NULL);
> - if (rc) {
> - printk(KERN_WARNING "EEH: Cannot reset, rc=%d\n", rc);
> - goto hard_fail;
> - }
> - result = PCI_ERS_RESULT_NONE;
> - eeh_pe_dev_traverse(pe, eeh_report_reset, &result);
> - }
> -
> - /* All devices should claim they have recovered by now. */
> - if ((result != PCI_ERS_RESULT_RECOVERED) &&
> - (result != PCI_ERS_RESULT_NONE)) {
> - printk(KERN_WARNING "EEH: Not recovered\n");
> - goto hard_fail;
> - }
> -
> - /* Tell all device drivers that they can resume operations */
> - eeh_pe_dev_traverse(pe, eeh_report_resume, NULL);
> -
> - return;
> -
> -excess_failures:
> - /*
> - * About 90% of all real-life EEH failures in the field
> - * are due to poorly seated PCI cards. Only 10% or so are
> - * due to actual, failed cards.
> - */
> - pr_err("EEH: PHB#%d-PE#%x has failed %d times in the\n"
> - "last hour and has been permanently disabled.\n"
> - "Please try reseating or replacing it.\n",
> - pe->phb->global_number, pe->addr,
> - pe->freeze_count);
> - goto perm_error;
> -
> -hard_fail:
> - pr_err("EEH: Unable to recover from failure from PHB#%d-PE#%x.\n"
> - "Please try reseating or replacing it\n",
> - pe->phb->global_number, pe->addr);
> -
> -perm_error:
> - eeh_slot_error_detail(pe, EEH_LOG_PERM);
> -
> - /* Notify all devices that they're about to go down. */
> - eeh_pe_dev_traverse(pe, eeh_report_failure, NULL);
> -
> - /* Shut down the device drivers for good. */
> - if (frozen_bus)
> - pcibios_remove_pci_devices(frozen_bus);
> -}
> -
> diff --git a/arch/powerpc/platforms/pseries/eeh_event.c b/arch/powerpc/platforms/pseries/eeh_event.c
> deleted file mode 100644
> index 185bedd..0000000
> --- a/arch/powerpc/platforms/pseries/eeh_event.c
> +++ /dev/null
> @@ -1,142 +0,0 @@
> -/*
> - * This program is free software; you can redistribute it and/or modify
> - * it under the terms of the GNU General Public License as published by
> - * the Free Software Foundation; either version 2 of the License, or
> - * (at your option) any later version.
> - *
> - * This program is distributed in the hope that it will be useful,
> - * but WITHOUT ANY WARRANTY; without even the implied warranty of
> - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> - * GNU General Public License for more details.
> - *
> - * You should have received a copy of the GNU General Public License
> - * along with this program; if not, write to the Free Software
> - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
> - *
> - * Copyright (c) 2005 Linas Vepstas <linas@linas.org>
> - */
> -
> -#include <linux/delay.h>
> -#include <linux/list.h>
> -#include <linux/mutex.h>
> -#include <linux/sched.h>
> -#include <linux/pci.h>
> -#include <linux/slab.h>
> -#include <linux/workqueue.h>
> -#include <linux/kthread.h>
> -#include <asm/eeh_event.h>
> -#include <asm/ppc-pci.h>
> -
> -/** Overview:
> - * EEH error states may be detected within exception handlers;
> - * however, the recovery processing needs to occur asynchronously
> - * in a normal kernel context and not an interrupt context.
> - * This pair of routines creates an event and queues it onto a
> - * work-queue, where a worker thread can drive recovery.
> - */
> -
> -/* EEH event workqueue setup. */
> -static DEFINE_SPINLOCK(eeh_eventlist_lock);
> -LIST_HEAD(eeh_eventlist);
> -static void eeh_thread_launcher(struct work_struct *);
> -DECLARE_WORK(eeh_event_wq, eeh_thread_launcher);
> -
> -/* Serialize reset sequences for a given pci device */
> -DEFINE_MUTEX(eeh_event_mutex);
> -
> -/**
> - * eeh_event_handler - Dispatch EEH events.
> - * @dummy - unused
> - *
> - * The detection of a frozen slot can occur inside an interrupt,
> - * where it can be hard to do anything about it. The goal of this
> - * routine is to pull these detection events out of the context
> - * of the interrupt handler, and re-dispatch them for processing
> - * at a later time in a normal context.
> - */
> -static int eeh_event_handler(void * dummy)
> -{
> - unsigned long flags;
> - struct eeh_event *event;
> - struct eeh_pe *pe;
> -
> - spin_lock_irqsave(&eeh_eventlist_lock, flags);
> - event = NULL;
> -
> - /* Unqueue the event, get ready to process. */
> - if (!list_empty(&eeh_eventlist)) {
> - event = list_entry(eeh_eventlist.next, struct eeh_event, list);
> - list_del(&event->list);
> - }
> - spin_unlock_irqrestore(&eeh_eventlist_lock, flags);
> -
> - if (event == NULL)
> - return 0;
> -
> - /* Serialize processing of EEH events */
> - mutex_lock(&eeh_event_mutex);
> - pe = event->pe;
> - eeh_pe_state_mark(pe, EEH_PE_RECOVERING);
> - pr_info("EEH: Detected PCI bus error on PHB#%d-PE#%x\n",
> - pe->phb->global_number, pe->addr);
> -
> - set_current_state(TASK_INTERRUPTIBLE); /* Don't add to load average */
> - eeh_handle_event(pe);
> - eeh_pe_state_clear(pe, EEH_PE_RECOVERING);
> -
> - kfree(event);
> - mutex_unlock(&eeh_event_mutex);
> -
> - /* If there are no new errors after an hour, clear the counter. */
> - if (pe && pe->freeze_count > 0) {
> - msleep_interruptible(3600*1000);
> - if (pe->freeze_count > 0)
> - pe->freeze_count--;
> -
> - }
> -
> - return 0;
> -}
> -
> -/**
> - * eeh_thread_launcher - Start kernel thread to handle EEH events
> - * @dummy - unused
> - *
> - * This routine is called to start the kernel thread for processing
> - * EEH event.
> - */
> -static void eeh_thread_launcher(struct work_struct *dummy)
> -{
> - if (IS_ERR(kthread_run(eeh_event_handler, NULL, "eehd")))
> - printk(KERN_ERR "Failed to start EEH daemon\n");
> -}
> -
> -/**
> - * eeh_send_failure_event - Generate a PCI error event
> - * @pe: EEH PE
> - *
> - * This routine can be called within an interrupt context;
> - * the actual event will be delivered in a normal context
> - * (from a workqueue).
> - */
> -int eeh_send_failure_event(struct eeh_pe *pe)
> -{
> - unsigned long flags;
> - struct eeh_event *event;
> -
> - event = kzalloc(sizeof(*event), GFP_ATOMIC);
> - if (!event) {
> - pr_err("EEH: out of memory, event not handled\n");
> - return -ENOMEM;
> - }
> - event->pe = pe;
> -
> - /* We may or may not be called in an interrupt context */
> - spin_lock_irqsave(&eeh_eventlist_lock, flags);
> - list_add(&event->list, &eeh_eventlist);
> - spin_unlock_irqrestore(&eeh_eventlist_lock, flags);
> -
> - schedule_work(&eeh_event_wq);
> -
> - return 0;
> -}
> diff --git a/arch/powerpc/platforms/pseries/eeh_pe.c b/arch/powerpc/platforms/pseries/eeh_pe.c
> deleted file mode 100644
> index 9d4a9e8..0000000
> --- a/arch/powerpc/platforms/pseries/eeh_pe.c
> +++ /dev/null
> @@ -1,653 +0,0 @@
> -/*
> - * The file intends to implement PE based on the information from
> - * platforms. Basically, there have 3 types of PEs: PHB/Bus/Device.
> - * All the PEs should be organized as hierarchy tree. The first level
> - * of the tree will be associated to existing PHBs since the particular
> - * PE is only meaningful in one PHB domain.
> - *
> - * Copyright Benjamin Herrenschmidt & Gavin Shan, IBM Corporation 2012.
> - *
> - * This program is free software; you can redistribute it and/or modify
> - * it under the terms of the GNU General Public License as published by
> - * the Free Software Foundation; either version 2 of the License, or
> - * (at your option) any later version.
> - *
> - * This program is distributed in the hope that it will be useful,
> - * but WITHOUT ANY WARRANTY; without even the implied warranty of
> - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> - * GNU General Public License for more details.
> - *
> - * You should have received a copy of the GNU General Public License
> - * along with this program; if not, write to the Free Software
> - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
> - */
> -
> -#include <linux/export.h>
> -#include <linux/gfp.h>
> -#include <linux/init.h>
> -#include <linux/kernel.h>
> -#include <linux/pci.h>
> -#include <linux/string.h>
> -
> -#include <asm/pci-bridge.h>
> -#include <asm/ppc-pci.h>
> -
> -static LIST_HEAD(eeh_phb_pe);
> -
> -/**
> - * eeh_pe_alloc - Allocate PE
> - * @phb: PCI controller
> - * @type: PE type
> - *
> - * Allocate PE instance dynamically.
> - */
> -static struct eeh_pe *eeh_pe_alloc(struct pci_controller *phb, int type)
> -{
> - struct eeh_pe *pe;
> -
> - /* Allocate PHB PE */
> - pe = kzalloc(sizeof(struct eeh_pe), GFP_KERNEL);
> - if (!pe) return NULL;
> -
> - /* Initialize PHB PE */
> - pe->type = type;
> - pe->phb = phb;
> - INIT_LIST_HEAD(&pe->child_list);
> - INIT_LIST_HEAD(&pe->child);
> - INIT_LIST_HEAD(&pe->edevs);
> -
> - return pe;
> -}
> -
> -/**
> - * eeh_phb_pe_create - Create PHB PE
> - * @phb: PCI controller
> - *
> - * The function should be called while the PHB is detected during
> - * system boot or PCI hotplug in order to create PHB PE.
> - */
> -int eeh_phb_pe_create(struct pci_controller *phb)
> -{
> - struct eeh_pe *pe;
> -
> - /* Allocate PHB PE */
> - pe = eeh_pe_alloc(phb, EEH_PE_PHB);
> - if (!pe) {
> - pr_err("%s: out of memory!\n", __func__);
> - return -ENOMEM;
> - }
> -
> - /* Put it into the list */
> - eeh_lock();
> - list_add_tail(&pe->child, &eeh_phb_pe);
> - eeh_unlock();
> -
> - pr_debug("EEH: Add PE for PHB#%d\n", phb->global_number);
> -
> - return 0;
> -}
> -
> -/**
> - * eeh_phb_pe_get - Retrieve PHB PE based on the given PHB
> - * @phb: PCI controller
> - *
> - * The overall PEs form hierarchy tree. The first layer of the
> - * hierarchy tree is composed of PHB PEs. The function is used
> - * to retrieve the corresponding PHB PE according to the given PHB.
> - */
> -static struct eeh_pe *eeh_phb_pe_get(struct pci_controller *phb)
> -{
> - struct eeh_pe *pe;
> -
> - list_for_each_entry(pe, &eeh_phb_pe, child) {
> - /*
> - * Actually, we needn't check the type since
> - * the PE for PHB has been determined when that
> - * was created.
> - */
> - if ((pe->type & EEH_PE_PHB) && pe->phb == phb)
> - return pe;
> - }
> -
> - return NULL;
> -}
> -
> -/**
> - * eeh_pe_next - Retrieve the next PE in the tree
> - * @pe: current PE
> - * @root: root PE
> - *
> - * The function is used to retrieve the next PE in the
> - * hierarchy PE tree.
> - */
> -static struct eeh_pe *eeh_pe_next(struct eeh_pe *pe,
> - struct eeh_pe *root)
> -{
> - struct list_head *next = pe->child_list.next;
> -
> - if (next == &pe->child_list) {
> - while (1) {
> - if (pe == root)
> - return NULL;
> - next = pe->child.next;
> - if (next != &pe->parent->child_list)
> - break;
> - pe = pe->parent;
> - }
> - }
> -
> - return list_entry(next, struct eeh_pe, child);
> -}
> -
> -/**
> - * eeh_pe_traverse - Traverse PEs in the specified PHB
> - * @root: root PE
> - * @fn: callback
> - * @flag: extra parameter to callback
> - *
> - * The function is used to traverse the specified PE and its
> - * child PEs. The traversing is to be terminated once the
> - * callback returns something other than NULL, or no more PEs
> - * to be traversed.
> - */
> -static void *eeh_pe_traverse(struct eeh_pe *root,
> - eeh_traverse_func fn, void *flag)
> -{
> - struct eeh_pe *pe;
> - void *ret;
> -
> - for (pe = root; pe; pe = eeh_pe_next(pe, root)) {
> - ret = fn(pe, flag);
> - if (ret) return ret;
> - }
> -
> - return NULL;
> -}
> -
> -/**
> - * eeh_pe_dev_traverse - Traverse the devices from the PE
> - * @root: EEH PE
> - * @fn: function callback
> - * @flag: extra parameter to callback
> - *
> - * The function is used to traverse the devices of the specified
> - * PE and its child PEs.
> - */
> -void *eeh_pe_dev_traverse(struct eeh_pe *root,
> - eeh_traverse_func fn, void *flag)
> -{
> - struct eeh_pe *pe;
> - struct eeh_dev *edev;
> - void *ret;
> -
> - if (!root) {
> - pr_warning("%s: Invalid PE %p\n", __func__, root);
> - return NULL;
> - }
> -
> - eeh_lock();
> -
> - /* Traverse root PE */
> - for (pe = root; pe; pe = eeh_pe_next(pe, root)) {
> - eeh_pe_for_each_dev(pe, edev) {
> - ret = fn(edev, flag);
> - if (ret) {
> - eeh_unlock();
> - return ret;
> - }
> - }
> - }
> -
> - eeh_unlock();
> -
> - return NULL;
> -}
> -
> -/**
> - * __eeh_pe_get - Check the PE address
> - * @data: EEH PE
> - * @flag: EEH device
> - *
> - * For one particular PE, it can be identified by PE address
> - * or tranditional BDF address. BDF address is composed of
> - * Bus/Device/Function number. The extra data referred by flag
> - * indicates which type of address should be used.
> - */
> -static void *__eeh_pe_get(void *data, void *flag)
> -{
> - struct eeh_pe *pe = (struct eeh_pe *)data;
> - struct eeh_dev *edev = (struct eeh_dev *)flag;
> -
> - /* Unexpected PHB PE */
> - if (pe->type & EEH_PE_PHB)
> - return NULL;
> -
> - /* We prefer PE address */
> - if (edev->pe_config_addr &&
> - (edev->pe_config_addr == pe->addr))
> - return pe;
> -
> - /* Try BDF address */
> - if (edev->pe_config_addr &&
> - (edev->config_addr == pe->config_addr))
> - return pe;
> -
> - return NULL;
> -}
> -
> -/**
> - * eeh_pe_get - Search PE based on the given address
> - * @edev: EEH device
> - *
> - * Search the corresponding PE based on the specified address which
> - * is included in the eeh device. The function is used to check if
> - * the associated PE has been created against the PE address. It's
> - * notable that the PE address has 2 format: traditional PE address
> - * which is composed of PCI bus/device/function number, or unified
> - * PE address.
> - */
> -static struct eeh_pe *eeh_pe_get(struct eeh_dev *edev)
> -{
> - struct eeh_pe *root = eeh_phb_pe_get(edev->phb);
> - struct eeh_pe *pe;
> -
> - pe = eeh_pe_traverse(root, __eeh_pe_get, edev);
> -
> - return pe;
> -}
> -
> -/**
> - * eeh_pe_get_parent - Retrieve the parent PE
> - * @edev: EEH device
> - *
> - * The whole PEs existing in the system are organized as hierarchy
> - * tree. The function is used to retrieve the parent PE according
> - * to the parent EEH device.
> - */
> -static struct eeh_pe *eeh_pe_get_parent(struct eeh_dev *edev)
> -{
> - struct device_node *dn;
> - struct eeh_dev *parent;
> -
> - /*
> - * It might have the case for the indirect parent
> - * EEH device already having associated PE, but
> - * the direct parent EEH device doesn't have yet.
> - */
> - dn = edev->dn->parent;
> - while (dn) {
> - /* We're poking out of PCI territory */
> - if (!PCI_DN(dn)) return NULL;
> -
> - parent = of_node_to_eeh_dev(dn);
> - /* We're poking out of PCI territory */
> - if (!parent) return NULL;
> -
> - if (parent->pe)
> - return parent->pe;
> -
> - dn = dn->parent;
> - }
> -
> - return NULL;
> -}
> -
> -/**
> - * eeh_add_to_parent_pe - Add EEH device to parent PE
> - * @edev: EEH device
> - *
> - * Add EEH device to the parent PE. If the parent PE already
> - * exists, the PE type will be changed to EEH_PE_BUS. Otherwise,
> - * we have to create new PE to hold the EEH device and the new
> - * PE will be linked to its parent PE as well.
> - */
> -int eeh_add_to_parent_pe(struct eeh_dev *edev)
> -{
> - struct eeh_pe *pe, *parent;
> -
> - eeh_lock();
> -
> - /*
> - * Search the PE has been existing or not according
> - * to the PE address. If that has been existing, the
> - * PE should be composed of PCI bus and its subordinate
> - * components.
> - */
> - pe = eeh_pe_get(edev);
> - if (pe && !(pe->type & EEH_PE_INVALID)) {
> - if (!edev->pe_config_addr) {
> - eeh_unlock();
> - pr_err("%s: PE with addr 0x%x already exists\n",
> - __func__, edev->config_addr);
> - return -EEXIST;
> - }
> -
> - /* Mark the PE as type of PCI bus */
> - pe->type = EEH_PE_BUS;
> - edev->pe = pe;
> -
> - /* Put the edev to PE */
> - list_add_tail(&edev->list, &pe->edevs);
> - eeh_unlock();
> - pr_debug("EEH: Add %s to Bus PE#%x\n",
> - edev->dn->full_name, pe->addr);
> -
> - return 0;
> - } else if (pe && (pe->type & EEH_PE_INVALID)) {
> - list_add_tail(&edev->list, &pe->edevs);
> - edev->pe = pe;
> - /*
> - * We're running to here because of PCI hotplug caused by
> - * EEH recovery. We need clear EEH_PE_INVALID until the top.
> - */
> - parent = pe;
> - while (parent) {
> - if (!(parent->type & EEH_PE_INVALID))
> - break;
> - parent->type &= ~EEH_PE_INVALID;
> - parent = parent->parent;
> - }
> - eeh_unlock();
> - pr_debug("EEH: Add %s to Device PE#%x, Parent PE#%x\n",
> - edev->dn->full_name, pe->addr, pe->parent->addr);
> -
> - return 0;
> - }
> -
> - /* Create a new EEH PE */
> - pe = eeh_pe_alloc(edev->phb, EEH_PE_DEVICE);
> - if (!pe) {
> - eeh_unlock();
> - pr_err("%s: out of memory!\n", __func__);
> - return -ENOMEM;
> - }
> - pe->addr = edev->pe_config_addr;
> - pe->config_addr = edev->config_addr;
> -
> - /*
> - * Put the new EEH PE into hierarchy tree. If the parent
> - * can't be found, the newly created PE will be attached
> - * to PHB directly. Otherwise, we have to associate the
> - * PE with its parent.
> - */
> - parent = eeh_pe_get_parent(edev);
> - if (!parent) {
> - parent = eeh_phb_pe_get(edev->phb);
> - if (!parent) {
> - eeh_unlock();
> - pr_err("%s: No PHB PE is found (PHB Domain=%d)\n",
> - __func__, edev->phb->global_number);
> - edev->pe = NULL;
> - kfree(pe);
> - return -EEXIST;
> - }
> - }
> - pe->parent = parent;
> -
> - /*
> - * Put the newly created PE into the child list and
> - * link the EEH device accordingly.
> - */
> - list_add_tail(&pe->child, &parent->child_list);
> - list_add_tail(&edev->list, &pe->edevs);
> - edev->pe = pe;
> - eeh_unlock();
> - pr_debug("EEH: Add %s to Device PE#%x, Parent PE#%x\n",
> - edev->dn->full_name, pe->addr, pe->parent->addr);
> -
> - return 0;
> -}
> -
> -/**
> - * eeh_rmv_from_parent_pe - Remove one EEH device from the associated PE
> - * @edev: EEH device
> - * @purge_pe: remove PE or not
> - *
> - * The PE hierarchy tree might be changed when doing PCI hotplug.
> - * Also, the PCI devices or buses could be removed from the system
> - * during EEH recovery. So we have to call the function remove the
> - * corresponding PE accordingly if necessary.
> - */
> -int eeh_rmv_from_parent_pe(struct eeh_dev *edev, int purge_pe)
> -{
> - struct eeh_pe *pe, *parent, *child;
> - int cnt;
> -
> - if (!edev->pe) {
> - pr_warning("%s: No PE found for EEH device %s\n",
> - __func__, edev->dn->full_name);
> - return -EEXIST;
> - }
> -
> - eeh_lock();
> -
> - /* Remove the EEH device */
> - pe = edev->pe;
> - edev->pe = NULL;
> - list_del(&edev->list);
> -
> - /*
> - * Check if the parent PE includes any EEH devices.
> - * If not, we should delete that. Also, we should
> - * delete the parent PE if it doesn't have associated
> - * child PEs and EEH devices.
> - */
> - while (1) {
> - parent = pe->parent;
> - if (pe->type & EEH_PE_PHB)
> - break;
> -
> - if (purge_pe) {
> - if (list_empty(&pe->edevs) &&
> - list_empty(&pe->child_list)) {
> - list_del(&pe->child);
> - kfree(pe);
> - } else {
> - break;
> - }
> - } else {
> - if (list_empty(&pe->edevs)) {
> - cnt = 0;
> - list_for_each_entry(child, &pe->child_list, child) {
> - if (!(child->type & EEH_PE_INVALID)) {
> - cnt++;
> - break;
> - }
> - }
> -
> - if (!cnt)
> - pe->type |= EEH_PE_INVALID;
> - else
> - break;
> - }
> - }
> -
> - pe = parent;
> - }
> -
> - eeh_unlock();
> -
> - return 0;
> -}
> -
> -/**
> - * __eeh_pe_state_mark - Mark the state for the PE
> - * @data: EEH PE
> - * @flag: state
> - *
> - * The function is used to mark the indicated state for the given
> - * PE. Also, the associated PCI devices will be put into IO frozen
> - * state as well.
> - */
> -static void *__eeh_pe_state_mark(void *data, void *flag)
> -{
> - struct eeh_pe *pe = (struct eeh_pe *)data;
> - int state = *((int *)flag);
> - struct eeh_dev *tmp;
> - struct pci_dev *pdev;
> -
> - /*
> - * Mark the PE with the indicated state. Also,
> - * the associated PCI device will be put into
> - * I/O frozen state to avoid I/O accesses from
> - * the PCI device driver.
> - */
> - pe->state |= state;
> - eeh_pe_for_each_dev(pe, tmp) {
> - pdev = eeh_dev_to_pci_dev(tmp);
> - if (pdev)
> - pdev->error_state = pci_channel_io_frozen;
> - }
> -
> - return NULL;
> -}
> -
> -/**
> - * eeh_pe_state_mark - Mark specified state for PE and its associated device
> - * @pe: EEH PE
> - *
> - * EEH error affects the current PE and its child PEs. The function
> - * is used to mark appropriate state for the affected PEs and the
> - * associated devices.
> - */
> -void eeh_pe_state_mark(struct eeh_pe *pe, int state)
> -{
> - eeh_lock();
> - eeh_pe_traverse(pe, __eeh_pe_state_mark, &state);
> - eeh_unlock();
> -}
> -
> -/**
> - * __eeh_pe_state_clear - Clear state for the PE
> - * @data: EEH PE
> - * @flag: state
> - *
> - * The function is used to clear the indicated state from the
> - * given PE. Besides, we also clear the check count of the PE
> - * as well.
> - */
> -static void *__eeh_pe_state_clear(void *data, void *flag)
> -{
> - struct eeh_pe *pe = (struct eeh_pe *)data;
> - int state = *((int *)flag);
> -
> - pe->state &= ~state;
> - pe->check_count = 0;
> -
> - return NULL;
> -}
> -
> -/**
> - * eeh_pe_state_clear - Clear state for the PE and its children
> - * @pe: PE
> - * @state: state to be cleared
> - *
> - * When the PE and its children has been recovered from error,
> - * we need clear the error state for that. The function is used
> - * for the purpose.
> - */
> -void eeh_pe_state_clear(struct eeh_pe *pe, int state)
> -{
> - eeh_lock();
> - eeh_pe_traverse(pe, __eeh_pe_state_clear, &state);
> - eeh_unlock();
> -}
> -
> -/**
> - * eeh_restore_one_device_bars - Restore the Base Address Registers for one device
> - * @data: EEH device
> - * @flag: Unused
> - *
> - * Loads the PCI configuration space base address registers,
> - * the expansion ROM base address, the latency timer, and etc.
> - * from the saved values in the device node.
> - */
> -static void *eeh_restore_one_device_bars(void *data, void *flag)
> -{
> - int i;
> - u32 cmd;
> - struct eeh_dev *edev = (struct eeh_dev *)data;
> - struct device_node *dn = eeh_dev_to_of_node(edev);
> -
> - for (i = 4; i < 10; i++)
> - eeh_ops->write_config(dn, i*4, 4, edev->config_space[i]);
> - /* 12 == Expansion ROM Address */
> - eeh_ops->write_config(dn, 12*4, 4, edev->config_space[12]);
> -
> -#define BYTE_SWAP(OFF) (8*((OFF)/4)+3-(OFF))
> -#define SAVED_BYTE(OFF) (((u8 *)(edev->config_space))[BYTE_SWAP(OFF)])
> -
> - eeh_ops->write_config(dn, PCI_CACHE_LINE_SIZE, 1,
> - SAVED_BYTE(PCI_CACHE_LINE_SIZE));
> - eeh_ops->write_config(dn, PCI_LATENCY_TIMER, 1,
> - SAVED_BYTE(PCI_LATENCY_TIMER));
> -
> - /* max latency, min grant, interrupt pin and line */
> - eeh_ops->write_config(dn, 15*4, 4, edev->config_space[15]);
> -
> - /*
> - * Restore PERR & SERR bits, some devices require it,
> - * don't touch the other command bits
> - */
> - eeh_ops->read_config(dn, PCI_COMMAND, 4, &cmd);
> - if (edev->config_space[1] & PCI_COMMAND_PARITY)
> - cmd |= PCI_COMMAND_PARITY;
> - else
> - cmd &= ~PCI_COMMAND_PARITY;
> - if (edev->config_space[1] & PCI_COMMAND_SERR)
> - cmd |= PCI_COMMAND_SERR;
> - else
> - cmd &= ~PCI_COMMAND_SERR;
> - eeh_ops->write_config(dn, PCI_COMMAND, 4, cmd);
> -
> - return NULL;
> -}
> -
> -/**
> - * eeh_pe_restore_bars - Restore the PCI config space info
> - * @pe: EEH PE
> - *
> - * This routine performs a recursive walk to the children
> - * of this device as well.
> - */
> -void eeh_pe_restore_bars(struct eeh_pe *pe)
> -{
> - /*
> - * We needn't take the EEH lock since eeh_pe_dev_traverse()
> - * will take that.
> - */
> - eeh_pe_dev_traverse(pe, eeh_restore_one_device_bars, NULL);
> -}
> -
> -/**
> - * eeh_pe_bus_get - Retrieve PCI bus according to the given PE
> - * @pe: EEH PE
> - *
> - * Retrieve the PCI bus according to the given PE. Basically,
> - * there're 3 types of PEs: PHB/Bus/Device. For PHB PE, the
> - * primary PCI bus will be retrieved. The parent bus will be
> - * returned for BUS PE. However, we don't have associated PCI
> - * bus for DEVICE PE.
> - */
> -struct pci_bus *eeh_pe_bus_get(struct eeh_pe *pe)
> -{
> - struct pci_bus *bus = NULL;
> - struct eeh_dev *edev;
> - struct pci_dev *pdev;
> -
> - eeh_lock();
> -
> - if (pe->type & EEH_PE_PHB) {
> - bus = pe->phb->bus;
> - } else if (pe->type & EEH_PE_BUS ||
> - pe->type & EEH_PE_DEVICE) {
> - edev = list_first_entry(&pe->edevs, struct eeh_dev, list);
> - pdev = eeh_dev_to_pci_dev(edev);
> - if (pdev)
> - bus = pdev->bus;
> - }
> -
> - eeh_unlock();
> -
> - return bus;
> -}
> diff --git a/arch/powerpc/platforms/pseries/eeh_sysfs.c b/arch/powerpc/platforms/pseries/eeh_sysfs.c
> deleted file mode 100644
> index d377083..0000000
> --- a/arch/powerpc/platforms/pseries/eeh_sysfs.c
> +++ /dev/null
> @@ -1,75 +0,0 @@
> -/*
> - * Sysfs entries for PCI Error Recovery for PAPR-compliant platform.
> - * Copyright IBM Corporation 2007
> - * Copyright Linas Vepstas <linas@austin.ibm.com> 2007
> - *
> - * All rights reserved.
> - *
> - * This program is free software; you can redistribute it and/or modify
> - * it under the terms of the GNU General Public License as published by
> - * the Free Software Foundation; either version 2 of the License, or (at
> - * your option) any later version.
> - *
> - * This program is distributed in the hope that it will be useful, but
> - * WITHOUT ANY WARRANTY; without even the implied warranty of
> - * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or
> - * NON INFRINGEMENT. See the GNU General Public License for more
> - * details.
> - *
> - * You should have received a copy of the GNU General Public License
> - * along with this program; if not, write to the Free Software
> - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
> - *
> - * Send comments and feedback to Linas Vepstas <linas@austin.ibm.com>
> - */
> -#include <linux/pci.h>
> -#include <linux/stat.h>
> -#include <asm/ppc-pci.h>
> -#include <asm/pci-bridge.h>
> -
> -/**
> - * EEH_SHOW_ATTR -- Create sysfs entry for eeh statistic
> - * @_name: name of file in sysfs directory
> - * @_memb: name of member in struct pci_dn to access
> - * @_format: printf format for display
> - *
> - * All of the attributes look very similar, so just
> - * auto-gen a cut-n-paste routine to display them.
> - */
> -#define EEH_SHOW_ATTR(_name,_memb,_format) \
> -static ssize_t eeh_show_##_name(struct device *dev, \
> - struct device_attribute *attr, char *buf) \
> -{ \
> - struct pci_dev *pdev = to_pci_dev(dev); \
> - struct eeh_dev *edev = pci_dev_to_eeh_dev(pdev); \
> - \
> - if (!edev) \
> - return 0; \
> - \
> - return sprintf(buf, _format "\n", edev->_memb); \
> -} \
> -static DEVICE_ATTR(_name, S_IRUGO, eeh_show_##_name, NULL);
> -
> -EEH_SHOW_ATTR(eeh_mode, mode, "0x%x");
> -EEH_SHOW_ATTR(eeh_config_addr, config_addr, "0x%x");
> -EEH_SHOW_ATTR(eeh_pe_config_addr, pe_config_addr, "0x%x");
> -
> -void eeh_sysfs_add_device(struct pci_dev *pdev)
> -{
> - int rc=0;
> -
> - rc += device_create_file(&pdev->dev, &dev_attr_eeh_mode);
> - rc += device_create_file(&pdev->dev, &dev_attr_eeh_config_addr);
> - rc += device_create_file(&pdev->dev, &dev_attr_eeh_pe_config_addr);
> -
> - if (rc)
> - printk(KERN_WARNING "EEH: Unable to create sysfs entries\n");
> -}
> -
> -void eeh_sysfs_remove_device(struct pci_dev *pdev)
> -{
> - device_remove_file(&pdev->dev, &dev_attr_eeh_mode);
> - device_remove_file(&pdev->dev, &dev_attr_eeh_config_addr);
> - device_remove_file(&pdev->dev, &dev_attr_eeh_pe_config_addr);
> -}
> -
> diff --git a/arch/powerpc/platforms/pseries/pci_dlpar.c b/arch/powerpc/platforms/pseries/pci_dlpar.c
> index c91b22b..efe6137 100644
> --- a/arch/powerpc/platforms/pseries/pci_dlpar.c
> +++ b/arch/powerpc/platforms/pseries/pci_dlpar.c
> @@ -64,91 +64,6 @@ pcibios_find_pci_bus(struct device_node *dn)
> }
> EXPORT_SYMBOL_GPL(pcibios_find_pci_bus);
>
> -/**
> - * __pcibios_remove_pci_devices - remove all devices under this bus
> - * @bus: the indicated PCI bus
> - * @purge_pe: destroy the PE on removal of PCI devices
> - *
> - * Remove all of the PCI devices under this bus both from the
> - * linux pci device tree, and from the powerpc EEH address cache.
> - * By default, the corresponding PE will be destroied during the
> - * normal PCI hotplug path. For PCI hotplug during EEH recovery,
> - * the corresponding PE won't be destroied and deallocated.
> - */
> -void __pcibios_remove_pci_devices(struct pci_bus *bus, int purge_pe)
> -{
> - struct pci_dev *dev, *tmp;
> - struct pci_bus *child_bus;
> -
> - /* First go down child busses */
> - list_for_each_entry(child_bus, &bus->children, node)
> - __pcibios_remove_pci_devices(child_bus, purge_pe);
> -
> - pr_debug("PCI: Removing devices on bus %04x:%02x\n",
> - pci_domain_nr(bus), bus->number);
> - list_for_each_entry_safe(dev, tmp, &bus->devices, bus_list) {
> - pr_debug(" * Removing %s...\n", pci_name(dev));
> - eeh_remove_bus_device(dev, purge_pe);
> - pci_stop_and_remove_bus_device(dev);
> - }
> -}
> -
> -/**
> - * pcibios_remove_pci_devices - remove all devices under this bus
> - *
> - * Remove all of the PCI devices under this bus both from the
> - * linux pci device tree, and from the powerpc EEH address cache.
> - */
> -void pcibios_remove_pci_devices(struct pci_bus *bus)
> -{
> - __pcibios_remove_pci_devices(bus, 1);
> -}
> -EXPORT_SYMBOL_GPL(pcibios_remove_pci_devices);
> -
> -/**
> - * pcibios_add_pci_devices - adds new pci devices to bus
> - *
> - * This routine will find and fixup new pci devices under
> - * the indicated bus. This routine presumes that there
> - * might already be some devices under this bridge, so
> - * it carefully tries to add only new devices. (And that
> - * is how this routine differs from other, similar pcibios
> - * routines.)
> - */
> -void pcibios_add_pci_devices(struct pci_bus * bus)
> -{
> - int slotno, num, mode, pass, max;
> - struct pci_dev *dev;
> - struct device_node *dn = pci_bus_to_OF_node(bus);
> -
> - eeh_add_device_tree_early(dn);
> -
> - mode = PCI_PROBE_NORMAL;
> - if (ppc_md.pci_probe_mode)
> - mode = ppc_md.pci_probe_mode(bus);
> -
> - if (mode == PCI_PROBE_DEVTREE) {
> - /* use ofdt-based probe */
> - of_rescan_bus(dn, bus);
> - } else if (mode == PCI_PROBE_NORMAL) {
> - /* use legacy probe */
> - slotno = PCI_SLOT(PCI_DN(dn->child)->devfn);
> - num = pci_scan_slot(bus, PCI_DEVFN(slotno, 0));
> - if (!num)
> - return;
> - pcibios_setup_bus_devices(bus);
> - max = bus->busn_res.start;
> - for (pass=0; pass < 2; pass++)
> - list_for_each_entry(dev, &bus->devices, bus_list) {
> - if (dev->hdr_type == PCI_HEADER_TYPE_BRIDGE ||
> - dev->hdr_type == PCI_HEADER_TYPE_CARDBUS)
> - max = pci_scan_bridge(bus, dev, max, pass);
> - }
> - }
> - pcibios_finish_adding_to_bus(bus);
> -}
> -EXPORT_SYMBOL_GPL(pcibios_add_pci_devices);
> -
> struct pci_controller *init_phb_dynamic(struct device_node *dn)
> {
> struct pci_controller *phb;
> --
> 1.7.5.4
>
> _______________________________________________
> Linuxppc-dev mailing list
> Linuxppc-dev@lists.ozlabs.org
> https://lists.ozlabs.org/listinfo/linuxppc-dev
>
^ permalink raw reply
* Re: Regression in RCU subsystem in latest mainline kernel
From: Paul E. McKenney @ 2013-06-19 4:09 UTC (permalink / raw)
To: Michael Ellerman
Cc: linuxppc-dev, Rojhalat Ibrahim, Steven Rostedt, linux-kernel
In-Reply-To: <20130617074213.GA3589@concordia>
On Mon, Jun 17, 2013 at 05:42:13PM +1000, Michael Ellerman wrote:
> On Sat, Jun 15, 2013 at 12:02:21PM +1000, Benjamin Herrenschmidt wrote:
> > On Fri, 2013-06-14 at 17:06 -0400, Steven Rostedt wrote:
> > > I was pretty much able to reproduce this on my PA Semi PPC box. Funny
> > > thing is, when I type on the console, it makes progress. Anyway, it
> > > seems that powerpc has an issue with irq_work(). I'll try to get some
> > > time either tonight or next week to figure it out.
> >
> > Does this help ?
> >
> > diff --git a/arch/powerpc/kernel/irq.c b/arch/powerpc/kernel/irq.c
> > index 5cbcf4d..ea185e0 100644
> > --- a/arch/powerpc/kernel/irq.c
> > +++ b/arch/powerpc/kernel/irq.c
> > @@ -162,7 +162,7 @@ notrace unsigned int __check_irq_replay(void)
> > * in case we also had a rollover while hard disabled
> > */
> > local_paca->irq_happened &= ~PACA_IRQ_DEC;
> > - if (decrementer_check_overflow())
> > + if ((happened & PACA_IRQ_DEC) || decrementer_check_overflow())
> > return 0x900;
> >
> > /* Finally check if an external interrupt happened */
> >
>
> This seems to help, but doesn't elminate the RCU stall warnings I am
> seeing. I now see them less often, but not never.
>
> Stack trace is something like:
Hmmm... How many CPUs are on your system? And how much work is
perf_event_for_each_child() having to do here?
If the amount of work is large and your kernel is built with
CONFIG_PREEMPT=n, the RCU CPU stall warning would be expected behavior.
If so, we might need a preemption point in perf_event_for_each_child().
Thanx, Paul
> INFO: rcu_sched detected stalls on CPUs/tasks: { 32} (detected by 12, t=21372 jiffies, g=18446744073709551503, c=18446744073709551502, q=1018)
> Task dump for CPU 32:
> power8-events R running task 4960 2009 1988 0x00000004
> Call Trace:
> [c000000fb0e3f910] [c000000fb0e3f9d0] 0xc000000fb0e3f9d0 (unreliable)
>
> [c000000fb0e3edc0] [c0000000000b2894] .__run_hrtimer+0xa4/0x2a0
> [c000000fb0e3ee70] [c0000000000b36d8] .hrtimer_interrupt+0x148/0x320
> [c000000fb0e3ef80] [c00000000001c754] .timer_interrupt+0x134/0x320
> [c000000fb0e3f040] [c00000000000a4f4] restore_check_irq_replay+0x68/0xa8
> --- Exception: 901 at .arch_local_irq_restore+0x24/0x90
> LR = .__do_softirq+0x100/0x3a0
> [c000000fb0e3f330] [c0000000000c4784] .vtime_account_irq_enter+0x34/0x70 (unreliable)
> [c000000fb0e3f3a0] [c000000000089680] .__do_softirq+0x100/0x3a0
> [c000000fb0e3f4c0] [c000000000089b38] .irq_exit+0xc8/0x110
> [c000000fb0e3f540] [c00000000001c788] .timer_interrupt+0x168/0x320
> [c000000fb0e3f600] [c0000000000025cc] decrementer_common+0x14c/0x180
> --- Exception: 901 at .arch_local_irq_restore+0x74/0x90
> LR = .arch_local_irq_restore+0x74/0x90
> [c000000fb0e3f8f0] [c000000fb0e3f970] 0xc000000fb0e3f970 (unreliable)
> [c000000fb0e3f960] [c0000000000e4ae0] .smp_call_function_single+0x1d0/0x1e0
> [c000000fb0e3fa10] [c000000000147aa4] .task_function_call+0x54/0x70
> [c000000fb0e3fab0] [c000000000147bc4] .perf_event_enable+0x104/0x1c0
> [c000000fb0e3fb60] [c000000000146800] .perf_event_for_each_child+0x60/0x110
> [c000000fb0e3fbf0] [c00000000014a528] .perf_ioctl+0x108/0x3f0
> [c000000fb0e3fca0] [c0000000001d7138] .do_vfs_ioctl+0xb8/0x730
> [c000000fb0e3fd80] [c0000000001d780c] .SyS_ioctl+0x5c/0xb0
> [c000000fb0e3fe30] [c000000000009d54] syscall_exit+0x0/0x98
>
>
> cheers
>
^ permalink raw reply
* Re: [PATCH 2/2] perf: Add support for the mem_xlvl field.
From: Michael Neuling @ 2013-06-19 4:32 UTC (permalink / raw)
To: Sukadev Bhattiprolu
Cc: Anton Blanchard, linux-kernel, Stephane Eranian, linuxppc-dev,
Paul Mackerras, mingo
In-Reply-To: <20130607204043.GB3281@us.ibm.com>
Sukadev Bhattiprolu <sukadev@linux.vnet.ibm.com> wrote:
> From 9f1a8a16e0ef36447e343d1cd4797c2b6a81225f Mon Sep 17 00:00:00 2001
> From: Sukadev Bhattiprolu <sukadev@linux.vnet.ibm.com>
> Date: Fri, 7 Jun 2013 13:26:31 -0700
> Subject: [PATCH 2/2] perf: Add support for the mem_xlvl field.
>
> A follow-on patch to adding perf_mem_data_src support for Power7.
> At this point, this is only touch-tested as am looking for feedback
> on the main kernel patch.
>
> Signed-off-by: Sukadev Bhattiprolu <sukadev@linux.vnet.ibm.com>
> ---
> tools/perf/util/sort.c | 31 ++++++++++++++++++++++++++++++-
> 1 files changed, 30 insertions(+), 1 deletions(-)
>
> diff --git a/tools/perf/util/sort.c b/tools/perf/util/sort.c
> index 5f52d49..24bbf4d 100644
> --- a/tools/perf/util/sort.c
> +++ b/tools/perf/util/sort.c
> @@ -631,9 +631,11 @@ static int hist_entry__tlb_snprintf(struct hist_entry *self, char *bf,
> static int64_t
> sort__lvl_cmp(struct hist_entry *left, struct hist_entry *right)
> {
> + int64_t rc;
> union perf_mem_data_src data_src_l;
> union perf_mem_data_src data_src_r;
>
> + data_src_l.val = data_src_r.val = (int64_t)0;
> if (left->mem_info)
> data_src_l = left->mem_info->data_src;
> else
> @@ -644,7 +646,11 @@ sort__lvl_cmp(struct hist_entry *left, struct hist_entry *right)
> else
> data_src_r.mem_lvl = PERF_MEM_LVL_NA;
>
> - return (int64_t)(data_src_r.mem_lvl - data_src_l.mem_lvl);
> + rc = data_src_r.mem_lvl - data_src_l.mem_lvl;
> + if (!rc)
> + rc = data_src_r.mem_xlvl - data_src_l.mem_xlvl;
> +
whitespace here
> + return rc;
> }
>
> static const char * const mem_lvl[] = {
> @@ -663,7 +669,14 @@ static const char * const mem_lvl[] = {
> "I/O",
> "Uncached",
> };
> +
> +static const char * const mem_xlvl[] = {
> + "Remote RAM (3 hops)",
> + "Remote Cache (3 hops)",
> +};
> +
> #define NUM_MEM_LVL (sizeof(mem_lvl)/sizeof(const char *))
> +#define NUM_MEM_XLVL (sizeof(mem_xlvl)/sizeof(const char *))
>
> static int hist_entry__lvl_snprintf(struct hist_entry *self, char *bf,
> size_t size, unsigned int width)
> @@ -695,6 +708,22 @@ static int hist_entry__lvl_snprintf(struct hist_entry *self, char *bf,
> strncat(out, mem_lvl[i], sz - l);
> l += strlen(mem_lvl[i]);
> }
> +
> + m = 0;
> + if (self->mem_info)
> + m = self->mem_info->data_src.mem_xlvl;
> +
> + for (i = 0; m && i < NUM_MEM_XLVL; i++, m >>= 1) {
> + if (!(m & 0x1))
> + continue;
> + if (l) {
> + strcat(out, " or ");
> + l += 4;
> + }
> + strncat(out, mem_xlvl[i], sz - l);
> + l += strlen(mem_xlvl[i]);
> + }
> +
> if (*out == '\0')
> strcpy(out, "N/A");
> if (hit)
> --
> 1.7.1
>
> _______________________________________________
> Linuxppc-dev mailing list
> Linuxppc-dev@lists.ozlabs.org
> https://lists.ozlabs.org/listinfo/linuxppc-dev
>
^ permalink raw reply
* Re: [PATCH 1/2] perf/Power7: Save dcache_src fields in sample record.
From: Michael Neuling @ 2013-06-19 4:41 UTC (permalink / raw)
To: Sukadev Bhattiprolu
Cc: Anton Blanchard, linux-kernel, Stephane Eranian, linuxppc-dev,
Paul Mackerras, mingo
In-Reply-To: <20130607204008.GA3281@us.ibm.com>
Suka,
One of these two patches breaks pmac32_defconfig and I suspect all other
32 bit configs (against mainline)
arch/powerpc/perf/core-book3s.c: In function 'record_and_restart':
arch/powerpc/perf/core-book3s.c:1632:4: error: passing argument 1 of 'ppmu->get_mem_data_src' from incompatible pointer type [-Werror]
arch/powerpc/perf/core-book3s.c:1632:4: note: expected 'struct perf_sample_data *' but argument is of type 'struct perf_sample_data *'
benh is busy enough without this junk. Please check the simple things
like white space and compile errors!
Mikey
Sukadev Bhattiprolu <sukadev@linux.vnet.ibm.com> wrote:
> From: Sukadev Bhattiprolu <sukadev@linux.vnet.ibm.com>
> Date: Wed, 8 May 2013 22:59:29 -0700
> Subject: [PATCH 1/2] perf/Power7: Save dcache_src fields in sample record.
>
> Power7 saves the "perf-event vector" information in the mmcra register.
> Included in this event vector is a "data-cache source" field which
> identifies where in the memory-hierarchy the data for an instruction
> was found.
>
> Use the 'struct perf_mem_data_source' to export the "data-cache source"
> field to user space.
>
> The mapping between the Power7 hierarchy levels and the arch-neutral
> levels is, unfortunately, not trivial.
>
> Arch-neutral levels Power7 levels
> ---------------------------------------------------------
> local LVL_L2 local (same core) L2 (FROM_L2)
> local LVL_L3 local (same core) L3 (FROM_L3)
>
> 1-hop REM_CCE1 different core on same chip (FROM_L2.1, _L3.1)
> 2-hops REM_CCE2 remote (different chip, same node) (FROM_RL2L3)
> 3-hops REM_CCE3* distant (different node) (FROM_DL2L3)
>
> 1-hop REM_MEM1 unused
> 2-hops REM_MEM2 remote (different chip, same node) (FROM_RMEM)
> 3-hops REM_MEM3* distant (different node) (FROM_DMEM)
>
> * proposed "extended" levels.
>
> AFAICT, Power7 supports one extra level in the cache-hierarchy, so we propose
> to add a new cache level, REM_CCE3 shown above.
>
> To maintain consistency in terminology (i.e 2-hops = remote, 3-hops = distant),
> I propose leaving the REM_MEM1 unused and adding another level, REM_MEM3.
>
> Further, in the above REM_CCE1 case, Power7 can also identify if the data came
> from the L2 or L3 cache of another core on the same chip. To describe this to
> user space, we propose to set ->mem_lvl to:
>
> PERF_MEM_LVL_REM_CCE1|PERF_MEM_LVL_L2
>
> PERF_MEM_LVL_REM_CCE1|PERF_MEM_LVL_L3
>
> Either that or we could leave REM_CCE1 unused in Power and add two more levels:
>
> PERF_MEM_XLVL_REM_L2_CCE1
> PERF_MEM_XLVL_REM_L3_CCE1
>
> The former approach seems less confusing and this patch uses that approach.
>
> Signed-off-by: Sukadev Bhattiprolu <sukadev@linux.vnet.ibm.com>
> ---
> arch/powerpc/include/asm/perf_event_server.h | 2 +
> arch/powerpc/perf/core-book3s.c | 4 +
> arch/powerpc/perf/power7-pmu.c | 81 ++++++++++++++++++++++++++
> include/uapi/linux/perf_event.h | 12 +++-
> 4 files changed, 97 insertions(+), 2 deletions(-)
>
> diff --git a/arch/powerpc/include/asm/perf_event_server.h b/arch/powerpc/include/asm/perf_event_server.h
> index f265049..f2d162b 100644
> --- a/arch/powerpc/include/asm/perf_event_server.h
> +++ b/arch/powerpc/include/asm/perf_event_server.h
> @@ -37,6 +37,8 @@ struct power_pmu {
> void (*config_bhrb)(u64 pmu_bhrb_filter);
> void (*disable_pmc)(unsigned int pmc, unsigned long mmcr[]);
> int (*limited_pmc_event)(u64 event_id);
> + void (*get_mem_data_src)(struct perf_sample_data *data,
> + struct pt_regs *regs);
> u32 flags;
> const struct attribute_group **attr_groups;
> int n_generic;
> diff --git a/arch/powerpc/perf/core-book3s.c b/arch/powerpc/perf/core-book3s.c
> index 426180b..7778fa9 100644
> --- a/arch/powerpc/perf/core-book3s.c
> +++ b/arch/powerpc/perf/core-book3s.c
> @@ -1632,6 +1632,10 @@ static void record_and_restart(struct perf_event *event, unsigned long val,
> data.br_stack = &cpuhw->bhrb_stack;
> }
>
> + if (event->attr.sample_type & PERF_SAMPLE_DATA_SRC &&
> + ppmu->get_mem_data_src)
> + ppmu->get_mem_data_src(&data, regs);
> +
> if (perf_event_overflow(event, &data, regs))
> power_pmu_stop(event, 0);
> }
> diff --git a/arch/powerpc/perf/power7-pmu.c b/arch/powerpc/perf/power7-pmu.c
> index 3c475d6..af92bfe 100644
> --- a/arch/powerpc/perf/power7-pmu.c
> +++ b/arch/powerpc/perf/power7-pmu.c
> @@ -209,6 +209,85 @@ static int power7_get_alternatives(u64 event, unsigned int flags, u64 alt[])
> return nalt;
> }
>
> +#define POWER7_MMCRA_PEMPTY (0x1L << 63)
> +#define POWER7_MMCRA_FIN_STALL (0x1L << 62)
> +#define POWER7_MMCRA_CMPL_STALL (0x1L << 61)
> +#define POWER7_MMCRA_STALL_REASON_MASK (0xFL << 60)
> +
> +#define POWER7_MMCRA_DCACHE_MISS (0x1L << 55)
> +
> +#define POWER7_MMCRA_DCACHE_SRC_SHIFT 51
> +#define POWER7_MMCRA_DCACHE_SRC_MASK (0xFL << POWER7_MMCRA_DCACHE_SRC_SHIFT)
> +
> +#define POWER7_MMCRA_MDTLB_MISS (0x1L << 50)
> +
> +#define POWER7_MMCRA_MDTLB_SRC_SHIFT 46
> +#define POWER7_MMCRA_MDTLB_SRC_MASK (0xFL << POWER7_MMCRA_MDTLB_SRC_SHIFT)
> +
> +#define POWER7_MMCRA_MDERAT_MISS (0x1L<< 45)
> +#define POWER7_MMCRA_MLSU_REJ (0x1L<< 44)
> +
> +/* and so on */
> +
> +/*
> + * Map DCACHE_SRC fields to the Linux memory hierarchy levels.
> + *
> + * Bits 9..12 in the MMCRA indicate the source of a data-cache entry, with
> + * each of the 16 possible values referring to a specific source. Eg: if
> + * the 4-bits have the value 1 (0b0001), the dcache entry was found local
> + * L3 cache.
> + *
> + * We use the table, dcache_src_map, to map this value 1 to PERF_MEM_LVL_L3,
> + * the arch-neutral representation of the L3 cache.
> + *
> + * Similarly, in case of marked data TLB miss, bits 14..17 of the MMCRA
> + * indicate the load source of a marked DTLB entry. dtlb_src_map[] gives
> + * the mapping to the arch-neutral values of the TLB source.
> + *
> + * Architecture neutral to Power7 hierarchy levels:
> + * 1-hop = different core on same chip (L2.1 or L3.1)
> + * 2-hops = remote (different chip on same node)
> + * 3-hops = distant (different node)
> + */
> +static u64 dcache_src_map[] = {
> + PERF_MEM_S(LVL, L2), /* 00: FROM_L2 */
> + PERF_MEM_S(LVL, L3), /* 01: FROM_L3 */
> + PERF_MEM_S(LVL, NA), /* 02: Reserved */
> + PERF_MEM_S(LVL, NA), /* 03: Reserved */
> +
> + PERF_MEM_LVL_L2|PERF_MEM_LVL_REM_CCE1, /* 04: FROM_L2.1_SHR */
> + PERF_MEM_LVL_L2|PERF_MEM_LVL_REM_CCE1, /* 05: FROM_L3.1_MOD */
> + PERF_MEM_LVL_L3|PERF_MEM_LVL_REM_CCE1, /* 06: FROM_L2.1_SHR */
> + PERF_MEM_LVL_L3|PERF_MEM_LVL_REM_CCE1, /* 07: FROM_L3.1_MOD */
> +
> + PERF_MEM_S(LVL, REM_CCE2), /* 08: FROM_RL2L3_SHR */
> + PERF_MEM_S(LVL, REM_CCE2), /* 09: FROM_RL2L3_MOD */
> + PERF_MEM_S(XLVL, REM_CCE3), /* 10: FROM_DL2L3_SHR */
> + PERF_MEM_S(XLVL, REM_CCE3), /* 11: FROM_DL2L3_MOD */
> +
> + PERF_MEM_S(LVL, LOC_RAM), /* 12: FROM_LMEM */
> + PERF_MEM_S(LVL, REM_RAM2), /* 13: FROM_RMEM */
> + PERF_MEM_S(XLVL, REM_RAM3), /* 14: FROM_DMEM */
> +
> + PERF_MEM_S(LVL, NA), /* 15: Reserved */
> +};
> +
> +
> +static void power7_get_mem_data_src(struct perf_sample_data *data,
> + struct pt_regs *regs)
> +{
> + unsigned long idx;
> + unsigned long mmcra = regs->dsisr;
> + union perf_mem_data_src *dsrc = &data->data_src;
> +
> + if (mmcra & POWER7_MMCRA_DCACHE_MISS) {
> + idx = mmcra & POWER7_MMCRA_DCACHE_SRC_MASK;
> + idx >>= POWER7_MMCRA_DCACHE_SRC_SHIFT;
> +
> + dsrc->val |= dcache_src_map[idx];
> + }
> +}
> +
> /*
> * Returns 1 if event counts things relating to marked instructions
> * and thus needs the MMCRA_SAMPLE_ENABLE bit set, or 0 if not.
> @@ -438,6 +517,7 @@ static const struct attribute_group *power7_pmu_attr_groups[] = {
> NULL,
> };
>
> +
> static struct power_pmu power7_pmu = {
> .name = "POWER7",
> .n_counter = 6,
> @@ -447,6 +527,7 @@ static struct power_pmu power7_pmu = {
> .compute_mmcr = power7_compute_mmcr,
> .get_constraint = power7_get_constraint,
> .get_alternatives = power7_get_alternatives,
> + .get_mem_data_src = power7_get_mem_data_src,
> .disable_pmc = power7_disable_pmc,
> .flags = PPMU_ALT_SIPR,
> .attr_groups = power7_pmu_attr_groups,
> diff --git a/include/uapi/linux/perf_event.h b/include/uapi/linux/perf_event.h
> index fb104e5..f8d3269 100644
> --- a/include/uapi/linux/perf_event.h
> +++ b/include/uapi/linux/perf_event.h
> @@ -627,7 +627,8 @@ union perf_mem_data_src {
> mem_snoop:5, /* snoop mode */
> mem_lock:2, /* lock instr */
> mem_dtlb:7, /* tlb access */
> - mem_rsvd:31;
> + mem_xlvl:2, /* extended memory levels */
> + mem_rsvd:29;
> };
> };
>
> @@ -654,7 +655,7 @@ union perf_mem_data_src {
> #define PERF_MEM_LVL_REM_CCE2 0x800 /* Remote Cache (2 hops) */
> #define PERF_MEM_LVL_IO 0x1000 /* I/O memory */
> #define PERF_MEM_LVL_UNC 0x2000 /* Uncached memory */
> -#define PERF_MEM_LVL_SHIFT 5
> +#define PERF_MEM_LVL_SHIFT 5 /* see also extended levels below */
>
> /* snoop mode */
> #define PERF_MEM_SNOOP_NA 0x01 /* not available */
> @@ -679,6 +680,13 @@ union perf_mem_data_src {
> #define PERF_MEM_TLB_OS 0x40 /* OS fault handler */
> #define PERF_MEM_TLB_SHIFT 26
>
> +#define PERF_MEM_XLVL_REM_RAM3 0x01 /* Remote memory (3 hops) */
> +#define PERF_MEM_XLVL_REM_CCE3 0x02 /* Remote cache (3 hops) */
> +#define PERF_MEM_XLVL_SHIFT 33
> +
> +/* Miscellaneous flags */
> +#define PERF_MEM_MISC_CCE_MOD 0x4000 /* cache-hit, but entry was modified */
> +
> #define PERF_MEM_S(a, s) \
> (((u64)PERF_MEM_##a##_##s) << PERF_MEM_##a##_SHIFT)
>
> --
> 1.7.1
>
> _______________________________________________
> Linuxppc-dev mailing list
> Linuxppc-dev@lists.ozlabs.org
> https://lists.ozlabs.org/listinfo/linuxppc-dev
>
^ permalink raw reply
* Re: [PATCH 3/4] KVM: PPC: Add support for IOMMU in-kernel handling
From: Benjamin Herrenschmidt @ 2013-06-19 4:59 UTC (permalink / raw)
To: Alex Williamson
Cc: kvm, Alexey Kardashevskiy, Rusty Russell, Alexander Graf, kvm-ppc,
linux-kernel, Paul Mackerras, linuxppc-dev, David Gibson
In-Reply-To: <87txkun568.fsf@rustcorp.com.au>
On Wed, 2013-06-19 at 13:05 +0930, Rusty Russell wrote:
> symbol_get() won't try to load a module; it'll just fail. This is what
> you want, since they must have vfio in the kernel to get a valid fd...
Ok, cool. I suppose what we want here Alexey is slightly higher level,
something like:
vfio_validate_iommu_id(file, iommu_id)
Which verifies that the file that was passed in is allowed to use
that iommu_id.
That's a simple and flexible interface (ie, it will work even if we
support multiple iommu IDs in the future for a vfio, for example
for DDW windows etc...), the logic to know about the ID remains
in qemu, this is strictly a validation call.
That way we also don't have to expose the containing vfio struct etc...
just that simple function.
Alex, any objection ?
Do we need to make it a get/put interface instead ?
vfio_validate_and_use_iommu(file, iommu_id);
vfio_release_iommu(file, iommu_id);
To ensure that the resource remains owned by the process until KVM
is closed as well ?
Or do we want to register with VFIO with a callback so that VFIO can
call us if it needs us to give it up ?
Cheers,
Ben.
^ permalink raw reply
* Re: [PATCH 1/2] perf/Power7: Save dcache_src fields in sample record.
From: Sukadev Bhattiprolu @ 2013-06-19 5:31 UTC (permalink / raw)
To: Michael Neuling
Cc: Anton Blanchard, linux-kernel, Stephane Eranian, linuxppc-dev,
Paul Mackerras, mingo
In-Reply-To: <23819.1371616910@ale.ozlabs.ibm.com>
Michael Neuling [mikey@neuling.org] wrote:
| Suka,
|
| One of these two patches breaks pmac32_defconfig and I suspect all other
| 32 bit configs (against mainline)
|
| arch/powerpc/perf/core-book3s.c: In function 'record_and_restart':
| arch/powerpc/perf/core-book3s.c:1632:4: error: passing argument 1 of 'ppmu->get_mem_data_src' from incompatible pointer type [-Werror]
| arch/powerpc/perf/core-book3s.c:1632:4: note: expected 'struct perf_sample_data *' but argument is of type 'struct perf_sample_data *'
|
| benh is busy enough without this junk. Please check the simple things
| like white space and compile errors!
Sorry about that.
BTW, this was an early patch more to get some feedback on mapping of
memory hierarchy levels to Power and not intended to be merged. I have
been reworking the patch based on other comments.
Sukadev
^ permalink raw reply
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox