aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAlan Modra <amodra@gmail.com>2012-09-25 16:30:06 -0500
committerRyan S. Arnold <rsa@linux.vnet.ibm.com>2012-09-25 16:30:06 -0500
commit7e2fca8dd22e3bd932581d6479b0c552deff00b6 (patch)
tree964db807642e388bfedf3684ab50442a86efadbe
parent1c530d024418c75bb61eecf23aea1f4179fbf0fe (diff)
downloadglibc-7e2fca8dd22e3bd932581d6479b0c552deff00b6.tar
glibc-7e2fca8dd22e3bd932581d6479b0c552deff00b6.tar.gz
glibc-7e2fca8dd22e3bd932581d6479b0c552deff00b6.tar.bz2
glibc-7e2fca8dd22e3bd932581d6479b0c552deff00b6.zip
Fix bugs in powerpc pthread_once.
Ref gcc.gnu.org/bugzilla/show_bug.cgi?id=52839#c10 Release barriers are needed to ensure that any memory written by init_routine is seen by other threads before *once_control changes. In the case of clear_once_control we need to flush any partially written state.
-rw-r--r--ChangeLog8
-rw-r--r--nptl/sysdeps/unix/sysv/linux/powerpc/pthread_once.c21
2 files changed, 24 insertions, 5 deletions
diff --git a/ChangeLog b/ChangeLog
index 9e102faf60..1fff9bad72 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,11 @@
+2012-09-25 Alan Modra <amodra@gmail.com>
+
+ * sysdeps/unix/sysv/linux/powerpc/pthread_once.c (__pthread_once):
+ Add release barrier before setting once_control to say
+ initialisation is done. Add hints on lwarx. Use macro in
+ place of isync.
+ (clear_once_control): Add release barrier.
+
2012-09-25 Joseph Myers <joseph@codesourcery.com>
[BZ #13629]
diff --git a/nptl/sysdeps/unix/sysv/linux/powerpc/pthread_once.c b/nptl/sysdeps/unix/sysv/linux/powerpc/pthread_once.c
index 4e3d7bd49a..52ab53f0a9 100644
--- a/nptl/sysdeps/unix/sysv/linux/powerpc/pthread_once.c
+++ b/nptl/sysdeps/unix/sysv/linux/powerpc/pthread_once.c
@@ -28,6 +28,7 @@ clear_once_control (void *arg)
{
pthread_once_t *once_control = (pthread_once_t *) arg;
+ __asm __volatile (__lll_rel_instr);
*once_control = 0;
lll_futex_wake (once_control, INT_MAX, LLL_PRIVATE);
}
@@ -47,15 +48,15 @@ __pthread_once (pthread_once_t *once_control, void (*init_routine) (void))
oldval = *once_control;
if ((oldval & 2) == 0)
*once_control = newval;
- Do this atomically.
+ Do this atomically with an acquire barrier.
*/
newval = __fork_generation | 1;
- __asm __volatile ("1: lwarx %0,0,%3\n"
+ __asm __volatile ("1: lwarx %0,0,%3" MUTEX_HINT_ACQ "\n"
" andi. %1,%0,2\n"
" bne 2f\n"
" stwcx. %4,0,%3\n"
" bne 1b\n"
- "2: isync"
+ "2: " __lll_acq_instr
: "=&r" (oldval), "=&r" (tmp), "=m" (*once_control)
: "r" (once_control), "r" (newval), "m" (*once_control)
: "cr0");
@@ -87,8 +88,18 @@ __pthread_once (pthread_once_t *once_control, void (*init_routine) (void))
pthread_cleanup_pop (0);
- /* Add one to *once_control to take the bottom 2 bits from 01 to 10. */
- atomic_increment (once_control);
+ /* Add one to *once_control to take the bottom 2 bits from 01 to 10.
+ A release barrier is needed to ensure memory written by init_routine
+ is seen in other threads before *once_control changes. */
+ int tmp;
+ __asm __volatile (__lll_rel_instr "\n"
+ "1: lwarx %0,0,%2" MUTEX_HINT_REL "\n"
+ " addi %0,%0,1\n"
+ " stwcx. %0,0,%2\n"
+ " bne- 1b"
+ : "=&b" (tmp), "=m" (*once_control)
+ : "r" (once_control), "m" (*once_control)
+ : "cr0");
/* Wake up all other threads. */
lll_futex_wake (once_control, INT_MAX, LLL_PRIVATE);