aboutsummaryrefslogtreecommitdiffstats
path: root/arch
diff options
context:
space:
mode:
authorHirokazu Takata <takata@linux-m32r.org>2005-11-28 16:43:58 -0500
committerLinus Torvalds <torvalds@g5.osdl.org>2005-11-28 17:42:24 -0500
commit91f4ab056d85d23fa6955927fdeb1558673e8cd1 (patch)
tree95745578b92f329b3fbe7761baf8c422b2975473 /arch
parentbce61dd49d6ba7799be2de17c772e4c701558f14 (diff)
[PATCH] m32r: Fix sys_tas() syscall
This patch fixes a deadlock problem of the m32r SMP kernel. In the m32r kernel, sys_tas() system call is provided as a test-and-set function for userspace, for backward compatibility. In some multi-threading application program, deadlocks were rarely caused at sys_tas() funcion. Such a deadlock was caused due to a collision of __pthread_lock() and __pthread_unlock() operations. The "tas" syscall is repeatedly called by pthread_mutex_lock() to get a lock, while a lock variable's value is not 0. On the other hand, pthead_mutex_unlock() sets the lock variable to 0 for unlocking. In the previous implementation of sys_tas() routine, there was a possibility that a unlock operation was ignored in the following case: - Assume a lock variable (*addr) was equal to 1 before sys_tas() execution. - __pthread_unlock() operation is executed by the other processor and the lock variable (*addr) is set to 0, between a read operation ("oldval = *addr;") and the following write operation ("*addr = 1;") during a execution of sys_tas(). In this case, the following write operation ("*addr = 1;") overwrites the __pthread_unlock() result, and sys_tas() fails to get a lock in the next turn and after that. According to the attatched patch, sys_tas() returns 0 value in the next turn and deadlocks never happen. Signed-off-by: Hitoshi Yamamoto <Yamamoto.Hitoshi@ap.MitsubishiElectric.co.jp> Signed-off-by: Hirokazu Takata <takata@linux-m32r.org> Signed-off-by: Andrew Morton <akpm@osdl.org> Signed-off-by: Linus Torvalds <torvalds@osdl.org>
Diffstat (limited to 'arch')
-rw-r--r--arch/m32r/kernel/sys_m32r.c6
1 files changed, 4 insertions, 2 deletions
diff --git a/arch/m32r/kernel/sys_m32r.c b/arch/m32r/kernel/sys_m32r.c
index e0500e12c5fb..fe55b28d3725 100644
--- a/arch/m32r/kernel/sys_m32r.c
+++ b/arch/m32r/kernel/sys_m32r.c
@@ -41,7 +41,8 @@ asmlinkage int sys_tas(int *addr)
41 return -EFAULT; 41 return -EFAULT;
42 local_irq_save(flags); 42 local_irq_save(flags);
43 oldval = *addr; 43 oldval = *addr;
44 *addr = 1; 44 if (!oldval)
45 *addr = 1;
45 local_irq_restore(flags); 46 local_irq_restore(flags);
46 return oldval; 47 return oldval;
47} 48}
@@ -59,7 +60,8 @@ asmlinkage int sys_tas(int *addr)
59 60
60 _raw_spin_lock(&tas_lock); 61 _raw_spin_lock(&tas_lock);
61 oldval = *addr; 62 oldval = *addr;
62 *addr = 1; 63 if (!oldval)
64 *addr = 1;
63 _raw_spin_unlock(&tas_lock); 65 _raw_spin_unlock(&tas_lock);
64 66
65 return oldval; 67 return oldval;