[patch 2.6.13-rc6] i386: semaphore ownership tracking

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



  This patch enables tracking semaphore ownership.  It saves a pointer to the
thread_info struct of the last process that got a semaphore.  I couldn't
think of a better way to print out the info, so I hacked up the code that
dumps a process's kernel stack so it prints out the PID of the owning process
when it finds another process waiting for a semaphore.

  So far it has worked under some light testing and should be fairly safe to use.
In general, if a process is sleeping on a semaphore, that semaphore shouldn't
disappear while this code is referencing it and neither should the owner.
Since this is really meant for finding deadlocks and not for general use I
think it should be OK.

Signed-off-by: Chuck Ebbert <[email protected]>

 arch/i386/kernel/semaphore.c |   16 ++++++++++
 arch/i386/kernel/traps.c     |   10 ++++++
 include/asm-i386/semaphore.h |   66 ++++++++++++++++++++++++++++++++++++++-----
 lib/Kconfig.debug            |    9 +++++
 4 files changed, 94 insertions(+), 7 deletions(-)

--- 2.6.13-rc6c.orig/include/asm-i386/semaphore.h
+++ 2.6.13-rc6c/include/asm-i386/semaphore.h
@@ -45,15 +45,29 @@ struct semaphore {
 	atomic_t count;
 	int sleepers;
 	wait_queue_head_t wait;
+#ifdef CONFIG_SEMAPHORE_OWNER
+	struct thread_info *owner;
+#endif
 };
 
-
+#ifdef CONFIG_SEMAPHORE_OWNER
+void __down_return(void);
+void __down_intr_return(void);
+#define __SEMAPHORE_INITIALIZER(name, n)				\
+{									\
+	.count		= ATOMIC_INIT(n),				\
+	.sleepers	= 0,						\
+	.wait		= __WAIT_QUEUE_HEAD_INITIALIZER((name).wait),	\
+	.owner		= NULL,						\
+}
+#else
 #define __SEMAPHORE_INITIALIZER(name, n)				\
 {									\
 	.count		= ATOMIC_INIT(n),				\
 	.sleepers	= 0,						\
 	.wait		= __WAIT_QUEUE_HEAD_INITIALIZER((name).wait)	\
 }
+#endif /* CONFIG_SEMAPHORE_OWNER */
 
 #define __MUTEX_INITIALIZER(name) \
 	__SEMAPHORE_INITIALIZER(name,1)
@@ -75,6 +89,9 @@ static inline void sema_init (struct sem
 	atomic_set(&sem->count, val);
 	sem->sleepers = 0;
 	init_waitqueue_head(&sem->wait);
+#ifdef CONFIG_SEMAPHORE_OWNER
+	sem->owner = NULL;
+#endif
 }
 
 static inline void init_MUTEX (struct semaphore *sem)
@@ -105,13 +122,22 @@ static inline void down(struct semaphore
 		LOCK "decl %0\n\t"     /* --sem->count */
 		"js 2f\n"
 		"1:\n"
+#ifdef CONFIG_SEMAPHORE_OWNER
+		"andl %%esp,%1\n"
+#endif
 		LOCK_SECTION_START("")
 		"2:\tlea %0,%%eax\n\t"
 		"call __down_failed\n\t"
 		"jmp 1b\n"
 		LOCK_SECTION_END
 		:"=m" (sem->count)
+#ifdef CONFIG_SEMAPHORE_OWNER
+			, "=r" (sem->owner)
+#endif
 		:
+#ifdef CONFIG_SEMAPHORE_OWNER
+		 "1" (~(THREAD_SIZE - 1))
+#endif
 		:"memory","ax");
 }
 
@@ -127,16 +153,29 @@ static inline int down_interruptible(str
 	__asm__ __volatile__(
 		"# atomic interruptible down operation\n\t"
 		LOCK "decl %1\n\t"     /* --sem->count */
-		"js 2f\n\t"
+		"js 3f\n\t"
 		"xorl %0,%0\n"
 		"1:\n"
+#ifdef CONFIG_SEMAPHORE_OWNER
+		"or %0,%0\n"
+		"jnz 2f\n"
+		"andl %%esp,%3\n"
+		"movl %3,%2\n"
+		"2:\n"
+#endif
 		LOCK_SECTION_START("")
-		"2:\tlea %1,%%eax\n\t"
+		"3:\tlea %1,%%eax\n\t"
 		"call __down_failed_interruptible\n\t"
 		"jmp 1b\n"
 		LOCK_SECTION_END
-		:"=a" (result), "=m" (sem->count)
+		:"=&a" (result), "=m" (sem->count)
+#ifdef CONFIG_SEMAPHORE_OWNER
+			, "=m" (sem->owner)
+#endif
 		:
+#ifdef CONFIG_SEMAPHORE_OWNER
+		 "r" (~(THREAD_SIZE - 1))
+#endif
 		:"memory");
 	return result;
 }
@@ -152,16 +191,29 @@ static inline int down_trylock(struct se
 	__asm__ __volatile__(
 		"# atomic interruptible down operation\n\t"
 		LOCK "decl %1\n\t"     /* --sem->count */
-		"js 2f\n\t"
+		"js 3f\n\t"
 		"xorl %0,%0\n"
 		"1:\n"
+#ifdef CONFIG_SEMAPHORE_OWNER
+		"or %0,%0\n"
+		"jnz 2f\n"
+		"andl %%esp,%3\n"
+		"movl %3,%2\n"
+		"2:\n"
+#endif
 		LOCK_SECTION_START("")
-		"2:\tlea %1,%%eax\n\t"
+		"3:\tlea %1,%%eax\n\t"
 		"call __down_failed_trylock\n\t"
 		"jmp 1b\n"
 		LOCK_SECTION_END
-		:"=a" (result), "=m" (sem->count)
+		:"=&a" (result), "=m" (sem->count)
+#ifdef CONFIG_SEMAPHORE_OWNER
+			, "=m" (sem->owner)
+#endif
 		:
+#ifdef CONFIG_SEMAPHORE_OWNER
+		 "r" (~(THREAD_SIZE - 1))
+#endif
 		:"memory");
 	return result;
 }
--- 2.6.13-rc6c.orig/arch/i386/kernel/semaphore.c
+++ 2.6.13-rc6c/arch/i386/kernel/semaphore.c
@@ -198,7 +198,15 @@ asm(
 #endif
 	"pushl %edx\n\t"
 	"pushl %ecx\n\t"
+#if defined(CONFIG_SEMAPHORE_OWNER)
+	"pushl %eax\n\t"
+#endif
 	"call __down\n\t"
+#if defined(CONFIG_SEMAPHORE_OWNER)
+"\n.globl __down_return\n"
+"__down_return:\n\t"
+	"popl %ecx\n\t"
+#endif
 	"popl %ecx\n\t"
 	"popl %edx\n\t"
 #if defined(CONFIG_FRAME_POINTER)
@@ -219,7 +227,15 @@ asm(
 #endif
 	"pushl %edx\n\t"
 	"pushl %ecx\n\t"
+#if defined(CONFIG_SEMAPHORE_OWNER)
+	"pushl %eax\n\t"
+#endif
 	"call __down_interruptible\n\t"
+#if defined(CONFIG_SEMAPHORE_OWNER)
+"\n.globl __down_intr_return\n"
+"__down_intr_return:\n\t"
+	"popl %ecx\n\t"
+#endif
 	"popl %ecx\n\t"
 	"popl %edx\n\t"
 #if defined(CONFIG_FRAME_POINTER)
--- 2.6.13-rc6c.orig/lib/Kconfig.debug
+++ 2.6.13-rc6c/lib/Kconfig.debug
@@ -159,3 +159,12 @@ config FRAME_POINTER
 	  If you don't debug the kernel, you can say N, but we may not be able
 	  to solve problems without frame pointers.
 
+config SEMAPHORE_OWNER
+	bool "Track semaphore owners"
+	depends on DEBUG_KERNEL && FRAME_POINTER && (X86 && !X86_64)
+	default n
+	help
+	  Say Y to enable tracking of semaphore owners.
+
+	  If unsure, say N.
+
--- 2.6.13-rc6c.orig/arch/i386/kernel/traps.c
+++ 2.6.13-rc6c/arch/i386/kernel/traps.c
@@ -123,6 +123,16 @@ static inline unsigned long print_contex
 		addr = *(unsigned long *)(ebp + 4);
 		printk(" [<%08lx>] ", addr);
 		print_symbol("%s", addr);
+#ifdef CONFIG_SEMAPHORE_OWNER
+		if (addr == (unsigned long)__down_return
+		    || addr == (unsigned long)__down_intr_return) {
+			struct semaphore *sem = *(struct semaphore **)(ebp + 8);
+			if (sem && !((unsigned long)sem->owner & (THREAD_SIZE - 1)))
+				printk(" sem owner %s %d",
+					sem->owner ? "pid" : "(none)",
+					sem->owner ? sem->owner->task->pid : 0);
+		}
+#endif
 		printk("\n");
 		ebp = *(unsigned long *)ebp;
 	}
__
Chuck
-
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to [email protected]
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/

[Index of Archives]     [Kernel Newbies]     [Netfilter]     [Bugtraq]     [Photo]     [Gimp]     [Yosemite News]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Linux RAID]     [Video 4 Linux]     [Linux for the blind]
  Powered by Linux