linux-mm.kvack.org archive mirror
 help / color / mirror / Atom feed
From: Chuck Lever <chuck.lever@oracle.com>
To: linux-mm@kvack.org
Cc: chuck.lever@oracle.com
Subject: [PATCH 2/2] PAGECACHE: Page lock tracing clean up
Date: Fri, 16 Jan 2009 15:07:13 -0500	[thread overview]
Message-ID: <20090116200713.23026.80924.stgit@ingres.1015granger.net> (raw)
In-Reply-To: <20090116193424.23026.45385.stgit@ingres.1015granger.net>

Clean up page lock back tracing instrumentation, and introduce some
additional features: count locks and unlocks, and capture a timestamp
for each stack trace so we can tell which order the
lock_page/unlock_page occurred.

Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
---

 include/linux/mm_types.h |   19 +++++--
 include/linux/pagemap.h  |   16 +++---
 kernel/sched.c           |    4 +
 mm/filemap.c             |  123 ++++++++++++++++++++++++++++++++++++----------
 mm/hugetlb.c             |    2 -
 mm/page_alloc.c          |    2 -
 mm/slub.c                |    6 +-
 mm/truncate.c            |   39 ++++-----------
 8 files changed, 137 insertions(+), 74 deletions(-)

diff --git a/include/linux/mm_types.h b/include/linux/mm_types.h
index d68122e..548eb0f 100644
--- a/include/linux/mm_types.h
+++ b/include/linux/mm_types.h
@@ -12,6 +12,8 @@
 #include <linux/completion.h>
 #include <linux/cpumask.h>
 #include <linux/stacktrace.h>
+#include <linux/hrtimer.h>
+
 #include <asm/page.h>
 #include <asm/mmu.h>
 
@@ -30,6 +32,12 @@ typedef unsigned long mm_counter_t;
 
 #define PAGE_STACKTRACE_SIZE	(12UL)
 
+struct page_trace {
+	ktime_t timestamp;
+	struct stack_trace stacktrace;
+	unsigned long entries[PAGE_STACKTRACE_SIZE];
+};
+
 /*
  * Each physical page in the system has a struct page associated with
  * it to keep track of whatever it is we are using the page for at the
@@ -100,15 +108,14 @@ struct page {
 #endif
 
 	/* XXX: DEBUGGING */
-	struct list_head bt_list;
 
-	struct stack_trace lock_backtrace;
-	unsigned long lock_entries[PAGE_STACKTRACE_SIZE];
+	struct list_head iip2r_list;	/* how we find stuck pages */
 
-	struct stack_trace unlock_backtrace;
-	unsigned long unlock_entries[PAGE_STACKTRACE_SIZE];
+	struct page_trace lock_trace;
+	struct page_trace unlock_trace;
+	struct page_trace debug_trace;
 
-	unsigned int woken_task;
+	unsigned int woken_task, waiters, locks, unlocks;
 };
 
 /*
diff --git a/include/linux/pagemap.h b/include/linux/pagemap.h
index d9548d5..bea9657 100644
--- a/include/linux/pagemap.h
+++ b/include/linux/pagemap.h
@@ -266,25 +266,27 @@ static inline pgoff_t linear_page_index(struct vm_area_struct *vma,
 	return pgoff >> (PAGE_CACHE_SHIFT - PAGE_SHIFT);
 }
 
-extern void __init_page_lock_backtrace(struct page *page);
-extern void __save_page_lock_backtrace(struct page *page);
-extern void __save_page_unlock_backtrace(struct page *page);
+extern void init_page_lock_stacktrace(struct page *page);
+extern void save_page_lock_stacktrace(struct page *page);
+extern void save_page_unlock_stacktrace(struct page *page);
+extern void save_page_debug_stacktrace(struct page *page);
+extern void show_one_locked_page(struct page *page);
 
 extern void __lock_page(struct page *page);
 extern int __lock_page_killable(struct page *page);
 extern void __lock_page_nosync(struct page *page);
-extern void __lock_page_no_backtrace(struct page *page);
+extern void __lock_page_no_stacktrace(struct page *page);
 extern void unlock_page(struct page *page);
 
 static inline void set_page_locked(struct page *page)
 {
 	set_bit(PG_locked, &page->flags);
-	__save_page_lock_backtrace(page);
+	save_page_lock_stacktrace(page);
 }
 
 static inline void clear_page_locked(struct page *page)
 {
-	__save_page_unlock_backtrace(page);
+	save_page_unlock_stacktrace(page);
 	clear_bit(PG_locked, &page->flags);
 }
 
@@ -299,7 +301,7 @@ static inline int trylock_page(struct page *page)
 
 	ret = __trylock_page(page);
 	if (ret)
-		__save_page_lock_backtrace(page);
+		save_page_lock_stacktrace(page);
 	return ret;
 }
 
diff --git a/kernel/sched.c b/kernel/sched.c
index ab3d0bc..37a6a69 100644
--- a/kernel/sched.c
+++ b/kernel/sched.c
@@ -4601,12 +4601,12 @@ EXPORT_SYMBOL(__wake_up);
 unsigned int __wake_up_cel(wait_queue_head_t *q, void *key)
 {
 	unsigned long flags;
-	unsigned int pid;
+	pid_t pid;
 
 	spin_lock_irqsave(&q->lock, flags);
 	pid = __wake_up_common(q, TASK_NORMAL, 1, 0, key);
 	spin_unlock_irqrestore(&q->lock, flags);
-	return pid;
+	return (unsigned int)pid;
 }
 EXPORT_SYMBOL(__wake_up_cel);
 
diff --git a/mm/filemap.c b/mm/filemap.c
index 015a90b..30a1f73 100644
--- a/mm/filemap.c
+++ b/mm/filemap.c
@@ -33,6 +33,8 @@
 #include <linux/cpuset.h>
 #include <linux/hardirq.h> /* for BUG_ON(!in_atomic()) only */
 #include <linux/memcontrol.h>
+#include <linux/hrtimer.h>
+
 #include "internal.h"
 
 /*
@@ -109,37 +111,99 @@
 /*
  * Debugging fields protected by the page lock
  */
-void __init_page_lock_backtrace(struct page *page)
+static void init_page_trace(struct page_trace *pt)
+{
+	pt->stacktrace.nr_entries = 0;
+	pt->stacktrace.max_entries = PAGE_STACKTRACE_SIZE;
+	pt->stacktrace.entries = pt->entries;
+	pt->stacktrace.skip = 2;
+}
+
+static void save_page_trace(struct page_trace *pt)
+{
+	pt->timestamp = ktime_get();
+
+	init_page_trace(pt);
+	save_stack_trace(&pt->stacktrace);
+	pt->stacktrace.nr_entries--;
+}
+
+static void print_trace_timestamp(struct page_trace *pt)
 {
-	INIT_LIST_HEAD(&page->bt_list);
-	page->lock_backtrace.nr_entries = 0;
-	page->unlock_backtrace.nr_entries = 0;
+	struct timespec ts = ktime_to_timespec(pt->timestamp);
+
+	printk(KERN_ERR "  trace timestamp: %ld sec, %ld nsec\n",
+		ts.tv_sec , ts.tv_nsec);
 }
-EXPORT_SYMBOL(__init_page_lock_backtrace);
 
-void __save_page_lock_backtrace(struct page *page)
+void init_page_lock_stacktrace(struct page *page)
 {
-	page->lock_backtrace.nr_entries = 0;
-	page->lock_backtrace.max_entries = PAGE_STACKTRACE_SIZE;
-	page->lock_backtrace.entries = page->lock_entries;
-	page->lock_backtrace.skip = 2;
+	INIT_LIST_HEAD(&page->iip2r_list);
 
-	save_stack_trace(&page->lock_backtrace);
-	page->lock_backtrace.nr_entries--;
+	init_page_trace(&page->lock_trace);
+	init_page_trace(&page->unlock_trace);
+	init_page_trace(&page->debug_trace);
+
+	page->woken_task = 0;
+	page->waiters = 0;
+	page->locks = 0;
+	page->unlocks = 0;
 }
-EXPORT_SYMBOL(__save_page_lock_backtrace);
+EXPORT_SYMBOL(init_page_lock_stacktrace);
 
-void __save_page_unlock_backtrace(struct page *page)
+void save_page_lock_stacktrace(struct page *page)
 {
-	page->unlock_backtrace.nr_entries = 0;
-	page->unlock_backtrace.max_entries = PAGE_STACKTRACE_SIZE;
-	page->unlock_backtrace.entries = page->unlock_entries;
-	page->unlock_backtrace.skip = 2;
+	save_page_trace(&page->lock_trace);
+	page->locks++;
+}
+EXPORT_SYMBOL(save_page_lock_stacktrace);
 
-	save_stack_trace(&page->unlock_backtrace);
-	page->unlock_backtrace.nr_entries--;
+void save_page_unlock_stacktrace(struct page *page)
+{
+	save_page_trace(&page->unlock_trace);
+	page->unlocks++;
 }
-EXPORT_SYMBOL(__save_page_unlock_backtrace);
+EXPORT_SYMBOL(save_page_unlock_stacktrace);
+
+void save_page_debug_stacktrace(struct page *page)
+{
+	save_page_trace(&page->debug_trace);
+}
+EXPORT_SYMBOL(save_page_debug_stacktrace);
+
+void show_one_locked_page(struct page *page)
+{
+	struct stack_trace *locker = &page->lock_trace.stacktrace;
+	struct stack_trace *unlocker = &page->unlock_trace.stacktrace;
+	struct stack_trace *stuck = &page->debug_trace.stacktrace;
+
+	printk(KERN_ERR "  index: %lu\n", page->index);
+	printk(KERN_ERR "  current flags: 0x%lx\n", page->flags);
+	printk(KERN_ERR "  lock_page() calls: %u unlock_page() calls(): %u\n",
+			page->locks, page->unlocks);
+
+	if (stuck->nr_entries) {
+		printk(KERN_ERR "  stack trace of stuck task:\n");
+		print_trace_timestamp(&page->debug_trace);
+		print_stack_trace(stuck, 5);
+	}
+
+	if (locker->nr_entries) {
+		printk(KERN_ERR "  stack trace of last locker:\n");
+		print_trace_timestamp(&page->lock_trace);
+		print_stack_trace(locker, 5);
+	}
+
+	if (unlocker->nr_entries) {
+		printk(KERN_ERR "  woken task: %u\n", page->woken_task);
+		printk(KERN_ERR "  stack trace of last unlocker:\n");
+		print_trace_timestamp(&page->unlock_trace);
+		print_stack_trace(unlocker, 5);
+	}
+
+	printk(KERN_ERR "\n");
+}
+EXPORT_SYMBOL(show_one_locked_page);
 
 /*
  * Remove a page from the page cache and free it. Caller has to make
@@ -599,7 +663,7 @@ EXPORT_SYMBOL(wait_on_page_bit);
  */
 void unlock_page(struct page *page)
 {
-	__save_page_unlock_backtrace(page);
+	save_page_unlock_stacktrace(page);
 	smp_mb__before_clear_bit();
 	if (!test_and_clear_bit(PG_locked, &page->flags))
 		BUG();
@@ -640,17 +704,18 @@ void __lock_page(struct page *page)
 	DEFINE_WAIT_BIT(wait, &page->flags, PG_locked);
 	__wait_on_bit_lock(page_waitqueue(page), &wait, sync_page,
 							TASK_UNINTERRUPTIBLE);
-	__save_page_lock_backtrace(page);
+	save_page_lock_stacktrace(page);
 }
 EXPORT_SYMBOL(__lock_page);
 
-void __lock_page_no_backtrace(struct page *page)
+void __lock_page_no_stacktrace(struct page *page)
 {
 	DEFINE_WAIT_BIT(wait, &page->flags, PG_locked);
 	__wait_on_bit_lock(page_waitqueue(page), &wait, sync_page,
 							TASK_UNINTERRUPTIBLE);
+	page->locks++;
 }
-EXPORT_SYMBOL(__lock_page_no_backtrace);
+EXPORT_SYMBOL(__lock_page_no_stacktrace);
 
 int __lock_page_killable(struct page *page)
 {
@@ -660,7 +725,11 @@ int __lock_page_killable(struct page *page)
 	ret = __wait_on_bit_lock(page_waitqueue(page), &wait,
 					sync_page_killable, TASK_KILLABLE);
 	if (ret == 0)
-		__save_page_lock_backtrace(page);
+		save_page_lock_stacktrace(page);
+	else {
+		printk(KERN_ERR "lock_page_killable:\n");
+		show_one_locked_page(page);
+	}
 	return ret;
 }
 
@@ -676,7 +745,7 @@ void __lock_page_nosync(struct page *page)
 	DEFINE_WAIT_BIT(wait, &page->flags, PG_locked);
 	__wait_on_bit_lock(page_waitqueue(page), &wait, __sleep_on_page_lock,
 							TASK_UNINTERRUPTIBLE);
-	__save_page_lock_backtrace(page);
+	save_page_lock_stacktrace(page);
 }
 
 /**
diff --git a/mm/hugetlb.c b/mm/hugetlb.c
index 1732317..c706dd9 100644
--- a/mm/hugetlb.c
+++ b/mm/hugetlb.c
@@ -458,7 +458,7 @@ static void update_and_free_page(struct hstate *h, struct page *page)
 	h->nr_huge_pages--;
 	h->nr_huge_pages_node[page_to_nid(page)]--;
 	for (i = 0; i < pages_per_huge_page(h); i++) {
-		__save_page_unlock_backtrace(&page[i]);
+		save_page_unlock_stacktrace(&page[i]);
 		page[i].flags &= ~(1 << PG_locked | 1 << PG_error | 1 << PG_referenced |
 				1 << PG_dirty | 1 << PG_active | 1 << PG_reserved |
 				1 << PG_private | 1<< PG_writeback);
diff --git a/mm/page_alloc.c b/mm/page_alloc.c
index 48cf7cc..2f97604 100644
--- a/mm/page_alloc.c
+++ b/mm/page_alloc.c
@@ -629,7 +629,7 @@ static int prep_new_page(struct page *page, int order, gfp_t gfp_flags)
 	if (order && (gfp_flags & __GFP_COMP))
 		prep_compound_page(page, order);
 
-	__init_page_lock_backtrace(page);
+	init_page_lock_stacktrace(page);
 
 	return 0;
 }
diff --git a/mm/slub.c b/mm/slub.c
index eb7fa4f..d86f46a 100644
--- a/mm/slub.c
+++ b/mm/slub.c
@@ -1205,12 +1205,12 @@ static void discard_slab(struct kmem_cache *s, struct page *page)
 static __always_inline void slab_lock(struct page *page)
 {
 	bit_spin_lock(PG_locked, &page->flags);
-	__save_page_lock_backtrace(page);
+	save_page_lock_stacktrace(page);
 }
 
 static __always_inline void slab_unlock(struct page *page)
 {
-	__save_page_unlock_backtrace(page);
+	save_page_unlock_stacktrace(page);
 	__bit_spin_unlock(PG_locked, &page->flags);
 }
 
@@ -1220,7 +1220,7 @@ static __always_inline int slab_trylock(struct page *page)
 
 	rc = bit_spin_trylock(PG_locked, &page->flags);
 	if (rc)
-		__save_page_lock_backtrace(page);
+		save_page_lock_stacktrace(page);
 
 	return rc;
 }
diff --git a/mm/truncate.c b/mm/truncate.c
index 4b348a7..cfd2df2 100644
--- a/mm/truncate.c
+++ b/mm/truncate.c
@@ -376,29 +376,8 @@ static int do_launder_page(struct address_space *mapping, struct page *page)
 static DEFINE_MUTEX(iip2r_mutex);
 static LIST_HEAD(iip2r_waiters);
 
-static void show_one_locked_page(struct list_head *pos)
-{
-	struct page *page = list_entry(pos, struct page, bt_list);
-
-	printk(KERN_ERR "  index: %lu\n", page->index);
-	printk(KERN_ERR "  current flags: 0x%lx\n", page->flags);
-
-	if (page->lock_backtrace.nr_entries) {
-		printk(KERN_ERR "  backtrace of last locker:\n");
-		print_stack_trace(&page->lock_backtrace, 5);
-	}
-
-	if (page->unlock_backtrace.nr_entries) {
-		printk(KERN_ERR "  woken task: %u\n", page->woken_task);
-		printk(KERN_ERR "  backtrace of last unlocker:\n");
-		print_stack_trace(&page->unlock_backtrace, 5);
-	}
-
-	printk(KERN_ERR "\n");
-}
-
 /**
- * show_locked_pages - Show backtraces for pages in iip2r_waiters
+ * show_locked_pages - Show stack backtraces for pages in iip2r_waiters
  *
  * Invoked via sysRq-T or sysRq-W.
  *
@@ -413,8 +392,11 @@ void show_locked_pages(void)
 		printk(KERN_ERR "pages waiting in "
 					"invalidate_inode_pages2_range:\n");
 
-		list_for_each(pos, &iip2r_waiters)
-			show_one_locked_page(pos);
+		list_for_each(pos, &iip2r_waiters) {
+			struct page *page = list_entry(pos, struct page,
+								iip2r_list);
+			show_one_locked_page(page);
+		}
 	} else
 		printk(KERN_ERR "no pages waiting in "
 					"invalidate_inode_pages2_range\n");
@@ -436,13 +418,16 @@ static void iip2r_lock_page(struct page *page)
 {
 	if (!__trylock_page(page)) {
 		mutex_lock(&iip2r_mutex);
-		list_add(&page->bt_list, &iip2r_waiters);
+		list_add(&page->iip2r_list, &iip2r_waiters);
 		mutex_unlock(&iip2r_mutex);
 
-		__lock_page_no_backtrace(page);
+		/* debug stacktrace not protected by page lock, but
+		 * instead by caller's serialization */
+		save_page_debug_stacktrace(page);
+		__lock_page_no_stacktrace(page);
 
 		mutex_lock(&iip2r_mutex);
-		list_del_init(&page->bt_list);
+		list_del_init(&page->iip2r_list);
 		mutex_unlock(&iip2r_mutex);
 	}
 }

--
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>

      parent reply	other threads:[~2009-01-16 20:07 UTC|newest]

Thread overview: 3+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2009-01-16 20:06 [PATCH 0/2] Page lock stack trace instrumentation Chuck Lever
2009-01-16 20:07 ` [PATCH 1/2] PAGECACHE: Record stack backtrace in lock_page() Chuck Lever
2009-01-16 20:07 ` Chuck Lever [this message]

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20090116200713.23026.80924.stgit@ingres.1015granger.net \
    --to=chuck.lever@oracle.com \
    --cc=linux-mm@kvack.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
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).