| From a85d9df1ea1d23682a0ed1e100e6965006595d06 Mon Sep 17 00:00:00 2001 |
| From: KOSAKI Motohiro <kosaki.motohiro@jp.fujitsu.com> |
| Date: Thu, 6 Feb 2014 12:04:24 -0800 |
| Subject: mm: __set_page_dirty_nobuffers() uses spin_lock_irqsave() instead of spin_lock_irq() |
| |
| From: KOSAKI Motohiro <kosaki.motohiro@jp.fujitsu.com> |
| |
| commit a85d9df1ea1d23682a0ed1e100e6965006595d06 upstream. |
| |
| During aio stress test, we observed the following lockdep warning. This |
| mean AIO+numa_balancing is currently deadlockable. |
| |
| The problem is, aio_migratepage disable interrupt, but |
| __set_page_dirty_nobuffers unintentionally enable it again. |
| |
| Generally, all helper function should use spin_lock_irqsave() instead of |
| spin_lock_irq() because they don't know caller at all. |
| |
| other info that might help us debug this: |
| Possible unsafe locking scenario: |
| |
| CPU0 |
| ---- |
| lock(&(&ctx->completion_lock)->rlock); |
| <Interrupt> |
| lock(&(&ctx->completion_lock)->rlock); |
| |
| *** DEADLOCK *** |
| |
| dump_stack+0x19/0x1b |
| print_usage_bug+0x1f7/0x208 |
| mark_lock+0x21d/0x2a0 |
| mark_held_locks+0xb9/0x140 |
| trace_hardirqs_on_caller+0x105/0x1d0 |
| trace_hardirqs_on+0xd/0x10 |
| _raw_spin_unlock_irq+0x2c/0x50 |
| __set_page_dirty_nobuffers+0x8c/0xf0 |
| migrate_page_copy+0x434/0x540 |
| aio_migratepage+0xb1/0x140 |
| move_to_new_page+0x7d/0x230 |
| migrate_pages+0x5e5/0x700 |
| migrate_misplaced_page+0xbc/0xf0 |
| do_numa_page+0x102/0x190 |
| handle_pte_fault+0x241/0x970 |
| handle_mm_fault+0x265/0x370 |
| __do_page_fault+0x172/0x5a0 |
| do_page_fault+0x1a/0x70 |
| page_fault+0x28/0x30 |
| |
| Signed-off-by: KOSAKI Motohiro <kosaki.motohiro@jp.fujitsu.com> |
| Cc: Larry Woodman <lwoodman@redhat.com> |
| Cc: Rik van Riel <riel@redhat.com> |
| Cc: Johannes Weiner <jweiner@redhat.com> |
| Acked-by: David Rientjes <rientjes@google.com> |
| Signed-off-by: Andrew Morton <akpm@linux-foundation.org> |
| Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org> |
| Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> |
| |
| --- |
| mm/page-writeback.c | 5 +++-- |
| 1 file changed, 3 insertions(+), 2 deletions(-) |
| |
| --- a/mm/page-writeback.c |
| +++ b/mm/page-writeback.c |
| @@ -1993,11 +1993,12 @@ int __set_page_dirty_nobuffers(struct pa |
| if (!TestSetPageDirty(page)) { |
| struct address_space *mapping = page_mapping(page); |
| struct address_space *mapping2; |
| + unsigned long flags; |
| |
| if (!mapping) |
| return 1; |
| |
| - spin_lock_irq(&mapping->tree_lock); |
| + spin_lock_irqsave(&mapping->tree_lock, flags); |
| mapping2 = page_mapping(page); |
| if (mapping2) { /* Race with truncate? */ |
| BUG_ON(mapping2 != mapping); |
| @@ -2006,7 +2007,7 @@ int __set_page_dirty_nobuffers(struct pa |
| radix_tree_tag_set(&mapping->page_tree, |
| page_index(page), PAGECACHE_TAG_DIRTY); |
| } |
| - spin_unlock_irq(&mapping->tree_lock); |
| + spin_unlock_irqrestore(&mapping->tree_lock, flags); |
| if (mapping->host) { |
| /* !PageAnon && !swapper_space */ |
| __mark_inode_dirty(mapping->host, I_DIRTY_PAGES); |