* Re: [PATCH 3/5] powerpc/dts: update MSI bindings doc for MPIC v4.3
From: Scott Wood @ 2013-06-18 0:28 UTC (permalink / raw)
To: Lian Minghuan-b31939; +Cc: Minghuan Lian, linuxppc-dev, Zang Roy-R61911
In-Reply-To: <51BE999D.2080207@freescale.com>
On 06/17/2013 12:07:41 AM, Lian Minghuan-b31939 wrote:
> Hi Soctt,
>=20
> please see my comments.
>=20
> On 06/15/2013 06:06 AM, Scott Wood wrote:
>> On 06/14/2013 02:15:57 AM, Minghuan Lian wrote:
>>> Add compatible "fsl,mpic-msi-v4.3" for MPIC v4.3. MPIC v4.3 contains
>>> MSIIR and MSIIR1. MSIIR supports 8 MSI registers and MSIIR1 supports
>>> 16 MSI registers, but uses different IBS and SRS shift. When using
>>> MSIR1, the interrupt number is not consecutive. It is hard to use
>>> 'msi-available-ranges' to describe the ranges of the available
>>> interrupt and the ranges are related to the application, rather than
>>> the description of the hardware. this patch also removes
>>> 'msi-available-ranges' property.
>>>=20
>>> Signed-off-by: Minghuan Lian <Minghuan.Lian@freescale.com>
>>> ---
>>> .../devicetree/bindings/powerpc/fsl/msi-pic.txt | 49 =20
>>> ++++++++++------------
>>> 1 file changed, 22 insertions(+), 27 deletions(-)
>>>=20
>>> diff --git =20
>>> a/Documentation/devicetree/bindings/powerpc/fsl/msi-pic.txt =20
>>> b/Documentation/devicetree/bindings/powerpc/fsl/msi-pic.txt
>>> index 5693877..e851e93 100644
>>> --- a/Documentation/devicetree/bindings/powerpc/fsl/msi-pic.txt
>>> +++ b/Documentation/devicetree/bindings/powerpc/fsl/msi-pic.txt
>>> @@ -1,26 +1,23 @@
>>> * Freescale MSI interrupt controller
>>>=20
>>> Required properties:
>>> -- compatible : compatible list, contains 2 entries,
>>> +- compatible : compatible list, may contains one or two entries,
>>> first is "fsl,CHIP-msi", where CHIP is the processor(mpc8610, =20
>>> mpc8572,
>>> - etc.) and the second is "fsl,mpic-msi" or "fsl,ipic-msi" =20
>>> depending on
>>> - the parent type.
>>> + etc.) and the second is "fsl,mpic-msi" or "fsl,ipic-msi" or
>>> + "fsl,mpic-msi-v4.3" depending on the parent type and version. If =20
>>> mpic
>>> + version is 4.3, the number of MSI registers is increased to 16, =20
>>> MSIIR1 is
>>> + provided to access these 16 registers, compatible =20
>>> "fsl,mpic-msi-v4.3"
>>> + should be used.
>>=20
>> Why "one or two"? What does it look like in the case where there's =20
>> just one?
>>=20
> [Minghuan] The original doc said 'contains 2 entries', but I notcie =20
> pq3-mpic.dtsi and qoriq-mpic.dtsi have only one entry "fsl,mpic-msi", =20
> do not have "fsl,CHIP-msi".
> for example:
> mpc8610_hpcd.dts: compatible =3D "fsl,mpc8610-msi", "fsl,mpic-msi";
> fsl/qoriq-mpic.dtsi: compatible =3D "fsl,mpic-msi"
>=20
> Maybe I should say " For some platforms, "fsl,CHIP-msi' is optional."
Well, this is more a matter of some device trees not complying with the =20
binding, rather than an update for MPIC v4.3.
In any case, if the plan is to update the binding to match what we've =20
been doing in the actual trees, at least word it so that it's clear =20
which one of the two is optional.
>> Why are you removing msi-available-ranges? It's not valid for MPIC =20
>> v4.3, but it's still valid for older MPICs. It should move to the =20
>> optional section, though.
> [Minghuan] Because I would like to add kernel parameter 'msiregs' =20
> instead of "msi-available-ranges", for all the MPICs, we will have a =20
> uniform way to configure
I've responded elsewhere to this, but I'd also like to add that we =20
don't break compatibility with older device tree bindings just for "a =20
uniform way".
>>> Example:
>>> msi@41600 {
>>> - compatible =3D "fsl,mpc8610-msi", "fsl,mpic-msi";
>>> - reg =3D <0x41600 0x80>;
>>> - msi-available-ranges =3D <0 0x100>;
>>> - interrupts =3D <
>>> - 0xe0 0
>>> - 0xe1 0
>>> - 0xe2 0
>>> - 0xe3 0
>>> - 0xe4 0
>>> - 0xe5 0
>>> - 0xe6 0
>>> - 0xe7 0>;
>>> - interrupt-parent =3D <&mpic>;
>>> - };
>>> + 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.
There is no MSIIRA on fsl,mpic-msi.
If you want to show an fsl,mpic-msi-v4.3 example, update the compatible =20
and add the extra 8 interrupts. We should probably show an example of =20
each.
BTW, why are you changing/breaking the whitespace in the example?
-Scott=
^ permalink raw reply
* Re: [PATCH 3/5] powerpc/dts: update MSI bindings doc for MPIC v4.3
From: Scott Wood @ 2013-06-18 0:42 UTC (permalink / raw)
To: Scott Wood
Cc: Minghuan Lian, linuxppc-dev, Zang Roy-R61911,
Lian Minghuan-b31939
In-Reply-To: <1371515287.9073.15@snotra>
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.
Sigh, I was thinking of MSIIR1A -- which of course is distinct from =20
both MSIIRA1 and MSIIRA. :-P
So it's just a bug that pq3-mpic.dtsi has a length of 0x80?
-Scott=
^ permalink raw reply
* Re: [PATCH 01/27] powerpc/eeh: Move common part to kernel directory
From: Gavin Shan @ 2013-06-18 0:55 UTC (permalink / raw)
To: Mike Qiu; +Cc: linuxppc-dev, Gavin Shan
In-Reply-To: <51BE7C77.1020602@linux.vnet.ibm.com>
On Mon, Jun 17, 2013 at 11:03:19AM +0800, Mike Qiu wrote:
>=E4=BA=8E 2013/6/15 17:02, Gavin Shan =E5=86=99=E9=81=93:
.../...
>>+
>>+ /* Gather bridge-specific registers */
>>+ if (dev->class >> 16 =3D=3D PCI_BASE_CLASS_BRIDGE) {
>>+ eeh_ops->read_config(dn, PCI_SEC_STATUS, 2, &cfg);
>>+ n +=3D 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 +=3D 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 =3D pci_find_capability(dev, PCI_CAP_ID_PCIX);
>BTW, when move common part , here you could use dev->pcie_cap at your
>convenience, and pcie_cap has
>been initialized in of_create_pci_dev--->set_pcie_port_type
Thanks, Mike. It's not safe enough to use the cached capability
offsets, which might be invalid when we running into here. However,
we probably use following code in future, but not now :-)
It would save some PCI-CFG access.
if (dev->pcie_cap)
cap =3D dev->pcie_cap;
else
cap =3D pci_find_capability(dev, PCI_CAP_ID_PCIX);=20
Thanks,
Gavin=20
^ permalink raw reply
* Re: [PATCH v2 08/11] irqdomain: Refactor irq_domain_associate_many()
From: Michael Neuling @ 2013-06-18 1:20 UTC (permalink / raw)
To: Grant Likely
Cc: sfr, linux-kernel, Linux PPC dev, linux-next, Thomas Gleixner
In-Reply-To: <1371244086-9189-9-git-send-email-grant.likely@linaro.org>
Grant,
In next-20130617 we are getting the below crash on POWER7. Bisecting,
points to this patch (d39046ec72 in next)
Any clues?
Mikey
Using pSeries machine description
Page sizes from device-tree:
base_shift=12: shift=12, sllp=0x0000, avpnm=0x00000000, tlbiel=1, penc=0
base_shift=24: shift=24, sllp=0x0100, avpnm=0x00000001, tlbiel=0, penc=0
base_shift=16: shift=16, sllp=0x0110, avpnm=0x00000000, tlbiel=1, penc=1
base_shift=20: shift=20, sllp=0x0111, avpnm=0x00000000, tlbiel=0, penc=2
base_shift=34: shift=34, sllp=0x0120, avpnm=0x000007ff, tlbiel=0, penc=3
Using 1TB segments
Found initrd at 0xc000000002e60000:0xc000000002e60600
CPU maps initialized for 1 thread per core
Starting Linux PPC64 #48 SMP Tue Jun 18 11:10:17 EST 2013
-----------------------------------------------------
ppc64_pft_size = 0x0
physicalMemorySize = 0x80000000
htab_address = 0xc00000007fe00000
htab_hash_mask = 0x3fff
-----------------------------------------------------
Initializing cgroup subsys cpuset
Initializing cgroup subsys cpuacct
Linux version 3.10.0-rc5-14354-gd39046e (mikey@ka1) (gcc version 4.6.0 (GCC) ) #48 SMP Tue Jun 18 11:10:17 EST 2013
[boot]0012 Setup Arch
Zone ranges:
DMA [mem 0x00000000-0x7fffffff]
Normal empty
Movable zone start for each node
Early memory node ranges
node 0: [mem 0x00000000-0x7fffffff]
[boot]0015 Setup Done
PERCPU: Embedded 2 pages/cpu @c000000002100000 s88448 r0 d42624 u1048576
Built 1 zonelists in Node order, mobility grouping on. Total pages: 32740
Policy zone: DMA
Kernel command line: ipr.enabled=0
PID hash table entries: 4096 (order: -1, 32768 bytes)
Sorting __ex_table...
freeing bootmem node 0
Memory: 2061696k/2097152k available (11840k kernel code, 35456k reserved, 1792k data, 1072k bss, 704k init)
SLUB: HWalign=128, Order=0-3, MinObjects=0, CPUs=1, Nodes=256
Hierarchical RCU implementation.
RCU restricting CPUs from NR_CPUS=2048 to nr_cpu_ids=1.
NR_IRQS:512 nr_irqs:512 16
error: reading the clock failed (-1)
clocksource: timebase mult[86bca1b] shift[23] registered
Console: colour dummy device 80x25
console [tty0] enabled
console [hvc0] enabled
pid_max: default: 32768 minimum: 301
Dentry cache hash table entries: 262144 (order: 5, 2097152 bytes)
Inode-cache hash table entries: 131072 (order: 4, 1048576 bytes)
Mount-cache hash table entries: 4096
Initializing cgroup subsys devices
Initializing cgroup subsys freezer
error: hwirq 0x2 is too large for (null)
------------[ cut here ]------------
WARNING: at /scratch/mikey/src/linux-next/kernel/irq/irqdomain.c:276
Modules linked in:
CPU: 0 PID: 1 Comm: swapper/0 Not tainted 3.10.0-rc5-14354-gd39046e #48
task: c00000007e500000 ti: c00000007e520000 task.ti: c00000007e520000
NIP: c00000000011433c LR: c000000000114338 CTR: c000000000422310
REGS: c00000007e523780 TRAP: 0700 Not tainted (3.10.0-rc5-14354-gd39046e)
MSR: 9000000000029032 <SF,HV,EE,ME,IR,DR,RI> CR: 28000084 XER: 02000000
SOFTE: 1
CFAR: c0000000007b0ab8
GPR00: c000000000114338 c00000007e523a00 c000000000cb9570 0000000000000028
GPR04: 0000000000000000 0000000000000043 ffffffffffffffff 0000000000000000
GPR08: 076507200766076f c000000000be3790 0000000000000720 0000000000000000
GPR12: 0000000028000082 c00000000fe00000 c00000000000be60 0000000000000000
GPR16: 0000000000000000 0000000000000000 0000000000000000 0000000000000000
GPR20: 0000000000000000 0000000000000000 0000000000000000 c000000000b704b0
GPR24: c000000000d44a80 c000000000cb9570 0000000000000010 0000000000000002
GPR28: c00000007e0c0000 0000000000000000 c00000007e0c0000 c00000007e052400
NIP [c00000000011433c] .irq_domain_associate+0x1ec/0x260
LR [c000000000114338] .irq_domain_associate+0x1e8/0x260
PACATMSCRATCH [9000000032009032]
Call Trace:
[c00000007e523a00] [c000000000114338] .irq_domain_associate+0x1e8/0x260 (unreliable)
[c00000007e523aa0] [c000000000114bf0] .irq_create_mapping+0xa0/0x170
[c00000007e523b30] [c000000000af4574] .xics_smp_probe+0x38/0xa4
[c00000007e523ba0] [c000000000af7af0] .pSeries_smp_probe+0x10/0x68
[c00000007e523c10] [c000000000aed740] .smp_prepare_cpus+0x20c/0x244
[c00000007e523cd0] [c000000000ae44c0] .kernel_init_freeable+0x138/0x328
[c00000007e523db0] [c00000000000be7c] .kernel_init+0x1c/0x120
[c00000007e523e30] [c00000000000a05c] .ret_from_kernel_thread+0x5c/0x80
Instruction dump:
482ca915 60000000 7fa3eb78 4868ea09 60000000 4bffff7c 3c62ffd5 e8bc0010
7f6407b4 3863c108 4869c731 60000000 <0fe00000> 3ba0ffea 4bffff7c 3c62ffd5
---[ end trace 31fd0ba7d8756001 ]---
------------[ cut here ]------------
kernel BUG at /scratch/mikey/src/linux-next/arch/powerpc/sysdev/xics/xics-common.c:134!
cpu 0x0: Vector: 700 (Program Check) at [c00000007e5238b0]
pc: c000000000af4580: .xics_smp_probe+0x44/0xa4
lr: c000000000af4574: .xics_smp_probe+0x38/0xa4
sp: c00000007e523b30
msr: 9000000000029032
current = 0xc00000007e500000
paca = 0xc00000000fe00000 softe: 0 irq_happened: 0x01
pid = 1, comm = swapper/0
kernel BUG at /scratch/mikey/src/linux-next/arch/powerpc/sysdev/xics/xics-common.c:134!
enter ? for help
[c00000007e523ba0] c000000000af7af0 .pSeries_smp_probe+0x10/0x68
[c00000007e523c10] c000000000aed740 .smp_prepare_cpus+0x20c/0x244
[c00000007e523cd0] c000000000ae44c0 .kernel_init_freeable+0x138/0x328
[c00000007e523db0] c00000000000be7c .kernel_init+0x1c/0x120
[c00000007e523e30] c00000000000a05c .ret_from_kernel_thread+0x5c/0x80
0:mon> <no input ...>
Oops: Exception in kernel mode, sig: 5 [#1]
SMP NR_CPUS=2048 NUMA pSeries
Modules linked in:
CPU: 0 PID: 1 Comm: swapper/0 Tainted: G W 3.10.0-rc5-14354-gd39046e #48
task: c00000007e500000 ti: c00000007e520000 task.ti: c00000007e520000
NIP: c000000000af4580 LR: c000000000af4574 CTR: 0000000000000002
REGS: c00000007e5238b0 TRAP: 0700 Tainted: G W (3.10.0-rc5-14354-gd39046e)
MSR: 9000000000029032 <SF,HV,EE,ME,IR,DR,RI> CR: 28000084 XER: 02000000
SOFTE: 1
CFAR: c000000000114c1c
GPR00: 0000000000000001 c00000007e523b30 c000000000cb9570 0000000000000000
GPR04: 0000000000000010 0000000000020000 c00000007e052600 c000000000dcdd10
GPR08: c000000000b85ac8 fffffffffffeffff 0000000000000001 000000000001ffff
GPR12: 0000000088000084 c00000000fe00000 c00000000000be60 0000000000000000
GPR16: 0000000000000000 0000000000000000 0000000000000000 0000000000000000
GPR20: 0000000000000000 0000000000000000 0000000000000000 c000000000b704b0
GPR24: c000000000d44a80 c000000000cb9570 c000000000cb9570 c000000000b703b0
GPR28: c000000000b704b0 c000000000d40858 0000000000004000 0000000000000800
NIP [c000000000af4580] .xics_smp_probe+0x44/0xa4
LR [c000000000af4574] .xics_smp_probe+0x38/0xa4
PACATMSCRATCH [9000000000029032]
Call Trace:
[c00000007e523b30] [c000000000af4574] .xics_smp_probe+0x38/0xa4 (unreliable)
[c00000007e523ba0] [c000000000af7af0] .pSeries_smp_probe+0x10/0x68
[c00000007e523c10] [c000000000aed740] .smp_prepare_cpus+0x20c/0x244
[c00000007e523cd0] [c000000000ae44c0] .kernel_init_freeable+0x138/0x328
[c00000007e523db0] [c00000000000be7c] .kernel_init+0x1c/0x120
[c00000007e523e30] [c00000000000a05c] .ret_from_kernel_thread+0x5c/0x80
Instruction dump:
39290f80 38800002 e86b0010 f8010010 f821ff91 e80a0028 e9290000 f8090008
4b6205e1 60000000 7c600074 7800d182 <0b000000> 3d22000d 3ce2ffd3 3929fde0
---[ end trace 31fd0ba7d8756002 ]---
> Originally, irq_domain_associate_many() was designed to unwind the
> mapped irqs on a failure of any individual association. However, that
> proved to be a problem with certain IRQ controllers. Some of them only
> support a subset of irqs, and will fail when attempting to map a
> reserved IRQ. In those cases we want to map as many IRQs as possible, so
> instead it is better for irq_domain_associate_many() to make a
> best-effort attempt to map irqs, but not fail if any or all of them
> don't succeed. If a caller really cares about how many irqs got
> associated, then it should instead go back and check that all of the
> irqs is cares about were mapped.
>
> The original design open-coded the individual association code into the
> body of irq_domain_associate_many(), but with no longer needing to
> unwind associations, the code becomes simpler to split out
> irq_domain_associate() to contain the bulk of the logic, and
> irq_domain_associate_many() to be a simple loop wrapper.
>
> This patch also adds a new error check to the associate path to make
> sure it isn't called for an irq larger than the controller can handle,
> and adds locking so that the irq_domain_mutex is held while setting up a
> new association.
>
> v2: Fixup x86 warning. irq_domain_associate_many() no longer returns an
> error code, but reports errors to the printk log directly. In the
> majority of cases we don't actually want to fail if there is a
> problem, but rather log it and still try to boot the system.
>
> Signed-off-by: Grant Likely <grant.likely@linaro.org>
> ---
> arch/x86/kernel/devicetree.c | 4 +-
> include/linux/irqdomain.h | 22 +++--
> kernel/irq/irqdomain.c | 185 +++++++++++++++++++++----------------------
> 3 files changed, 102 insertions(+), 109 deletions(-)
>
> diff --git a/arch/x86/kernel/devicetree.c b/arch/x86/kernel/devicetree.c
> index b158152..4934890 100644
> --- a/arch/x86/kernel/devicetree.c
> +++ b/arch/x86/kernel/devicetree.c
> @@ -364,9 +364,7 @@ static void dt_add_ioapic_domain(unsigned int ioapic_num,
> * and assigned so we can keep the 1:1 mapping which the ioapic
> * is having.
> */
> - ret = irq_domain_associate_many(id, 0, 0, NR_IRQS_LEGACY);
> - if (ret)
> - pr_err("Error mapping legacy IRQs: %d\n", ret);
> + irq_domain_associate_many(id, 0, 0, NR_IRQS_LEGACY);
>
> if (num > NR_IRQS_LEGACY) {
> ret = irq_create_strict_mappings(id, NR_IRQS_LEGACY,
> diff --git a/include/linux/irqdomain.h b/include/linux/irqdomain.h
> index fd4b26f..f9e8e06 100644
> --- a/include/linux/irqdomain.h
> +++ b/include/linux/irqdomain.h
> @@ -103,6 +103,7 @@ struct irq_domain {
> struct irq_domain_chip_generic *gc;
>
> /* reverse map data. The linear map gets appended to the irq_domain */
> + irq_hw_number_t hwirq_max;
> unsigned int revmap_direct_max_irq;
> unsigned int revmap_size;
> struct radix_tree_root revmap_tree;
> @@ -110,8 +111,8 @@ struct irq_domain {
> };
>
> #ifdef CONFIG_IRQ_DOMAIN
> -struct irq_domain *__irq_domain_add(struct device_node *of_node,
> - int size, int direct_max,
> +struct irq_domain *__irq_domain_add(struct device_node *of_node, int size,
> + irq_hw_number_t hwirq_max, int direct_max,
> const struct irq_domain_ops *ops,
> void *host_data);
> struct irq_domain *irq_domain_add_simple(struct device_node *of_node,
> @@ -140,14 +141,14 @@ static inline struct irq_domain *irq_domain_add_linear(struct device_node *of_no
> const struct irq_domain_ops *ops,
> void *host_data)
> {
> - return __irq_domain_add(of_node, size, 0, ops, host_data);
> + return __irq_domain_add(of_node, size, size, 0, ops, host_data);
> }
> static inline struct irq_domain *irq_domain_add_nomap(struct device_node *of_node,
> unsigned int max_irq,
> const struct irq_domain_ops *ops,
> void *host_data)
> {
> - return __irq_domain_add(of_node, 0, max_irq, ops, host_data);
> + return __irq_domain_add(of_node, 0, max_irq, max_irq, ops, host_data);
> }
> static inline struct irq_domain *irq_domain_add_legacy_isa(
> struct device_node *of_node,
> @@ -166,14 +167,11 @@ static inline struct irq_domain *irq_domain_add_tree(struct device_node *of_node
>
> extern void irq_domain_remove(struct irq_domain *host);
>
> -extern int irq_domain_associate_many(struct irq_domain *domain,
> - unsigned int irq_base,
> - irq_hw_number_t hwirq_base, int count);
> -static inline int irq_domain_associate(struct irq_domain *domain, unsigned int irq,
> - irq_hw_number_t hwirq)
> -{
> - return irq_domain_associate_many(domain, irq, hwirq, 1);
> -}
> +extern int irq_domain_associate(struct irq_domain *domain, unsigned int irq,
> + irq_hw_number_t hwirq);
> +extern void irq_domain_associate_many(struct irq_domain *domain,
> + unsigned int irq_base,
> + irq_hw_number_t hwirq_base, int count);
>
> extern unsigned int irq_create_mapping(struct irq_domain *host,
> irq_hw_number_t hwirq);
> diff --git a/kernel/irq/irqdomain.c b/kernel/irq/irqdomain.c
> index 280b804..80e9249 100644
> --- a/kernel/irq/irqdomain.c
> +++ b/kernel/irq/irqdomain.c
> @@ -35,8 +35,8 @@ static struct irq_domain *irq_default_domain;
> * register allocated irq_domain with irq_domain_register(). Returns pointer
> * to IRQ domain, or NULL on failure.
> */
> -struct irq_domain *__irq_domain_add(struct device_node *of_node,
> - int size, int direct_max,
> +struct irq_domain *__irq_domain_add(struct device_node *of_node, int size,
> + irq_hw_number_t hwirq_max, int direct_max,
> const struct irq_domain_ops *ops,
> void *host_data)
> {
> @@ -52,6 +52,7 @@ struct irq_domain *__irq_domain_add(struct device_node *of_node,
> domain->ops = ops;
> domain->host_data = host_data;
> domain->of_node = of_node_get(of_node);
> + domain->hwirq_max = hwirq_max;
> domain->revmap_size = size;
> domain->revmap_direct_max_irq = direct_max;
>
> @@ -126,7 +127,7 @@ struct irq_domain *irq_domain_add_simple(struct device_node *of_node,
> {
> struct irq_domain *domain;
>
> - domain = __irq_domain_add(of_node, size, 0, ops, host_data);
> + domain = __irq_domain_add(of_node, size, size, 0, ops, host_data);
> if (!domain)
> return NULL;
>
> @@ -139,7 +140,7 @@ struct irq_domain *irq_domain_add_simple(struct device_node *of_node,
> pr_info("Cannot allocate irq_descs @ IRQ%d, assuming pre-allocated\n",
> first_irq);
> }
> - WARN_ON(irq_domain_associate_many(domain, first_irq, 0, size));
> + irq_domain_associate_many(domain, first_irq, 0, size);
> }
>
> return domain;
> @@ -170,11 +171,12 @@ struct irq_domain *irq_domain_add_legacy(struct device_node *of_node,
> {
> struct irq_domain *domain;
>
> - domain = __irq_domain_add(of_node, first_hwirq + size, 0, ops, host_data);
> + domain = __irq_domain_add(of_node, first_hwirq + size,
> + first_hwirq + size, 0, ops, host_data);
> if (!domain)
> return NULL;
>
> - WARN_ON(irq_domain_associate_many(domain, first_irq, first_hwirq, size));
> + irq_domain_associate_many(domain, first_irq, first_hwirq, size);
>
> return domain;
> }
> @@ -228,109 +230,109 @@ void irq_set_default_host(struct irq_domain *domain)
> }
> EXPORT_SYMBOL_GPL(irq_set_default_host);
>
> -static void irq_domain_disassociate_many(struct irq_domain *domain,
> - unsigned int irq_base, int count)
> +static void irq_domain_disassociate(struct irq_domain *domain, unsigned int irq)
> {
> - /*
> - * disassociate in reverse order;
> - * not strictly necessary, but nice for unwinding
> - */
> - while (count--) {
> - int irq = irq_base + count;
> - struct irq_data *irq_data = irq_get_irq_data(irq);
> - irq_hw_number_t hwirq;
> + struct irq_data *irq_data = irq_get_irq_data(irq);
> + irq_hw_number_t hwirq;
>
> - if (WARN_ON(!irq_data || irq_data->domain != domain))
> - continue;
> + if (WARN(!irq_data || irq_data->domain != domain,
> + "virq%i doesn't exist; cannot disassociate\n", irq))
> + return;
>
> - hwirq = irq_data->hwirq;
> - irq_set_status_flags(irq, IRQ_NOREQUEST);
> + hwirq = irq_data->hwirq;
> + irq_set_status_flags(irq, IRQ_NOREQUEST);
>
> - /* remove chip and handler */
> - irq_set_chip_and_handler(irq, NULL, NULL);
> + /* remove chip and handler */
> + irq_set_chip_and_handler(irq, NULL, NULL);
>
> - /* Make sure it's completed */
> - synchronize_irq(irq);
> + /* Make sure it's completed */
> + synchronize_irq(irq);
>
> - /* Tell the PIC about it */
> - if (domain->ops->unmap)
> - domain->ops->unmap(domain, irq);
> - smp_mb();
> + /* Tell the PIC about it */
> + if (domain->ops->unmap)
> + domain->ops->unmap(domain, irq);
> + smp_mb();
>
> - irq_data->domain = NULL;
> - irq_data->hwirq = 0;
> + irq_data->domain = NULL;
> + irq_data->hwirq = 0;
>
> - /* Clear reverse map for this hwirq */
> - if (hwirq < domain->revmap_size) {
> - domain->linear_revmap[hwirq] = 0;
> - } else {
> - mutex_lock(&revmap_trees_mutex);
> - radix_tree_delete(&domain->revmap_tree, hwirq);
> - mutex_unlock(&revmap_trees_mutex);
> - }
> + /* Clear reverse map for this hwirq */
> + if (hwirq < domain->revmap_size) {
> + domain->linear_revmap[hwirq] = 0;
> + } else {
> + mutex_lock(&revmap_trees_mutex);
> + radix_tree_delete(&domain->revmap_tree, hwirq);
> + mutex_unlock(&revmap_trees_mutex);
> }
> }
>
> -int irq_domain_associate_many(struct irq_domain *domain, unsigned int irq_base,
> - irq_hw_number_t hwirq_base, int count)
> +int irq_domain_associate(struct irq_domain *domain, unsigned int virq,
> + irq_hw_number_t hwirq)
> {
> - unsigned int virq = irq_base;
> - irq_hw_number_t hwirq = hwirq_base;
> - int i, ret;
> + struct irq_data *irq_data = irq_get_irq_data(virq);
> + int ret;
>
> - pr_debug("%s(%s, irqbase=%i, hwbase=%i, count=%i)\n", __func__,
> - of_node_full_name(domain->of_node), irq_base, (int)hwirq_base, count);
> + if (WARN(hwirq >= domain->hwirq_max,
> + "error: hwirq 0x%x is too large for %s\n", (int)hwirq, domain->name))
> + return -EINVAL;
> + if (WARN(!irq_data, "error: virq%i is not allocated", virq))
> + return -EINVAL;
> + if (WARN(irq_data->domain, "error: virq%i is already associated", virq))
> + return -EINVAL;
>
> - for (i = 0; i < count; i++) {
> - struct irq_data *irq_data = irq_get_irq_data(virq + i);
> -
> - if (WARN(!irq_data, "error: irq_desc not allocated; "
> - "irq=%i hwirq=0x%x\n", virq + i, (int)hwirq + i))
> - return -EINVAL;
> - if (WARN(irq_data->domain, "error: irq_desc already associated; "
> - "irq=%i hwirq=0x%x\n", virq + i, (int)hwirq + i))
> - return -EINVAL;
> - };
> -
> - for (i = 0; i < count; i++, virq++, hwirq++) {
> - struct irq_data *irq_data = irq_get_irq_data(virq);
> -
> - irq_data->hwirq = hwirq;
> - irq_data->domain = domain;
> - if (domain->ops->map) {
> - ret = domain->ops->map(domain, virq, hwirq);
> - if (ret != 0) {
> - /*
> - * If map() returns -EPERM, this interrupt is protected
> - * by the firmware or some other service and shall not
> - * be mapped. Don't bother telling the user about it.
> - */
> - if (ret != -EPERM) {
> - pr_info("%s didn't like hwirq-0x%lx to VIRQ%i mapping (rc=%d)\n",
> - domain->name, hwirq, virq, ret);
> - }
> - irq_data->domain = NULL;
> - irq_data->hwirq = 0;
> - continue;
> + mutex_lock(&irq_domain_mutex);
> + irq_data->hwirq = hwirq;
> + irq_data->domain = domain;
> + if (domain->ops->map) {
> + ret = domain->ops->map(domain, virq, hwirq);
> + if (ret != 0) {
> + /*
> + * If map() returns -EPERM, this interrupt is protected
> + * by the firmware or some other service and shall not
> + * be mapped. Don't bother telling the user about it.
> + */
> + if (ret != -EPERM) {
> + pr_info("%s didn't like hwirq-0x%lx to VIRQ%i mapping (rc=%d)\n",
> + domain->name, hwirq, virq, ret);
> }
> - /* If not already assigned, give the domain the chip's name */
> - if (!domain->name && irq_data->chip)
> - domain->name = irq_data->chip->name;
> + irq_data->domain = NULL;
> + irq_data->hwirq = 0;
> + mutex_unlock(&irq_domain_mutex);
> + return ret;
> }
>
> - if (hwirq < domain->revmap_size) {
> - domain->linear_revmap[hwirq] = virq;
> - } else {
> - mutex_lock(&revmap_trees_mutex);
> - radix_tree_insert(&domain->revmap_tree, hwirq, irq_data);
> - mutex_unlock(&revmap_trees_mutex);
> - }
> + /* If not already assigned, give the domain the chip's name */
> + if (!domain->name && irq_data->chip)
> + domain->name = irq_data->chip->name;
> + }
>
> - irq_clear_status_flags(virq, IRQ_NOREQUEST);
> + if (hwirq < domain->revmap_size) {
> + domain->linear_revmap[hwirq] = virq;
> + } else {
> + mutex_lock(&revmap_trees_mutex);
> + radix_tree_insert(&domain->revmap_tree, hwirq, irq_data);
> + mutex_unlock(&revmap_trees_mutex);
> }
> + mutex_unlock(&irq_domain_mutex);
> +
> + irq_clear_status_flags(virq, IRQ_NOREQUEST);
>
> return 0;
> }
> +EXPORT_SYMBOL_GPL(irq_domain_associate);
> +
> +void irq_domain_associate_many(struct irq_domain *domain, unsigned int irq_base,
> + irq_hw_number_t hwirq_base, int count)
> +{
> + int i;
> +
> + pr_debug("%s(%s, irqbase=%i, hwbase=%i, count=%i)\n", __func__,
> + of_node_full_name(domain->of_node), irq_base, (int)hwirq_base, count);
> +
> + for (i = 0; i < count; i++) {
> + irq_domain_associate(domain, irq_base + i, hwirq_base + i);
> + }
> +}
> EXPORT_SYMBOL_GPL(irq_domain_associate_many);
>
> /**
> @@ -460,12 +462,7 @@ int irq_create_strict_mappings(struct irq_domain *domain, unsigned int irq_base,
> if (unlikely(ret < 0))
> return ret;
>
> - ret = irq_domain_associate_many(domain, irq_base, hwirq_base, count);
> - if (unlikely(ret < 0)) {
> - irq_free_descs(irq_base, count);
> - return ret;
> - }
> -
> + irq_domain_associate_many(domain, irq_base, hwirq_base, count);
> return 0;
> }
> EXPORT_SYMBOL_GPL(irq_create_strict_mappings);
> @@ -535,7 +532,7 @@ void irq_dispose_mapping(unsigned int virq)
> if (WARN_ON(domain == NULL))
> return;
>
> - irq_domain_disassociate_many(domain, virq, 1);
> + irq_domain_disassociate(domain, virq);
> irq_free_desc(virq);
> }
> EXPORT_SYMBOL_GPL(irq_dispose_mapping);
> --
> 1.8.1.2
>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at http://vger.kernel.org/majordomo-info.html
> Please read the FAQ at http://www.tux.org/lkml/
>
^ permalink raw reply
* Re: [PATCH v2 08/11] irqdomain: Refactor irq_domain_associate_many()
From: Michael Neuling @ 2013-06-18 1:25 UTC (permalink / raw)
To: Grant Likely
Cc: sfr, linux-kernel, Linux PPC dev, linux-next, Thomas Gleixner
In-Reply-To: <4521.1371518445@ale.ozlabs.ibm.com>
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.
Mikey
>
> Any clues?
>
> Mikey
>
>
> Using pSeries machine description
> Page sizes from device-tree:
> base_shift=12: shift=12, sllp=0x0000, avpnm=0x00000000, tlbiel=1, penc=0
> base_shift=24: shift=24, sllp=0x0100, avpnm=0x00000001, tlbiel=0, penc=0
> base_shift=16: shift=16, sllp=0x0110, avpnm=0x00000000, tlbiel=1, penc=1
> base_shift=20: shift=20, sllp=0x0111, avpnm=0x00000000, tlbiel=0, penc=2
> base_shift=34: shift=34, sllp=0x0120, avpnm=0x000007ff, tlbiel=0, penc=3
> Using 1TB segments
> Found initrd at 0xc000000002e60000:0xc000000002e60600
> CPU maps initialized for 1 thread per core
> Starting Linux PPC64 #48 SMP Tue Jun 18 11:10:17 EST 2013
> -----------------------------------------------------
> ppc64_pft_size = 0x0
> physicalMemorySize = 0x80000000
> htab_address = 0xc00000007fe00000
> htab_hash_mask = 0x3fff
> -----------------------------------------------------
> Initializing cgroup subsys cpuset
> Initializing cgroup subsys cpuacct
> Linux version 3.10.0-rc5-14354-gd39046e (mikey@ka1) (gcc version 4.6.0 (GCC) ) #48 SMP Tue Jun 18 11:10:17 EST 2013
> [boot]0012 Setup Arch
> Zone ranges:
> DMA [mem 0x00000000-0x7fffffff]
> Normal empty
> Movable zone start for each node
> Early memory node ranges
> node 0: [mem 0x00000000-0x7fffffff]
> [boot]0015 Setup Done
> PERCPU: Embedded 2 pages/cpu @c000000002100000 s88448 r0 d42624 u1048576
> Built 1 zonelists in Node order, mobility grouping on. Total pages: 32740
> Policy zone: DMA
> Kernel command line: ipr.enabled=0
> PID hash table entries: 4096 (order: -1, 32768 bytes)
> Sorting __ex_table...
> freeing bootmem node 0
> Memory: 2061696k/2097152k available (11840k kernel code, 35456k reserved, 1792k data, 1072k bss, 704k init)
> SLUB: HWalign=128, Order=0-3, MinObjects=0, CPUs=1, Nodes=256
> Hierarchical RCU implementation.
> RCU restricting CPUs from NR_CPUS=2048 to nr_cpu_ids=1.
> NR_IRQS:512 nr_irqs:512 16
> error: reading the clock failed (-1)
> clocksource: timebase mult[86bca1b] shift[23] registered
> Console: colour dummy device 80x25
> console [tty0] enabled
> console [hvc0] enabled
> pid_max: default: 32768 minimum: 301
> Dentry cache hash table entries: 262144 (order: 5, 2097152 bytes)
> Inode-cache hash table entries: 131072 (order: 4, 1048576 bytes)
> Mount-cache hash table entries: 4096
> Initializing cgroup subsys devices
> Initializing cgroup subsys freezer
> error: hwirq 0x2 is too large for (null)
> ------------[ cut here ]------------
> WARNING: at /scratch/mikey/src/linux-next/kernel/irq/irqdomain.c:276
> Modules linked in:
> CPU: 0 PID: 1 Comm: swapper/0 Not tainted 3.10.0-rc5-14354-gd39046e #48
> task: c00000007e500000 ti: c00000007e520000 task.ti: c00000007e520000
> NIP: c00000000011433c LR: c000000000114338 CTR: c000000000422310
> REGS: c00000007e523780 TRAP: 0700 Not tainted (3.10.0-rc5-14354-gd39046e)
> MSR: 9000000000029032 <SF,HV,EE,ME,IR,DR,RI> CR: 28000084 XER: 02000000
> SOFTE: 1
> CFAR: c0000000007b0ab8
>
> GPR00: c000000000114338 c00000007e523a00 c000000000cb9570 0000000000000028
> GPR04: 0000000000000000 0000000000000043 ffffffffffffffff 0000000000000000
> GPR08: 076507200766076f c000000000be3790 0000000000000720 0000000000000000
> GPR12: 0000000028000082 c00000000fe00000 c00000000000be60 0000000000000000
> GPR16: 0000000000000000 0000000000000000 0000000000000000 0000000000000000
> GPR20: 0000000000000000 0000000000000000 0000000000000000 c000000000b704b0
> GPR24: c000000000d44a80 c000000000cb9570 0000000000000010 0000000000000002
> GPR28: c00000007e0c0000 0000000000000000 c00000007e0c0000 c00000007e052400
> NIP [c00000000011433c] .irq_domain_associate+0x1ec/0x260
> LR [c000000000114338] .irq_domain_associate+0x1e8/0x260
> PACATMSCRATCH [9000000032009032]
> Call Trace:
> [c00000007e523a00] [c000000000114338] .irq_domain_associate+0x1e8/0x260 (unreliable)
> [c00000007e523aa0] [c000000000114bf0] .irq_create_mapping+0xa0/0x170
> [c00000007e523b30] [c000000000af4574] .xics_smp_probe+0x38/0xa4
> [c00000007e523ba0] [c000000000af7af0] .pSeries_smp_probe+0x10/0x68
> [c00000007e523c10] [c000000000aed740] .smp_prepare_cpus+0x20c/0x244
> [c00000007e523cd0] [c000000000ae44c0] .kernel_init_freeable+0x138/0x328
> [c00000007e523db0] [c00000000000be7c] .kernel_init+0x1c/0x120
> [c00000007e523e30] [c00000000000a05c] .ret_from_kernel_thread+0x5c/0x80
> Instruction dump:
> 482ca915 60000000 7fa3eb78 4868ea09 60000000 4bffff7c 3c62ffd5 e8bc0010
> 7f6407b4 3863c108 4869c731 60000000 <0fe00000> 3ba0ffea 4bffff7c 3c62ffd5
> ---[ end trace 31fd0ba7d8756001 ]---
> ------------[ cut here ]------------
> kernel BUG at /scratch/mikey/src/linux-next/arch/powerpc/sysdev/xics/xics-common.c:134!
> cpu 0x0: Vector: 700 (Program Check) at [c00000007e5238b0]
> pc: c000000000af4580: .xics_smp_probe+0x44/0xa4
> lr: c000000000af4574: .xics_smp_probe+0x38/0xa4
> sp: c00000007e523b30
> msr: 9000000000029032
> current = 0xc00000007e500000
> paca = 0xc00000000fe00000 softe: 0 irq_happened: 0x01
> pid = 1, comm = swapper/0
> kernel BUG at /scratch/mikey/src/linux-next/arch/powerpc/sysdev/xics/xics-common.c:134!
> enter ? for help
> [c00000007e523ba0] c000000000af7af0 .pSeries_smp_probe+0x10/0x68
> [c00000007e523c10] c000000000aed740 .smp_prepare_cpus+0x20c/0x244
> [c00000007e523cd0] c000000000ae44c0 .kernel_init_freeable+0x138/0x328
> [c00000007e523db0] c00000000000be7c .kernel_init+0x1c/0x120
> [c00000007e523e30] c00000000000a05c .ret_from_kernel_thread+0x5c/0x80
> 0:mon> <no input ...>
> Oops: Exception in kernel mode, sig: 5 [#1]
> SMP NR_CPUS=2048 NUMA pSeries
> Modules linked in:
> CPU: 0 PID: 1 Comm: swapper/0 Tainted: G W 3.10.0-rc5-14354-gd39046e #48
> task: c00000007e500000 ti: c00000007e520000 task.ti: c00000007e520000
> NIP: c000000000af4580 LR: c000000000af4574 CTR: 0000000000000002
> REGS: c00000007e5238b0 TRAP: 0700 Tainted: G W (3.10.0-rc5-14354-gd39046e)
> MSR: 9000000000029032 <SF,HV,EE,ME,IR,DR,RI> CR: 28000084 XER: 02000000
> SOFTE: 1
> CFAR: c000000000114c1c
>
> GPR00: 0000000000000001 c00000007e523b30 c000000000cb9570 0000000000000000
> GPR04: 0000000000000010 0000000000020000 c00000007e052600 c000000000dcdd10
> GPR08: c000000000b85ac8 fffffffffffeffff 0000000000000001 000000000001ffff
> GPR12: 0000000088000084 c00000000fe00000 c00000000000be60 0000000000000000
> GPR16: 0000000000000000 0000000000000000 0000000000000000 0000000000000000
> GPR20: 0000000000000000 0000000000000000 0000000000000000 c000000000b704b0
> GPR24: c000000000d44a80 c000000000cb9570 c000000000cb9570 c000000000b703b0
> GPR28: c000000000b704b0 c000000000d40858 0000000000004000 0000000000000800
> NIP [c000000000af4580] .xics_smp_probe+0x44/0xa4
> LR [c000000000af4574] .xics_smp_probe+0x38/0xa4
> PACATMSCRATCH [9000000000029032]
> Call Trace:
> [c00000007e523b30] [c000000000af4574] .xics_smp_probe+0x38/0xa4 (unreliable)
> [c00000007e523ba0] [c000000000af7af0] .pSeries_smp_probe+0x10/0x68
> [c00000007e523c10] [c000000000aed740] .smp_prepare_cpus+0x20c/0x244
> [c00000007e523cd0] [c000000000ae44c0] .kernel_init_freeable+0x138/0x328
> [c00000007e523db0] [c00000000000be7c] .kernel_init+0x1c/0x120
> [c00000007e523e30] [c00000000000a05c] .ret_from_kernel_thread+0x5c/0x80
> Instruction dump:
> 39290f80 38800002 e86b0010 f8010010 f821ff91 e80a0028 e9290000 f8090008
> 4b6205e1 60000000 7c600074 7800d182 <0b000000> 3d22000d 3ce2ffd3 3929fde0
> ---[ end trace 31fd0ba7d8756002 ]---
>
>
>
> > Originally, irq_domain_associate_many() was designed to unwind the
> > mapped irqs on a failure of any individual association. However, that
> > proved to be a problem with certain IRQ controllers. Some of them only
> > support a subset of irqs, and will fail when attempting to map a
> > reserved IRQ. In those cases we want to map as many IRQs as possible, so
> > instead it is better for irq_domain_associate_many() to make a
> > best-effort attempt to map irqs, but not fail if any or all of them
> > don't succeed. If a caller really cares about how many irqs got
> > associated, then it should instead go back and check that all of the
> > irqs is cares about were mapped.
> >
> > The original design open-coded the individual association code into the
> > body of irq_domain_associate_many(), but with no longer needing to
> > unwind associations, the code becomes simpler to split out
> > irq_domain_associate() to contain the bulk of the logic, and
> > irq_domain_associate_many() to be a simple loop wrapper.
> >
> > This patch also adds a new error check to the associate path to make
> > sure it isn't called for an irq larger than the controller can handle,
> > and adds locking so that the irq_domain_mutex is held while setting up a
> > new association.
> >
> > v2: Fixup x86 warning. irq_domain_associate_many() no longer returns an
> > error code, but reports errors to the printk log directly. In the
> > majority of cases we don't actually want to fail if there is a
> > problem, but rather log it and still try to boot the system.
> >
> > Signed-off-by: Grant Likely <grant.likely@linaro.org>
> > ---
> > arch/x86/kernel/devicetree.c | 4 +-
> > include/linux/irqdomain.h | 22 +++--
> > kernel/irq/irqdomain.c | 185 +++++++++++++++++++++----------------------
> > 3 files changed, 102 insertions(+), 109 deletions(-)
> >
> > diff --git a/arch/x86/kernel/devicetree.c b/arch/x86/kernel/devicetree.c
> > index b158152..4934890 100644
> > --- a/arch/x86/kernel/devicetree.c
> > +++ b/arch/x86/kernel/devicetree.c
> > @@ -364,9 +364,7 @@ static void dt_add_ioapic_domain(unsigned int ioapic_num,
> > * and assigned so we can keep the 1:1 mapping which the ioapic
> > * is having.
> > */
> > - ret = irq_domain_associate_many(id, 0, 0, NR_IRQS_LEGACY);
> > - if (ret)
> > - pr_err("Error mapping legacy IRQs: %d\n", ret);
> > + irq_domain_associate_many(id, 0, 0, NR_IRQS_LEGACY);
> >
> > if (num > NR_IRQS_LEGACY) {
> > ret = irq_create_strict_mappings(id, NR_IRQS_LEGACY,
> > diff --git a/include/linux/irqdomain.h b/include/linux/irqdomain.h
> > index fd4b26f..f9e8e06 100644
> > --- a/include/linux/irqdomain.h
> > +++ b/include/linux/irqdomain.h
> > @@ -103,6 +103,7 @@ struct irq_domain {
> > struct irq_domain_chip_generic *gc;
> >
> > /* reverse map data. The linear map gets appended to the irq_domain */
> > + irq_hw_number_t hwirq_max;
> > unsigned int revmap_direct_max_irq;
> > unsigned int revmap_size;
> > struct radix_tree_root revmap_tree;
> > @@ -110,8 +111,8 @@ struct irq_domain {
> > };
> >
> > #ifdef CONFIG_IRQ_DOMAIN
> > -struct irq_domain *__irq_domain_add(struct device_node *of_node,
> > - int size, int direct_max,
> > +struct irq_domain *__irq_domain_add(struct device_node *of_node, int size,
> > + irq_hw_number_t hwirq_max, int direct_max,
> > const struct irq_domain_ops *ops,
> > void *host_data);
> > struct irq_domain *irq_domain_add_simple(struct device_node *of_node,
> > @@ -140,14 +141,14 @@ static inline struct irq_domain *irq_domain_add_linear(struct device_node *of_no
> > const struct irq_domain_ops *ops,
> > void *host_data)
> > {
> > - return __irq_domain_add(of_node, size, 0, ops, host_data);
> > + return __irq_domain_add(of_node, size, size, 0, ops, host_data);
> > }
> > static inline struct irq_domain *irq_domain_add_nomap(struct device_node *of_node,
> > unsigned int max_irq,
> > const struct irq_domain_ops *ops,
> > void *host_data)
> > {
> > - return __irq_domain_add(of_node, 0, max_irq, ops, host_data);
> > + return __irq_domain_add(of_node, 0, max_irq, max_irq, ops, host_data);
> > }
> > static inline struct irq_domain *irq_domain_add_legacy_isa(
> > struct device_node *of_node,
> > @@ -166,14 +167,11 @@ static inline struct irq_domain *irq_domain_add_tree(struct device_node *of_node
> >
> > extern void irq_domain_remove(struct irq_domain *host);
> >
> > -extern int irq_domain_associate_many(struct irq_domain *domain,
> > - unsigned int irq_base,
> > - irq_hw_number_t hwirq_base, int count);
> > -static inline int irq_domain_associate(struct irq_domain *domain, unsigned int irq,
> > - irq_hw_number_t hwirq)
> > -{
> > - return irq_domain_associate_many(domain, irq, hwirq, 1);
> > -}
> > +extern int irq_domain_associate(struct irq_domain *domain, unsigned int irq,
> > + irq_hw_number_t hwirq);
> > +extern void irq_domain_associate_many(struct irq_domain *domain,
> > + unsigned int irq_base,
> > + irq_hw_number_t hwirq_base, int count);
> >
> > extern unsigned int irq_create_mapping(struct irq_domain *host,
> > irq_hw_number_t hwirq);
> > diff --git a/kernel/irq/irqdomain.c b/kernel/irq/irqdomain.c
> > index 280b804..80e9249 100644
> > --- a/kernel/irq/irqdomain.c
> > +++ b/kernel/irq/irqdomain.c
> > @@ -35,8 +35,8 @@ static struct irq_domain *irq_default_domain;
> > * register allocated irq_domain with irq_domain_register(). Returns pointer
> > * to IRQ domain, or NULL on failure.
> > */
> > -struct irq_domain *__irq_domain_add(struct device_node *of_node,
> > - int size, int direct_max,
> > +struct irq_domain *__irq_domain_add(struct device_node *of_node, int size,
> > + irq_hw_number_t hwirq_max, int direct_max,
> > const struct irq_domain_ops *ops,
> > void *host_data)
> > {
> > @@ -52,6 +52,7 @@ struct irq_domain *__irq_domain_add(struct device_node *of_node,
> > domain->ops = ops;
> > domain->host_data = host_data;
> > domain->of_node = of_node_get(of_node);
> > + domain->hwirq_max = hwirq_max;
> > domain->revmap_size = size;
> > domain->revmap_direct_max_irq = direct_max;
> >
> > @@ -126,7 +127,7 @@ struct irq_domain *irq_domain_add_simple(struct device_node *of_node,
> > {
> > struct irq_domain *domain;
> >
> > - domain = __irq_domain_add(of_node, size, 0, ops, host_data);
> > + domain = __irq_domain_add(of_node, size, size, 0, ops, host_data);
> > if (!domain)
> > return NULL;
> >
> > @@ -139,7 +140,7 @@ struct irq_domain *irq_domain_add_simple(struct device_node *of_node,
> > pr_info("Cannot allocate irq_descs @ IRQ%d, assuming pre-allocated\n",
> > first_irq);
> > }
> > - WARN_ON(irq_domain_associate_many(domain, first_irq, 0, size));
> > + irq_domain_associate_many(domain, first_irq, 0, size);
> > }
> >
> > return domain;
> > @@ -170,11 +171,12 @@ struct irq_domain *irq_domain_add_legacy(struct device_node *of_node,
> > {
> > struct irq_domain *domain;
> >
> > - domain = __irq_domain_add(of_node, first_hwirq + size, 0, ops, host_data);
> > + domain = __irq_domain_add(of_node, first_hwirq + size,
> > + first_hwirq + size, 0, ops, host_data);
> > if (!domain)
> > return NULL;
> >
> > - WARN_ON(irq_domain_associate_many(domain, first_irq, first_hwirq, size));
> > + irq_domain_associate_many(domain, first_irq, first_hwirq, size);
> >
> > return domain;
> > }
> > @@ -228,109 +230,109 @@ void irq_set_default_host(struct irq_domain *domain)
> > }
> > EXPORT_SYMBOL_GPL(irq_set_default_host);
> >
> > -static void irq_domain_disassociate_many(struct irq_domain *domain,
> > - unsigned int irq_base, int count)
> > +static void irq_domain_disassociate(struct irq_domain *domain, unsigned int irq)
> > {
> > - /*
> > - * disassociate in reverse order;
> > - * not strictly necessary, but nice for unwinding
> > - */
> > - while (count--) {
> > - int irq = irq_base + count;
> > - struct irq_data *irq_data = irq_get_irq_data(irq);
> > - irq_hw_number_t hwirq;
> > + struct irq_data *irq_data = irq_get_irq_data(irq);
> > + irq_hw_number_t hwirq;
> >
> > - if (WARN_ON(!irq_data || irq_data->domain != domain))
> > - continue;
> > + if (WARN(!irq_data || irq_data->domain != domain,
> > + "virq%i doesn't exist; cannot disassociate\n", irq))
> > + return;
> >
> > - hwirq = irq_data->hwirq;
> > - irq_set_status_flags(irq, IRQ_NOREQUEST);
> > + hwirq = irq_data->hwirq;
> > + irq_set_status_flags(irq, IRQ_NOREQUEST);
> >
> > - /* remove chip and handler */
> > - irq_set_chip_and_handler(irq, NULL, NULL);
> > + /* remove chip and handler */
> > + irq_set_chip_and_handler(irq, NULL, NULL);
> >
> > - /* Make sure it's completed */
> > - synchronize_irq(irq);
> > + /* Make sure it's completed */
> > + synchronize_irq(irq);
> >
> > - /* Tell the PIC about it */
> > - if (domain->ops->unmap)
> > - domain->ops->unmap(domain, irq);
> > - smp_mb();
> > + /* Tell the PIC about it */
> > + if (domain->ops->unmap)
> > + domain->ops->unmap(domain, irq);
> > + smp_mb();
> >
> > - irq_data->domain = NULL;
> > - irq_data->hwirq = 0;
> > + irq_data->domain = NULL;
> > + irq_data->hwirq = 0;
> >
> > - /* Clear reverse map for this hwirq */
> > - if (hwirq < domain->revmap_size) {
> > - domain->linear_revmap[hwirq] = 0;
> > - } else {
> > - mutex_lock(&revmap_trees_mutex);
> > - radix_tree_delete(&domain->revmap_tree, hwirq);
> > - mutex_unlock(&revmap_trees_mutex);
> > - }
> > + /* Clear reverse map for this hwirq */
> > + if (hwirq < domain->revmap_size) {
> > + domain->linear_revmap[hwirq] = 0;
> > + } else {
> > + mutex_lock(&revmap_trees_mutex);
> > + radix_tree_delete(&domain->revmap_tree, hwirq);
> > + mutex_unlock(&revmap_trees_mutex);
> > }
> > }
> >
> > -int irq_domain_associate_many(struct irq_domain *domain, unsigned int irq_base,
> > - irq_hw_number_t hwirq_base, int count)
> > +int irq_domain_associate(struct irq_domain *domain, unsigned int virq,
> > + irq_hw_number_t hwirq)
> > {
> > - unsigned int virq = irq_base;
> > - irq_hw_number_t hwirq = hwirq_base;
> > - int i, ret;
> > + struct irq_data *irq_data = irq_get_irq_data(virq);
> > + int ret;
> >
> > - pr_debug("%s(%s, irqbase=%i, hwbase=%i, count=%i)\n", __func__,
> > - of_node_full_name(domain->of_node), irq_base, (int)hwirq_base, count);
> > + if (WARN(hwirq >= domain->hwirq_max,
> > + "error: hwirq 0x%x is too large for %s\n", (int)hwirq, domain->name))
> > + return -EINVAL;
> > + if (WARN(!irq_data, "error: virq%i is not allocated", virq))
> > + return -EINVAL;
> > + if (WARN(irq_data->domain, "error: virq%i is already associated", virq))
> > + return -EINVAL;
> >
> > - for (i = 0; i < count; i++) {
> > - struct irq_data *irq_data = irq_get_irq_data(virq + i);
> > -
> > - if (WARN(!irq_data, "error: irq_desc not allocated; "
> > - "irq=%i hwirq=0x%x\n", virq + i, (int)hwirq + i))
> > - return -EINVAL;
> > - if (WARN(irq_data->domain, "error: irq_desc already associated; "
> > - "irq=%i hwirq=0x%x\n", virq + i, (int)hwirq + i))
> > - return -EINVAL;
> > - };
> > -
> > - for (i = 0; i < count; i++, virq++, hwirq++) {
> > - struct irq_data *irq_data = irq_get_irq_data(virq);
> > -
> > - irq_data->hwirq = hwirq;
> > - irq_data->domain = domain;
> > - if (domain->ops->map) {
> > - ret = domain->ops->map(domain, virq, hwirq);
> > - if (ret != 0) {
> > - /*
> > - * If map() returns -EPERM, this interrupt is protected
> > - * by the firmware or some other service and shall not
> > - * be mapped. Don't bother telling the user about it.
> > - */
> > - if (ret != -EPERM) {
> > - pr_info("%s didn't like hwirq-0x%lx to VIRQ%i mapping (rc=%d)\n",
> > - domain->name, hwirq, virq, ret);
> > - }
> > - irq_data->domain = NULL;
> > - irq_data->hwirq = 0;
> > - continue;
> > + mutex_lock(&irq_domain_mutex);
> > + irq_data->hwirq = hwirq;
> > + irq_data->domain = domain;
> > + if (domain->ops->map) {
> > + ret = domain->ops->map(domain, virq, hwirq);
> > + if (ret != 0) {
> > + /*
> > + * If map() returns -EPERM, this interrupt is protected
> > + * by the firmware or some other service and shall not
> > + * be mapped. Don't bother telling the user about it.
> > + */
> > + if (ret != -EPERM) {
> > + pr_info("%s didn't like hwirq-0x%lx to VIRQ%i mapping (rc=%d)\n",
> > + domain->name, hwirq, virq, ret);
> > }
> > - /* If not already assigned, give the domain the chip's name */
> > - if (!domain->name && irq_data->chip)
> > - domain->name = irq_data->chip->name;
> > + irq_data->domain = NULL;
> > + irq_data->hwirq = 0;
> > + mutex_unlock(&irq_domain_mutex);
> > + return ret;
> > }
> >
> > - if (hwirq < domain->revmap_size) {
> > - domain->linear_revmap[hwirq] = virq;
> > - } else {
> > - mutex_lock(&revmap_trees_mutex);
> > - radix_tree_insert(&domain->revmap_tree, hwirq, irq_data);
> > - mutex_unlock(&revmap_trees_mutex);
> > - }
> > + /* If not already assigned, give the domain the chip's name */
> > + if (!domain->name && irq_data->chip)
> > + domain->name = irq_data->chip->name;
> > + }
> >
> > - irq_clear_status_flags(virq, IRQ_NOREQUEST);
> > + if (hwirq < domain->revmap_size) {
> > + domain->linear_revmap[hwirq] = virq;
> > + } else {
> > + mutex_lock(&revmap_trees_mutex);
> > + radix_tree_insert(&domain->revmap_tree, hwirq, irq_data);
> > + mutex_unlock(&revmap_trees_mutex);
> > }
> > + mutex_unlock(&irq_domain_mutex);
> > +
> > + irq_clear_status_flags(virq, IRQ_NOREQUEST);
> >
> > return 0;
> > }
> > +EXPORT_SYMBOL_GPL(irq_domain_associate);
> > +
> > +void irq_domain_associate_many(struct irq_domain *domain, unsigned int irq_base,
> > + irq_hw_number_t hwirq_base, int count)
> > +{
> > + int i;
> > +
> > + pr_debug("%s(%s, irqbase=%i, hwbase=%i, count=%i)\n", __func__,
> > + of_node_full_name(domain->of_node), irq_base, (int)hwirq_base, count);
> > +
> > + for (i = 0; i < count; i++) {
> > + irq_domain_associate(domain, irq_base + i, hwirq_base + i);
> > + }
> > +}
> > EXPORT_SYMBOL_GPL(irq_domain_associate_many);
> >
> > /**
> > @@ -460,12 +462,7 @@ int irq_create_strict_mappings(struct irq_domain *domain, unsigned int irq_base,
> > if (unlikely(ret < 0))
> > return ret;
> >
> > - ret = irq_domain_associate_many(domain, irq_base, hwirq_base, count);
> > - if (unlikely(ret < 0)) {
> > - irq_free_descs(irq_base, count);
> > - return ret;
> > - }
> > -
> > + irq_domain_associate_many(domain, irq_base, hwirq_base, count);
> > return 0;
> > }
> > EXPORT_SYMBOL_GPL(irq_create_strict_mappings);
> > @@ -535,7 +532,7 @@ void irq_dispose_mapping(unsigned int virq)
> > if (WARN_ON(domain == NULL))
> > return;
> >
> > - irq_domain_disassociate_many(domain, virq, 1);
> > + irq_domain_disassociate(domain, virq);
> > irq_free_desc(virq);
> > }
> > EXPORT_SYMBOL_GPL(irq_dispose_mapping);
> > --
> > 1.8.1.2
> >
> > --
> > To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
> > the body of a message to majordomo@vger.kernel.org
> > More majordomo info at http://vger.kernel.org/majordomo-info.html
> > Please read the FAQ at http://www.tux.org/lkml/
> >
^ permalink raw reply
* Re: [PATCH 1/2] powerpc: add Book E support to 64-bit hibernation
From: Benjamin Herrenschmidt @ 2013-06-18 1:29 UTC (permalink / raw)
To: Scott Wood
Cc: Wood Scott-B07421, anton@enomsg.org, Wang Dongsheng-B40534,
johannes@sipsolutions.net, linuxppc-dev@lists.ozlabs.org
In-Reply-To: <1371514923.9073.14@snotra>
On Mon, 2013-06-17 at 19:22 -0500, Scott Wood wrote:
> On 06/17/2013 07:17:30 PM, Benjamin Herrenschmidt wrote:
> > On Mon, 2013-06-17 at 19:01 -0500, Scott Wood wrote:
> > > I really doubt the exception scratch registers need to be saved --
> > > we're not trying to restore into the middle of an exception
> > > prolog/epilog.
> > >
> > > book3s has the PACA as well and they don't save it. Don't we rely
> > on
> > > things like boot-time memory allocations happening in the same place
> > > when we resume? extlb is part of the PACA, so the same applies.
> >
> > I doubt we seriously tested hibernation :-) The PACA SPR should
> > definitely be saved/restored.
>
> OK. It's not obvious to me how much the entire mechanism depends on
> things like boot time allocations being the same each time -- if we do
> depend on that in general, then the PACA shouldn't change on a
> particular CPU, right?
No we shouldn't be depending on that stuff.
> Is it possible to restore on a different CPU than we saved on? If so,
> could restoring the PACA leave us pointing to a different CPU's PACA?
Today on your code no, but of course this needs to be handled, the PACA
should be restored along with other thing, but for the right
processor :-)
Cheers,
Ben.
^ permalink raw reply
* Re: [PATCH v2 08/11] irqdomain: Refactor irq_domain_associate_many()
From: Stephen Rothwell @ 2013-06-18 1:37 UTC (permalink / raw)
To: Michael Neuling
Cc: linux-kernel, Linux PPC dev, linux-next, Grant Likely,
Thomas Gleixner
In-Reply-To: <5220.1371518734@ale.ozlabs.ibm.com>
[-- Attachment #1: Type: text/plain, Size: 463 bytes --]
Hi all,
On Tue, 18 Jun 2013 11:25:34 +1000 Michael Neuling <mikey@neuling.org> wrote:
>
> Michael Neuling <mikey@neuling.org> wrote:
>
> > 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.
I will revert that commit in linux-next today.
--
Cheers,
Stephen Rothwell sfr@canb.auug.org.au
[-- Attachment #2: Type: application/pgp-signature, Size: 836 bytes --]
^ permalink raw reply
* Re: [PATCH 3/4] KVM: PPC: Add support for IOMMU in-kernel handling
From: Alex Williamson @ 2013-06-18 2:32 UTC (permalink / raw)
To: Benjamin Herrenschmidt
Cc: kvm, Alexey Kardashevskiy, Alexander Graf, kvm-ppc, linux-kernel,
Paul Mackerras, linuxppc-dev, David Gibson
In-Reply-To: <1371441361.21896.152.camel@pasglop>
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,
Alex
^ permalink raw reply
* Re: [PATCH 2/5] powerpc/fsl_msi: add MSIIR1 support for MPIC v4.3
From: Lian Minghuan-b31939 @ 2013-06-18 2:34 UTC (permalink / raw)
To: Scott Wood; +Cc: Minghuan Lian, linuxppc-dev, Zang Roy-R61911
In-Reply-To: <1371514512.9073.11@snotra>
Hi Soctt,
please see my comments inline.
On 06/18/2013 08:15 AM, Scott Wood wrote:
> On 06/16/2013 10:00:01 PM, Lian Minghuan-b31939 wrote:
>> Hi Scott,
>>
>> please see my comments inline.
>>
>> 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
>>>> 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>
>>>>
>>>> -#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)
>>>
>>> I don't see where you update all_avail in fsl_of_msi_probe.
>>>
>>> We should also be bounds-checking the contents of msi-available-ranges.
>>> Currently it looks like we just silently overrun the bitmap if we
>>> get bad
>>> input from the device tree.
>>>
>> [Minghuan] all_avail definition: static const u32 all_avail[] = { 0,
>> NR_MSI_IRQS };
>> When changing NR_MSI_REG to 16, NR_MSI_IRQS has been changed to
>> 16*32, and all_avail also is updated.
>
> That's my point. It shouldn't change for older hardware.
[Minghaun] the older hardware has 8 registers, mipcv4.3 has 16
registers. If we do not use 16*32 bitmap to indicate 8*32 irqs.(this way
just only wastes some memory and has no other harm)
we have two choice I think.
1. Use a variable assigned value 8 or 16 based on compatible, then
dynamically create bitmap
2. Add a new file for mpic v4.3.
What do you think?
>
>> Before calling fsl_msi_setup_hwirq(), the code has checked
>> 'msi-available-ranges', only the interrupts lied in
>> 'msi-available-ranges' will be initialized by call
>> fsl_msi_setup_hwirq() , and the corresponding bitmap will be freed. I
>> moved msi_bitmap_free_hwirqs() to fsl_msi_setup_hwirq(), because the
>> code would generate different bitmap when using MSIIR or MSIIR1.
>
> And what happens if msi-available-ranges is bad, and refers to
> non-existent MSIs past the end of the bitmap?
[Minghuan] If msi-available-ranges is bad, the below code will get error.
virt_msir = irq_of_parse_and_map(dev->dev.of_node, irq_index);
And the related error will be printed out and fsl_msi_setup_hwirq() will
return error directly. There is no chance to set non-existent MSIs past
the end of the bitmap.
>
> -Scott
^ permalink raw reply
* Re: [PATCH 3/5] powerpc/dts: update MSI bindings doc for MPIC v4.3
From: Lian Minghuan-b31939 @ 2013-06-18 2:49 UTC (permalink / raw)
To: Scott Wood; +Cc: Minghuan Lian, linuxppc-dev, Zang Roy-R61911
In-Reply-To: <1371516126.9073.16@snotra>
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 = "fsl,mpic-msi";
>>>>> + reg = <0x41600 0x200 0x44140 4>;
>>>>
>>>> Why 0x200?
>>>>
>>> [Minghuan] The offsets of the MSIA registers are from 0x41600 to
>>> 0x417ff, and the size is 0x200.
>>> offset 0x41600-0x4170 are MSIIRA1-7.
>>> 0x41720 is MSISRA,
>>> 0x41750 is MSIIR.
>>> The others are reserved.
>>
>> There is no MSIIRA on fsl,mpic-msi.
>
> Sigh, I was thinking of MSIIR1A -- which of course is distinct from
> both MSIIRA1 and MSIIRA. :-P
>
> 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
MSIIR, and provide some new features in feature.
>
> -Scott
^ permalink raw reply
* Re: [RFC 08/10] irqdomain: Refactor irq_domain_associate_many()
From: Mike Qiu @ 2013-06-18 3:09 UTC (permalink / raw)
To: Grant Likely; +Cc: Thomas Gleixner, linuxppc-dev, linux-kernel, Arnd Bergmann
In-Reply-To: <1370825362-11145-9-git-send-email-grant.likely@linaro.org>
于 2013/6/10 8:49, Grant Likely 写道:
> Originally, irq_domain_associate_many() was designed to unwind the
> mapped irqs on a failure of any individual association. However, that
> proved to be a problem with certain IRQ controllers. Some of them only
> support a subset of irqs, and will fail when attempting to map a
> reserved IRQ. In those cases we want to map as many IRQs as possible, so
> instead it is better for irq_domain_associate_many() to make a
> best-effort attempt to map irqs, but not fail if any or all of them
> don't succeed. If a caller really cares about how many irqs got
> associated, then it should instead go back and check that all of the
> irqs is cares about were mapped.
>
> The original design open-coded the individual association code into the
> body of irq_domain_associate_many(), but with no longer needing to
> unwind associations, the code becomes simpler to split out
> irq_domain_associate() to contain the bulk of the logic, and
> irq_domain_associate_many() to be a simple loop wrapper.
>
> This patch also adds a new error check to the associate path to make
> sure it isn't called for an irq larger than the controller can handle,
> and adds locking so that the irq_domain_mutex is held while setting up a
> new association.
>
> Signed-off-by: Grant Likely <grant.likely@linaro.org>
> ---
> include/linux/irqdomain.h | 22 +++---
> kernel/irq/irqdomain.c | 185 +++++++++++++++++++++++-----------------------
> 2 files changed, 101 insertions(+), 106 deletions(-)
>
> diff --git a/include/linux/irqdomain.h b/include/linux/irqdomain.h
> index fd4b26f..f9e8e06 100644
> --- a/include/linux/irqdomain.h
> +++ b/include/linux/irqdomain.h
> @@ -103,6 +103,7 @@ struct irq_domain {
> struct irq_domain_chip_generic *gc;
>
> /* reverse map data. The linear map gets appended to the irq_domain */
> + irq_hw_number_t hwirq_max;
> unsigned int revmap_direct_max_irq;
> unsigned int revmap_size;
> struct radix_tree_root revmap_tree;
> @@ -110,8 +111,8 @@ struct irq_domain {
> };
>
> #ifdef CONFIG_IRQ_DOMAIN
> -struct irq_domain *__irq_domain_add(struct device_node *of_node,
> - int size, int direct_max,
> +struct irq_domain *__irq_domain_add(struct device_node *of_node, int size,
> + irq_hw_number_t hwirq_max, int direct_max,
> const struct irq_domain_ops *ops,
> void *host_data);
> struct irq_domain *irq_domain_add_simple(struct device_node *of_node,
> @@ -140,14 +141,14 @@ static inline struct irq_domain *irq_domain_add_linear(struct device_node *of_no
> const struct irq_domain_ops *ops,
> void *host_data)
> {
> - return __irq_domain_add(of_node, size, 0, ops, host_data);
> + return __irq_domain_add(of_node, size, size, 0, ops, host_data);
> }
> static inline struct irq_domain *irq_domain_add_nomap(struct device_node *of_node,
> unsigned int max_irq,
> const struct irq_domain_ops *ops,
> void *host_data)
> {
> - return __irq_domain_add(of_node, 0, max_irq, ops, host_data);
> + return __irq_domain_add(of_node, 0, max_irq, max_irq, ops, host_data);
> }
> static inline struct irq_domain *irq_domain_add_legacy_isa(
> struct device_node *of_node,
> @@ -166,14 +167,11 @@ static inline struct irq_domain *irq_domain_add_tree(struct device_node *of_node
>
> extern void irq_domain_remove(struct irq_domain *host);
>
> -extern int irq_domain_associate_many(struct irq_domain *domain,
> - unsigned int irq_base,
> - irq_hw_number_t hwirq_base, int count);
> -static inline int irq_domain_associate(struct irq_domain *domain, unsigned int irq,
> - irq_hw_number_t hwirq)
> -{
> - return irq_domain_associate_many(domain, irq, hwirq, 1);
> -}
> +extern int irq_domain_associate(struct irq_domain *domain, unsigned int irq,
> + irq_hw_number_t hwirq);
> +extern void irq_domain_associate_many(struct irq_domain *domain,
> + unsigned int irq_base,
> + irq_hw_number_t hwirq_base, int count);
>
> extern unsigned int irq_create_mapping(struct irq_domain *host,
> irq_hw_number_t hwirq);
> diff --git a/kernel/irq/irqdomain.c b/kernel/irq/irqdomain.c
> index 280b804..80e9249 100644
> --- a/kernel/irq/irqdomain.c
> +++ b/kernel/irq/irqdomain.c
> @@ -35,8 +35,8 @@ static struct irq_domain *irq_default_domain;
> * register allocated irq_domain with irq_domain_register(). Returns pointer
> * to IRQ domain, or NULL on failure.
> */
> -struct irq_domain *__irq_domain_add(struct device_node *of_node,
> - int size, int direct_max,
> +struct irq_domain *__irq_domain_add(struct device_node *of_node, int size,
> + irq_hw_number_t hwirq_max, int direct_max,
> const struct irq_domain_ops *ops,
> void *host_data)
> {
> @@ -52,6 +52,7 @@ struct irq_domain *__irq_domain_add(struct device_node *of_node,
> domain->ops = ops;
> domain->host_data = host_data;
> domain->of_node = of_node_get(of_node);
> + domain->hwirq_max = hwirq_max;
> domain->revmap_size = size;
> domain->revmap_direct_max_irq = direct_max;
>
> @@ -126,7 +127,7 @@ struct irq_domain *irq_domain_add_simple(struct device_node *of_node,
> {
> struct irq_domain *domain;
>
> - domain = __irq_domain_add(of_node, size, 0, ops, host_data);
> + domain = __irq_domain_add(of_node, size, size, 0, ops, host_data);
> if (!domain)
> return NULL;
>
> @@ -139,7 +140,7 @@ struct irq_domain *irq_domain_add_simple(struct device_node *of_node,
> pr_info("Cannot allocate irq_descs @ IRQ%d, assuming pre-allocated\n",
> first_irq);
> }
> - WARN_ON(irq_domain_associate_many(domain, first_irq, 0, size));
> + irq_domain_associate_many(domain, first_irq, 0, size);
> }
>
> return domain;
> @@ -170,11 +171,12 @@ struct irq_domain *irq_domain_add_legacy(struct device_node *of_node,
> {
> struct irq_domain *domain;
>
> - domain = __irq_domain_add(of_node, first_hwirq + size, 0, ops, host_data);
> + domain = __irq_domain_add(of_node, first_hwirq + size,
> + first_hwirq + size, 0, ops, host_data);
> if (!domain)
> return NULL;
>
> - WARN_ON(irq_domain_associate_many(domain, first_irq, first_hwirq, size));
> + irq_domain_associate_many(domain, first_irq, first_hwirq, size);
>
> return domain;
> }
> @@ -228,109 +230,109 @@ void irq_set_default_host(struct irq_domain *domain)
> }
> EXPORT_SYMBOL_GPL(irq_set_default_host);
>
> -static void irq_domain_disassociate_many(struct irq_domain *domain,
> - unsigned int irq_base, int count)
> +static void irq_domain_disassociate(struct irq_domain *domain, unsigned int irq)
> {
> - /*
> - * disassociate in reverse order;
> - * not strictly necessary, but nice for unwinding
> - */
> - while (count--) {
> - int irq = irq_base + count;
> - struct irq_data *irq_data = irq_get_irq_data(irq);
> - irq_hw_number_t hwirq;
> + struct irq_data *irq_data = irq_get_irq_data(irq);
> + irq_hw_number_t hwirq;
>
> - if (WARN_ON(!irq_data || irq_data->domain != domain))
> - continue;
> + if (WARN(!irq_data || irq_data->domain != domain,
> + "virq%i doesn't exist; cannot disassociate\n", irq))
> + return;
>
> - hwirq = irq_data->hwirq;
> - irq_set_status_flags(irq, IRQ_NOREQUEST);
> + hwirq = irq_data->hwirq;
> + irq_set_status_flags(irq, IRQ_NOREQUEST);
>
> - /* remove chip and handler */
> - irq_set_chip_and_handler(irq, NULL, NULL);
> + /* remove chip and handler */
> + irq_set_chip_and_handler(irq, NULL, NULL);
>
> - /* Make sure it's completed */
> - synchronize_irq(irq);
> + /* Make sure it's completed */
> + synchronize_irq(irq);
>
> - /* Tell the PIC about it */
> - if (domain->ops->unmap)
> - domain->ops->unmap(domain, irq);
> - smp_mb();
> + /* Tell the PIC about it */
> + if (domain->ops->unmap)
> + domain->ops->unmap(domain, irq);
> + smp_mb();
>
> - irq_data->domain = NULL;
> - irq_data->hwirq = 0;
> + irq_data->domain = NULL;
> + irq_data->hwirq = 0;
>
> - /* Clear reverse map for this hwirq */
> - if (hwirq < domain->revmap_size) {
> - domain->linear_revmap[hwirq] = 0;
> - } else {
> - mutex_lock(&revmap_trees_mutex);
> - radix_tree_delete(&domain->revmap_tree, hwirq);
> - mutex_unlock(&revmap_trees_mutex);
> - }
> + /* Clear reverse map for this hwirq */
> + if (hwirq < domain->revmap_size) {
> + domain->linear_revmap[hwirq] = 0;
> + } else {
> + mutex_lock(&revmap_trees_mutex);
> + radix_tree_delete(&domain->revmap_tree, hwirq);
> + mutex_unlock(&revmap_trees_mutex);
> }
> }
>
> -int irq_domain_associate_many(struct irq_domain *domain, unsigned int irq_base,
> - irq_hw_number_t hwirq_base, int count)
> +int irq_domain_associate(struct irq_domain *domain, unsigned int virq,
> + irq_hw_number_t hwirq)
> {
> - unsigned int virq = irq_base;
> - irq_hw_number_t hwirq = hwirq_base;
> - int i, ret;
> + struct irq_data *irq_data = irq_get_irq_data(virq);
> + int ret;
>
> - pr_debug("%s(%s, irqbase=%i, hwbase=%i, count=%i)\n", __func__,
> - of_node_full_name(domain->of_node), irq_base, (int)hwirq_base, count);
> + if (WARN(hwirq >= domain->hwirq_max,
> + "error: hwirq 0x%x is too large for %s\n", (int)hwirq, domain->name))
> + return -EINVAL;
> + if (WARN(!irq_data, "error: virq%i is not allocated", virq))
> + return -EINVAL;
> + if (WARN(irq_data->domain, "error: virq%i is already associated", virq))
> + return -EINVAL;
Hi Grant,
I have a qestion here, assume that we have three hwirqs and alloc three
virqs, for first irq, it check irq_data
and irq_data->domain pass, but the second is failed, then this code do
nothing with failed( in
irq_domain_associate_many()) and continue to associated the third one.
This should be very dangours, even though I have no idea of when this
could happen.
Thanks
Mike
> - for (i = 0; i < count; i++) {
> - struct irq_data *irq_data = irq_get_irq_data(virq + i);
> -
> - if (WARN(!irq_data, "error: irq_desc not allocated; "
> - "irq=%i hwirq=0x%x\n", virq + i, (int)hwirq + i))
> - return -EINVAL;
> - if (WARN(irq_data->domain, "error: irq_desc already associated; "
> - "irq=%i hwirq=0x%x\n", virq + i, (int)hwirq + i))
> - return -EINVAL;
> - };
> -
> - for (i = 0; i < count; i++, virq++, hwirq++) {
> - struct irq_data *irq_data = irq_get_irq_data(virq);
> -
> - irq_data->hwirq = hwirq;
> - irq_data->domain = domain;
> - if (domain->ops->map) {
> - ret = domain->ops->map(domain, virq, hwirq);
> - if (ret != 0) {
> - /*
> - * If map() returns -EPERM, this interrupt is protected
> - * by the firmware or some other service and shall not
> - * be mapped. Don't bother telling the user about it.
> - */
> - if (ret != -EPERM) {
> - pr_info("%s didn't like hwirq-0x%lx to VIRQ%i mapping (rc=%d)\n",
> - domain->name, hwirq, virq, ret);
> - }
> - irq_data->domain = NULL;
> - irq_data->hwirq = 0;
> - continue;
> + mutex_lock(&irq_domain_mutex);
> + irq_data->hwirq = hwirq;
> + irq_data->domain = domain;
> + if (domain->ops->map) {
> + ret = domain->ops->map(domain, virq, hwirq);
> + if (ret != 0) {
> + /*
> + * If map() returns -EPERM, this interrupt is protected
> + * by the firmware or some other service and shall not
> + * be mapped. Don't bother telling the user about it.
> + */
> + if (ret != -EPERM) {
> + pr_info("%s didn't like hwirq-0x%lx to VIRQ%i mapping (rc=%d)\n",
> + domain->name, hwirq, virq, ret);
> }
> - /* If not already assigned, give the domain the chip's name */
> - if (!domain->name && irq_data->chip)
> - domain->name = irq_data->chip->name;
> + irq_data->domain = NULL;
> + irq_data->hwirq = 0;
> + mutex_unlock(&irq_domain_mutex);
> + return ret;
> }
>
> - if (hwirq < domain->revmap_size) {
> - domain->linear_revmap[hwirq] = virq;
> - } else {
> - mutex_lock(&revmap_trees_mutex);
> - radix_tree_insert(&domain->revmap_tree, hwirq, irq_data);
> - mutex_unlock(&revmap_trees_mutex);
> - }
> + /* If not already assigned, give the domain the chip's name */
> + if (!domain->name && irq_data->chip)
> + domain->name = irq_data->chip->name;
> + }
>
> - irq_clear_status_flags(virq, IRQ_NOREQUEST);
> + if (hwirq < domain->revmap_size) {
> + domain->linear_revmap[hwirq] = virq;
> + } else {
> + mutex_lock(&revmap_trees_mutex);
> + radix_tree_insert(&domain->revmap_tree, hwirq, irq_data);
> + mutex_unlock(&revmap_trees_mutex);
> }
> + mutex_unlock(&irq_domain_mutex);
> +
> + irq_clear_status_flags(virq, IRQ_NOREQUEST);
>
> return 0;
> }
> +EXPORT_SYMBOL_GPL(irq_domain_associate);
> +
> +void irq_domain_associate_many(struct irq_domain *domain, unsigned int irq_base,
> + irq_hw_number_t hwirq_base, int count)
> +{
> + int i;
> +
> + pr_debug("%s(%s, irqbase=%i, hwbase=%i, count=%i)\n", __func__,
> + of_node_full_name(domain->of_node), irq_base, (int)hwirq_base, count);
> +
> + for (i = 0; i < count; i++) {
> + irq_domain_associate(domain, irq_base + i, hwirq_base + i);
> + }
> +}
> EXPORT_SYMBOL_GPL(irq_domain_associate_many);
>
> /**
> @@ -460,12 +462,7 @@ int irq_create_strict_mappings(struct irq_domain *domain, unsigned int irq_base,
> if (unlikely(ret < 0))
> return ret;
>
> - ret = irq_domain_associate_many(domain, irq_base, hwirq_base, count);
> - if (unlikely(ret < 0)) {
> - irq_free_descs(irq_base, count);
> - return ret;
> - }
> -
> + irq_domain_associate_many(domain, irq_base, hwirq_base, count);
> return 0;
> }
> EXPORT_SYMBOL_GPL(irq_create_strict_mappings);
> @@ -535,7 +532,7 @@ void irq_dispose_mapping(unsigned int virq)
> if (WARN_ON(domain == NULL))
> return;
>
> - irq_domain_disassociate_many(domain, virq, 1);
> + irq_domain_disassociate(domain, virq);
> irq_free_desc(virq);
> }
> EXPORT_SYMBOL_GPL(irq_dispose_mapping);
^ permalink raw reply
* Re: [PATCH 5/5] powerpc/fsl_msi: add 'msiregs' kernel parameter
From: Lian Minghuan-b31939 @ 2013-06-18 3:10 UTC (permalink / raw)
To: Scott Wood; +Cc: Minghuan Lian, linuxppc-dev, Zang Roy-R61911
In-Reply-To: <1371514692.9073.12@snotra>
Hi Scott,
please see my comments inline.
On 06/18/2013 08:18 AM, Scott Wood wrote:
> On 06/17/2013 12:36:50 AM, Lian Minghuan-b31939 wrote:
>> Hi Scott,
>>
>> please see my comments inline.
>>
>> 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.
>>>>
>>>> 2. Add 'msiregs' kernel parameter instead of 'msi-available-ranges'
>>>> functionality.
>>>
>>> The reason we used a device tree property was because this is for
>>> virtualization and AMP scenarios where this instance of Linux does
>>> not own all of the MSI registers.
>>>
>>> I don't see any reasonable way to partition an MPIC v4.3 MSI group
>>> -- but there are more groups, so it's not that bad. What's the use
>>> case for this patch?
>>>
>> [Minghuan] I do not known any case about this patch. I add 'msiregs'
>> just for achieving "msi-available-ranges" functionality. I do not
>> want to remove partition functionality when updating to mpic4.3,
>> although I do not see virtualization and AMP cases on T4(KVM does not
>> need this functionality).
>
> Such functionality does not work on mpic v4.3. There are conflicting
> requirements of contiguous MSIs (because PCI devices can use them that
> way) and the inability to partition a single register (because they
> all go to the same MPIC interrupt).
>
> Keep msi-available-ranges as is for older hardware, and just ignore it
> (with a warning printed) if it's present on MPIC v4.3.
>
[Minghuan] Thanks for your guidance.
But 'msireg' should be remained or removed? if remaining 'msiregs', it
should be for all mpic or only for mpic v4.3?
> -Scott
^ permalink raw reply
* Re: [PATCH 3/4] KVM: PPC: Add support for IOMMU in-kernel handling
From: Benjamin Herrenschmidt @ 2013-06-18 4:38 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: <1371522772.22681.140.camel@ul30vt.home>
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....
>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: Build regressions/improvements in v3.10-rc6
From: Michael Ellerman @ 2013-06-18 4:54 UTC (permalink / raw)
To: Geert Uytterhoeven; +Cc: Linux/PPC Development, Linux Kernel Development
In-Reply-To: <alpine.DEB.2.00.1306172115300.17174@ayla.of.borg>
On Mon, Jun 17, 2013 at 09:19:51PM +0200, Geert Uytterhoeven wrote:
> On Mon, 17 Jun 2013, Geert Uytterhoeven wrote:
>
> powerpc-randconfig
> + arch/powerpc/include/asm/mmu-hash64.h: error: control reaches end of non-void function [-Werror=return-type]: => 180:1
This is running past a BUG(), which must mean we have CONFIG_BUG=n.
BUG() turning into nothing is a bug in the CONFIG_BUG=n implementation IMHO.
There was a discussion about this recently, I didn't see what the resolution
was.
> + arch/powerpc/kvm/book3s_hv.c: error: 'vcpus_to_update[need_vpa_update]' may be used uninitialized in this function [-Werror=uninitialized]: => 1187:22
This looks bogus to me:
if (need_vpa_update) {
spin_unlock(&vc->lock);
for (i = 0; i < need_vpa_update; ++i)
kvmppc_update_vpas(vcpus_to_update[i]);
I fail to see how that accesses vcpus_to_update[need_vpa_update].
> + arch/powerpc/platforms/cell/beat_iommu.c: error: 'dma_base' may be used uninitialized in this function [-Werror=uninitialized]: => 69:11
> + arch/powerpc/platforms/cell/beat_iommu.c: error: 'dma_size' may be used uninitialized in this function [-Werror=uninitialized]: => 68:2
> + arch/powerpc/platforms/cell/beat_iommu.c: error: 'io_page_size' may be used uninitialized in this function [-Werror=uninitialized]: => 68:54
> + arch/powerpc/platforms/cell/beat_wrapper.h: error: 'io_space_id' may be used uninitialized in this function [-Werror=uninitialized]: => 249:2
> + arch/powerpc/platforms/cell/beat_wrapper.h: error: 'ioid' may be used uninitialized in this function [-Werror=uninitialized]: => 249:2
The above are all false warnings AFAICS.
> We need more randconfig builds to divert attention from powerpc ;-)
Or we could just drop them, with all the false positives from -Wuninitialized
it's hard to spot any real problems.
cheers
^ permalink raw reply
* [PATCH v5 00/31] EEH Support for PowerNV platform
From: Gavin Shan @ 2013-06-18 8:33 UTC (permalink / raw)
To: linuxppc-dev; +Cc: Gavin Shan
Initially, the series of patches is built based on 3.10.RC1 and the patchset
doesn't intend to enable EEH functionality for PHB3 for now. Obviously, PHB3
EEH support on PowerNV platform is something to do in future.
The series of patches intends to support EEH for PowerNV platform. The EEH
core already supports multiple probe methods: device tree nodes and PCI
devices. For EEH on PowerNV, we're using PCI devices to do EEH probe, which
is different from the probe type used on pSeries platform. Another point I
should mention is that the overall EEH would be split up to 3 layers: EEH
core, platform layer and I/O chip layer. It would make the EEH on PowerNV
platform can achieve more flexibility and support more I/O chips in future.
Besides, the EEH event can be produced by detecting 0xFF's from reading
PCI config or I/O registers, or from interrupts dedicated for EEH error
reporting. So we have to handle the EEH error interrupts. On the other hand,
the EEH events will be processed by EEH core like pSeries platform does.
We will have exported debugfs entries ("/sys/kernel/debug/powerpc/PCIxxxx/err_injct"),
which allows you to control the 0xD10 register in order to force errors like
frozen PE and fenced PHB for testing purpose. The following example is usualy
what I'm using to control that register. The patchset has been verified on
Firebird-L machine where I have 2 Emulex ethernet card on PHB#0. I keep pinging
to one of the ethernet cards (eth0) from external and then use following commands
to produce frozen PE or fenced PHB errors. Eventually, the errors can be recovered
and the ethernet card is reachable after temporary connection lost.
Trigger frozen PE:
echo 0x0000000002000000 > /sys/kernel/debug/powerpc/PCI0000/err_injct
sleep 1
echo 0x0 > /sys/kernel/debug/powerpc/PCI0000/err_injct
Trigger fenced PHB:
echo 0x8000000000000000 > /sys/kernel/debug/powerpc/PCI0000/err_injct
Change log
==========
v4 -> v5:
* Add patch [10/31] to make EEH core running with single kthread.
* Add patch [11/31] to trace the time stamp of last error in the last
hour for specific PE
* Add patch [12/31] to purge duplicate EEH events
* Add patch [14/31] for EEH core to handle special event, which doesn't
have binding PE
* Add patch [22/31] to support I/O chip next_error() backend. Almost all
stuff from original pci_err.c moved to eeh-ioda.c. Appropriate cleanup
is applied as well.
* Changed [27/31] to allow clearing specific OPAL notifier event in the
cache traced by variable "last_notified_mask"
* Changed [29/31] to register OPAL event notifier on post-initilization
period.
v3 -> v4:
* Rebase to 3.10.RC5 with originally first 2 patches from v3 applied and
won't resend the first 2 patches again.
* Add 2 (first) patches to move the EEH core from pSeries platform to
arch/powerpc/kernel and applied necessary cleanup.
* PowerNV platform layer initialize the delay for temporarily unavailable
PE state to 0 and set it to default value (1 second) if necessary.
* Change variable names according to Ben's comments.
* Account for the maximal allowed waiting time in eeh-powernv.c::powernv_eeh_wait_state()
* Introduce eeh_serialize_lock/unlock so that pci-err.c can inject EEH
event with consistent PE state (isolated/dead state). In a result,
pci-err.c::pci_err_seq_sem has been removed completely.
* Introduce PE state (EEH_PE_PHB_DEAD) and the logic to remove the corresponding
PCI domain upon detected dead IOC or PHB, instead of panicing the system.
* Remove unnecessary contiguous check on one specific PHB in pci-err.c::pci_err_handler().
* Refactor functions in pci-err.c for printing PHB diag-data. The diag-data header
(including version/ioType) have been parsed and call into appropriate function
for outputing the diag-data.
* Changelog adjustment on "OPAL notifier" according to Ben's comments.
* Split original opal_notifier_enable() to opal_notifier_enable/disable.
* Allow multiple clients to listen same OPAL event change in OPAL notifier.
* OPAL notifier is tracing the event change, instead of events.
v2 -> v3:
* Rebase to 3.10.RC4
* Replace eeh_pci_dev_traverse() with pci_walk_bus()
* Changlog adjustment to make that more clear
* To call msleep() if possible after opal_pci_poll()
* Make sure we have OPALv3
* OPAL notifier so that we can register callback for the monitored events.
The OPAL notifier is disabled while restarting or powering off the system.
* Make the debugfs entries something like (PCIxxxx/err_injct)
* Split the patch so that can be backported to stable kernel
* Allow to detect fenced PHB proactively (without interrupt)
* Start to use opal_pci_get_phb_diag_data2()
* Stack dump upon fenced PHB
v1 -> v2:
* Rebase to 3.10.RC3
* Don't fetch PE state for the case of fenced PHB. It usually takes long
time and possiblly incurs softlock warning. It requires the corresponding
changes for the underly firmware
* Add debugfs entries so that we can inject errors like frozen PE and
fenced PHB for testing purpose
---
arch/powerpc/include/asm/eeh.h | 28 +-
arch/powerpc/include/asm/eeh_event.h | 2 +
arch/powerpc/include/asm/opal.h | 140 +++-
arch/powerpc/kernel/Makefile | 4 +-
arch/powerpc/kernel/eeh.c | 1049 ++++++++++++++++++++++++
arch/powerpc/kernel/eeh_cache.c | 319 +++++++
arch/powerpc/kernel/eeh_dev.c | 112 +++
arch/powerpc/kernel/eeh_driver.c | 648 +++++++++++++++
arch/powerpc/kernel/eeh_event.c | 181 ++++
arch/powerpc/kernel/eeh_pe.c | 702 ++++++++++++++++
arch/powerpc/kernel/eeh_sysfs.c | 75 ++
arch/powerpc/kernel/pci_hotplug.c | 111 +++
arch/powerpc/platforms/Kconfig | 5 +
arch/powerpc/platforms/powernv/Makefile | 1 +
arch/powerpc/platforms/powernv/eeh-ioda.c | 892 ++++++++++++++++++++
arch/powerpc/platforms/powernv/eeh-powernv.c | 419 ++++++++++
arch/powerpc/platforms/powernv/opal-wrappers.S | 3 +
arch/powerpc/platforms/powernv/opal.c | 87 ++-
arch/powerpc/platforms/powernv/pci-ioda.c | 38 +-
arch/powerpc/platforms/powernv/pci-p5ioc2.c | 6 +-
arch/powerpc/platforms/powernv/pci.c | 43 +-
arch/powerpc/platforms/powernv/pci.h | 28 +
arch/powerpc/platforms/powernv/setup.c | 4 +
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_pseries.c | 3 +-
arch/powerpc/platforms/pseries/eeh_sysfs.c | 75 --
arch/powerpc/platforms/pseries/pci_dlpar.c | 85 --
34 files changed, 4869 insertions(+), 2920 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
create mode 100644 arch/powerpc/platforms/powernv/eeh-ioda.c
create mode 100644 arch/powerpc/platforms/powernv/eeh-powernv.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
Thanks,
Gavin
^ permalink raw reply
* [PATCH 04/31] powerpc/eeh: Make eeh_pe_get() public
From: Gavin Shan @ 2013-06-18 8:33 UTC (permalink / raw)
To: linuxppc-dev; +Cc: Gavin Shan
In-Reply-To: <1371544435-4943-1-git-send-email-shangw@linux.vnet.ibm.com>
While processing EEH event interrupt from P7IOC, we need function
to retrieve the PE according to the indicated EEH device. The patch
makes function eeh_pe_get() public so that other source files can call
it for that purpose. Also, the patch fixes referring to wrong BDF
(Bus/Device/Function) address while searching PE in function
__eeh_pe_get().
Signed-off-by: Gavin Shan <shangw@linux.vnet.ibm.com>
---
arch/powerpc/include/asm/eeh.h | 1 +
arch/powerpc/kernel/eeh_pe.c | 4 ++--
2 files changed, 3 insertions(+), 2 deletions(-)
diff --git a/arch/powerpc/include/asm/eeh.h b/arch/powerpc/include/asm/eeh.h
index 4ac6f70..acdfcaa 100644
--- a/arch/powerpc/include/asm/eeh.h
+++ b/arch/powerpc/include/asm/eeh.h
@@ -185,6 +185,7 @@ static inline void eeh_unlock(void)
typedef void *(*eeh_traverse_func)(void *data, void *flag);
int eeh_phb_pe_create(struct pci_controller *phb);
struct eeh_pe *eeh_phb_pe_get(struct pci_controller *phb);
+struct eeh_pe *eeh_pe_get(struct eeh_dev *edev);
int eeh_add_to_parent_pe(struct eeh_dev *edev);
int eeh_rmv_from_parent_pe(struct eeh_dev *edev, int purge_pe);
void *eeh_pe_dev_traverse(struct eeh_pe *root,
diff --git a/arch/powerpc/kernel/eeh_pe.c b/arch/powerpc/kernel/eeh_pe.c
index 71c4544..3d2dcf5 100644
--- a/arch/powerpc/kernel/eeh_pe.c
+++ b/arch/powerpc/kernel/eeh_pe.c
@@ -228,7 +228,7 @@ static void *__eeh_pe_get(void *data, void *flag)
return pe;
/* Try BDF address */
- if (edev->pe_config_addr &&
+ if (edev->config_addr &&
(edev->config_addr == pe->config_addr))
return pe;
@@ -246,7 +246,7 @@ static void *__eeh_pe_get(void *data, void *flag)
* 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 *eeh_pe_get(struct eeh_dev *edev)
{
struct eeh_pe *root = eeh_phb_pe_get(edev->phb);
struct eeh_pe *pe;
--
1.7.5.4
^ permalink raw reply related
* [PATCH 05/31] powerpc/eeh: Trace PCI bus from PE
From: Gavin Shan @ 2013-06-18 8:33 UTC (permalink / raw)
To: linuxppc-dev; +Cc: Gavin Shan
In-Reply-To: <1371544435-4943-1-git-send-email-shangw@linux.vnet.ibm.com>
There're several types of PEs can be supported for now: PHB, Bus
and Device dependent PE. For PCI bus dependent PE, tracing the
corresponding PCI bus from PE (struct eeh_pe) would make the code
more efficient. The patch also enables the retrieval of PCI bus based
on the PCI bus dependent PE.
Signed-off-by: Gavin Shan <shangw@linux.vnet.ibm.com>
---
arch/powerpc/include/asm/eeh.h | 1 +
arch/powerpc/kernel/eeh_pe.c | 22 ++++++++++++++++++++++
2 files changed, 23 insertions(+), 0 deletions(-)
diff --git a/arch/powerpc/include/asm/eeh.h b/arch/powerpc/include/asm/eeh.h
index acdfcaa..f3b49d6 100644
--- a/arch/powerpc/include/asm/eeh.h
+++ b/arch/powerpc/include/asm/eeh.h
@@ -59,6 +59,7 @@ struct eeh_pe {
int config_addr; /* Traditional PCI address */
int addr; /* PE configuration address */
struct pci_controller *phb; /* Associated PHB */
+ struct pci_bus *bus; /* Top PCI bus for bus PE */
int check_count; /* Times of ignored error */
int freeze_count; /* Times of froze up */
int false_positives; /* Times of reported #ff's */
diff --git a/arch/powerpc/kernel/eeh_pe.c b/arch/powerpc/kernel/eeh_pe.c
index 3d2dcf5..5bd1637 100644
--- a/arch/powerpc/kernel/eeh_pe.c
+++ b/arch/powerpc/kernel/eeh_pe.c
@@ -304,6 +304,7 @@ static struct eeh_pe *eeh_pe_get_parent(struct eeh_dev *edev)
int eeh_add_to_parent_pe(struct eeh_dev *edev)
{
struct eeh_pe *pe, *parent;
+ struct eeh_dev *first_edev;
eeh_lock();
@@ -326,6 +327,21 @@ int eeh_add_to_parent_pe(struct eeh_dev *edev)
pe->type = EEH_PE_BUS;
edev->pe = pe;
+ /*
+ * For PCI bus sensitive PE, we can reset the parent
+ * bridge in order for hot-reset. However, the PCI
+ * devices including the associated EEH devices might
+ * be removed when EEH core is doing recovery. So that
+ * won't safe to retrieve the bridge through downstream
+ * EEH device. We have to trace the parent PCI bus, then
+ * the parent bridge explicitly.
+ */
+ if (eeh_probe_mode_dev() && !pe->bus) {
+ first_edev = list_first_entry(&pe->edevs,
+ struct eeh_dev, list);
+ pe->bus = eeh_dev_to_pci_dev(first_edev)->bus;
+ }
+
/* Put the edev to PE */
list_add_tail(&edev->list, &pe->edevs);
eeh_unlock();
@@ -641,12 +657,18 @@ struct pci_bus *eeh_pe_bus_get(struct eeh_pe *pe)
bus = pe->phb->bus;
} else if (pe->type & EEH_PE_BUS ||
pe->type & EEH_PE_DEVICE) {
+ if (pe->bus) {
+ bus = pe->bus;
+ goto out;
+ }
+
edev = list_first_entry(&pe->edevs, struct eeh_dev, list);
pdev = eeh_dev_to_pci_dev(edev);
if (pdev)
bus = pdev->bus;
}
+out:
eeh_unlock();
return bus;
--
1.7.5.4
^ permalink raw reply related
* [PATCH 08/31] powerpc/eeh: Refactor eeh_reset_pe_once()
From: Gavin Shan @ 2013-06-18 8:33 UTC (permalink / raw)
To: linuxppc-dev; +Cc: Gavin Shan
In-Reply-To: <1371544435-4943-1-git-send-email-shangw@linux.vnet.ibm.com>
We shouldn't check that the returned PE status is exactly equal to
(EEH_STATE_MMIO_ACTIVE | EEH_STATE_DMA_ACTIVE) but instead only check
that they are both set.
[benh: changelog]
Signed-off-by: Gavin Shan <shangw@linux.vnet.ibm.com>
---
arch/powerpc/kernel/eeh.c | 3 ++-
1 files changed, 2 insertions(+), 1 deletions(-)
diff --git a/arch/powerpc/kernel/eeh.c b/arch/powerpc/kernel/eeh.c
index a29cf47..cda0b62 100644
--- a/arch/powerpc/kernel/eeh.c
+++ b/arch/powerpc/kernel/eeh.c
@@ -565,6 +565,7 @@ static void eeh_reset_pe_once(struct eeh_pe *pe)
*/
int eeh_reset_pe(struct eeh_pe *pe)
{
+ int flags = (EEH_STATE_MMIO_ACTIVE | EEH_STATE_DMA_ACTIVE);
int i, rc;
/* Take three shots at resetting the bus */
@@ -572,7 +573,7 @@ int eeh_reset_pe(struct eeh_pe *pe)
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))
+ if ((rc & flags) == flags)
return 0;
if (rc < 0) {
--
1.7.5.4
^ permalink raw reply related
* [PATCH 09/31] powerpc/eeh: Delay EEH probe during hotplug
From: Gavin Shan @ 2013-06-18 8:33 UTC (permalink / raw)
To: linuxppc-dev; +Cc: Gavin Shan
In-Reply-To: <1371544435-4943-1-git-send-email-shangw@linux.vnet.ibm.com>
While doing EEH recovery, the PCI devices of the problematic PE
should be removed and then added to the system again. During the
so-called hotplug event, the PCI devices of the problematic PE
will be probed through early/late phase. We would delay EEH probe
on late point for PowerNV platform since the PCI device isn't
available in early phase.
Signed-off-by: Gavin Shan <shangw@linux.vnet.ibm.com>
---
arch/powerpc/kernel/eeh.c | 16 +++++++++++++++-
1 files changed, 15 insertions(+), 1 deletions(-)
diff --git a/arch/powerpc/kernel/eeh.c b/arch/powerpc/kernel/eeh.c
index cda0b62..7d169d3 100644
--- a/arch/powerpc/kernel/eeh.c
+++ b/arch/powerpc/kernel/eeh.c
@@ -758,6 +758,14 @@ static void eeh_add_device_early(struct device_node *dn)
{
struct pci_controller *phb;
+ /*
+ * If we're doing EEH probe based on PCI device, we
+ * would delay the probe until late stage because
+ * the PCI device isn't available this moment.
+ */
+ if (!eeh_probe_mode_devtree())
+ return;
+
if (!of_node_to_eeh_dev(dn))
return;
phb = of_node_to_eeh_dev(dn)->phb;
@@ -766,7 +774,6 @@ static void eeh_add_device_early(struct device_node *dn)
if (NULL == phb || 0 == phb->buid)
return;
- /* FIXME: hotplug support on POWERNV */
eeh_ops->of_probe(dn, NULL);
}
@@ -817,6 +824,13 @@ static void eeh_add_device_late(struct pci_dev *dev)
edev->pdev = dev;
dev->dev.archdata.edev = edev;
+ /*
+ * We have to do the EEH probe here because the PCI device
+ * hasn't been created yet in the early stage.
+ */
+ if (eeh_probe_mode_dev())
+ eeh_ops->dev_probe(dev, NULL);
+
eeh_addr_cache_insert_dev(dev);
}
--
1.7.5.4
^ permalink raw reply related
* [PATCH 07/31] powerpc/eeh: EEH post initialization operation
From: Gavin Shan @ 2013-06-18 8:33 UTC (permalink / raw)
To: linuxppc-dev; +Cc: Gavin Shan
In-Reply-To: <1371544435-4943-1-git-send-email-shangw@linux.vnet.ibm.com>
The patch adds new EEH operation post_init. It's used to notify
the platform that EEH core has completed the EEH probe. By that,
PowerNV platform starts to use the services supplied by EEH
functionality.
Signed-off-by: Gavin Shan <shangw@linux.vnet.ibm.com>
---
arch/powerpc/include/asm/eeh.h | 1 +
arch/powerpc/kernel/eeh.c | 11 +++++++++++
2 files changed, 12 insertions(+), 0 deletions(-)
diff --git a/arch/powerpc/include/asm/eeh.h b/arch/powerpc/include/asm/eeh.h
index beb3cbc..beec788 100644
--- a/arch/powerpc/include/asm/eeh.h
+++ b/arch/powerpc/include/asm/eeh.h
@@ -131,6 +131,7 @@ static inline struct pci_dev *eeh_dev_to_pci_dev(struct eeh_dev *edev)
struct eeh_ops {
char *name;
int (*init)(void);
+ int (*post_init)(void);
void* (*of_probe)(struct device_node *dn, void *flag);
int (*dev_probe)(struct pci_dev *dev, void *flag);
int (*set_option)(struct eeh_pe *pe, int option);
diff --git a/arch/powerpc/kernel/eeh.c b/arch/powerpc/kernel/eeh.c
index c865c5f..a29cf47 100644
--- a/arch/powerpc/kernel/eeh.c
+++ b/arch/powerpc/kernel/eeh.c
@@ -720,6 +720,17 @@ int __init eeh_init(void)
return -EINVAL;
}
+ /*
+ * Call platform post-initialization. Actually, It's good chance
+ * to inform platform that EEH is ready to supply service if the
+ * I/O cache stuff has been built up.
+ */
+ if (eeh_ops->post_init) {
+ ret = eeh_ops->post_init();
+ if (ret)
+ return ret;
+ }
+
if (eeh_subsystem_enabled)
pr_info("EEH: PCI Enhanced I/O Error Handling Enabled\n");
else
--
1.7.5.4
^ permalink raw reply related
* [PATCH 12/31] powerpc/eeh: Allow to purge EEH events
From: Gavin Shan @ 2013-06-18 8:33 UTC (permalink / raw)
To: linuxppc-dev; +Cc: Gavin Shan
In-Reply-To: <1371544435-4943-1-git-send-email-shangw@linux.vnet.ibm.com>
On PowerNV platform, we might run into the situation where subsequent
events are duplicated events of former one, which is being processed.
For the case, we need the function implemented by the patch to purge
EEH events accordingly.
Signed-off-by: Gavin Shan <shangw@linux.vnet.ibm.com>
---
arch/powerpc/include/asm/eeh_event.h | 1 +
arch/powerpc/kernel/eeh_event.c | 37 ++++++++++++++++++++++++++++++++++
2 files changed, 38 insertions(+), 0 deletions(-)
diff --git a/arch/powerpc/include/asm/eeh_event.h b/arch/powerpc/include/asm/eeh_event.h
index de92c86..89d5670 100644
--- a/arch/powerpc/include/asm/eeh_event.h
+++ b/arch/powerpc/include/asm/eeh_event.h
@@ -33,6 +33,7 @@ struct eeh_event {
int eeh_event_init(void);
int eeh_send_failure_event(struct eeh_pe *pe);
+void eeh_remove_event(struct eeh_pe *pe);
void eeh_handle_event(struct eeh_pe *pe);
#endif /* __KERNEL__ */
diff --git a/arch/powerpc/kernel/eeh_event.c b/arch/powerpc/kernel/eeh_event.c
index 62e532d..39bcd81 100644
--- a/arch/powerpc/kernel/eeh_event.c
+++ b/arch/powerpc/kernel/eeh_event.c
@@ -142,3 +142,40 @@ int eeh_send_failure_event(struct eeh_pe *pe)
return 0;
}
+
+/**
+ * eeh_remove_event - Remove EEH event from the queue
+ * @pe: Event binding to the PE
+ *
+ * On PowerNV platform, we might have subsequent coming events
+ * is part of the former one. For that case, those subsequent
+ * coming events are totally duplicated and unnecessary, thus
+ * they should be removed.
+ */
+void eeh_remove_event(struct eeh_pe *pe)
+{
+ unsigned long flags;
+ struct eeh_event *event, *tmp;
+
+ spin_lock_irqsave(&eeh_eventlist_lock, flags);
+ list_for_each_entry_safe(event, tmp, &eeh_eventlist, list) {
+ /*
+ * If we don't have valid PE passed in, that means
+ * we already have event corresponding to dead IOC
+ * and all events should be purged.
+ */
+ if (!pe) {
+ list_del(&event->list);
+ kfree(event);
+ } else if (pe->type & EEH_PE_PHB) {
+ if (event->pe && event->pe->phb == pe->phb) {
+ list_del(&event->list);
+ kfree(event);
+ }
+ } else if (event->pe == pe) {
+ list_del(&event->list);
+ kfree(event);
+ }
+ }
+ spin_unlock_irqrestore(&eeh_eventlist_lock, flags);
+}
--
1.7.5.4
^ permalink raw reply related
* [PATCH 19/31] powerpc/eeh: I/O chip EEH state retrieval
From: Gavin Shan @ 2013-06-18 8:33 UTC (permalink / raw)
To: linuxppc-dev; +Cc: Gavin Shan
In-Reply-To: <1371544435-4943-1-git-send-email-shangw@linux.vnet.ibm.com>
The patch adds I/O chip backend to retrieve the state for the
indicated PE. While the PE state is temperarily unavailable,
the upper layer (powernv platform) should return default delay
(1 second).
Signed-off-by: Gavin Shan <shangw@linux.vnet.ibm.com>
---
arch/powerpc/platforms/powernv/eeh-ioda.c | 99 ++++++++++++++++++++++++++++-
1 files changed, 98 insertions(+), 1 deletions(-)
diff --git a/arch/powerpc/platforms/powernv/eeh-ioda.c b/arch/powerpc/platforms/powernv/eeh-ioda.c
index 744eb9e..a76870b 100644
--- a/arch/powerpc/platforms/powernv/eeh-ioda.c
+++ b/arch/powerpc/platforms/powernv/eeh-ioda.c
@@ -116,10 +116,107 @@ static int ioda_eeh_set_option(struct eeh_pe *pe, int option)
return ret;
}
+/**
+ * ioda_eeh_get_state - Retrieve the state of PE
+ * @pe: EEH PE
+ *
+ * The PE's state should be retrieved from the PEEV, PEST
+ * IODA tables. Since the OPAL has exported the function
+ * to do it, it'd better to use that.
+ */
+static int ioda_eeh_get_state(struct eeh_pe *pe)
+{
+ s64 ret = 0;
+ u8 fstate;
+ u16 pcierr;
+ u32 pe_no;
+ int result;
+ struct pci_controller *hose = pe->phb;
+ struct pnv_phb *phb = hose->private_data;
+
+ /*
+ * Sanity check on PE address. The PHB PE address should
+ * be zero.
+ */
+ if (pe->addr < 0 || pe->addr >= phb->ioda.total_pe) {
+ pr_err("%s: PE address %x out of range [0, %x] "
+ "on PHB#%x\n",
+ __func__, pe->addr, phb->ioda.total_pe,
+ hose->global_number);
+ return EEH_STATE_NOT_SUPPORT;
+ }
+
+ /* Retrieve PE status through OPAL */
+ pe_no = pe->addr;
+ ret = opal_pci_eeh_freeze_status(phb->opal_id, pe_no,
+ &fstate, &pcierr, NULL);
+ if (ret) {
+ pr_err("%s: Failed to get EEH status on "
+ "PHB#%x-PE#%x\n, err=%lld\n",
+ __func__, hose->global_number, pe_no, ret);
+ return EEH_STATE_NOT_SUPPORT;
+ }
+
+ /* Check PHB status */
+ if (pe->type & EEH_PE_PHB) {
+ result = 0;
+ result &= ~EEH_STATE_RESET_ACTIVE;
+
+ if (pcierr != OPAL_EEH_PHB_ERROR) {
+ result |= EEH_STATE_MMIO_ACTIVE;
+ result |= EEH_STATE_DMA_ACTIVE;
+ result |= EEH_STATE_MMIO_ENABLED;
+ result |= EEH_STATE_DMA_ENABLED;
+ }
+
+ return result;
+ }
+
+ /* Parse result out */
+ result = 0;
+ switch (fstate) {
+ case OPAL_EEH_STOPPED_NOT_FROZEN:
+ result &= ~EEH_STATE_RESET_ACTIVE;
+ result |= EEH_STATE_MMIO_ACTIVE;
+ result |= EEH_STATE_DMA_ACTIVE;
+ result |= EEH_STATE_MMIO_ENABLED;
+ result |= EEH_STATE_DMA_ENABLED;
+ break;
+ case OPAL_EEH_STOPPED_MMIO_FREEZE:
+ result &= ~EEH_STATE_RESET_ACTIVE;
+ result |= EEH_STATE_DMA_ACTIVE;
+ result |= EEH_STATE_DMA_ENABLED;
+ break;
+ case OPAL_EEH_STOPPED_DMA_FREEZE:
+ result &= ~EEH_STATE_RESET_ACTIVE;
+ result |= EEH_STATE_MMIO_ACTIVE;
+ result |= EEH_STATE_MMIO_ENABLED;
+ break;
+ case OPAL_EEH_STOPPED_MMIO_DMA_FREEZE:
+ result &= ~EEH_STATE_RESET_ACTIVE;
+ break;
+ case OPAL_EEH_STOPPED_RESET:
+ result |= EEH_STATE_RESET_ACTIVE;
+ break;
+ case OPAL_EEH_STOPPED_TEMP_UNAVAIL:
+ result |= EEH_STATE_UNAVAILABLE;
+ break;
+ case OPAL_EEH_STOPPED_PERM_UNAVAIL:
+ result |= EEH_STATE_NOT_SUPPORT;
+ break;
+ default:
+ pr_warning("%s: Unexpected EEH status 0x%x "
+ "on PHB#%x-PE#%x\n",
+ __func__, fstate, hose->global_number, pe_no);
+ }
+
+ return result;
+}
+
struct pnv_eeh_ops ioda_eeh_ops = {
.post_init = ioda_eeh_post_init,
.set_option = ioda_eeh_set_option,
- .get_state = NULL,
+ .get_state = ioda_eeh_get_state,
.reset = NULL,
.get_log = NULL,
.configure_bridge = NULL,
--
1.7.5.4
^ permalink raw reply related
* [PATCH 22/31] powerpc/eeh: I/O chip next error
From: Gavin Shan @ 2013-06-18 8:33 UTC (permalink / raw)
To: linuxppc-dev; +Cc: Gavin Shan
In-Reply-To: <1371544435-4943-1-git-send-email-shangw@linux.vnet.ibm.com>
The patch implements the backend for EEH core to retrieve next
EEH error to handle. For the informational errors, we won't bother
the EEH core. Otherwise, the EEH should take appropriate actions
depending on the return value:
0 - No further errors detected
1 - Frozen PE
2 - Fenced PHB
3 - Dead PHB
4 - Dead IOC
Signed-off-by: Gavin Shan <shangw@linux.vnet.ibm.com>
---
arch/powerpc/platforms/powernv/eeh-ioda.c | 334 ++++++++++++++++++++++++++++-
arch/powerpc/platforms/powernv/pci.h | 1 +
2 files changed, 333 insertions(+), 2 deletions(-)
diff --git a/arch/powerpc/platforms/powernv/eeh-ioda.c b/arch/powerpc/platforms/powernv/eeh-ioda.c
index 8d9c2d2..a3eebd1 100644
--- a/arch/powerpc/platforms/powernv/eeh-ioda.c
+++ b/arch/powerpc/platforms/powernv/eeh-ioda.c
@@ -34,6 +34,15 @@
#include "powernv.h"
#include "pci.h"
+/* Debugging option */
+#ifdef IODA_EEH_DBG_ON
+#define IODA_EEH_DBG(args...) pr_info(args)
+#else
+#define IODA_EEH_DBG(args...)
+#endif
+
+static char *hub_diag = NULL;
+
/**
* ioda_eeh_post_init - Chip dependent post initialization
* @hose: PCI controller
@@ -47,8 +56,19 @@ static int ioda_eeh_post_init(struct pci_controller *hose)
struct pnv_phb *phb = hose->private_data;
/* FIXME: Enable it for PHB3 later */
- if (phb->type == PNV_PHB_IODA1)
+ if (phb->type == PNV_PHB_IODA1) {
+ if (!hub_diag) {
+ hub_diag = (char *)__get_free_page(GFP_KERNEL |
+ __GFP_ZERO);
+ if (!hub_diag) {
+ pr_err("%s: Out of memory !\n",
+ __func__);
+ return -ENOMEM;
+ }
+ }
+
phb->eeh_enabled = 1;
+ }
return 0;
}
@@ -498,6 +518,316 @@ static int ioda_eeh_configure_bridge(struct eeh_pe *pe)
return 0;
}
+static void ioda_eeh_hub_diag_common(struct OpalIoP7IOCErrorData *data)
+{
+ /* GEM */
+ pr_info(" GEM XFIR: %016llx\n", data->gemXfir);
+ pr_info(" GEM RFIR: %016llx\n", data->gemRfir);
+ pr_info(" GEM RIRQFIR: %016llx\n", data->gemRirqfir);
+ pr_info(" GEM Mask: %016llx\n", data->gemMask);
+ pr_info(" GEM RWOF: %016llx\n", data->gemRwof);
+
+ /* LEM */
+ pr_info(" LEM FIR: %016llx\n", data->lemFir);
+ pr_info(" LEM Error Mask: %016llx\n", data->lemErrMask);
+ pr_info(" LEM Action 0: %016llx\n", data->lemAction0);
+ pr_info(" LEM Action 1: %016llx\n", data->lemAction1);
+ pr_info(" LEM WOF: %016llx\n", data->lemWof);
+}
+
+static void ioda_eeh_hub_diag(struct pci_controller *hose)
+{
+ struct pnv_phb *phb = hose->private_data;
+ struct OpalIoP7IOCErrorData *data;
+ long rc;
+
+ data = (struct OpalIoP7IOCErrorData *)ioda_eeh_hub_diag;
+ rc = opal_pci_get_hub_diag_data(phb->hub_id, data, PAGE_SIZE);
+ if (rc != OPAL_SUCCESS) {
+ pr_warning("%s: Failed to get HUB#%llx diag-data (%ld)\n",
+ __func__, phb->hub_id, rc);
+ return;
+ }
+
+ switch (data->type) {
+ case OPAL_P7IOC_DIAG_TYPE_RGC:
+ pr_info("P7IOC diag-data for RGC\n\n");
+ ioda_eeh_hub_diag_common(data);
+ pr_info(" RGC Status: %016llx\n", data->rgc.rgcStatus);
+ pr_info(" RGC LDCP: %016llx\n", data->rgc.rgcLdcp);
+ break;
+ case OPAL_P7IOC_DIAG_TYPE_BI:
+ pr_info("P7IOC diag-data for BI %s\n\n",
+ data->bi.biDownbound ? "Downbound" : "Upbound");
+ ioda_eeh_hub_diag_common(data);
+ pr_info(" BI LDCP 0: %016llx\n", data->bi.biLdcp0);
+ pr_info(" BI LDCP 1: %016llx\n", data->bi.biLdcp1);
+ pr_info(" BI LDCP 2: %016llx\n", data->bi.biLdcp2);
+ pr_info(" BI Fence Status: %016llx\n", data->bi.biFenceStatus);
+ break;
+ case OPAL_P7IOC_DIAG_TYPE_CI:
+ pr_info("P7IOC diag-data for CI Port %d\\nn",
+ data->ci.ciPort);
+ ioda_eeh_hub_diag_common(data);
+ pr_info(" CI Port Status: %016llx\n", data->ci.ciPortStatus);
+ pr_info(" CI Port LDCP: %016llx\n", data->ci.ciPortLdcp);
+ break;
+ case OPAL_P7IOC_DIAG_TYPE_MISC:
+ pr_info("P7IOC diag-data for MISC\n\n");
+ ioda_eeh_hub_diag_common(data);
+ break;
+ case OPAL_P7IOC_DIAG_TYPE_I2C:
+ pr_info("P7IOC diag-data for I2C\n\n");
+ ioda_eeh_hub_diag_common(data);
+ break;
+ default:
+ pr_warning("%s: Invalid type of HUB#%llx diag-data (%d)\n",
+ __func__, phb->hub_id, data->type);
+ }
+}
+
+static void ioda_eeh_p7ioc_phb_diag(struct pci_controller *hose,
+ struct OpalIoPhbErrorCommon *common)
+{
+ struct OpalIoP7IOCPhbErrorData *data;
+ int i;
+
+ data = (struct OpalIoP7IOCPhbErrorData *)common;
+
+ pr_info("P7IOC PHB#%x Diag-data (Version: %d)\n\n",
+ hose->global_number, common->version);
+
+ pr_info(" brdgCtl: %08x\n", data->brdgCtl);
+
+ pr_info(" portStatusReg: %08x\n", data->portStatusReg);
+ pr_info(" rootCmplxStatus: %08x\n", data->rootCmplxStatus);
+ pr_info(" busAgentStatus: %08x\n", data->busAgentStatus);
+
+ pr_info(" deviceStatus: %08x\n", data->deviceStatus);
+ pr_info(" slotStatus: %08x\n", data->slotStatus);
+ pr_info(" linkStatus: %08x\n", data->linkStatus);
+ pr_info(" devCmdStatus: %08x\n", data->devCmdStatus);
+ pr_info(" devSecStatus: %08x\n", data->devSecStatus);
+
+ pr_info(" rootErrorStatus: %08x\n", data->rootErrorStatus);
+ pr_info(" uncorrErrorStatus: %08x\n", data->uncorrErrorStatus);
+ pr_info(" corrErrorStatus: %08x\n", data->corrErrorStatus);
+ pr_info(" tlpHdr1: %08x\n", data->tlpHdr1);
+ pr_info(" tlpHdr2: %08x\n", data->tlpHdr2);
+ pr_info(" tlpHdr3: %08x\n", data->tlpHdr3);
+ pr_info(" tlpHdr4: %08x\n", data->tlpHdr4);
+ pr_info(" sourceId: %08x\n", data->sourceId);
+
+ pr_info(" errorClass: %016llx\n", data->errorClass);
+ pr_info(" correlator: %016llx\n", data->correlator);
+ pr_info(" p7iocPlssr: %016llx\n", data->p7iocPlssr);
+ pr_info(" p7iocCsr: %016llx\n", data->p7iocCsr);
+ pr_info(" lemFir: %016llx\n", data->lemFir);
+ pr_info(" lemErrorMask: %016llx\n", data->lemErrorMask);
+ pr_info(" lemWOF: %016llx\n", data->lemWOF);
+ pr_info(" phbErrorStatus: %016llx\n", data->phbErrorStatus);
+ pr_info(" phbFirstErrorStatus: %016llx\n", data->phbFirstErrorStatus);
+ pr_info(" phbErrorLog0: %016llx\n", data->phbErrorLog0);
+ pr_info(" phbErrorLog1: %016llx\n", data->phbErrorLog1);
+ pr_info(" mmioErrorStatus: %016llx\n", data->mmioErrorStatus);
+ pr_info(" mmioFirstErrorStatus: %016llx\n", data->mmioFirstErrorStatus);
+ pr_info(" mmioErrorLog0: %016llx\n", data->mmioErrorLog0);
+ pr_info(" mmioErrorLog1: %016llx\n", data->mmioErrorLog1);
+ pr_info(" dma0ErrorStatus: %016llx\n", data->dma0ErrorStatus);
+ pr_info(" dma0FirstErrorStatus: %016llx\n", data->dma0FirstErrorStatus);
+ pr_info(" dma0ErrorLog0: %016llx\n", data->dma0ErrorLog0);
+ pr_info(" dma0ErrorLog1: %016llx\n", data->dma0ErrorLog1);
+ pr_info(" dma1ErrorStatus: %016llx\n", data->dma1ErrorStatus);
+ pr_info(" dma1FirstErrorStatus: %016llx\n", data->dma1FirstErrorStatus);
+ pr_info(" dma1ErrorLog0: %016llx\n", data->dma1ErrorLog0);
+ pr_info(" dma1ErrorLog1: %016llx\n", data->dma1ErrorLog1);
+
+ for (i = 0; i < OPAL_P7IOC_NUM_PEST_REGS; i++) {
+ if ((data->pestA[i] >> 63) == 0 &&
+ (data->pestB[i] >> 63) == 0)
+ continue;
+
+ pr_info(" PE[%3d] PESTA: %016llx\n", i, data->pestA[i]);
+ pr_info(" PESTB: %016llx\n", data->pestB[i]);
+ }
+}
+
+static void ioda_eeh_phb_diag(struct pci_controller *hose)
+{
+ struct pnv_phb *phb = hose->private_data;
+ struct OpalIoPhbErrorCommon *common;
+ long rc;
+
+ common = (struct OpalIoPhbErrorCommon *)phb->diag.blob;
+ rc = opal_pci_get_phb_diag_data2(phb->opal_id, common, PAGE_SIZE);
+ if (rc != OPAL_SUCCESS) {
+ pr_warning("%s: Failed to get diag-data for PHB#%x (%ld)\n",
+ __func__, hose->global_number, rc);
+ return;
+ }
+
+ switch (common->ioType) {
+ case OPAL_PHB_ERROR_DATA_TYPE_P7IOC:
+ ioda_eeh_p7ioc_phb_diag(hose, common);
+ break;
+ default:
+ pr_warning("%s: Unrecognized I/O chip %d\n",
+ __func__, common->ioType);
+ }
+}
+
+static int ioda_eeh_get_phb_pe(struct pci_controller *hose,
+ struct eeh_pe **pe)
+{
+ struct eeh_pe *phb_pe;
+
+ phb_pe = eeh_phb_pe_get(hose);
+ if (!phb_pe) {
+ pr_warning("%s Can't find PE for PHB#%d\n",
+ __func__, hose->global_number);
+ return -EEXIST;
+ }
+
+ *pe = phb_pe;
+ return 0;
+}
+
+static int ioda_eeh_get_pe(struct pci_controller *hose,
+ u16 pe_no, struct eeh_pe **pe)
+{
+ struct eeh_pe *phb_pe, *dev_pe;
+ struct eeh_dev dev;
+
+ /* Find the PHB PE */
+ if (ioda_eeh_get_phb_pe(hose, &phb_pe))
+ return -EEXIST;
+
+ /* Find the PE according to PE# */
+ memset(&dev, 0, sizeof(struct eeh_dev));
+ dev.phb = hose;
+ dev.pe_config_addr = pe_no;
+ dev_pe = eeh_pe_get(&dev);
+ if (!dev_pe) {
+ pr_warning("%s: Can't find PE for PHB#%x - PE#%x\n",
+ __func__, hose->global_number, pe_no);
+ return -EEXIST;
+ }
+
+ *pe = dev_pe;
+ return 0;
+}
+
+/**
+ * ioda_eeh_next_error - Retrieve next error for EEH core to handle
+ * @pe: The affected PE
+ *
+ * The function is expected to be called by EEH core while it gets
+ * special EEH event (without binding PE). The function calls to
+ * OPAL APIs for next error to handle. The informational error is
+ * handled internally by platform. However, the dead IOC, dead PHB,
+ * fenced PHB and frozen PE should be handled by EEH core eventually.
+ */
+static int ioda_eeh_next_error(struct eeh_pe **pe)
+{
+ struct pci_controller *hose, *tmp;
+ struct pnv_phb *phb;
+ u64 frozen_pe_no;
+ u16 err_type, severity;
+ long rc;
+ int ret = 1;
+
+ /* While running here, it's safe to purge the event queue */
+ eeh_remove_event(NULL);
+
+ list_for_each_entry_safe(hose, tmp, &hose_list, list_node) {
+ /*
+ * If the subordinate PCI buses of the PHB has been
+ * removed, we needn't take care of it any more.
+ */
+ phb = hose->private_data;
+ if (phb->removed)
+ continue;
+
+ rc = opal_pci_next_error(phb->opal_id,
+ &frozen_pe_no, &err_type, &severity);
+
+ /* If OPAL API returns error, we needn't proceed */
+ if (rc != OPAL_SUCCESS) {
+ IODA_EEH_DBG("%s: Invalid return value on "
+ "PHB#%x (0x%lx) from opal_pci_next_error",
+ __func__, hose->global_number, rc);
+ continue;
+ }
+
+ /* If the PHB doesn't have error, stop processing */
+ if (err_type == OPAL_EEH_NO_ERROR ||
+ severity == OPAL_EEH_SEV_NO_ERROR) {
+ IODA_EEH_DBG("%s: No error found on PHB#%x\n",
+ __func__, hose->global_number);
+ continue;
+ }
+
+ /*
+ * Processing the error. We're expecting the error with
+ * highest priority reported upon multiple errors on the
+ * specific PHB.
+ */
+ IODA_EEH_DBG("%s: Error (%d, %d, %d) on PHB#%x\n",
+ err_type, severity, pe_no, hose->global_number);
+ switch (err_type) {
+ case OPAL_EEH_IOC_ERROR:
+ if (severity == OPAL_EEH_SEV_IOC_DEAD) {
+ list_for_each_entry_safe(hose, tmp,
+ &hose_list, list_node) {
+ phb = hose->private_data;
+ phb->removed = 1;
+ }
+
+ WARN(1, "EEH: dead IOC detected\n");
+ ret = 4;
+ goto out;
+ } else if (severity == OPAL_EEH_SEV_INF)
+ ioda_eeh_hub_diag(hose);
+
+ break;
+ case OPAL_EEH_PHB_ERROR:
+ if (severity == OPAL_EEH_SEV_PHB_DEAD) {
+ if (ioda_eeh_get_phb_pe(hose, pe))
+ break;
+
+ WARN(1, "EEH: dead PHB#%x detected\n",
+ hose->global_number);
+ phb->removed = 1;
+ ret = 3;
+ goto out;
+ } else if (severity == OPAL_EEH_SEV_PHB_FENCED) {
+ if (ioda_eeh_get_phb_pe(hose, pe))
+ break;
+
+ WARN(1, "EEH: fenced PHB#%x detected\n",
+ hose->global_number);
+ ret = 2;
+ goto out;
+ } else if (severity == OPAL_EEH_SEV_INF)
+ ioda_eeh_phb_diag(hose);
+
+ break;
+ case OPAL_EEH_PE_ERROR:
+ if (ioda_eeh_get_pe(hose, frozen_pe_no, pe))
+ break;
+
+ WARN(1, "EEH: Frozen PE#%x on PHB#%x detected\n",
+ (*pe)->addr, (*pe)->phb->global_number);
+ ret = 1;
+ goto out;
+ }
+ }
+
+ ret = 0;
+out:
+ return ret;
+}
+
struct pnv_eeh_ops ioda_eeh_ops = {
.post_init = ioda_eeh_post_init,
.set_option = ioda_eeh_set_option,
@@ -505,5 +835,5 @@ struct pnv_eeh_ops ioda_eeh_ops = {
.reset = ioda_eeh_reset,
.get_log = ioda_eeh_get_log,
.configure_bridge = ioda_eeh_configure_bridge,
- .next_error = NULL
+ .next_error = ioda_eeh_next_error
};
diff --git a/arch/powerpc/platforms/powernv/pci.h b/arch/powerpc/platforms/powernv/pci.h
index 336c9dc..3656a240 100644
--- a/arch/powerpc/platforms/powernv/pci.h
+++ b/arch/powerpc/platforms/powernv/pci.h
@@ -93,6 +93,7 @@ struct pnv_phb {
#ifdef CONFIG_EEH
struct pnv_eeh_ops *eeh_ops;
int eeh_enabled;
+ int removed;
#endif
#ifdef CONFIG_PCI_MSI
--
1.7.5.4
^ permalink raw reply related
* [PATCH 23/31] powerpc/eeh: PowerNV EEH backends
From: Gavin Shan @ 2013-06-18 8:33 UTC (permalink / raw)
To: linuxppc-dev; +Cc: Gavin Shan
In-Reply-To: <1371544435-4943-1-git-send-email-shangw@linux.vnet.ibm.com>
The patch adds EEH backends for PowerNV platform. It's notable that
part of those EEH backends call to the I/O chip dependent backends.
Signed-off-by: Gavin Shan <shangw@linux.vnet.ibm.com>
---
arch/powerpc/platforms/powernv/Makefile | 2 +-
arch/powerpc/platforms/powernv/eeh-powernv.c | 419 ++++++++++++++++++++++++++
arch/powerpc/platforms/pseries/eeh_pseries.c | 3 +-
3 files changed, 422 insertions(+), 2 deletions(-)
create mode 100644 arch/powerpc/platforms/powernv/eeh-powernv.c
diff --git a/arch/powerpc/platforms/powernv/Makefile b/arch/powerpc/platforms/powernv/Makefile
index 09bd0cb..7fe5951 100644
--- a/arch/powerpc/platforms/powernv/Makefile
+++ b/arch/powerpc/platforms/powernv/Makefile
@@ -3,4 +3,4 @@ obj-y += opal-rtc.o opal-nvram.o
obj-$(CONFIG_SMP) += smp.o
obj-$(CONFIG_PCI) += pci.o pci-p5ioc2.o pci-ioda.o
-obj-$(CONFIG_EEH) += eeh-ioda.o
+obj-$(CONFIG_EEH) += eeh-ioda.o eeh-powernv.o
diff --git a/arch/powerpc/platforms/powernv/eeh-powernv.c b/arch/powerpc/platforms/powernv/eeh-powernv.c
new file mode 100644
index 0000000..9559115
--- /dev/null
+++ b/arch/powerpc/platforms/powernv/eeh-powernv.c
@@ -0,0 +1,419 @@
+/*
+ * The file intends to implement the platform dependent EEH operations on
+ * powernv platform. Actually, the powernv was created in order to fully
+ * hypervisor support.
+ *
+ * Copyright Benjamin Herrenschmidt & Gavin Shan, IBM Corporation 2013.
+ *
+ * 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/atomic.h>
+#include <linux/delay.h>
+#include <linux/export.h>
+#include <linux/init.h>
+#include <linux/list.h>
+#include <linux/msi.h>
+#include <linux/of.h>
+#include <linux/pci.h>
+#include <linux/proc_fs.h>
+#include <linux/rbtree.h>
+#include <linux/sched.h>
+#include <linux/seq_file.h>
+#include <linux/spinlock.h>
+
+#include <asm/eeh.h>
+#include <asm/eeh_event.h>
+#include <asm/firmware.h>
+#include <asm/io.h>
+#include <asm/iommu.h>
+#include <asm/machdep.h>
+#include <asm/msi_bitmap.h>
+#include <asm/opal.h>
+#include <asm/ppc-pci.h>
+
+#include "powernv.h"
+#include "pci.h"
+
+/**
+ * powernv_eeh_init - EEH platform dependent initialization
+ *
+ * EEH platform dependent initialization on powernv
+ */
+static int powernv_eeh_init(void)
+{
+ /* We require OPALv3 */
+ if (!firmware_has_feature(FW_FEATURE_OPALv3)) {
+ pr_warning("%s: OPALv3 is required !\n", __func__);
+ return -EINVAL;
+ }
+
+ /* Set EEH probe mode */
+ eeh_probe_mode_set(EEH_PROBE_MODE_DEV);
+
+ return 0;
+}
+
+/**
+ * powernv_eeh_post_init - EEH platform dependent post initialization
+ *
+ * EEH platform dependent post initialization on powernv. When
+ * the function is called, the EEH PEs and devices should have
+ * been built. If the I/O cache staff has been built, EEH is
+ * ready to supply service.
+ */
+static int powernv_eeh_post_init(void)
+{
+ struct pci_controller *hose;
+ struct pnv_phb *phb;
+ int ret = 0;
+
+ list_for_each_entry(hose, &hose_list, list_node) {
+ phb = hose->private_data;
+
+ if (phb->eeh_ops && phb->eeh_ops->post_init) {
+ ret = phb->eeh_ops->post_init(hose);
+ if (ret)
+ break;
+ }
+ }
+
+ return ret;
+}
+
+/**
+ * powernv_eeh_dev_probe - Do probe on PCI device
+ * @dev: PCI device
+ * @flag: unused
+ *
+ * When EEH module is installed during system boot, all PCI devices
+ * are checked one by one to see if it supports EEH. The function
+ * is introduced for the purpose. By default, EEH has been enabled
+ * on all PCI devices. That's to say, we only need do necessary
+ * initialization on the corresponding eeh device and create PE
+ * accordingly.
+ *
+ * It's notable that's unsafe to retrieve the EEH device through
+ * the corresponding PCI device. During the PCI device hotplug, which
+ * was possiblly triggered by EEH core, the binding between EEH device
+ * and the PCI device isn't built yet.
+ */
+static int powernv_eeh_dev_probe(struct pci_dev *dev, void *flag)
+{
+ struct pci_controller *hose = pci_bus_to_host(dev->bus);
+ struct pnv_phb *phb = hose->private_data;
+ struct device_node *dn = pci_device_to_OF_node(dev);
+ struct eeh_dev *edev = of_node_to_eeh_dev(dn);
+
+ /*
+ * When probing the root bridge, which doesn't have any
+ * subordinate PCI devices. We don't have OF node for
+ * the root bridge. So it's not reasonable to continue
+ * the probing.
+ */
+ if (!dn || !edev)
+ return 0;
+
+ /* Skip for PCI-ISA bridge */
+ if ((dev->class >> 8) == PCI_CLASS_BRIDGE_ISA)
+ return 0;
+
+ /* Initialize eeh device */
+ edev->class_code = dev->class;
+ edev->mode = 0;
+ edev->config_addr = ((dev->bus->number << 8) | dev->devfn);
+ edev->pe_config_addr = phb->bdfn_to_pe(phb, dev->bus, dev->devfn & 0xff);
+
+ /* Create PE */
+ eeh_add_to_parent_pe(edev);
+
+ /*
+ * Enable EEH explicitly so that we will do EEH check
+ * while accessing I/O stuff
+ *
+ * FIXME: Enable that for PHB3 later
+ */
+ if (phb->type == PNV_PHB_IODA1)
+ eeh_subsystem_enabled = 1;
+
+ /* Save memory bars */
+ eeh_save_bars(edev);
+
+ return 0;
+}
+
+/**
+ * powernv_eeh_set_option - Initialize EEH or MMIO/DMA reenable
+ * @pe: EEH PE
+ * @option: operation to be issued
+ *
+ * The function is used to control the EEH functionality globally.
+ * Currently, following options are support according to PAPR:
+ * Enable EEH, Disable EEH, Enable MMIO and Enable DMA
+ */
+static int powernv_eeh_set_option(struct eeh_pe *pe, int option)
+{
+ struct pci_controller *hose = pe->phb;
+ struct pnv_phb *phb = hose->private_data;
+ int ret = -EEXIST;
+
+ /*
+ * What we need do is pass it down for hardware
+ * implementation to handle it.
+ */
+ if (phb->eeh_ops && phb->eeh_ops->set_option)
+ ret = phb->eeh_ops->set_option(pe, option);
+
+ return ret;
+}
+
+/**
+ * powernv_eeh_get_pe_addr - Retrieve PE address
+ * @pe: EEH PE
+ *
+ * Retrieve the PE address according to the given tranditional
+ * PCI BDF (Bus/Device/Function) address.
+ */
+static int powernv_eeh_get_pe_addr(struct eeh_pe *pe)
+{
+ return pe->addr;
+}
+
+/**
+ * powernv_eeh_get_state - Retrieve PE state
+ * @pe: EEH PE
+ * @delay: delay while PE state is temporarily unavailable
+ *
+ * Retrieve the state of the specified PE. For IODA-compitable
+ * platform, it should be retrieved from IODA table. Therefore,
+ * we prefer passing down to hardware implementation to handle
+ * it.
+ */
+static int powernv_eeh_get_state(struct eeh_pe *pe, int *delay)
+{
+ struct pci_controller *hose = pe->phb;
+ struct pnv_phb *phb = hose->private_data;
+ int ret = EEH_STATE_NOT_SUPPORT;
+
+ if (phb->eeh_ops && phb->eeh_ops->get_state) {
+ ret = phb->eeh_ops->get_state(pe);
+
+ /*
+ * If the PE state is temporarily unavailable,
+ * to inform the EEH core delay for default
+ * period (1 second)
+ */
+ if (delay) {
+ *delay = 0;
+ if (ret & EEH_STATE_UNAVAILABLE)
+ *delay = 1000;
+ }
+ }
+
+ return ret;
+}
+
+/**
+ * powernv_eeh_reset - Reset the specified PE
+ * @pe: EEH PE
+ * @option: reset option
+ *
+ * Reset the specified PE
+ */
+static int powernv_eeh_reset(struct eeh_pe *pe, int option)
+{
+ struct pci_controller *hose = pe->phb;
+ struct pnv_phb *phb = hose->private_data;
+ int ret = -EEXIST;
+
+ if (phb->eeh_ops && phb->eeh_ops->reset)
+ ret = phb->eeh_ops->reset(pe, option);
+
+ return ret;
+}
+
+/**
+ * powernv_eeh_wait_state - Wait for PE state
+ * @pe: EEH PE
+ * @max_wait: maximal period in microsecond
+ *
+ * Wait for the state of associated PE. It might take some time
+ * to retrieve the PE's state.
+ */
+static int powernv_eeh_wait_state(struct eeh_pe *pe, int max_wait)
+{
+ int ret;
+ int mwait;
+
+ while (1) {
+ ret = powernv_eeh_get_state(pe, &mwait);
+
+ /*
+ * If the PE's state is temporarily unavailable,
+ * we have to wait for the specified time. Otherwise,
+ * the PE's state will be returned immediately.
+ */
+ if (ret != EEH_STATE_UNAVAILABLE)
+ return ret;
+
+ max_wait -= mwait;
+ if (max_wait <= 0) {
+ pr_warning("%s: Timeout getting PE#%x's state (%d)\n",
+ __func__, pe->addr, max_wait);
+ return EEH_STATE_NOT_SUPPORT;
+ }
+
+ msleep(mwait);
+ }
+
+ return EEH_STATE_NOT_SUPPORT;
+}
+
+/**
+ * powernv_eeh_get_log - Retrieve error log
+ * @pe: EEH PE
+ * @severity: temporary or permanent error log
+ * @drv_log: driver log to be combined with retrieved error log
+ * @len: length of driver log
+ *
+ * Retrieve the temporary or permanent error from the PE.
+ */
+static int powernv_eeh_get_log(struct eeh_pe *pe, int severity,
+ char *drv_log, unsigned long len)
+{
+ struct pci_controller *hose = pe->phb;
+ struct pnv_phb *phb = hose->private_data;
+ int ret = -EEXIST;
+
+ if (phb->eeh_ops && phb->eeh_ops->get_log)
+ ret = phb->eeh_ops->get_log(pe, severity, drv_log, len);
+
+ return ret;
+}
+
+/**
+ * powernv_eeh_configure_bridge - Configure PCI bridges in the indicated PE
+ * @pe: EEH PE
+ *
+ * The function will be called to reconfigure the bridges included
+ * in the specified PE so that the mulfunctional PE would be recovered
+ * again.
+ */
+static int powernv_eeh_configure_bridge(struct eeh_pe *pe)
+{
+ struct pci_controller *hose = pe->phb;
+ struct pnv_phb *phb = hose->private_data;
+ int ret = 0;
+
+ if (phb->eeh_ops && phb->eeh_ops->configure_bridge)
+ ret = phb->eeh_ops->configure_bridge(pe);
+
+ return ret;
+}
+
+/**
+ * powernv_eeh_read_config - Read PCI config space
+ * @dn: device node
+ * @where: PCI address
+ * @size: size to read
+ * @val: return value
+ *
+ * Read config space from the speicifed device
+ */
+static int powernv_eeh_read_config(struct device_node *dn, int where,
+ int size, u32 *val)
+{
+ struct eeh_dev *edev = of_node_to_eeh_dev(dn);
+ struct pci_dev *dev = eeh_dev_to_pci_dev(edev);
+ struct pci_controller *hose = edev->phb;
+
+ return hose->ops->read(dev->bus, dev->devfn, where, size, val);
+}
+
+/**
+ * powernv_eeh_write_config - Write PCI config space
+ * @dn: device node
+ * @where: PCI address
+ * @size: size to write
+ * @val: value to be written
+ *
+ * Write config space to the specified device
+ */
+static int powernv_eeh_write_config(struct device_node *dn, int where,
+ int size, u32 val)
+{
+ struct eeh_dev *edev = of_node_to_eeh_dev(dn);
+ struct pci_dev *dev = eeh_dev_to_pci_dev(edev);
+ struct pci_controller *hose = edev->phb;
+
+ hose = pci_bus_to_host(dev->bus);
+
+ return hose->ops->write(dev->bus, dev->devfn, where, size, val);
+}
+
+/**
+ * powernv_eeh_next_error - Retrieve next EEH error to handle
+ * @pe: Affected PE
+ *
+ * Using OPAL API, to retrieve next EEH error for EEH core to handle
+ */
+static int powernv_eeh_next_error(struct eeh_pe **pe)
+{
+ struct pci_controller *hose;
+ struct pnv_phb *phb = NULL;
+
+ list_for_each_entry(hose, &hose_list, list_node) {
+ phb = hose->private_data;
+ break;
+ }
+
+ if (phb && phb->eeh_ops->next_error)
+ return phb->eeh_ops->next_error(pe);
+
+ return -EEXIST;
+}
+
+static struct eeh_ops powernv_eeh_ops = {
+ .name = "powernv",
+ .init = powernv_eeh_init,
+ .post_init = powernv_eeh_post_init,
+ .of_probe = NULL,
+ .dev_probe = powernv_eeh_dev_probe,
+ .set_option = powernv_eeh_set_option,
+ .get_pe_addr = powernv_eeh_get_pe_addr,
+ .get_state = powernv_eeh_get_state,
+ .reset = powernv_eeh_reset,
+ .wait_state = powernv_eeh_wait_state,
+ .get_log = powernv_eeh_get_log,
+ .configure_bridge = powernv_eeh_configure_bridge,
+ .read_config = powernv_eeh_read_config,
+ .write_config = powernv_eeh_write_config,
+ .next_error = powernv_eeh_next_error
+};
+
+/**
+ * eeh_powernv_init - Register platform dependent EEH operations
+ *
+ * EEH initialization on powernv platform. This function should be
+ * called before any EEH related functions.
+ */
+static int __init eeh_powernv_init(void)
+{
+ int ret = -EINVAL;
+
+ if (!machine_is(powernv))
+ return ret;
+
+ ret = eeh_ops_register(&powernv_eeh_ops);
+ if (!ret)
+ pr_info("EEH: PowerNV platform initialized\n");
+ else
+ pr_info("EEH: Failed to initialize PowerNV platform (%d)\n", ret);
+
+ return ret;
+}
+
+early_initcall(eeh_powernv_init);
diff --git a/arch/powerpc/platforms/pseries/eeh_pseries.c b/arch/powerpc/platforms/pseries/eeh_pseries.c
index b456b15..62415f2 100644
--- a/arch/powerpc/platforms/pseries/eeh_pseries.c
+++ b/arch/powerpc/platforms/pseries/eeh_pseries.c
@@ -625,7 +625,8 @@ static struct eeh_ops pseries_eeh_ops = {
.get_log = pseries_eeh_get_log,
.configure_bridge = pseries_eeh_configure_bridge,
.read_config = pseries_eeh_read_config,
- .write_config = pseries_eeh_write_config
+ .write_config = pseries_eeh_write_config,
+ .next_error = NULL
};
/**
--
1.7.5.4
^ permalink raw reply related
* [PATCH 21/31] powerpc/eeh: I/O chip PE log and bridge setup
From: Gavin Shan @ 2013-06-18 8:33 UTC (permalink / raw)
To: linuxppc-dev; +Cc: Gavin Shan
In-Reply-To: <1371544435-4943-1-git-send-email-shangw@linux.vnet.ibm.com>
The patch adds backends to retrieve error log and configure p2p
bridges for the indicated PE.
Signed-off-by: Gavin Shan <shangw@linux.vnet.ibm.com>
---
arch/powerpc/platforms/powernv/eeh-ioda.c | 57 ++++++++++++++++++++++++++++-
1 files changed, 55 insertions(+), 2 deletions(-)
diff --git a/arch/powerpc/platforms/powernv/eeh-ioda.c b/arch/powerpc/platforms/powernv/eeh-ioda.c
index ea5fa05..8d9c2d2 100644
--- a/arch/powerpc/platforms/powernv/eeh-ioda.c
+++ b/arch/powerpc/platforms/powernv/eeh-ioda.c
@@ -445,12 +445,65 @@ static int ioda_eeh_reset(struct eeh_pe *pe, int option)
return ret;
}
+/**
+ * ioda_eeh_get_log - Retrieve error log
+ * @pe: EEH PE
+ * @severity: Severity level of the log
+ * @drv_log: buffer to store the log
+ * @len: space of the log buffer
+ *
+ * The function is used to retrieve error log from P7IOC.
+ */
+static int ioda_eeh_get_log(struct eeh_pe *pe, int severity,
+ char *drv_log, unsigned long len)
+{
+ s64 ret;
+ unsigned long flags;
+ struct pci_controller *hose = pe->phb;
+ struct pnv_phb *phb = hose->private_data;
+
+ spin_lock_irqsave(&phb->lock, flags);
+
+ ret = opal_pci_get_phb_diag_data2(phb->opal_id,
+ phb->diag.blob, PNV_PCI_DIAG_BUF_SIZE);
+ if (ret) {
+ spin_unlock_irqrestore(&phb->lock, flags);
+ pr_warning("%s: Failed to get log for PHB#%x-PE#%x\n",
+ __func__, hose->global_number, pe->addr);
+ return -EIO;
+ }
+
+ /*
+ * FIXME: We probably need log the error in somewhere.
+ * Lets make it up in future.
+ */
+ /* pr_info("%s", phb->diag.blob); */
+
+ spin_unlock_irqrestore(&phb->lock, flags);
+
+ return 0;
+}
+
+/**
+ * ioda_eeh_configure_bridge - Configure the PCI bridges for the indicated PE
+ * @pe: EEH PE
+ *
+ * For particular PE, it might have included PCI bridges. In order
+ * to make the PE work properly, those PCI bridges should be configured
+ * correctly. However, we need do nothing on P7IOC since the reset
+ * function will do everything that should be covered by the function.
+ */
+static int ioda_eeh_configure_bridge(struct eeh_pe *pe)
+{
+ return 0;
+}
+
struct pnv_eeh_ops ioda_eeh_ops = {
.post_init = ioda_eeh_post_init,
.set_option = ioda_eeh_set_option,
.get_state = ioda_eeh_get_state,
.reset = ioda_eeh_reset,
- .get_log = NULL,
- .configure_bridge = NULL,
+ .get_log = ioda_eeh_get_log,
+ .configure_bridge = ioda_eeh_configure_bridge,
.next_error = NULL
};
--
1.7.5.4
^ permalink raw reply related
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