Hi ,
here is a respin of the patches I posted last week for the RT kernel now targeted
for mainline (http://lkml.org/lkml/2008/7/24/98). Thomas, steven, a note for you
at the end.
The goal of this patchset is to simplify the locking constraints on the radix
tree used for IRQ reverse mapping on the pSeries machines and provide lockless
access to this tree.
This also solves the following BUG under preempt-rt:
BUG: sleeping function called from invalid context swapper(1) at kernel/rtmutex.c:739
in_atomic():1 [00000002], irqs_disabled():1
Call Trace:
[c0000001e20f3340] [c000000000010370] .show_stack+0x70/0x1bc (unreliable)
[c0000001e20f33f0] [c000000000049380] .__might_sleep+0x11c/0x138
[c0000001e20f3470] [c0000000002a2f64] .__rt_spin_lock+0x3c/0x98
[c0000001e20f34f0] [c0000000000c3f20] .kmem_cache_alloc+0x68/0x184
[c0000001e20f3590] [c000000000193f3c] .radix_tree_node_alloc+0xf0/0x144
[c0000001e20f3630] [c000000000195190] .radix_tree_insert+0x18c/0x2fc
[c0000001e20f36f0] [c00000000000c710] .irq_radix_revmap+0x1a4/0x1e4
[c0000001e20f37b0] [c00000000003b3f0] .xics_startup+0x30/0x54
[c0000001e20f3840] [c00000000008b864] .setup_irq+0x26c/0x370
[c0000001e20f38f0] [c00000000008ba68] .request_irq+0x100/0x158
[c0000001e20f39a0] [c0000000001ee9c0] .hvc_open+0xb4/0x148
[c0000001e20f3a40] [c0000000001d72ec] .tty_open+0x200/0x368
[c0000001e20f3af0] [c0000000000ce928] .chrdev_open+0x1f4/0x25c
[c0000001e20f3ba0] [c0000000000c8bf0] .__dentry_open+0x188/0x2c8
[c0000001e20f3c50] [c0000000000c8dec] .do_filp_open+0x50/0x70
[c0000001e20f3d70] [c0000000000c8e8c] .do_sys_open+0x80/0x148
[c0000001e20f3e20] [c00000000000928c] .init_post+0x4c/0x100
[c0000001e20f3ea0] [c0000000003c0e0c] .kernel_init+0x428/0x478
[c0000001e20f3f90] [c000000000027448] .kernel_thread+0x4c/0x68
The root cause of this bug lies in the fact that the XICS interrupt controller
uses a radix tree for its reverse irq mapping and that we cannot allocate the tree
nodes (even GFP_ATOMIC) with preemption disabled.
In fact, we have 2 nested preemption disabling when we want to allocate
a new node:
- setup_irq() does a spin_lock_irqsave() before calling xics_startup() which
then calls irq_radix_revmap() to insert a new node in the tree
- irq_radix_revmap() also does a spin_lock_irqsave() (in irq_radix_wrlock())
before the radix_tree_insert()
Also, if an IRQ gets registered before the tree is initialized (namely the
IPI), it will be inserted into the tree in interrupt context once the tree
have been initialized, hence the need for a spin_lock_irqsave() in the insertion
path.
This serie is split into 3 patches:
- The first patch moves the initialization of the radix tree earlier in the
boot process before any IRQ gets registered, but after the mm is up.
- The second patch splits irq_radix_revmap() into its 2 components: one
for lookup and one for insertion into the radix tree.
- And finally, the third patch makes the radix tree fully lockless on the
lookup side.
Here is the diffstat for the whole patchset:
arch/powerpc/kernel/irq.c | 134 ++++++++-------------------------
arch/powerpc/platforms/pseries/smp.c | 1 +
arch/powerpc/platforms/pseries/xics.c | 11 +--
include/asm-powerpc/irq.h | 24 +++++-
4 files changed, 58 insertions(+), 112 deletions(-)
Thomas, Steven, the first 2 patches can be applied seamlessly to 2.6.26-rt1
with offsets, the third patch has a trivial to fix reject in
arch/powerpc/kernel/irq.c because the irq_big_lock is changed to a raw spinlock
in preempt-rt. If you want those patches for RT, just flag me, I have those
sitting on my test box.
Thanks,
Sebastien.
irq_radix_revmap() currently serves 2 purposes, irq mapping lookup
and insertion which happen in interrupt and process context respectively.
Separate the function into its 2 components, one for lookup only and one
for insertion only.
Signed-off-by: Sebastien Dugue <redacted>
Cc: Benjamin Herrenschmidt <benh@kernel.crashing.org>
Cc: Paul Mackerras <redacted>
---
arch/powerpc/kernel/irq.c | 25 ++++++++++++++-----------
arch/powerpc/platforms/pseries/xics.c | 11 ++++-------
include/asm-powerpc/irq.h | 18 +++++++++++++++---
3 files changed, 33 insertions(+), 21 deletions(-)
@@ -900,34 +900,37 @@ void __init irq_radix_revmap_init(void)}}-unsignedintirq_radix_revmap(structirq_host*host,-irq_hw_number_thwirq)+unsignedintirq_radix_revmap_lookup(structirq_host*host,+irq_hw_number_thwirq){structirq_map_entry*ptr;-unsignedintvirq;+unsignedintvirq=NO_IRQ;unsignedlongflags;WARN_ON(host->revmap_type!=IRQ_HOST_MAP_TREE);-/* Try to resolve */irq_radix_rdlock(&flags);ptr=radix_tree_lookup(&host->revmap_data.tree,hwirq);irq_radix_rdunlock(flags);-/* Found it, return */-if(ptr){+if(ptr)virq=ptr-irq_map;-returnvirq;-}-/* If not there, try to insert it */-virq=irq_find_mapping(host,hwirq);+returnvirq;+}++voidirq_radix_revmap_insert(structirq_host*host,unsignedintvirq,+irq_hw_number_thwirq)+{+unsignedlongflags;++WARN_ON(host->revmap_type!=IRQ_HOST_MAP_TREE);+if(virq!=NO_IRQ){irq_radix_wrlock(&flags);radix_tree_insert(&host->revmap_data.tree,hwirq,&irq_map[virq]);irq_radix_wrunlock(flags);}-returnvirq;}unsignedintirq_linear_revmap(structirq_host*host,
@@ -310,12 +310,6 @@ static void xics_mask_irq(unsigned int virq)staticunsignedintxics_startup(unsignedintvirq){-unsignedintirq;--/* force a reverse mapping of the interrupt so it gets in the cache */-irq=(unsignedint)irq_map[virq].hwirq;-irq_radix_revmap(xics_host,irq);-/* unmask it */xics_unmask_irq(virq);return0;
@@ -346,7 +340,7 @@ static inline unsigned int xics_remap_irq(unsigned int vec)if(vec==XICS_IRQ_SPURIOUS)returnNO_IRQ;-irq=irq_radix_revmap(xics_host,vec);+irq=irq_radix_revmap_lookup(xics_host,vec);if(likely(irq!=NO_IRQ))returnirq;
@@ -530,6 +524,9 @@ static int xics_host_map(struct irq_host *h, unsigned int virq,{pr_debug("xics: map virq %d, hwirq 0x%lx\n",virq,hw);+/* Insert the interrupt mapping into the radix tree for fast lookup */+irq_radix_revmap_insert(xics_host,virq,hw);+get_irq_desc(virq)->status|=IRQ_LEVEL;set_irq_chip_and_handler(virq,xics_irq_chip,handle_fasteoi_irq);return0;
The radix tree used for fast irq reverse mapping by the XICS is initialized
late in the boot process, after the first interrupt (IPI) gets registered
and after the first IPI is received.
This patch moves the initialization of the XICS radix tree earlier into
the boot process in smp_xics_probe() (the mm is already up but no interrupts
have been registered at that point) to avoid having to insert a mapping into
the tree in interrupt context. This will help in simplifying the locking
constraints and move to a lockless radix tree in subsequent patches.
As a nice side effect, there is no need any longer to check for
(host->revmap_data.tree.gfp_mask != 0) to know if the tree have been
initialized.
Signed-off-by: Sebastien Dugue <redacted>
Cc: Benjamin Herrenschmidt <benh@kernel.crashing.org>
Cc: Paul Mackerras <redacted>
---
arch/powerpc/kernel/irq.c | 44 +++++++++------------------------
arch/powerpc/platforms/pseries/smp.c | 1 +
include/asm-powerpc/irq.h | 5 ++++
3 files changed, 18 insertions(+), 32 deletions(-)
@@ -840,9 +840,6 @@ void irq_dispose_mapping(unsigned int virq)host->revmap_data.linear.revmap[hwirq]=NO_IRQ;break;caseIRQ_HOST_MAP_TREE:-/* Check if radix tree allocated yet */-if(host->revmap_data.tree.gfp_mask==0)-break;irq_radix_wrlock(&flags);radix_tree_delete(&host->revmap_data.tree,hwirq);irq_radix_wrunlock(flags);
@@ -893,28 +890,28 @@ unsigned int irq_find_mapping(struct irq_host *host,}EXPORT_SYMBOL_GPL(irq_find_mapping);+void__initirq_radix_revmap_init(void)+{+structirq_host*h;++list_for_each_entry(h,&irq_hosts,link){+if(h->revmap_type==IRQ_HOST_MAP_TREE)+INIT_RADIX_TREE(&h->revmap_data.tree,GFP_ATOMIC);+}+}unsignedintirq_radix_revmap(structirq_host*host,irq_hw_number_thwirq){-structradix_tree_root*tree;structirq_map_entry*ptr;unsignedintvirq;unsignedlongflags;WARN_ON(host->revmap_type!=IRQ_HOST_MAP_TREE);-/* Check if the radix tree exist yet. We test the value of-*thegfp_maskforthat.Sneakybutsavesanotherintinthe-*structure.Ifnot,wefallbacktoslowmode-*/-tree=&host->revmap_data.tree;-if(tree->gfp_mask==0)-returnirq_find_mapping(host,hwirq);--/* Now try to resolve */+/* Try to resolve */irq_radix_rdlock(&flags);-ptr=radix_tree_lookup(tree,hwirq);+ptr=radix_tree_lookup(&host->revmap_data.tree,hwirq);irq_radix_rdunlock(flags);/* Found it, return */
@@ -927,7 +924,7 @@ unsigned int irq_radix_revmap(struct irq_host *host,virq=irq_find_mapping(host,hwirq);if(virq!=NO_IRQ){irq_radix_wrlock(&flags);-radix_tree_insert(tree,hwirq,&irq_map[virq]);+radix_tree_insert(&host->revmap_data.tree,hwirq,&irq_map[virq]);irq_radix_wrunlock(flags);}returnvirq;
@@ -1035,23 +1032,6 @@ void irq_early_init(void)get_irq_desc(i)->status|=IRQ_NOREQUEST;}-/* We need to create the radix trees late */-staticintirq_late_init(void)-{-structirq_host*h;-unsignedlongflags;--irq_radix_wrlock(&flags);-list_for_each_entry(h,&irq_hosts,link){-if(h->revmap_type==IRQ_HOST_MAP_TREE)-INIT_RADIX_TREE(&h->revmap_data.tree,GFP_ATOMIC);-}-irq_radix_wrunlock(flags);--return0;-}-arch_initcall(irq_late_init);-#ifdef CONFIG_VIRQ_DEBUGstaticintvirq_debug_show(structseq_file*m,void*private){
From: Michael Ellerman <hidden> Date: 2008-07-31 11:41:12
On Thu, 2008-07-31 at 11:40 +0200, Sebastien Dugue wrote:
The radix tree used for fast irq reverse mapping by the XICS is initialized
late in the boot process, after the first interrupt (IPI) gets registered
and after the first IPI is received.
This patch moves the initialization of the XICS radix tree earlier into
the boot process in smp_xics_probe() (the mm is already up but no interrupts
have been registered at that point) to avoid having to insert a mapping into
the tree in interrupt context. This will help in simplifying the locking
constraints and move to a lockless radix tree in subsequent patches.
As a nice side effect, there is no need any longer to check for
(host->revmap_data.tree.gfp_mask != 0) to know if the tree have been
initialized.
@@ -130,6 +130,7 @@ static void smp_xics_message_pass(int target, int msg)staticint__initsmp_xics_probe(void){+irq_radix_revmap_init();xics_request_IPIs();
But now it's only called from the xics setup code.
Which seems a bit ugly. In practice it doesn't matter because at the
moment xics is the only user of the radix revmap. But if we're going to
switch to this sort of initialisation I think xics should only be
init'ing the revmap for itself.
This boot ordering stuff is pretty hairy, so I might have missed
something, but this is how the code is ordered AFAICT:
start_kernel()
init_IRQ()
...
local_irq_enable()
...
rest_init()
kernel_thread()
kernel_init()
smp_prepare_cpus()
smp_xics_probe() (via smp_ops->probe())
What's stopping us from taking an irq between local_irq_enable() and
smp_xics_probe() ? Is it just that no one's request_irq()'ed them yet?
cheers
--
Michael Ellerman
OzLabs, IBM Australia Development Lab
wwweb: http://michael.ellerman.id.au
phone: +61 2 6212 1183 (tie line 70 21183)
We do not inherit the earth from our ancestors,
we borrow it from our children. - S.M.A.R.T Person
Hi Michael,
On Thu, 31 Jul 2008 21:40:56 +1000 Michael Ellerman <michael@ellerman.id.au=
wrote:
On Thu, 2008-07-31 at 11:40 +0200, Sebastien Dugue wrote:
quoted
The radix tree used for fast irq reverse mapping by the XICS is initial=
ized
quoted
late in the boot process, after the first interrupt (IPI) gets register=
ed
quoted
and after the first IPI is received.
=20
This patch moves the initialization of the XICS radix tree earlier in=
to
quoted
the boot process in smp_xics_probe() (the mm is already up but no inter=
rupts
quoted
have been registered at that point) to avoid having to insert a mapping=
into
quoted
the tree in interrupt context. This will help in simplifying the locking
constraints and move to a lockless radix tree in subsequent patches.
=20
As a nice side effect, there is no need any longer to check for
(host->revmap_data.tree.gfp_mask !=3D 0) to know if the tree have been
initialized.
=20
Hi Sebastien,
=20
This is a nice cleanup, I think :)
@@ -130,6 +130,7 @@ static void smp_xics_message_pass(int target, int m=
sg)
quoted
=20
static int __init smp_xics_probe(void)
{
+ irq_radix_revmap_init();
xics_request_IPIs();
=20
But now it's only called from the xics setup code.
=20
Which seems a bit ugly. In practice it doesn't matter because at the
moment xics is the only user of the radix revmap. But if we're going to
switch to this sort of initialisation I think xics should only be
init'ing the revmap for itself.
You're right, that's what I intended to do from the beginning but
stumbled upon ... hmm, can't remember what, that made me change
my mind. But I agree, I'm not particularly proud of that. Will look
again into that.
=20
=20
This boot ordering stuff is pretty hairy, so I might have missed
something, but this is how the code is ordered AFAICT:
=EF=BB=BF
start_kernel()
init_IRQ()
...
local_irq_enable()
...
rest_init()
kernel_thread()
kernel_init()
smp_prepare_cpus()
smp_xics_probe() (via smp_ops->probe())
=20
=20
What's stopping us from taking an irq between local_irq_enable() and
smp_xics_probe() ? Is it just that no one's request_irq()'ed them yet?
It's hairy, I agree, but as you've mentioned no one has done a request_ir=
q()
at that point. The first one to do it is smp_xics_probe() for the IPI.
Thanks for your comments.
Sebastien.
=20
Hi Michael,
=20
On Thu, 31 Jul 2008 21:40:56 +1000 Michael Ellerman <michael@ellerman.id.=
au> wrote:
=20
quoted
On Thu, 2008-07-31 at 11:40 +0200, Sebastien Dugue wrote:
quoted
The radix tree used for fast irq reverse mapping by the XICS is initi=
alized
quoted
quoted
late in the boot process, after the first interrupt (IPI) gets regist=
ered
quoted
quoted
and after the first IPI is received.
=20
This patch moves the initialization of the XICS radix tree earlier =
into
quoted
quoted
the boot process in smp_xics_probe() (the mm is already up but no int=
errupts
quoted
quoted
have been registered at that point) to avoid having to insert a mappi=
ng into
quoted
quoted
the tree in interrupt context. This will help in simplifying the lock=
ing
quoted
quoted
constraints and move to a lockless radix tree in subsequent patches.
=20
As a nice side effect, there is no need any longer to check for
(host->revmap_data.tree.gfp_mask !=3D 0) to know if the tree have been
initialized.
=20
Hi Sebastien,
=20
This is a nice cleanup, I think :)
=20
static int __init smp_xics_probe(void)
{
+ irq_radix_revmap_init();
xics_request_IPIs();
=20
But now it's only called from the xics setup code.
=20
Which seems a bit ugly. In practice it doesn't matter because at the
moment xics is the only user of the radix revmap. But if we're going to
switch to this sort of initialisation I think xics should only be
init'ing the revmap for itself.
=20
You're right, that's what I intended to do from the beginning but
stumbled upon ... hmm, can't remember what, that made me change
my mind.
Ah yes, I wanted to do it from xics_init_host() but backed off
because at that point the mm is not up. But it does not make a difference
as the first request_irq() happens after the mm is up. A bit shaky I
concede.
But I agree, I'm not particularly proud of that. Will look
again into that.
=20
quoted
=20
=20
This boot ordering stuff is pretty hairy, so I might have missed
something, but this is how the code is ordered AFAICT:
=EF=BB=BF
start_kernel()
init_IRQ()
...
local_irq_enable()
...
rest_init()
kernel_thread()
kernel_init()
smp_prepare_cpus()
smp_xics_probe() (via smp_ops->probe())
=20
=20
What's stopping us from taking an irq between local_irq_enable() and
smp_xics_probe() ? Is it just that no one's request_irq()'ed them yet?
=20
It's hairy, I agree, but as you've mentioned no one has done a request_=
irq()
at that point. The first one to do it is smp_xics_probe() for the IPI.
=20
Thanks for your comments.
=20
Sebastien.
_______________________________________________
Linuxppc-dev mailing list
Linuxppc-dev@ozlabs.org
https://ozlabs.org/mailman/listinfo/linuxppc-dev
From: Michael Ellerman <hidden> Date: 2008-07-31 12:58:40
On Thu, 2008-07-31 at 14:00 +0200, Sebastien Dugue wrote:
On Thu, 31 Jul 2008 21:40:56 +1000 Michael Ellerman [off-list ref] wrote:
quoted
This boot ordering stuff is pretty hairy, so I might have missed
something, but this is how the code is ordered AFAICT:
start_kernel()
init_IRQ()
...
local_irq_enable()
...
rest_init()
kernel_thread()
kernel_init()
smp_prepare_cpus()
smp_xics_probe() (via smp_ops->probe())
What's stopping us from taking an irq between local_irq_enable() and
smp_xics_probe() ? Is it just that no one's request_irq()'ed them yet?
It's hairy, I agree, but as you've mentioned no one has done a request_irq()
at that point. The first one to do it is smp_xics_probe() for the IPI.
Hmm, I don't think that's strong enough. I can trivially cause irqs to
fire during a kexec reboot just by mashing the keyboard.
And during a kdump boot all sorts of stuff could be firing. Even during
a clean boot, from firmware, I don't think we can guarantee that
nothing's going to fire.
.. after a bit of testing ..
It seems it actually works (sort of).
xics_remap_irq() calls irq_radix_revmap_lookup(), which calls:
ptr = radix_tree_lookup(&host->revmap_data.tree, hwirq);
And because host->revmap_data.tree was zalloc'ed we trip on the first
check here:
cheers
--
Michael Ellerman
OzLabs, IBM Australia Development Lab
wwweb: http://michael.ellerman.id.au
phone: +61 2 6212 1183 (tie line 70 21183)
We do not inherit the earth from our ancestors,
we borrow it from our children. - S.M.A.R.T Person
From: Michael Ellerman <hidden> Date: 2008-07-31 13:01:54
On Thu, 2008-07-31 at 22:58 +1000, Michael Ellerman wrote:
On Thu, 2008-07-31 at 14:00 +0200, Sebastien Dugue wrote:
quoted
On Thu, 31 Jul 2008 21:40:56 +1000 Michael Ellerman [off-list ref] wrote:
quoted
This boot ordering stuff is pretty hairy, so I might have missed
something, but this is how the code is ordered AFAICT:
start_kernel()
init_IRQ()
...
local_irq_enable()
...
rest_init()
kernel_thread()
kernel_init()
smp_prepare_cpus()
smp_xics_probe() (via smp_ops->probe())
What's stopping us from taking an irq between local_irq_enable() and
smp_xics_probe() ? Is it just that no one's request_irq()'ed them yet?
It's hairy, I agree, but as you've mentioned no one has done a request_irq()
at that point. The first one to do it is smp_xics_probe() for the IPI.
Hmm, I don't think that's strong enough. I can trivially cause irqs to
fire during a kexec reboot just by mashing the keyboard.
And during a kdump boot all sorts of stuff could be firing. Even during
a clean boot, from firmware, I don't think we can guarantee that
nothing's going to fire.
.. after a bit of testing ..
It seems it actually works (sort of).
xics_remap_irq() calls irq_radix_revmap_lookup(), which calls:
ptr = radix_tree_lookup(&host->revmap_data.tree, hwirq);
And because host->revmap_data.tree was zalloc'ed we trip on the first
check here:
@#$% ctrl-enter == send!
Continuing ...
void *radix_tree_lookup(struct radix_tree_root *root, unsigned long index)
{
unsigned int height, shift;
struct radix_tree_node *node, **slot;
node = rcu_dereference(root->rnode);
if (node == NULL)
return NULL;
Which means irq_radix_revmap_lookup() will return NO_IRQ, which is cool.
So I think it can fly, as long as we're happy that we can't reverse map
anything until smp_xics_probe() - and I think that's true, as any irq we
take will be invalid.
cheers
--
Michael Ellerman
OzLabs, IBM Australia Development Lab
wwweb: http://michael.ellerman.id.au
phone: +61 2 6212 1183 (tie line 70 21183)
We do not inherit the earth from our ancestors,
we borrow it from our children. - S.M.A.R.T Person
On Thu, 31 Jul 2008 23:01:39 +1000 Michael Ellerman <michael@ellerman.id.au=
wrote:
On Thu, 2008-07-31 at 22:58 +1000, Michael Ellerman wrote:
quoted
On Thu, 2008-07-31 at 14:00 +0200, Sebastien Dugue wrote:
quoted
On Thu, 31 Jul 2008 21:40:56 +1000 Michael Ellerman <michael@ellerman=
.id.au> wrote:
quoted
quoted
quoted
=20
This boot ordering stuff is pretty hairy, so I might have missed
something, but this is how the code is ordered AFAICT:
=EF=BB=BF
start_kernel()
init_IRQ()
...
local_irq_enable()
...
rest_init()
kernel_thread()
kernel_init()
smp_prepare_cpus()
smp_xics_probe() (via smp_ops->probe())
=20
=20
What's stopping us from taking an irq between local_irq_enable() and
smp_xics_probe() ? Is it just that no one's request_irq()'ed them =
yet?
quoted
quoted
=20
It's hairy, I agree, but as you've mentioned no one has done a requ=
est_irq()
quoted
quoted
at that point. The first one to do it is smp_xics_probe() for the IPI.
=20
Hmm, I don't think that's strong enough. I can trivially cause irqs to
fire during a kexec reboot just by mashing the keyboard.
=20
And during a kdump boot all sorts of stuff could be firing. Even during
a clean boot, from firmware, I don't think we can guarantee that
nothing's going to fire.
=20
.. after a bit of testing ..
=20
It seems it actually works (sort of).=20
=20
xics_remap_irq() calls irq_radix_revmap_lookup(), which calls:
=20
ptr =3D radix_tree_lookup(&host->revmap_data.tree, hwirq);
=20
And because =EF=BB=BFhost->revmap_data.tree was zalloc'ed we trip on th=
e first
quoted
check here:
=20
@#$% ctrl-enter =3D=3D send!
=20
Continuing ...
=20
void *radix_tree_lookup(struct radix_tree_root *root, unsigned long index)
{
unsigned int height, shift;
struct radix_tree_node *node, **slot;
=20
node =3D rcu_dereference(root->rnode);
if (node =3D=3D NULL)
return NULL;
=20
Which means =EF=BB=BFirq_radix_revmap_lookup() will return NO_IRQ, which =
is cool.
Which is what I intended so that as long as no IRQ is registered we
return NO_IRQ.
=20
=20
So I think it can fly, as long as we're happy that we can't reverse map
anything until smp_xics_probe() - and I think that's true, as any irq we
take will be invalid.
That's true as no IRQs are registered before smp_xics_probe() and for any
interrupt we might get before that, irq_radix_revmap_lookup() will return
NO_IRQ.
Thanks,
Sebastien.
From: Michael Ellerman <hidden> Date: 2008-07-31 13:39:42
On Thu, 2008-07-31 at 15:26 +0200, Sebastien Dugue wrote:
On Thu, 31 Jul 2008 23:01:39 +1000 Michael Ellerman [off-list ref] wrote:
quoted
On Thu, 2008-07-31 at 22:58 +1000, Michael Ellerman wrote:
quoted
On Thu, 2008-07-31 at 14:00 +0200, Sebastien Dugue wrote:
quoted
On Thu, 31 Jul 2008 21:40:56 +1000 Michael Ellerman [off-list ref] wrote:
quoted
This boot ordering stuff is pretty hairy, so I might have missed
something, but this is how the code is ordered AFAICT:
start_kernel()
init_IRQ()
...
local_irq_enable()
...
rest_init()
kernel_thread()
kernel_init()
smp_prepare_cpus()
smp_xics_probe() (via smp_ops->probe())
What's stopping us from taking an irq between local_irq_enable() and
smp_xics_probe() ? Is it just that no one's request_irq()'ed them yet?
It's hairy, I agree, but as you've mentioned no one has done a request_irq()
at that point. The first one to do it is smp_xics_probe() for the IPI.
Hmm, I don't think that's strong enough. I can trivially cause irqs to
fire during a kexec reboot just by mashing the keyboard.
And during a kdump boot all sorts of stuff could be firing. Even during
a clean boot, from firmware, I don't think we can guarantee that
nothing's going to fire.
.. after a bit of testing ..
It seems it actually works (sort of).
xics_remap_irq() calls irq_radix_revmap_lookup(), which calls:
ptr = radix_tree_lookup(&host->revmap_data.tree, hwirq);
And because host->revmap_data.tree was zalloc'ed we trip on the first
check here:
@#$% ctrl-enter == send!
Continuing ...
void *radix_tree_lookup(struct radix_tree_root *root, unsigned long index)
{
unsigned int height, shift;
struct radix_tree_node *node, **slot;
node = rcu_dereference(root->rnode);
if (node == NULL)
return NULL;
Which means irq_radix_revmap_lookup() will return NO_IRQ, which is cool.
Which is what I intended so that as long as no IRQ is registered we
return NO_IRQ.
quoted
So I think it can fly, as long as we're happy that we can't reverse map
anything until smp_xics_probe() - and I think that's true, as any irq we
take will be invalid.
That's true as no IRQs are registered before smp_xics_probe() and for any
interrupt we might get before that, irq_radix_revmap_lookup() will return
NO_IRQ.
Cool, we agree :)
My only worry is that we might be relying on on the particular radix
tree implementation a bit too much. Is it documented somewhere that
the /very/ first check is for root->rnode != NULL, and the rest of the
root may be unintialised?
And I think it needs a big fat comment in the irq code saying that it's
safe because revmap_data is zalloc'ed, and that means the radix lookup
will fail (safely).
cheers
--
Michael Ellerman
OzLabs, IBM Australia Development Lab
wwweb: http://michael.ellerman.id.au
phone: +61 2 6212 1183 (tie line 70 21183)
We do not inherit the earth from our ancestors,
we borrow it from our children. - S.M.A.R.T Person
On Thu, 31 Jul 2008 23:39:26 +1000 Michael Ellerman <michael@ellerman.id.au=
wrote:
On Thu, 2008-07-31 at 15:26 +0200, Sebastien Dugue wrote:
quoted
On Thu, 31 Jul 2008 23:01:39 +1000 Michael Ellerman <michael@ellerman.i=
d.au> wrote:
quoted
=20
quoted
On Thu, 2008-07-31 at 22:58 +1000, Michael Ellerman wrote:
quoted
On Thu, 2008-07-31 at 14:00 +0200, Sebastien Dugue wrote:
quoted
On Thu, 31 Jul 2008 21:40:56 +1000 Michael Ellerman <michael@elle=
rman.id.au> wrote:
quoted
quoted
quoted
quoted
quoted
=20
This boot ordering stuff is pretty hairy, so I might have missed
something, but this is how the code is ordered AFAICT:
=EF=BB=BF
start_kernel()
init_IRQ()
...
local_irq_enable()
...
rest_init()
kernel_thread()
kernel_init()
smp_prepare_cpus()
smp_xics_probe() (via smp_ops->probe())
=20
=20
What's stopping us from taking an irq between local_irq_enable(=
) and
quoted
quoted
quoted
quoted
quoted
smp_xics_probe() ? Is it just that no one's request_irq()'ed t=
hem yet?
quoted
quoted
quoted
quoted
=20
It's hairy, I agree, but as you've mentioned no one has done a =
request_irq()
quoted
quoted
quoted
quoted
at that point. The first one to do it is smp_xics_probe() for the=
IPI.
quoted
quoted
quoted
=20
Hmm, I don't think that's strong enough. I can trivially cause irqs=
to
quoted
quoted
quoted
fire during a kexec reboot just by mashing the keyboard.
=20
And during a kdump boot all sorts of stuff could be firing. Even du=
ring
quoted
quoted
quoted
a clean boot, from firmware, I don't think we can guarantee that
nothing's going to fire.
=20
.. after a bit of testing ..
=20
It seems it actually works (sort of).=20
=20
xics_remap_irq() calls irq_radix_revmap_lookup(), which calls:
=20
ptr =3D radix_tree_lookup(&host->revmap_data.tree, hwirq);
=20
And because =EF=BB=BFhost->revmap_data.tree was zalloc'ed we trip o=
{
unsigned int height, shift;
struct radix_tree_node *node, **slot;
=20
node =3D rcu_dereference(root->rnode);
if (node =3D=3D NULL)
return NULL;
=20
Which means =EF=BB=BFirq_radix_revmap_lookup() will return NO_IRQ, wh=
ich is cool.
quoted
=20
Which is what I intended so that as long as no IRQ is registered we
return NO_IRQ.
=20
quoted
=20
=20
So I think it can fly, as long as we're happy that we can't reverse m=
ap
quoted
quoted
anything until smp_xics_probe() - and I think that's true, as any irq=
we
quoted
quoted
take will be invalid.
=20
That's true as no IRQs are registered before smp_xics_probe() and for=
any
quoted
interrupt we might get before that, irq_radix_revmap_lookup() will retu=
rn
quoted
NO_IRQ.
=20
Cool, we agree :)=20
=20
My only worry is that we might be relying on on the particular radix
tree implementation a bit too much.
Well maybe we could revert back to testing a flag just like we
do for host->revmap_data.tree.gfp_mask !=3D 0. Dunno.
Is it documented somewhere that
the /very/ first check is for =EF=BB=BFroot->rnode !=3D NULL, and the res=
t of the
root may be unintialised?
Not in anything I could read except in looking at the code.
=20
And I think it needs a big fat comment in the irq code saying that it's
safe because revmap_data is zalloc'ed, and that means the radix lookup
will fail (safely).
Yep, right. Will advertise this properly for the next round if this
remains the prefered solution.
Thanks,
Sebastien.
The radix trees used by interrupt controllers for their irq reverse mapping
(currently only the XICS found on pSeries) have a complex locking scheme
dating back to before the advent of the lockless radix tree.
Take advantage of this and of the fact that the items of the tree are
pointers to a static array (irq_map) elements which can never go under us
to simplify the locking.
Concurrency between readers and writers is handled by the intrinsic
properties of the lockless radix tree. Concurrency between writers is handled
with a spinlock added to the irq_host structure.
Signed-off-by: Sebastien Dugue <redacted>
Cc: Benjamin Herrenschmidt <benh@kernel.crashing.org>
Cc: Paul Mackerras <redacted>
---
arch/powerpc/kernel/irq.c | 75 ++++++--------------------------------------
include/asm-powerpc/irq.h | 1 +
2 files changed, 12 insertions(+), 64 deletions(-)
@@ -602,57 +600,6 @@ void irq_set_virq_count(unsigned int count)irq_virq_count=count;}-/* radix tree not lockless safe ! we use a brlock-type mecanism-*fornow,untilwecanusealocklessradixtree-*/-staticvoidirq_radix_wrlock(unsignedlong*flags)-{-unsignedintcpu,ok;--spin_lock_irqsave(&irq_big_lock,*flags);-irq_radix_writer=1;-smp_mb();-do{-barrier();-ok=1;-for_each_possible_cpu(cpu){-if(per_cpu(irq_radix_reader,cpu)){-ok=0;-break;-}-}-if(!ok)-cpu_relax();-}while(!ok);-}--staticvoidirq_radix_wrunlock(unsignedlongflags)-{-smp_wmb();-irq_radix_writer=0;-spin_unlock_irqrestore(&irq_big_lock,flags);-}--staticvoidirq_radix_rdlock(unsignedlong*flags)-{-local_irq_save(*flags);-__get_cpu_var(irq_radix_reader)=1;-smp_mb();-if(likely(irq_radix_writer==0))-return;-__get_cpu_var(irq_radix_reader)=0;-smp_wmb();-spin_lock(&irq_big_lock);-__get_cpu_var(irq_radix_reader)=1;-spin_unlock(&irq_big_lock);-}--staticvoidirq_radix_rdunlock(unsignedlongflags)-{-__get_cpu_var(irq_radix_reader)=0;-local_irq_restore(flags);-}-staticintirq_setup_virq(structirq_host*host,unsignedintvirq,irq_hw_number_thwirq){
@@ -807,7 +754,6 @@ void irq_dispose_mapping(unsigned int virq){structirq_host*host;irq_hw_number_thwirq;-unsignedlongflags;if(virq==NO_IRQ)return;
@@ -840,9 +786,9 @@ void irq_dispose_mapping(unsigned int virq)host->revmap_data.linear.revmap[hwirq]=NO_IRQ;break;caseIRQ_HOST_MAP_TREE:-irq_radix_wrlock(&flags);+spin_lock(&host->tree_lock);radix_tree_delete(&host->revmap_data.tree,hwirq);-irq_radix_wrunlock(flags);+spin_unlock(&host->tree_lock);break;}
OK, I goofed up with git-format-patch, forgot the --numbered option.
The patches subjects should read:
[PATCH 1/3] powerpc - Initialize the irq radix tree earlier
[PATCH 2/3] powerpc - Separate the irq radix tree insertion and lookup
[PATCH 3/3] powerpc - Make the irq reverse mapping radix tree lockless
Sorry for that.
Sebastien.