* [patch] mm: fix lockless pagecache reordering bug (was Re: BUG: soft lockup - is this XFS problem?) [not found] ` <49623384.2070801@aon.at> @ 2009-01-05 16:41 ` Nick Piggin 2009-01-05 17:30 ` Linus Torvalds 0 siblings, 1 reply; 17+ messages in thread From: Nick Piggin @ 2009-01-05 16:41 UTC (permalink / raw) To: Peter Klotz, Linus Torvalds, stable, Linux Memory Management List Cc: Christoph Hellwig, Roman Kononov, linux-kernel, xfs, Andrew Morton Hi, This patch should be applied to 2.6.29 and 27/28 stable kernels, please. -- Peter Klotz and Roman Kononov both reported a bug where in XFS workloads where they were seeing softlockups in find_get_pages (http://oss.sgi.com/bugzilla/show_bug.cgi?id=805). Basically it would go into an "infinite" loop, although it would sometimes be able to break out of the loop depending on the phase of the moon. This turns out to be a bug in the lockless pagecache patch. There is a missing compiler barrier in the "increment reference count unless it was zero" failure case of the lockless pagecache protocol in the gang lookup functions. This would cause the compiler to use a cached value of struct page pointer to retry the operation with, rather than reload it. So the page might have been removed from pagecache and freed (refcount==0) but the lookup would not correctly notice the page is no longer in pagecache, and keep attempting to increment the refcount and failing, until the page gets reallocated for something else. This isn't a data corruption because the condition will be properly handled if the page does get reallocated. However it can result in a lockup. Add a the required compiler barrier and comment to fix this. Assembly snippet from find_get_pages, before: .L220: movq (%rbx), %rax #* ivtmp.1162, tmp82 movq (%rax), %rdi #, prephitmp.1149 .L218: testb $1, %dil #, prephitmp.1149 jne .L217 #, testq %rdi, %rdi # prephitmp.1149 je .L203 #, cmpq $-1, %rdi #, prephitmp.1149 je .L217 #, movl 8(%rdi), %esi # <variable>._count.counter, c testl %esi, %esi # c je .L218 #, after: .L212: movq (%rbx), %rax #* ivtmp.1109, tmp81 movq (%rax), %rdi #, ret testb $1, %dil #, ret jne .L211 #, testq %rdi, %rdi # ret je .L197 #, cmpq $-1, %rdi #, ret je .L211 #, movl 8(%rdi), %esi # <variable>._count.counter, c testl %esi, %esi # c je .L212 #, (notice the obvious infinite loop in the first example, if page->count remains 0) The problem was noticed and resolved on 2.6.27 stable kernels, and also applies upstream (where I was able to reproduce it and verify the fix). Reported-by: Peter Klotz <peter.klotz@aon.at> Reported-by: Roman Kononov <kononov@ftml.net> Tested-by: Peter Klotz <peter.klotz@aon.at> Tested-by: Roman Kononov <kononov@ftml.net> Signed-off-by: Nick Piggin <npiggin@suse.de> --- Index: linux-2.6/mm/filemap.c =================================================================== --- linux-2.6.orig/mm/filemap.c 2009-01-05 17:22:57.000000000 +1100 +++ linux-2.6/mm/filemap.c 2009-01-05 17:28:40.000000000 +1100 @@ -794,8 +794,19 @@ repeat: if (unlikely(page == RADIX_TREE_RETRY)) goto restart; - if (!page_cache_get_speculative(page)) + if (!page_cache_get_speculative(page)) { + /* + * A failed page_cache_get_speculative operation does + * not imply any barriers (Documentation/atomic_ops.txt), + * and as such, we must force the compiler to deref the + * radix-tree slot again rather than using the cached + * value (because we need to give up if the page has been + * removed from the radix-tree, rather than looping until + * it gets reused for something else). + */ + barrier(); goto repeat; + } /* Has the page moved? */ if (unlikely(page != *((void **)pages[i]))) { @@ -850,8 +861,11 @@ repeat: if (page->mapping == NULL || page->index != index) break; - if (!page_cache_get_speculative(page)) + if (!page_cache_get_speculative(page)) { + /* barrier: see find_get_pages() */ + barrier(); goto repeat; + } /* Has the page moved? */ if (unlikely(page != *((void **)pages[i]))) { @@ -904,8 +918,11 @@ repeat: if (unlikely(page == RADIX_TREE_RETRY)) goto restart; - if (!page_cache_get_speculative(page)) + if (!page_cache_get_speculative(page)) { + /* barrier: see find_get_pages() */ + barrier(); goto repeat; + } /* Has the page moved? */ if (unlikely(page != *((void **)pages[i]))) { -- To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to majordomo@kvack.org. For more info on Linux MM, see: http://www.linux-mm.org/ . Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a> ^ permalink raw reply [flat|nested] 17+ messages in thread
* Re: [patch] mm: fix lockless pagecache reordering bug (was Re: BUG: soft lockup - is this XFS problem?) 2009-01-05 16:41 ` [patch] mm: fix lockless pagecache reordering bug (was Re: BUG: soft lockup - is this XFS problem?) Nick Piggin @ 2009-01-05 17:30 ` Linus Torvalds 2009-01-05 18:00 ` Nick Piggin 0 siblings, 1 reply; 17+ messages in thread From: Linus Torvalds @ 2009-01-05 17:30 UTC (permalink / raw) To: Nick Piggin Cc: Peter Klotz, stable, Linux Memory Management List, Christoph Hellwig, Roman Kononov, linux-kernel, xfs, Andrew Morton On Mon, 5 Jan 2009, Nick Piggin wrote: > > This patch should be applied to 2.6.29 and 27/28 stable kernels, please. No. I think this patch is utter crap. But please feel free to educate me on why that is not the case. Here's my explanation: Not only is it ugly (which is already sufficient ground to suspect it is wrong or could at least be done better), but reading the comment, it makes no sense at all. You only put the barrier in the "goto repeat" case, but the thing is, if you worry about radix tree slot not being reloaded in the repeat case, then you damn well should worry about it not being reloaded in the non-repeat case too! The code is immediately followed by a test to see that the page is still the same in the slot, ie this: /* * Has the page moved? * This is part of the lockless pagecache protocol. See * include/linux/pagemap.h for details. */ if (unlikely(page != *pagep)) { and if you need a barrier for the repeat case, you need one for this case too. In other words, it looks like you fixed the symptom, but not the real cause! That's now how we work in the kernel. The real cause, btw, appears to be that radix_tree_deref_slot() is a piece of slimy sh*t, and has not been correctly updated to RCU. The proper fix doesn't require any barriers that I can see - I think the proper fix is this simple one-liner. If you use RCU to protect a data structure, then any data loaded from that data structure that can change due to RCU should be loaded with "rcu_dereference()". Now, I can't test this, because it makes absolutely no difference for me (the diff isn't empty, but the asm changes seem to be all due to just gcc variable numbering changing). I can't seem to see the buggy code. Maybe it needs a specific compiler version, or some specific config option to trigger? So because I can't see the issue, I also obviously can't verify that it's the only possible case. Maybe there is some other memory access that should also be done with the proper rcu accessors? Of course, it's also possible that we should just put a barrier in page_cache_get_speculative(). That doesn't seem to make a whole lot of conceptual sense, though (the same way that your barrier() didn't make any sense - I don't see that the barrier has absolutely _anything_ to do with whether the speculative getting of the page fails or not!) In general, I'd like fewer "band-aid" patches, and more "deep thinking" patches. I'm not saying mine is very deep either, but I think it's at least scrathing the surface of the real problem rather than just trying to cover it up. Linus --- include/linux/radix-tree.h | 2 +- 1 files changed, 1 insertions(+), 1 deletions(-) diff --git a/include/linux/radix-tree.h b/include/linux/radix-tree.h index a916c66..355f6e8 100644 --- a/include/linux/radix-tree.h +++ b/include/linux/radix-tree.h @@ -136,7 +136,7 @@ do { \ */ static inline void *radix_tree_deref_slot(void **pslot) { - void *ret = *pslot; + void *ret = rcu_dereference(*pslot); if (unlikely(radix_tree_is_indirect_ptr(ret))) ret = RADIX_TREE_RETRY; return ret; -- To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to majordomo@kvack.org. For more info on Linux MM, see: http://www.linux-mm.org/ . Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a> ^ permalink raw reply related [flat|nested] 17+ messages in thread
* Re: [patch] mm: fix lockless pagecache reordering bug (was Re: BUG: soft lockup - is this XFS problem?) 2009-01-05 17:30 ` Linus Torvalds @ 2009-01-05 18:00 ` Nick Piggin 2009-01-05 18:44 ` Linus Torvalds 0 siblings, 1 reply; 17+ messages in thread From: Nick Piggin @ 2009-01-05 18:00 UTC (permalink / raw) To: Linus Torvalds Cc: Peter Klotz, stable, Linux Memory Management List, Christoph Hellwig, Roman Kononov, linux-kernel, xfs, Andrew Morton On Mon, Jan 05, 2009 at 09:30:55AM -0800, Linus Torvalds wrote: > > > On Mon, 5 Jan 2009, Nick Piggin wrote: > > > > This patch should be applied to 2.6.29 and 27/28 stable kernels, please. > > No. I think this patch is utter crap. But please feel free to educate me > on why that is not the case. > > Here's my explanation: > > Not only is it ugly (which is already sufficient ground to suspect it is > wrong or could at least be done better), but reading the comment, it makes > no sense at all. You only put the barrier in the "goto repeat" case, but > the thing is, if you worry about radix tree slot not being reloaded in the > repeat case, then you damn well should worry about it not being reloaded > in the non-repeat case too! In which case atomic_inc_unless is defined to provide a barrier. > The code is immediately followed by a test to see that the page is still > the same in the slot, ie this: > > /* > * Has the page moved? > * This is part of the lockless pagecache protocol. See > * include/linux/pagemap.h for details. > */ > if (unlikely(page != *pagep)) { > > and if you need a barrier for the repeat case, you need one for this case > too. > > In other words, it looks like you fixed the symptom, but not the real > cause! That's now how we work in the kernel. > > The real cause, btw, appears to be that radix_tree_deref_slot() is a piece > of slimy sh*t, and has not been correctly updated to RCU. The proper fix > doesn't require any barriers that I can see - I think the proper fix is > this simple one-liner. > > If you use RCU to protect a data structure, then any data loaded from that > data structure that can change due to RCU should be loaded with > "rcu_dereference()". It doesn't need that because the last level pointers in the radix tree are not necessarily under RCU, but whatever synchronisation the caller uses (in this case, speculative page references, which should not require smp_read_barrier_depends, AFAIKS). Putting an rcu_dereference there might work, but I think it misses a subtlety of this code. > Now, I can't test this, because it makes absolutely no difference for me > (the diff isn't empty, but the asm changes seem to be all due to just gcc > variable numbering changing). I can't seem to see the buggy code. Maybe it > needs a specific compiler version, or some specific config option to > trigger? > > So because I can't see the issue, I also obviously can't verify that it's > the only possible case. Maybe there is some other memory access that > should also be done with the proper rcu accessors? > > Of course, it's also possible that we should just put a barrier in > page_cache_get_speculative(). That doesn't seem to make a whole lot of > conceptual sense, though (the same way that your barrier() didn't make any > sense - I don't see that the barrier has absolutely _anything_ to do with > whether the speculative getting of the page fails or not!) When that fails, the caller can (almost) assume the pointer has changed. So it has to load the new pointer to continue. The object pointed to is not protected with RCU, nor is there a requirement to see a specific load execution ordering. > > In general, I'd like fewer "band-aid" patches, and more "deep thinking" > patches. I'm not saying mine is very deep either, but I think it's at > least scrathing the surface of the real problem rather than just trying to > cover it up. -- To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to majordomo@kvack.org. For more info on Linux MM, see: http://www.linux-mm.org/ . Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a> ^ permalink raw reply [flat|nested] 17+ messages in thread
* Re: [patch] mm: fix lockless pagecache reordering bug (was Re: BUG: soft lockup - is this XFS problem?) 2009-01-05 18:00 ` Nick Piggin @ 2009-01-05 18:44 ` Linus Torvalds 2009-01-05 19:39 ` Linus Torvalds 2009-01-05 20:12 ` Paul E. McKenney 0 siblings, 2 replies; 17+ messages in thread From: Linus Torvalds @ 2009-01-05 18:44 UTC (permalink / raw) To: Nick Piggin Cc: Peter Klotz, stable, Linux Memory Management List, Christoph Hellwig, Roman Kononov, linux-kernel, xfs, Andrew Morton On Mon, 5 Jan 2009, Nick Piggin wrote: > On Mon, Jan 05, 2009 at 09:30:55AM -0800, Linus Torvalds wrote: > > > > Not only is it ugly (which is already sufficient ground to suspect it is > > wrong or could at least be done better), but reading the comment, it makes > > no sense at all. You only put the barrier in the "goto repeat" case, but > > the thing is, if you worry about radix tree slot not being reloaded in the > > repeat case, then you damn well should worry about it not being reloaded > > in the non-repeat case too! > > In which case atomic_inc_unless is defined to provide a barrier. Hmm. Ok, granted. > > If you use RCU to protect a data structure, then any data loaded from that > > data structure that can change due to RCU should be loaded with > > "rcu_dereference()". > > It doesn't need that because the last level pointers in the radix > tree are not necessarily under RCU, but whatever synchronisation > the caller uses (in this case, speculative page references, which > should not require smp_read_barrier_depends, AFAIKS). rcu_dereference() does more than that smp_read_barrier_depends() (which is a no-op on all sane architectures). The important part of rcu_dereference() is the ACCESS_ONCE() part. That's the one that guarantees the access to happen - exactly once. > Putting an rcu_dereference there might work, but I think it misses a > subtlety of this code. No, _you_ miss the subtlety of something that can change under you. Look at radix_tree_deref_slot(), and realize that without the rcu_dereference(), the compiler would actually be allowed to think that it can re-load anything from *pslot several times. So without my one-liner patch, the compiler can actually do this: register = load_from_memory(pslot) if (radix_tree_is_indirect_ptr(register)) goto fail: return load_from_memory(pslot); fail: return RADIX_TREE_RETRY; see? Imagine if you are low on registers (x86, anyone?) and look at that radix_tree_is_indirect_ptr() test: it does a logical "and" which can be done with a memory instruction on x86. So the compiler could _literally_ compile this as testb $1,(%eax) ; %eax is "pslot" jne indirect_pointer movl (%eax),%eax ; now we load it for real rather than movl (%eax),%eax testl $1,%eax jne indirect_pointer because the first version actually keeps more registers live for the indirect case. In fact, the compiler might be delaying that "movl" until much later (depending on barriers and needs). And notice how that "now we load it for real" may be getting a new value - including a possible indirect pointer value, even though we tested that it wasn't an indirect pointer! And THIS is why code that depends on RCU needs to use "rcu_dereference()". Because otherwise you may be testing one thing, and then later using some _other_ value than the one you tested. You must guarantee that you really just load it once, and that the compiler doesn't decide that it can load it multiple times, and test the multiple (possibly different) values using different logic. > > Of course, it's also possible that we should just put a barrier in > > page_cache_get_speculative(). That doesn't seem to make a whole lot of > > conceptual sense, though (the same way that your barrier() didn't make any > > sense - I don't see that the barrier has absolutely _anything_ to do with > > whether the speculative getting of the page fails or not!) > > When that fails, the caller can (almost) assume the pointer has changed. Not relevant. Yes, when it fails, the caller can obviously assume that the pointer has almost certainly changed, but that's neither here nor there - if the page_cache_get_speculative() fails, you mustn't use that pointer *whether* it has changed or not. So there's no point in even testing, and the code obviously doesn't. > So it has to load the new pointer to continue. The object pointed to is > not protected with RCU, nor is there a requirement to see a specific > load execution ordering. Either the value can change, or it can not. It's that simple. If it cannot change, then we can load it just once, or we can load it multiple times, and it won't matter. Barriers won't do anything but screw up the code. If it can change from under us, you need to use rcu_dereference(), or open-code it with an ACCESS_ONCE() or put in barriers. But your placement of a barrier was NONSENSICAL. Your barrier didn't protect anything else - like the test for the RADIX_TREE_INDIRECT_PTR bit. And that was the fundamental problem. And once you fix that fundamental problem, your barrier no longer makes any sense, because the barrier HAS NOTHING TO DO WITH WHETHER page_cache_get_speculative() fails or not! Linus -- To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to majordomo@kvack.org. For more info on Linux MM, see: http://www.linux-mm.org/ . Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a> ^ permalink raw reply [flat|nested] 17+ messages in thread
* Re: [patch] mm: fix lockless pagecache reordering bug (was Re: BUG: soft lockup - is this XFS problem?) 2009-01-05 18:44 ` Linus Torvalds @ 2009-01-05 19:39 ` Linus Torvalds 2009-01-06 17:17 ` Paul E. McKenney 2009-01-05 20:12 ` Paul E. McKenney 1 sibling, 1 reply; 17+ messages in thread From: Linus Torvalds @ 2009-01-05 19:39 UTC (permalink / raw) To: Nick Piggin Cc: Peter Klotz, stable, Linux Memory Management List, Christoph Hellwig, Roman Kononov, linux-kernel, xfs, Andrew Morton On Mon, 5 Jan 2009, Linus Torvalds wrote: > > Either the value can change, or it can not. It's that simple. > > If it cannot change, then we can load it just once, or we can load it > multiple times, and it won't matter. Barriers won't do anything but screw > up the code. > > If it can change from under us, you need to use rcu_dereference(), or > open-code it with an ACCESS_ONCE() or put in barriers. But your placement > of a barrier was NONSENSICAL. Your barrier didn't protect anything else - > like the test for the RADIX_TREE_INDIRECT_PTR bit. > > And that was the fundamental problem. Btw, this is the real issue with anything that does "locking vs optimistic" accesses. If you use locking, then by definition (if you did things right), the values you are working with do not change. As a result, it doesn't matter if the compiler re-orders accesses, splits them up, or coalesces them. It's why normal code should never need barriers, because it doesn't matter whether some access gets optimized away or gets done multiple times. But whenever you use an optimistic algorithm, and the data may change under you, you need to use barriers or other things to limit the things the CPU and/or compiler does. And yes, "rcu_dereference()" is one such thing - it's not a barrier in the sense that it doesn't necessarily affect ordering of accesses to other variables around it (although the read_barrier_depends() obviously _is_ a very special kind of ordering wrt the pointer itself on alpha). But it does make sure that the compiler at least does not coalesce - or split - that _one_ particular access. It's true that it has "rcu" in its name, and it's also true that that may be a bit misleading in that it's very much useful not just for rcu, but for _any_ algorithm that depends on rcu-like behavior - ie optimistic accesses to data that may change underneath it. RCU is just the most commonly used (and perhaps best codified) variant of that kind of code. Linus -- To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to majordomo@kvack.org. For more info on Linux MM, see: http://www.linux-mm.org/ . Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a> ^ permalink raw reply [flat|nested] 17+ messages in thread
* Re: [patch] mm: fix lockless pagecache reordering bug (was Re: BUG: soft lockup - is this XFS problem?) 2009-01-05 19:39 ` Linus Torvalds @ 2009-01-06 17:17 ` Paul E. McKenney 0 siblings, 0 replies; 17+ messages in thread From: Paul E. McKenney @ 2009-01-06 17:17 UTC (permalink / raw) To: Linus Torvalds Cc: Nick Piggin, Peter Klotz, stable, Linux Memory Management List, Christoph Hellwig, Roman Kononov, linux-kernel, xfs, Andrew Morton On Mon, Jan 05, 2009 at 11:39:29AM -0800, Linus Torvalds wrote: > On Mon, 5 Jan 2009, Linus Torvalds wrote: > > Either the value can change, or it can not. It's that simple. > > > > If it cannot change, then we can load it just once, or we can load it > > multiple times, and it won't matter. Barriers won't do anything but screw > > up the code. > > > > If it can change from under us, you need to use rcu_dereference(), or > > open-code it with an ACCESS_ONCE() or put in barriers. But your placement > > of a barrier was NONSENSICAL. Your barrier didn't protect anything else - > > like the test for the RADIX_TREE_INDIRECT_PTR bit. > > > > And that was the fundamental problem. > > Btw, this is the real issue with anything that does "locking vs > optimistic" accesses. > > If you use locking, then by definition (if you did things right), the > values you are working with do not change. As a result, it doesn't matter > if the compiler re-orders accesses, splits them up, or coalesces them. > It's why normal code should never need barriers, because it doesn't matter > whether some access gets optimized away or gets done multiple times. > > But whenever you use an optimistic algorithm, and the data may change > under you, you need to use barriers or other things to limit the things > the CPU and/or compiler does. > > And yes, "rcu_dereference()" is one such thing - it's not a barrier in the > sense that it doesn't necessarily affect ordering of accesses to other > variables around it (although the read_barrier_depends() obviously _is_ a > very special kind of ordering wrt the pointer itself on alpha). But it > does make sure that the compiler at least does not coalesce - or split - > that _one_ particular access. > > It's true that it has "rcu" in its name, and it's also true that that may > be a bit misleading in that it's very much useful not just for rcu, but > for _any_ algorithm that depends on rcu-like behavior - ie optimistic > accesses to data that may change underneath it. RCU is just the most > commonly used (and perhaps best codified) variant of that kind of code. The codification is quite important -- otherwise RCU would be a knife without a handle. And some would no doubt argue that RCU is -still- a knife without a handle, but so it goes. It does still need more work. And I hope that additional codification of other optimistic concurrency algorithms will make them more usable as well. Thanx, Paul -- To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to majordomo@kvack.org. For more info on Linux MM, see: http://www.linux-mm.org/ . Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a> ^ permalink raw reply [flat|nested] 17+ messages in thread
* Re: [patch] mm: fix lockless pagecache reordering bug (was Re: BUG: soft lockup - is this XFS problem?) 2009-01-05 18:44 ` Linus Torvalds 2009-01-05 19:39 ` Linus Torvalds @ 2009-01-05 20:12 ` Paul E. McKenney 2009-01-05 20:39 ` Linus Torvalds 2009-01-05 21:04 ` [patch] mm: fix lockless pagecache reordering bug (was Peter Zijlstra 1 sibling, 2 replies; 17+ messages in thread From: Paul E. McKenney @ 2009-01-05 20:12 UTC (permalink / raw) To: Linus Torvalds Cc: Nick Piggin, Peter Klotz, stable, Linux Memory Management List, Christoph Hellwig, Roman Kononov, linux-kernel, xfs, Andrew Morton On Mon, Jan 05, 2009 at 10:44:27AM -0800, Linus Torvalds wrote: > On Mon, 5 Jan 2009, Nick Piggin wrote: > > On Mon, Jan 05, 2009 at 09:30:55AM -0800, Linus Torvalds wrote: > > Putting an rcu_dereference there might work, but I think it misses a > > subtlety of this code. > > No, _you_ miss the subtlety of something that can change under you. > > Look at radix_tree_deref_slot(), and realize that without the > rcu_dereference(), the compiler would actually be allowed to think that it > can re-load anything from *pslot several times. So without my one-liner > patch, the compiler can actually do this: > > register = load_from_memory(pslot) > if (radix_tree_is_indirect_ptr(register)) > goto fail: > return load_from_memory(pslot); > > fail: > return RADIX_TREE_RETRY; My guess is that Nick believes that the value in *pslot cannot change in such as way as to cause radix_tree_is_indirect_ptr()'s return value to change within a given RCU grace period, and that Linus disagrees. Whatever the answer, I would argue for -at- -least- a comment explaining why it is safe. I am not seeing the objection to rcu_dereference(), but I must confess that it has been awhile since I have looked closely at the radix_tree code. :-/ Thanx, Paul -- To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to majordomo@kvack.org. For more info on Linux MM, see: http://www.linux-mm.org/ . Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a> ^ permalink raw reply [flat|nested] 17+ messages in thread
* Re: [patch] mm: fix lockless pagecache reordering bug (was Re: BUG: soft lockup - is this XFS problem?) 2009-01-05 20:12 ` Paul E. McKenney @ 2009-01-05 20:39 ` Linus Torvalds 2009-01-05 21:57 ` Paul E. McKenney 2009-01-05 21:04 ` [patch] mm: fix lockless pagecache reordering bug (was Peter Zijlstra 1 sibling, 1 reply; 17+ messages in thread From: Linus Torvalds @ 2009-01-05 20:39 UTC (permalink / raw) To: Paul E. McKenney Cc: Nick Piggin, Peter Klotz, stable, Linux Memory Management List, Christoph Hellwig, Roman Kononov, linux-kernel, xfs, Andrew Morton On Mon, 5 Jan 2009, Paul E. McKenney wrote: > > My guess is that Nick believes that the value in *pslot cannot change > in such as way as to cause radix_tree_is_indirect_ptr()'s return value > to change within a given RCU grace period, and that Linus disagrees. Oh, it's entirely possible that there are some lifetime rules or others that make it impossible for things to go from "not indirect" -> "indirect". So if that was Nick's point, then I'm not "disagreeing" per se. What I'm disagreeing about is that Nick apparently thinks that this is all subtle code, and as a result we should add barriers in some very non-obvious places. While _I_ think that the problem isn't properly solved by barriers, but by just making the code less subtle. If the barrier only exists because of the reload issue, then the obvious solution - to me - is to just use what is already the proper accessor function that forces a nice reload. That way the compiler is forced to create code that does what the source clearly means it to do, regardless of any barriers at all. Barriers in general should be the _last_ thing added. And if they are added, they should be added as deeply in the call-chain as possible, so that we don't need to add them in multiple call-sites. Again, using the rcu_dereference() approach seems to solve that issue too - rather than add three barriers in three different places, we just add the proper dereference in _one_ place. > Whatever the answer, I would argue for -at- -least- a comment explaining > why it is safe. I am not seeing the objection to rcu_dereference(), but > I must confess that it has been awhile since I have looked closely at > the radix_tree code. :-/ And I'm actually suprised that gcc can generate the problematic code in the first place. I'd expect that a "atomic_add_unless()" would always be at LEAST a compiler barrier, even if it isn't necessarily a CPU memory barrier. But because we inline it, and because we allow gcc to see that it doesn't do anything if it gets just the right value from memory, I guess gcc ends up able to change the "for()" loop so that the first iteration can exit specially, and then for that case (and no other case) it can cache variables over the whole atomic_add_unless(). Again, that's very fragile. The fact that Documentation/atomic_ops.txt says that the failure case doesn't contain any barriers is really _meant_ to be about the architecture-specific CPU barriers, not so much about something as simple as a compiler re-ordering. So while I think that we should use rcu_dereference() (regardless of any other issues), I _also_ think that part of the problem really is the excessive subtlety in the whole code, and the (obviously very surprising) fact that gcc could end up caching an unrelated memory load across that whole atomic op. Maybe we should make atomics always imply a compiler barrier, even when they do not imply a memory barrier. The one exception would be the (special) case of "atomic_read()/atomic_set()", which don't really do any kind of complex operation at all, and where we really do want the compiler to be able to coalesce multiple atomic_reads() to a single one. In contrast, there's no sense in allowing the compiler to coalesce a "atomic_add_unless()" with anything else. Making it a compiler barrier (possibly by uninlining it, or just adding a barrier to it) would also have avoided the whole subtle case - which is always a good thing. Linus -- To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to majordomo@kvack.org. For more info on Linux MM, see: http://www.linux-mm.org/ . Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a> ^ permalink raw reply [flat|nested] 17+ messages in thread
* Re: [patch] mm: fix lockless pagecache reordering bug (was Re: BUG: soft lockup - is this XFS problem?) 2009-01-05 20:39 ` Linus Torvalds @ 2009-01-05 21:57 ` Paul E. McKenney 2009-01-06 2:05 ` Nick Piggin 0 siblings, 1 reply; 17+ messages in thread From: Paul E. McKenney @ 2009-01-05 21:57 UTC (permalink / raw) To: Linus Torvalds Cc: Nick Piggin, Peter Klotz, stable, Linux Memory Management List, Christoph Hellwig, Roman Kononov, linux-kernel, xfs, Andrew Morton On Mon, Jan 05, 2009 at 12:39:14PM -0800, Linus Torvalds wrote: > > > On Mon, 5 Jan 2009, Paul E. McKenney wrote: > > > > My guess is that Nick believes that the value in *pslot cannot change > > in such as way as to cause radix_tree_is_indirect_ptr()'s return value > > to change within a given RCU grace period, and that Linus disagrees. > > Oh, it's entirely possible that there are some lifetime rules or others > that make it impossible for things to go from "not indirect" -> > "indirect". So if that was Nick's point, then I'm not "disagreeing" per > se. > > What I'm disagreeing about is that Nick apparently thinks that this is all > subtle code, and as a result we should add barriers in some very > non-obvious places. > > While _I_ think that the problem isn't properly solved by barriers, but by > just making the code less subtle. If the barrier only exists because of > the reload issue, then the obvious solution - to me - is to just use what > is already the proper accessor function that forces a nice reload. That > way the compiler is forced to create code that does what the source > clearly means it to do, regardless of any barriers at all. > > Barriers in general should be the _last_ thing added. And if they are > added, they should be added as deeply in the call-chain as possible, so > that we don't need to add them in multiple call-sites. Again, using the > rcu_dereference() approach seems to solve that issue too - rather than add > three barriers in three different places, we just add the proper > dereference in _one_ place. I don't have any argument with this line of reasoning, and am myself a bit puzzled as to why rcu_dereference() isn't the right tool for Nick's job. Then again, I don't claim to fully understand what he is trying to do. > > Whatever the answer, I would argue for -at- -least- a comment explaining > > why it is safe. I am not seeing the objection to rcu_dereference(), but > > I must confess that it has been awhile since I have looked closely at > > the radix_tree code. :-/ > > And I'm actually suprised that gcc can generate the problematic code in > the first place. I'd expect that a "atomic_add_unless()" would always be > at LEAST a compiler barrier, even if it isn't necessarily a CPU memory > barrier. > > But because we inline it, and because we allow gcc to see that it doesn't > do anything if it gets just the right value from memory, I guess gcc ends > up able to change the "for()" loop so that the first iteration can exit > specially, and then for that case (and no other case) it can cache > variables over the whole atomic_add_unless(). > > Again, that's very fragile. The fact that Documentation/atomic_ops.txt > says that the failure case doesn't contain any barriers is really _meant_ > to be about the architecture-specific CPU barriers, not so much about > something as simple as a compiler re-ordering. > > So while I think that we should use rcu_dereference() (regardless of any > other issues), I _also_ think that part of the problem really is the > excessive subtlety in the whole code, and the (obviously very surprising) > fact that gcc could end up caching an unrelated memory load across that > whole atomic op. > > Maybe we should make atomics always imply a compiler barrier, even when > they do not imply a memory barrier. The one exception would be the > (special) case of "atomic_read()/atomic_set()", which don't really do any > kind of complex operation at all, and where we really do want the compiler > to be able to coalesce multiple atomic_reads() to a single one. > > In contrast, there's no sense in allowing the compiler to coalesce a > "atomic_add_unless()" with anything else. Making it a compiler barrier > (possibly by uninlining it, or just adding a barrier to it) would also > have avoided the whole subtle case - which is always a good thing. That makes a lot of sense to me! Thanx, Paul -- To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to majordomo@kvack.org. For more info on Linux MM, see: http://www.linux-mm.org/ . Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a> ^ permalink raw reply [flat|nested] 17+ messages in thread
* Re: [patch] mm: fix lockless pagecache reordering bug (was Re: BUG: soft lockup - is this XFS problem?) 2009-01-05 21:57 ` Paul E. McKenney @ 2009-01-06 2:05 ` Nick Piggin 2009-01-06 2:23 ` Paul E. McKenney ` (3 more replies) 0 siblings, 4 replies; 17+ messages in thread From: Nick Piggin @ 2009-01-06 2:05 UTC (permalink / raw) To: Paul E. McKenney Cc: Linus Torvalds, Peter Klotz, stable, Linux Memory Management List, Christoph Hellwig, Roman Kononov, linux-kernel, xfs, Andrew Morton On Mon, Jan 05, 2009 at 01:57:27PM -0800, Paul E. McKenney wrote: > On Mon, Jan 05, 2009 at 12:39:14PM -0800, Linus Torvalds wrote: > > > > > > On Mon, 5 Jan 2009, Paul E. McKenney wrote: > > > > > > My guess is that Nick believes that the value in *pslot cannot change > > > in such as way as to cause radix_tree_is_indirect_ptr()'s return value > > > to change within a given RCU grace period, and that Linus disagrees. > > > > Oh, it's entirely possible that there are some lifetime rules or others > > that make it impossible for things to go from "not indirect" -> > > "indirect". So if that was Nick's point, then I'm not "disagreeing" per > > se. > > > > What I'm disagreeing about is that Nick apparently thinks that this is all > > subtle code, and as a result we should add barriers in some very > > non-obvious places. > > > > While _I_ think that the problem isn't properly solved by barriers, but by > > just making the code less subtle. If the barrier only exists because of > > the reload issue, then the obvious solution - to me - is to just use what > > is already the proper accessor function that forces a nice reload. That > > way the compiler is forced to create code that does what the source > > clearly means it to do, regardless of any barriers at all. > > > > Barriers in general should be the _last_ thing added. And if they are > > added, they should be added as deeply in the call-chain as possible, so > > that we don't need to add them in multiple call-sites. Again, using the > > rcu_dereference() approach seems to solve that issue too - rather than add > > three barriers in three different places, we just add the proper > > dereference in _one_ place. > > I don't have any argument with this line of reasoning, and am myself a bit > puzzled as to why rcu_dereference() isn't the right tool for Nick's job. > Then again, I don't claim to fully understand what he is trying to do. OK, granted I do need the ACCESS_ONCE. It is loading a pointer who's target can be changed concurrently with the rcu algorithm. The rcu_derefernce thing kind of set me thinking down the wrong track, because the object of the pointer it loads is not RCU protected and doesn't need the memory barrier (on alpha). But... RCU radix tree is not only used for the pagecache, so it's probably not worth complicating things to seperate out those two cases. rcu_dereference might be the best fit. > > > Whatever the answer, I would argue for -at- -least- a comment explaining > > > why it is safe. I am not seeing the objection to rcu_dereference(), but > > > I must confess that it has been awhile since I have looked closely at > > > the radix_tree code. :-/ > > > > And I'm actually suprised that gcc can generate the problematic code in > > the first place. I'd expect that a "atomic_add_unless()" would always be > > at LEAST a compiler barrier, even if it isn't necessarily a CPU memory > > barrier. > > > > But because we inline it, and because we allow gcc to see that it doesn't > > do anything if it gets just the right value from memory, I guess gcc ends > > up able to change the "for()" loop so that the first iteration can exit > > specially, and then for that case (and no other case) it can cache > > variables over the whole atomic_add_unless(). > > > > Again, that's very fragile. The fact that Documentation/atomic_ops.txt > > says that the failure case doesn't contain any barriers is really _meant_ > > to be about the architecture-specific CPU barriers, not so much about > > something as simple as a compiler re-ordering. > > > > So while I think that we should use rcu_dereference() (regardless of any > > other issues), I _also_ think that part of the problem really is the > > excessive subtlety in the whole code, and the (obviously very surprising) > > fact that gcc could end up caching an unrelated memory load across that > > whole atomic op. > > > > Maybe we should make atomics always imply a compiler barrier, even when > > they do not imply a memory barrier. The one exception would be the > > (special) case of "atomic_read()/atomic_set()", which don't really do any > > kind of complex operation at all, and where we really do want the compiler > > to be able to coalesce multiple atomic_reads() to a single one. > > > > In contrast, there's no sense in allowing the compiler to coalesce a > > "atomic_add_unless()" with anything else. Making it a compiler barrier > > (possibly by uninlining it, or just adding a barrier to it) would also > > have avoided the whole subtle case - which is always a good thing. > > That makes a lot of sense to me! It would have avoided one problem (the same one my patch did). But it doesn't solve the problem of the missing ACCESS_ONCE allowing the pointer to be reloaded from the slot pointer. Sticking an rcu_dereference in radix_tree_deref_slot seems to fix the assembly for me too, I grafted the changelog onto that. Linus probably you are using -Os? -- Subject: mm lockless pagecache barrier fix An XFS workload showed up a bug in the lockless pagecache patch. Basically it would go into an "infinite" loop, although it would sometimes be able to break out of the loop! The reason is a missing compiler barrier in the "increment reference count unless it was zero" case of the lockless pagecache protocol in the gang lookup functions. This would cause the compiler to use a cached value of struct page pointer to retry the operation with, rather than reload it. So the page might have been removed from pagecache and freed (refcount==0) but the lookup would not correctly notice the page is no longer in pagecache, and keep attempting to increment the refcount and failing, until the page gets reallocated for something else. This isn't a data corruption because the condition will be detected if the page has been reallocated. However it can result in a lockup. Linus points out that ACCESS_ONCE is also required in that pointer load, even if it's absence is not causing a bug on our particular build. The most general way to solve this is just to put an rcu_dereference in radix_tree_deref_slot. Assembly of find_get_pages, before: .L220: movq (%rbx), %rax #* ivtmp.1162, tmp82 movq (%rax), %rdi #, prephitmp.1149 .L218: testb $1, %dil #, prephitmp.1149 jne .L217 #, testq %rdi, %rdi # prephitmp.1149 je .L203 #, cmpq $-1, %rdi #, prephitmp.1149 je .L217 #, movl 8(%rdi), %esi # <variable>._count.counter, c testl %esi, %esi # c je .L218 #, after: .L212: movq (%rbx), %rax #* ivtmp.1109, tmp81 movq (%rax), %rdi #, ret testb $1, %dil #, ret jne .L211 #, testq %rdi, %rdi # ret je .L197 #, cmpq $-1, %rdi #, ret je .L211 #, movl 8(%rdi), %esi # <variable>._count.counter, c testl %esi, %esi # c je .L212 #, (notice the obvious infinite loop in the first example, if page->count remains 0) Signed-off-by: Nick Piggin <npiggin@suse.de> --- include/linux/radix-tree.h | 2 +- mm/filemap.c | 23 ++++++++++++++++++++--- 2 files changed, 21 insertions(+), 4 deletions(-) Index: linux-2.6/include/linux/radix-tree.h =================================================================== --- linux-2.6.orig/include/linux/radix-tree.h +++ linux-2.6/include/linux/radix-tree.h @@ -136,7 +136,7 @@ do { \ */ static inline void *radix_tree_deref_slot(void **pslot) { - void *ret = *pslot; + void *ret = rcu_dereference(*pslot); if (unlikely(radix_tree_is_indirect_ptr(ret))) ret = RADIX_TREE_RETRY; return ret; -- To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to majordomo@kvack.org. For more info on Linux MM, see: http://www.linux-mm.org/ . Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a> ^ permalink raw reply [flat|nested] 17+ messages in thread
* Re: [patch] mm: fix lockless pagecache reordering bug (was Re: BUG: soft lockup - is this XFS problem?) 2009-01-06 2:05 ` Nick Piggin @ 2009-01-06 2:23 ` Paul E. McKenney 2009-01-06 2:29 ` Linus Torvalds ` (2 subsequent siblings) 3 siblings, 0 replies; 17+ messages in thread From: Paul E. McKenney @ 2009-01-06 2:23 UTC (permalink / raw) To: Nick Piggin Cc: Linus Torvalds, Peter Klotz, stable, Linux Memory Management List, Christoph Hellwig, Roman Kononov, linux-kernel, xfs, Andrew Morton On Tue, Jan 06, 2009 at 03:05:50AM +0100, Nick Piggin wrote: > On Mon, Jan 05, 2009 at 01:57:27PM -0800, Paul E. McKenney wrote: > > On Mon, Jan 05, 2009 at 12:39:14PM -0800, Linus Torvalds wrote: > > > > > > > > > On Mon, 5 Jan 2009, Paul E. McKenney wrote: > > > > > > > > My guess is that Nick believes that the value in *pslot cannot change > > > > in such as way as to cause radix_tree_is_indirect_ptr()'s return value > > > > to change within a given RCU grace period, and that Linus disagrees. > > > > > > Oh, it's entirely possible that there are some lifetime rules or others > > > that make it impossible for things to go from "not indirect" -> > > > "indirect". So if that was Nick's point, then I'm not "disagreeing" per > > > se. > > > > > > What I'm disagreeing about is that Nick apparently thinks that this is all > > > subtle code, and as a result we should add barriers in some very > > > non-obvious places. > > > > > > While _I_ think that the problem isn't properly solved by barriers, but by > > > just making the code less subtle. If the barrier only exists because of > > > the reload issue, then the obvious solution - to me - is to just use what > > > is already the proper accessor function that forces a nice reload. That > > > way the compiler is forced to create code that does what the source > > > clearly means it to do, regardless of any barriers at all. > > > > > > Barriers in general should be the _last_ thing added. And if they are > > > added, they should be added as deeply in the call-chain as possible, so > > > that we don't need to add them in multiple call-sites. Again, using the > > > rcu_dereference() approach seems to solve that issue too - rather than add > > > three barriers in three different places, we just add the proper > > > dereference in _one_ place. > > > > I don't have any argument with this line of reasoning, and am myself a bit > > puzzled as to why rcu_dereference() isn't the right tool for Nick's job. > > Then again, I don't claim to fully understand what he is trying to do. > > OK, granted I do need the ACCESS_ONCE. It is loading a pointer who's target > can be changed concurrently with the rcu algorithm. The rcu_derefernce > thing kind of set me thinking down the wrong track, because the object of the > pointer it loads is not RCU protected and doesn't need the memory barrier > (on alpha). > > But... RCU radix tree is not only used for the pagecache, so it's probably not > worth complicating things to seperate out those two cases. rcu_dereference > might be the best fit. Works for me! > > > > Whatever the answer, I would argue for -at- -least- a comment explaining > > > > why it is safe. I am not seeing the objection to rcu_dereference(), but > > > > I must confess that it has been awhile since I have looked closely at > > > > the radix_tree code. :-/ > > > > > > And I'm actually suprised that gcc can generate the problematic code in > > > the first place. I'd expect that a "atomic_add_unless()" would always be > > > at LEAST a compiler barrier, even if it isn't necessarily a CPU memory > > > barrier. > > > > > > But because we inline it, and because we allow gcc to see that it doesn't > > > do anything if it gets just the right value from memory, I guess gcc ends > > > up able to change the "for()" loop so that the first iteration can exit > > > specially, and then for that case (and no other case) it can cache > > > variables over the whole atomic_add_unless(). > > > > > > Again, that's very fragile. The fact that Documentation/atomic_ops.txt > > > says that the failure case doesn't contain any barriers is really _meant_ > > > to be about the architecture-specific CPU barriers, not so much about > > > something as simple as a compiler re-ordering. > > > > > > So while I think that we should use rcu_dereference() (regardless of any > > > other issues), I _also_ think that part of the problem really is the > > > excessive subtlety in the whole code, and the (obviously very surprising) > > > fact that gcc could end up caching an unrelated memory load across that > > > whole atomic op. > > > > > > Maybe we should make atomics always imply a compiler barrier, even when > > > they do not imply a memory barrier. The one exception would be the > > > (special) case of "atomic_read()/atomic_set()", which don't really do any > > > kind of complex operation at all, and where we really do want the compiler > > > to be able to coalesce multiple atomic_reads() to a single one. > > > > > > In contrast, there's no sense in allowing the compiler to coalesce a > > > "atomic_add_unless()" with anything else. Making it a compiler barrier > > > (possibly by uninlining it, or just adding a barrier to it) would also > > > have avoided the whole subtle case - which is always a good thing. > > > > That makes a lot of sense to me! > > It would have avoided one problem (the same one my patch did). But it > doesn't solve the problem of the missing ACCESS_ONCE allowing the > pointer to be reloaded from the slot pointer. Agreed. > Sticking an rcu_dereference in radix_tree_deref_slot seems to fix the > assembly for me too, I grafted the changelog onto that. Linus probably > you are using -Os? > > -- > Subject: mm lockless pagecache barrier fix > > An XFS workload showed up a bug in the lockless pagecache patch. Basically it > would go into an "infinite" loop, although it would sometimes be able to break > out of the loop! The reason is a missing compiler barrier in the "increment > reference count unless it was zero" case of the lockless pagecache protocol in > the gang lookup functions. > > This would cause the compiler to use a cached value of struct page pointer to > retry the operation with, rather than reload it. So the page might have been > removed from pagecache and freed (refcount==0) but the lookup would not correctly > notice the page is no longer in pagecache, and keep attempting to increment the > refcount and failing, until the page gets reallocated for something else. This > isn't a data corruption because the condition will be detected if the page has > been reallocated. However it can result in a lockup. > > Linus points out that ACCESS_ONCE is also required in that pointer load, even > if it's absence is not causing a bug on our particular build. The most general > way to solve this is just to put an rcu_dereference in radix_tree_deref_slot. > > Assembly of find_get_pages, > before: > .L220: > movq (%rbx), %rax #* ivtmp.1162, tmp82 > movq (%rax), %rdi #, prephitmp.1149 > .L218: > testb $1, %dil #, prephitmp.1149 > jne .L217 #, > testq %rdi, %rdi # prephitmp.1149 > je .L203 #, > cmpq $-1, %rdi #, prephitmp.1149 > je .L217 #, > movl 8(%rdi), %esi # <variable>._count.counter, c > testl %esi, %esi # c > je .L218 #, > > after: > .L212: > movq (%rbx), %rax #* ivtmp.1109, tmp81 > movq (%rax), %rdi #, ret > testb $1, %dil #, ret > jne .L211 #, > testq %rdi, %rdi # ret > je .L197 #, > cmpq $-1, %rdi #, ret > je .L211 #, > movl 8(%rdi), %esi # <variable>._count.counter, c > testl %esi, %esi # c > je .L212 #, > > (notice the obvious infinite loop in the first example, if page->count remains 0) Reviewed-by: Paul E. McKenney <paulmck@linux.vnet.ibm.com> > Signed-off-by: Nick Piggin <npiggin@suse.de> > --- > include/linux/radix-tree.h | 2 +- > mm/filemap.c | 23 ++++++++++++++++++++--- > 2 files changed, 21 insertions(+), 4 deletions(-) > > Index: linux-2.6/include/linux/radix-tree.h > =================================================================== > --- linux-2.6.orig/include/linux/radix-tree.h > +++ linux-2.6/include/linux/radix-tree.h > @@ -136,7 +136,7 @@ do { \ > */ > static inline void *radix_tree_deref_slot(void **pslot) > { > - void *ret = *pslot; > + void *ret = rcu_dereference(*pslot); > if (unlikely(radix_tree_is_indirect_ptr(ret))) > ret = RADIX_TREE_RETRY; > return ret; > -- To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to majordomo@kvack.org. For more info on Linux MM, see: http://www.linux-mm.org/ . Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a> ^ permalink raw reply [flat|nested] 17+ messages in thread
* Re: [patch] mm: fix lockless pagecache reordering bug (was Re: BUG: soft lockup - is this XFS problem?) 2009-01-06 2:05 ` Nick Piggin 2009-01-06 2:23 ` Paul E. McKenney @ 2009-01-06 2:29 ` Linus Torvalds 2009-01-06 8:38 ` Peter Klotz 2009-01-06 16:16 ` Roman Kononov 3 siblings, 0 replies; 17+ messages in thread From: Linus Torvalds @ 2009-01-06 2:29 UTC (permalink / raw) To: Nick Piggin Cc: Paul E. McKenney, Peter Klotz, stable, Linux Memory Management List, Christoph Hellwig, Roman Kononov, linux-kernel, xfs, Andrew Morton On Tue, 6 Jan 2009, Nick Piggin wrote: > > Sticking an rcu_dereference in radix_tree_deref_slot seems to fix the > assembly for me too, I grafted the changelog onto that. Linus probably > you are using -Os? Ahh, yes. I am. That explains why I can't see any difference. Linus -- To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to majordomo@kvack.org. For more info on Linux MM, see: http://www.linux-mm.org/ . Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a> ^ permalink raw reply [flat|nested] 17+ messages in thread
* Re: [patch] mm: fix lockless pagecache reordering bug (was Re: BUG: soft lockup - is this XFS problem?) 2009-01-06 2:05 ` Nick Piggin 2009-01-06 2:23 ` Paul E. McKenney 2009-01-06 2:29 ` Linus Torvalds @ 2009-01-06 8:38 ` Peter Klotz 2009-01-06 8:43 ` Nick Piggin 2009-01-06 16:16 ` Roman Kononov 3 siblings, 1 reply; 17+ messages in thread From: Peter Klotz @ 2009-01-06 8:38 UTC (permalink / raw) To: Nick Piggin Cc: Paul E. McKenney, Linus Torvalds, stable, Linux Memory Management List, Christoph Hellwig, Roman Kononov, linux-kernel, xfs, Andrew Morton Nick Piggin wrote: > -- > Subject: mm lockless pagecache barrier fix > > An XFS workload showed up a bug in the lockless pagecache patch. Basically it > would go into an "infinite" loop, although it would sometimes be able to break > out of the loop! The reason is a missing compiler barrier in the "increment > reference count unless it was zero" case of the lockless pagecache protocol in > the gang lookup functions. > > This would cause the compiler to use a cached value of struct page pointer to > retry the operation with, rather than reload it. So the page might have been > removed from pagecache and freed (refcount==0) but the lookup would not correctly > notice the page is no longer in pagecache, and keep attempting to increment the > refcount and failing, until the page gets reallocated for something else. This > isn't a data corruption because the condition will be detected if the page has > been reallocated. However it can result in a lockup. > > Linus points out that ACCESS_ONCE is also required in that pointer load, even > if it's absence is not causing a bug on our particular build. The most general > way to solve this is just to put an rcu_dereference in radix_tree_deref_slot. > > Assembly of find_get_pages, > before: > .L220: > movq (%rbx), %rax #* ivtmp.1162, tmp82 > movq (%rax), %rdi #, prephitmp.1149 > .L218: > testb $1, %dil #, prephitmp.1149 > jne .L217 #, > testq %rdi, %rdi # prephitmp.1149 > je .L203 #, > cmpq $-1, %rdi #, prephitmp.1149 > je .L217 #, > movl 8(%rdi), %esi # <variable>._count.counter, c > testl %esi, %esi # c > je .L218 #, > > after: > .L212: > movq (%rbx), %rax #* ivtmp.1109, tmp81 > movq (%rax), %rdi #, ret > testb $1, %dil #, ret > jne .L211 #, > testq %rdi, %rdi # ret > je .L197 #, > cmpq $-1, %rdi #, ret > je .L211 #, > movl 8(%rdi), %esi # <variable>._count.counter, c > testl %esi, %esi # c > je .L212 #, > > (notice the obvious infinite loop in the first example, if page->count remains 0) > > Signed-off-by: Nick Piggin <npiggin@suse.de> > --- > include/linux/radix-tree.h | 2 +- > mm/filemap.c | 23 ++++++++++++++++++++--- > 2 files changed, 21 insertions(+), 4 deletions(-) > > Index: linux-2.6/include/linux/radix-tree.h > =================================================================== > --- linux-2.6.orig/include/linux/radix-tree.h > +++ linux-2.6/include/linux/radix-tree.h > @@ -136,7 +136,7 @@ do { \ > */ > static inline void *radix_tree_deref_slot(void **pslot) > { > - void *ret = *pslot; > + void *ret = rcu_dereference(*pslot); > if (unlikely(radix_tree_is_indirect_ptr(ret))) > ret = RADIX_TREE_RETRY; > return ret; > > The patch above fixes my problem. I did two complete test runs that normally fail rather quickly. Regards, Peter. -- To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to majordomo@kvack.org. For more info on Linux MM, see: http://www.linux-mm.org/ . Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a> ^ permalink raw reply [flat|nested] 17+ messages in thread
* Re: [patch] mm: fix lockless pagecache reordering bug (was Re: BUG: soft lockup - is this XFS problem?) 2009-01-06 8:38 ` Peter Klotz @ 2009-01-06 8:43 ` Nick Piggin 0 siblings, 0 replies; 17+ messages in thread From: Nick Piggin @ 2009-01-06 8:43 UTC (permalink / raw) To: Peter Klotz Cc: Paul E. McKenney, Linus Torvalds, stable, Linux Memory Management List, Christoph Hellwig, Roman Kononov, linux-kernel, xfs, Andrew Morton On Tue, Jan 06, 2009 at 09:38:15AM +0100, Peter Klotz wrote: > >Index: linux-2.6/include/linux/radix-tree.h > >=================================================================== > >--- linux-2.6.orig/include/linux/radix-tree.h > >+++ linux-2.6/include/linux/radix-tree.h > >@@ -136,7 +136,7 @@ do { \ > > */ > > static inline void *radix_tree_deref_slot(void **pslot) > > { > >- void *ret = *pslot; > >+ void *ret = rcu_dereference(*pslot); > > if (unlikely(radix_tree_is_indirect_ptr(ret))) > > ret = RADIX_TREE_RETRY; > > return ret; > > > > > > The patch above fixes my problem. I did two complete test runs that > normally fail rather quickly. OK, thanks for reporting and testing. I think this patch is a candidate for -stable too. Thanks, Nick -- To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to majordomo@kvack.org. For more info on Linux MM, see: http://www.linux-mm.org/ . Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a> ^ permalink raw reply [flat|nested] 17+ messages in thread
* Re: [patch] mm: fix lockless pagecache reordering bug (was Re: BUG: soft lockup - is this XFS problem?) 2009-01-06 2:05 ` Nick Piggin ` (2 preceding siblings ...) 2009-01-06 8:38 ` Peter Klotz @ 2009-01-06 16:16 ` Roman Kononov 3 siblings, 0 replies; 17+ messages in thread From: Roman Kononov @ 2009-01-06 16:16 UTC (permalink / raw) To: Nick Piggin Cc: Paul E. McKenney, Linus Torvalds, Peter Klotz, stable, Linux Memory Management List, Christoph Hellwig, Roman Kononov, linux-kernel, xfs, Andrew Morton On 2009-01-05 20:05 Nick Piggin said the following: > Subject: mm lockless pagecache barrier fix > static inline void *radix_tree_deref_slot(void **pslot) > { > - void *ret = *pslot; > + void *ret = rcu_dereference(*pslot); > if (unlikely(radix_tree_is_indirect_ptr(ret))) > ret = RADIX_TREE_RETRY; > return ret; 3 systems are working fine for a few hours with the patch. They would fail within 20 minutes without it. Thanks. -- To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to majordomo@kvack.org. For more info on Linux MM, see: http://www.linux-mm.org/ . Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a> ^ permalink raw reply [flat|nested] 17+ messages in thread
* Re: [patch] mm: fix lockless pagecache reordering bug (was Re: 2009-01-05 20:12 ` Paul E. McKenney 2009-01-05 20:39 ` Linus Torvalds @ 2009-01-05 21:04 ` Peter Zijlstra 2009-01-05 21:58 ` Paul E. McKenney 1 sibling, 1 reply; 17+ messages in thread From: Peter Zijlstra @ 2009-01-05 21:04 UTC (permalink / raw) To: paulmck Cc: Linus Torvalds, Nick Piggin, Peter Klotz, stable, Linux Memory Management List, Christoph Hellwig, Roman Kononov, linux-kernel, xfs, Andrew Morton On Mon, 2009-01-05 at 12:12 -0800, Paul E. McKenney wrote: > On Mon, Jan 05, 2009 at 10:44:27AM -0800, Linus Torvalds wrote: > > On Mon, 5 Jan 2009, Nick Piggin wrote: > > > On Mon, Jan 05, 2009 at 09:30:55AM -0800, Linus Torvalds wrote: > > > Putting an rcu_dereference there might work, but I think it misses a > > > subtlety of this code. > > > > No, _you_ miss the subtlety of something that can change under you. > > > > Look at radix_tree_deref_slot(), and realize that without the > > rcu_dereference(), the compiler would actually be allowed to think that it > > can re-load anything from *pslot several times. So without my one-liner > > patch, the compiler can actually do this: > > > > register = load_from_memory(pslot) > > if (radix_tree_is_indirect_ptr(register)) > > goto fail: > > return load_from_memory(pslot); > > > > fail: > > return RADIX_TREE_RETRY; > > My guess is that Nick believes that the value in *pslot cannot change > in such as way as to cause radix_tree_is_indirect_ptr()'s return value > to change within a given RCU grace period, and that Linus disagrees. Nick's belief would indeed be true IFF all modifying ops including all uses of radix_tree_replace_slot() are serialized wrt. each other. However, since radix_tree_deref_slot() is the counterpart of radix_tree_replace_slot(), one would indeed expect rcu_dereference() therein, much like Linus suggests. While what Nick says is true, the lifetime management of the data objects is arranged externally from the radix tree -- I still think we need the rcu_dereference() even for that argument, as we want to support RCU lifetime management as well. -- To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to majordomo@kvack.org. For more info on Linux MM, see: http://www.linux-mm.org/ . Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a> ^ permalink raw reply [flat|nested] 17+ messages in thread
* Re: [patch] mm: fix lockless pagecache reordering bug (was Re: 2009-01-05 21:04 ` [patch] mm: fix lockless pagecache reordering bug (was Peter Zijlstra @ 2009-01-05 21:58 ` Paul E. McKenney 0 siblings, 0 replies; 17+ messages in thread From: Paul E. McKenney @ 2009-01-05 21:58 UTC (permalink / raw) To: Peter Zijlstra Cc: Linus Torvalds, Nick Piggin, Peter Klotz, stable, Linux Memory Management List, Christoph Hellwig, Roman Kononov, linux-kernel, xfs, Andrew Morton On Mon, Jan 05, 2009 at 10:04:51PM +0100, Peter Zijlstra wrote: > On Mon, 2009-01-05 at 12:12 -0800, Paul E. McKenney wrote: > > On Mon, Jan 05, 2009 at 10:44:27AM -0800, Linus Torvalds wrote: > > > On Mon, 5 Jan 2009, Nick Piggin wrote: > > > > On Mon, Jan 05, 2009 at 09:30:55AM -0800, Linus Torvalds wrote: > > > > Putting an rcu_dereference there might work, but I think it misses a > > > > subtlety of this code. > > > > > > No, _you_ miss the subtlety of something that can change under you. > > > > > > Look at radix_tree_deref_slot(), and realize that without the > > > rcu_dereference(), the compiler would actually be allowed to think that it > > > can re-load anything from *pslot several times. So without my one-liner > > > patch, the compiler can actually do this: > > > > > > register = load_from_memory(pslot) > > > if (radix_tree_is_indirect_ptr(register)) > > > goto fail: > > > return load_from_memory(pslot); > > > > > > fail: > > > return RADIX_TREE_RETRY; > > > > My guess is that Nick believes that the value in *pslot cannot change > > in such as way as to cause radix_tree_is_indirect_ptr()'s return value > > to change within a given RCU grace period, and that Linus disagrees. > > Nick's belief would indeed be true IFF all modifying ops including all > uses of radix_tree_replace_slot() are serialized wrt. each other. > > However, since radix_tree_deref_slot() is the counterpart of > radix_tree_replace_slot(), one would indeed expect rcu_dereference() > therein, much like Linus suggests. > > While what Nick says is true, the lifetime management of the data > objects is arranged externally from the radix tree -- I still think we > need the rcu_dereference() even for that argument, as we want to support > RCU lifetime management as well. Makes sense to me! Thanx, Paul -- To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to majordomo@kvack.org. For more info on Linux MM, see: http://www.linux-mm.org/ . Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a> ^ permalink raw reply [flat|nested] 17+ messages in thread
end of thread, other threads:[~2009-01-06 17:17 UTC | newest]
Thread overview: 17+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
[not found] <gifgp1$8ic$1@ger.gmane.org>
[not found] ` <20081223171259.GA11945@infradead.org>
[not found] ` <20081230042333.GC27679@wotan.suse.de>
[not found] ` <20090103214443.GA6612@infradead.org>
[not found] ` <20090105014821.GA367@wotan.suse.de>
[not found] ` <20090105041959.GC367@wotan.suse.de>
[not found] ` <20090105064838.GA5209@wotan.suse.de>
[not found] ` <49623384.2070801@aon.at>
2009-01-05 16:41 ` [patch] mm: fix lockless pagecache reordering bug (was Re: BUG: soft lockup - is this XFS problem?) Nick Piggin
2009-01-05 17:30 ` Linus Torvalds
2009-01-05 18:00 ` Nick Piggin
2009-01-05 18:44 ` Linus Torvalds
2009-01-05 19:39 ` Linus Torvalds
2009-01-06 17:17 ` Paul E. McKenney
2009-01-05 20:12 ` Paul E. McKenney
2009-01-05 20:39 ` Linus Torvalds
2009-01-05 21:57 ` Paul E. McKenney
2009-01-06 2:05 ` Nick Piggin
2009-01-06 2:23 ` Paul E. McKenney
2009-01-06 2:29 ` Linus Torvalds
2009-01-06 8:38 ` Peter Klotz
2009-01-06 8:43 ` Nick Piggin
2009-01-06 16:16 ` Roman Kononov
2009-01-05 21:04 ` [patch] mm: fix lockless pagecache reordering bug (was Peter Zijlstra
2009-01-05 21:58 ` Paul E. McKenney
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox; as well as URLs for NNTP newsgroup(s).