Accepting request 677136 from home:Andreas_Schwab:Factory
- pthread-rwlock-trylock-stalls.patch: nptl: Fix pthread_rwlock_try*lock stalls (BZ #23844) - arm-systemtap-probe-constraint.patch: arm: Use "nr" constraint for Systemtap probes (BZ #24164) - pthread-mutex-barrier.patch: Add compiler barriers around modifications of the robust mutex list for pthread_mutex_trylock (BZ #24180) - fork-handler-lock.patch: nptl: Avoid fork handler lock for async-signal-safe fork (BZ #24161) - pthread-join-probe.patch: nptl: Fix invalid Systemtap probe in pthread_join (BZ #24211) - riscv-clone-unwind.patch: RISC-V: Fix elfutils testsuite unwind failures (BZ #24040) OBS-URL: https://build.opensuse.org/request/show/677136 OBS-URL: https://build.opensuse.org/package/show/Base:System/glibc?expand=0&rev=518
This commit is contained in:
parent
add9e7bf61
commit
afe7a231ef
74
arm-systemtap-probe-constraint.patch
Normal file
74
arm-systemtap-probe-constraint.patch
Normal file
@ -0,0 +1,74 @@
|
|||||||
|
2019-02-05 Florian Weimer <fweimer@redhat.com>
|
||||||
|
|
||||||
|
[BZ #24164]
|
||||||
|
arm: Use "nr" constraint for Systemtap probes, to avoid the
|
||||||
|
compiler using memory operands for constants, due to the "o"
|
||||||
|
alternative in the default "nor" constraint.
|
||||||
|
* include/stap-probe.h [USE_STAP_PROBE]: Include
|
||||||
|
<stap-probe-machine.h>
|
||||||
|
* sysdeps/generic/stap-probe-machine.h: New file.
|
||||||
|
* sysdeps/arm/stap-probe-machine.h: Likewise.
|
||||||
|
|
||||||
|
Index: glibc-2.29/include/stap-probe.h
|
||||||
|
===================================================================
|
||||||
|
--- glibc-2.29.orig/include/stap-probe.h
|
||||||
|
+++ glibc-2.29/include/stap-probe.h
|
||||||
|
@@ -21,6 +21,7 @@
|
||||||
|
|
||||||
|
#ifdef USE_STAP_PROBE
|
||||||
|
|
||||||
|
+# include <stap-probe-machine.h>
|
||||||
|
# include <sys/sdt.h>
|
||||||
|
|
||||||
|
/* Our code uses one macro LIBC_PROBE (name, n, arg1, ..., argn).
|
||||||
|
Index: glibc-2.29/sysdeps/arm/stap-probe-machine.h
|
||||||
|
===================================================================
|
||||||
|
--- /dev/null
|
||||||
|
+++ glibc-2.29/sysdeps/arm/stap-probe-machine.h
|
||||||
|
@@ -0,0 +1,22 @@
|
||||||
|
+/* Macros for customizing Systemtap <sys/sdt.h>. Arm version.
|
||||||
|
+ Copyright (C) 2019 Free Software Foundation, Inc.
|
||||||
|
+ This file is part of the GNU C Library.
|
||||||
|
+
|
||||||
|
+ The GNU C Library is free software; you can redistribute it and/or
|
||||||
|
+ modify it under the terms of the GNU Lesser General Public
|
||||||
|
+ License as published by the Free Software Foundation; either
|
||||||
|
+ version 2.1 of the License, or (at your option) any later version.
|
||||||
|
+
|
||||||
|
+ The GNU C Library is distributed in the hope that it will be useful,
|
||||||
|
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
+ Lesser General Public License for more details.
|
||||||
|
+
|
||||||
|
+ You should have received a copy of the GNU Lesser General Public
|
||||||
|
+ License along with the GNU C Library; if not, see
|
||||||
|
+ <http://www.gnu.org/licenses/>. */
|
||||||
|
+
|
||||||
|
+/* The default "nor" constraint produces unparseable memory references
|
||||||
|
+ for constants. Omit the problematic "o" constraint. See bug 24164
|
||||||
|
+ and GCC PR 89146. */
|
||||||
|
+#define STAP_SDT_ARG_CONSTRAINT nr
|
||||||
|
Index: glibc-2.29/sysdeps/generic/stap-probe-machine.h
|
||||||
|
===================================================================
|
||||||
|
--- /dev/null
|
||||||
|
+++ glibc-2.29/sysdeps/generic/stap-probe-machine.h
|
||||||
|
@@ -0,0 +1,19 @@
|
||||||
|
+/* Macros for customizing Systemtap <sys/sdt.h>. Generic version.
|
||||||
|
+ Copyright (C) 2019 Free Software Foundation, Inc.
|
||||||
|
+ This file is part of the GNU C Library.
|
||||||
|
+
|
||||||
|
+ The GNU C Library is free software; you can redistribute it and/or
|
||||||
|
+ modify it under the terms of the GNU Lesser General Public
|
||||||
|
+ License as published by the Free Software Foundation; either
|
||||||
|
+ version 2.1 of the License, or (at your option) any later version.
|
||||||
|
+
|
||||||
|
+ The GNU C Library is distributed in the hope that it will be useful,
|
||||||
|
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
+ Lesser General Public License for more details.
|
||||||
|
+
|
||||||
|
+ You should have received a copy of the GNU Lesser General Public
|
||||||
|
+ License along with the GNU C Library; if not, see
|
||||||
|
+ <http://www.gnu.org/licenses/>. */
|
||||||
|
+
|
||||||
|
+/* By default, there are no customizations. */
|
91
fork-handler-lock.patch
Normal file
91
fork-handler-lock.patch
Normal file
@ -0,0 +1,91 @@
|
|||||||
|
2019-02-08 Florian Weimer <fweimer@redhat.com>
|
||||||
|
|
||||||
|
[BZ #24161]
|
||||||
|
* sysdeps/nptl/fork.h (__run_fork_handlers): Add multiple_threads
|
||||||
|
argument.
|
||||||
|
* nptl/register-atfork.c (__run_fork_handlers): Only perform
|
||||||
|
locking if the new do_locking argument is true.
|
||||||
|
* sysdeps/nptl/fork.c (__libc_fork): Pass multiple_threads to
|
||||||
|
__run_fork_handlers.
|
||||||
|
|
||||||
|
Index: glibc-2.29/nptl/register-atfork.c
|
||||||
|
===================================================================
|
||||||
|
--- glibc-2.29.orig/nptl/register-atfork.c
|
||||||
|
+++ glibc-2.29/nptl/register-atfork.c
|
||||||
|
@@ -107,13 +107,14 @@ __unregister_atfork (void *dso_handle)
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
-__run_fork_handlers (enum __run_fork_handler_type who)
|
||||||
|
+__run_fork_handlers (enum __run_fork_handler_type who, _Bool do_locking)
|
||||||
|
{
|
||||||
|
struct fork_handler *runp;
|
||||||
|
|
||||||
|
if (who == atfork_run_prepare)
|
||||||
|
{
|
||||||
|
- lll_lock (atfork_lock, LLL_PRIVATE);
|
||||||
|
+ if (do_locking)
|
||||||
|
+ lll_lock (atfork_lock, LLL_PRIVATE);
|
||||||
|
size_t sl = fork_handler_list_size (&fork_handlers);
|
||||||
|
for (size_t i = sl; i > 0; i--)
|
||||||
|
{
|
||||||
|
@@ -133,7 +134,8 @@ __run_fork_handlers (enum __run_fork_han
|
||||||
|
else if (who == atfork_run_parent && runp->parent_handler)
|
||||||
|
runp->parent_handler ();
|
||||||
|
}
|
||||||
|
- lll_unlock (atfork_lock, LLL_PRIVATE);
|
||||||
|
+ if (do_locking)
|
||||||
|
+ lll_unlock (atfork_lock, LLL_PRIVATE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Index: glibc-2.29/sysdeps/nptl/fork.c
|
||||||
|
===================================================================
|
||||||
|
--- glibc-2.29.orig/sysdeps/nptl/fork.c
|
||||||
|
+++ glibc-2.29/sysdeps/nptl/fork.c
|
||||||
|
@@ -55,7 +55,7 @@ __libc_fork (void)
|
||||||
|
but our current fork implementation is not. */
|
||||||
|
bool multiple_threads = THREAD_GETMEM (THREAD_SELF, header.multiple_threads);
|
||||||
|
|
||||||
|
- __run_fork_handlers (atfork_run_prepare);
|
||||||
|
+ __run_fork_handlers (atfork_run_prepare, multiple_threads);
|
||||||
|
|
||||||
|
/* If we are not running multiple threads, we do not have to
|
||||||
|
preserve lock state. If fork runs from a signal handler, only
|
||||||
|
@@ -134,7 +134,7 @@ __libc_fork (void)
|
||||||
|
__rtld_lock_initialize (GL(dl_load_lock));
|
||||||
|
|
||||||
|
/* Run the handlers registered for the child. */
|
||||||
|
- __run_fork_handlers (atfork_run_child);
|
||||||
|
+ __run_fork_handlers (atfork_run_child, multiple_threads);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
@@ -149,7 +149,7 @@ __libc_fork (void)
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Run the handlers registered for the parent. */
|
||||||
|
- __run_fork_handlers (atfork_run_parent);
|
||||||
|
+ __run_fork_handlers (atfork_run_parent, multiple_threads);
|
||||||
|
}
|
||||||
|
|
||||||
|
return pid;
|
||||||
|
Index: glibc-2.29/sysdeps/nptl/fork.h
|
||||||
|
===================================================================
|
||||||
|
--- glibc-2.29.orig/sysdeps/nptl/fork.h
|
||||||
|
+++ glibc-2.29/sysdeps/nptl/fork.h
|
||||||
|
@@ -52,9 +52,11 @@ enum __run_fork_handler_type
|
||||||
|
- atfork_run_child: run all the CHILD_HANDLER and unlocks the internal
|
||||||
|
lock.
|
||||||
|
- atfork_run_parent: run all the PARENT_HANDLER and unlocks the internal
|
||||||
|
- lock. */
|
||||||
|
-extern void __run_fork_handlers (enum __run_fork_handler_type who)
|
||||||
|
- attribute_hidden;
|
||||||
|
+ lock.
|
||||||
|
+
|
||||||
|
+ Perform locking only if DO_LOCKING. */
|
||||||
|
+extern void __run_fork_handlers (enum __run_fork_handler_type who,
|
||||||
|
+ _Bool do_locking) attribute_hidden;
|
||||||
|
|
||||||
|
/* C library side function to register new fork handlers. */
|
||||||
|
extern int __register_atfork (void (*__prepare) (void),
|
@ -1,3 +1,19 @@
|
|||||||
|
-------------------------------------------------------------------
|
||||||
|
Mon Feb 18 09:28:08 UTC 2019 - schwab@suse.de
|
||||||
|
|
||||||
|
- pthread-rwlock-trylock-stalls.patch: nptl: Fix pthread_rwlock_try*lock
|
||||||
|
stalls (BZ #23844)
|
||||||
|
- arm-systemtap-probe-constraint.patch: arm: Use "nr" constraint for
|
||||||
|
Systemtap probes (BZ #24164)
|
||||||
|
- pthread-mutex-barrier.patch: Add compiler barriers around modifications
|
||||||
|
of the robust mutex list for pthread_mutex_trylock (BZ #24180)
|
||||||
|
- fork-handler-lock.patch: nptl: Avoid fork handler lock for
|
||||||
|
async-signal-safe fork (BZ #24161)
|
||||||
|
- pthread-join-probe.patch: nptl: Fix invalid Systemtap probe in
|
||||||
|
pthread_join (BZ #24211)
|
||||||
|
- riscv-clone-unwind.patch: RISC-V: Fix elfutils testsuite unwind failures
|
||||||
|
(BZ #24040)
|
||||||
|
|
||||||
-------------------------------------------------------------------
|
-------------------------------------------------------------------
|
||||||
Fri Feb 1 10:34:39 UTC 2019 - schwab@suse.de
|
Fri Feb 1 10:34:39 UTC 2019 - schwab@suse.de
|
||||||
|
|
||||||
|
23
glibc.spec
23
glibc.spec
@ -270,6 +270,18 @@ Patch306: glibc-fix-double-loopback.diff
|
|||||||
###
|
###
|
||||||
# Patches from upstream
|
# Patches from upstream
|
||||||
###
|
###
|
||||||
|
# PATCH-FIX-UPSTREAM nptl: Fix pthread_rwlock_try*lock stalls (BZ #23844)
|
||||||
|
Patch1000: pthread-rwlock-trylock-stalls.patch
|
||||||
|
# PATCH-FIX-UPSTREAM arm: Use "nr" constraint for Systemtap probes (BZ #24164)
|
||||||
|
Patch1001: arm-systemtap-probe-constraint.patch
|
||||||
|
# PATCH-FIX-UPSTREAM Add compiler barriers around modifications of the robust mutex list for pthread_mutex_trylock (BZ #24180)
|
||||||
|
Patch1002: pthread-mutex-barrier.patch
|
||||||
|
# PATCH-FIX-UPSTREAM nptl: Avoid fork handler lock for async-signal-safe fork (BZ #24161)
|
||||||
|
Patch1003: fork-handler-lock.patch
|
||||||
|
# PATCH-FIX-UPSTREAM nptl: Fix invalid Systemtap probe in pthread_join (BZ #24211)
|
||||||
|
Patch1004: pthread-join-probe.patch
|
||||||
|
# PATCH-FIX-UPSTREAM RISC-V: Fix elfutils testsuite unwind failures (BZ #24040)
|
||||||
|
Patch1005: riscv-clone-unwind.patch
|
||||||
|
|
||||||
###
|
###
|
||||||
# Patches awaiting upstream approval
|
# Patches awaiting upstream approval
|
||||||
@ -479,6 +491,13 @@ makedb: A program to create a database for nss
|
|||||||
%patch304 -p1
|
%patch304 -p1
|
||||||
%patch306 -p1
|
%patch306 -p1
|
||||||
|
|
||||||
|
%patch1000 -p1
|
||||||
|
%patch1001 -p1
|
||||||
|
%patch1002 -p1
|
||||||
|
%patch1003 -p1
|
||||||
|
%patch1004 -p1
|
||||||
|
%patch1005 -p1
|
||||||
|
|
||||||
%patch2000 -p1
|
%patch2000 -p1
|
||||||
%patch2004 -p1
|
%patch2004 -p1
|
||||||
%patch2005 -p1
|
%patch2005 -p1
|
||||||
@ -850,9 +869,9 @@ cc-base/elf/ldconfig -vn $destdir
|
|||||||
# this will not work if we generate them in parallel.
|
# this will not work if we generate them in parallel.
|
||||||
# thus we need to run fdupes on /usr/lib/locale/
|
# thus we need to run fdupes on /usr/lib/locale/
|
||||||
# Still, on my system this is a speed advantage:
|
# Still, on my system this is a speed advantage:
|
||||||
# non-parallel build for install-locale-files: 9:34mins
|
# non-parallel build for install-locales: 9:34mins
|
||||||
# parallel build with fdupes: 7:08mins
|
# parallel build with fdupes: 7:08mins
|
||||||
make %{?_smp_mflags} install_root=%{buildroot} localedata/install-locale-files
|
make %{?_smp_mflags} install_root=%{buildroot} localedata/install-locales
|
||||||
# Avoid hardlinks across subpackages
|
# Avoid hardlinks across subpackages
|
||||||
mv %{buildroot}/usr/lib/locale/{en_US,C}.utf8 .
|
mv %{buildroot}/usr/lib/locale/{en_US,C}.utf8 .
|
||||||
%fdupes %{buildroot}/usr/lib/locale
|
%fdupes %{buildroot}/usr/lib/locale
|
||||||
|
36
pthread-join-probe.patch
Normal file
36
pthread-join-probe.patch
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
2019-02-15 Florian Weimer <fweimer@redhat.com>
|
||||||
|
|
||||||
|
[BZ #24211]
|
||||||
|
* nptl/pthread_join_common.c (__pthread_timedjoin_ex): Do not read
|
||||||
|
pd->result after the thread descriptor has been freed.
|
||||||
|
|
||||||
|
Index: glibc-2.29/nptl/pthread_join_common.c
|
||||||
|
===================================================================
|
||||||
|
--- glibc-2.29.orig/nptl/pthread_join_common.c
|
||||||
|
+++ glibc-2.29/nptl/pthread_join_common.c
|
||||||
|
@@ -86,6 +86,7 @@ __pthread_timedjoin_ex (pthread_t thread
|
||||||
|
pthread_cleanup_pop (0);
|
||||||
|
}
|
||||||
|
|
||||||
|
+ void *pd_result = pd->result;
|
||||||
|
if (__glibc_likely (result == 0))
|
||||||
|
{
|
||||||
|
/* We mark the thread as terminated and as joined. */
|
||||||
|
@@ -93,7 +94,7 @@ __pthread_timedjoin_ex (pthread_t thread
|
||||||
|
|
||||||
|
/* Store the return value if the caller is interested. */
|
||||||
|
if (thread_return != NULL)
|
||||||
|
- *thread_return = pd->result;
|
||||||
|
+ *thread_return = pd_result;
|
||||||
|
|
||||||
|
/* Free the TCB. */
|
||||||
|
__free_tcb (pd);
|
||||||
|
@@ -101,7 +102,7 @@ __pthread_timedjoin_ex (pthread_t thread
|
||||||
|
else
|
||||||
|
pd->joinid = NULL;
|
||||||
|
|
||||||
|
- LIBC_PROBE (pthread_join_ret, 3, threadid, result, pd->result);
|
||||||
|
+ LIBC_PROBE (pthread_join_ret, 3, threadid, result, pd_result);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
173
pthread-mutex-barrier.patch
Normal file
173
pthread-mutex-barrier.patch
Normal file
@ -0,0 +1,173 @@
|
|||||||
|
2019-02-07 Stefan Liebler <stli@linux.ibm.com>
|
||||||
|
|
||||||
|
[BZ #24180]
|
||||||
|
* nptl/pthread_mutex_trylock.c (__pthread_mutex_trylock):
|
||||||
|
Add compiler barriers and comments.
|
||||||
|
|
||||||
|
Index: glibc-2.29/nptl/pthread_mutex_trylock.c
|
||||||
|
===================================================================
|
||||||
|
--- glibc-2.29.orig/nptl/pthread_mutex_trylock.c
|
||||||
|
+++ glibc-2.29/nptl/pthread_mutex_trylock.c
|
||||||
|
@@ -94,6 +94,9 @@ __pthread_mutex_trylock (pthread_mutex_t
|
||||||
|
case PTHREAD_MUTEX_ROBUST_ADAPTIVE_NP:
|
||||||
|
THREAD_SETMEM (THREAD_SELF, robust_head.list_op_pending,
|
||||||
|
&mutex->__data.__list.__next);
|
||||||
|
+ /* We need to set op_pending before starting the operation. Also
|
||||||
|
+ see comments at ENQUEUE_MUTEX. */
|
||||||
|
+ __asm ("" ::: "memory");
|
||||||
|
|
||||||
|
oldval = mutex->__data.__lock;
|
||||||
|
do
|
||||||
|
@@ -119,7 +122,12 @@ __pthread_mutex_trylock (pthread_mutex_t
|
||||||
|
/* But it is inconsistent unless marked otherwise. */
|
||||||
|
mutex->__data.__owner = PTHREAD_MUTEX_INCONSISTENT;
|
||||||
|
|
||||||
|
+ /* We must not enqueue the mutex before we have acquired it.
|
||||||
|
+ Also see comments at ENQUEUE_MUTEX. */
|
||||||
|
+ __asm ("" ::: "memory");
|
||||||
|
ENQUEUE_MUTEX (mutex);
|
||||||
|
+ /* We need to clear op_pending after we enqueue the mutex. */
|
||||||
|
+ __asm ("" ::: "memory");
|
||||||
|
THREAD_SETMEM (THREAD_SELF, robust_head.list_op_pending, NULL);
|
||||||
|
|
||||||
|
/* Note that we deliberately exist here. If we fall
|
||||||
|
@@ -135,6 +143,8 @@ __pthread_mutex_trylock (pthread_mutex_t
|
||||||
|
int kind = PTHREAD_MUTEX_TYPE (mutex);
|
||||||
|
if (kind == PTHREAD_MUTEX_ROBUST_ERRORCHECK_NP)
|
||||||
|
{
|
||||||
|
+ /* We do not need to ensure ordering wrt another memory
|
||||||
|
+ access. Also see comments at ENQUEUE_MUTEX. */
|
||||||
|
THREAD_SETMEM (THREAD_SELF, robust_head.list_op_pending,
|
||||||
|
NULL);
|
||||||
|
return EDEADLK;
|
||||||
|
@@ -142,6 +152,8 @@ __pthread_mutex_trylock (pthread_mutex_t
|
||||||
|
|
||||||
|
if (kind == PTHREAD_MUTEX_ROBUST_RECURSIVE_NP)
|
||||||
|
{
|
||||||
|
+ /* We do not need to ensure ordering wrt another memory
|
||||||
|
+ access. */
|
||||||
|
THREAD_SETMEM (THREAD_SELF, robust_head.list_op_pending,
|
||||||
|
NULL);
|
||||||
|
|
||||||
|
@@ -160,6 +172,9 @@ __pthread_mutex_trylock (pthread_mutex_t
|
||||||
|
id, 0);
|
||||||
|
if (oldval != 0 && (oldval & FUTEX_OWNER_DIED) == 0)
|
||||||
|
{
|
||||||
|
+ /* We haven't acquired the lock as it is already acquired by
|
||||||
|
+ another owner. We do not need to ensure ordering wrt another
|
||||||
|
+ memory access. */
|
||||||
|
THREAD_SETMEM (THREAD_SELF, robust_head.list_op_pending, NULL);
|
||||||
|
|
||||||
|
return EBUSY;
|
||||||
|
@@ -173,13 +188,20 @@ __pthread_mutex_trylock (pthread_mutex_t
|
||||||
|
if (oldval == id)
|
||||||
|
lll_unlock (mutex->__data.__lock,
|
||||||
|
PTHREAD_ROBUST_MUTEX_PSHARED (mutex));
|
||||||
|
+ /* FIXME This violates the mutex destruction requirements. See
|
||||||
|
+ __pthread_mutex_unlock_full. */
|
||||||
|
THREAD_SETMEM (THREAD_SELF, robust_head.list_op_pending, NULL);
|
||||||
|
return ENOTRECOVERABLE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
while ((oldval & FUTEX_OWNER_DIED) != 0);
|
||||||
|
|
||||||
|
+ /* We must not enqueue the mutex before we have acquired it.
|
||||||
|
+ Also see comments at ENQUEUE_MUTEX. */
|
||||||
|
+ __asm ("" ::: "memory");
|
||||||
|
ENQUEUE_MUTEX (mutex);
|
||||||
|
+ /* We need to clear op_pending after we enqueue the mutex. */
|
||||||
|
+ __asm ("" ::: "memory");
|
||||||
|
THREAD_SETMEM (THREAD_SELF, robust_head.list_op_pending, NULL);
|
||||||
|
|
||||||
|
mutex->__data.__owner = id;
|
||||||
|
@@ -211,10 +233,15 @@ __pthread_mutex_trylock (pthread_mutex_t
|
||||||
|
}
|
||||||
|
|
||||||
|
if (robust)
|
||||||
|
- /* Note: robust PI futexes are signaled by setting bit 0. */
|
||||||
|
- THREAD_SETMEM (THREAD_SELF, robust_head.list_op_pending,
|
||||||
|
- (void *) (((uintptr_t) &mutex->__data.__list.__next)
|
||||||
|
- | 1));
|
||||||
|
+ {
|
||||||
|
+ /* Note: robust PI futexes are signaled by setting bit 0. */
|
||||||
|
+ THREAD_SETMEM (THREAD_SELF, robust_head.list_op_pending,
|
||||||
|
+ (void *) (((uintptr_t) &mutex->__data.__list.__next)
|
||||||
|
+ | 1));
|
||||||
|
+ /* We need to set op_pending before starting the operation. Also
|
||||||
|
+ see comments at ENQUEUE_MUTEX. */
|
||||||
|
+ __asm ("" ::: "memory");
|
||||||
|
+ }
|
||||||
|
|
||||||
|
oldval = mutex->__data.__lock;
|
||||||
|
|
||||||
|
@@ -223,12 +250,16 @@ __pthread_mutex_trylock (pthread_mutex_t
|
||||||
|
{
|
||||||
|
if (kind == PTHREAD_MUTEX_ERRORCHECK_NP)
|
||||||
|
{
|
||||||
|
+ /* We do not need to ensure ordering wrt another memory
|
||||||
|
+ access. */
|
||||||
|
THREAD_SETMEM (THREAD_SELF, robust_head.list_op_pending, NULL);
|
||||||
|
return EDEADLK;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (kind == PTHREAD_MUTEX_RECURSIVE_NP)
|
||||||
|
{
|
||||||
|
+ /* We do not need to ensure ordering wrt another memory
|
||||||
|
+ access. */
|
||||||
|
THREAD_SETMEM (THREAD_SELF, robust_head.list_op_pending, NULL);
|
||||||
|
|
||||||
|
/* Just bump the counter. */
|
||||||
|
@@ -250,6 +281,9 @@ __pthread_mutex_trylock (pthread_mutex_t
|
||||||
|
{
|
||||||
|
if ((oldval & FUTEX_OWNER_DIED) == 0)
|
||||||
|
{
|
||||||
|
+ /* We haven't acquired the lock as it is already acquired by
|
||||||
|
+ another owner. We do not need to ensure ordering wrt another
|
||||||
|
+ memory access. */
|
||||||
|
THREAD_SETMEM (THREAD_SELF, robust_head.list_op_pending, NULL);
|
||||||
|
|
||||||
|
return EBUSY;
|
||||||
|
@@ -270,6 +304,9 @@ __pthread_mutex_trylock (pthread_mutex_t
|
||||||
|
if (INTERNAL_SYSCALL_ERROR_P (e, __err)
|
||||||
|
&& INTERNAL_SYSCALL_ERRNO (e, __err) == EWOULDBLOCK)
|
||||||
|
{
|
||||||
|
+ /* The kernel has not yet finished the mutex owner death.
|
||||||
|
+ We do not need to ensure ordering wrt another memory
|
||||||
|
+ access. */
|
||||||
|
THREAD_SETMEM (THREAD_SELF, robust_head.list_op_pending, NULL);
|
||||||
|
|
||||||
|
return EBUSY;
|
||||||
|
@@ -287,7 +324,12 @@ __pthread_mutex_trylock (pthread_mutex_t
|
||||||
|
/* But it is inconsistent unless marked otherwise. */
|
||||||
|
mutex->__data.__owner = PTHREAD_MUTEX_INCONSISTENT;
|
||||||
|
|
||||||
|
+ /* We must not enqueue the mutex before we have acquired it.
|
||||||
|
+ Also see comments at ENQUEUE_MUTEX. */
|
||||||
|
+ __asm ("" ::: "memory");
|
||||||
|
ENQUEUE_MUTEX (mutex);
|
||||||
|
+ /* We need to clear op_pending after we enqueue the mutex. */
|
||||||
|
+ __asm ("" ::: "memory");
|
||||||
|
THREAD_SETMEM (THREAD_SELF, robust_head.list_op_pending, NULL);
|
||||||
|
|
||||||
|
/* Note that we deliberately exit here. If we fall
|
||||||
|
@@ -310,13 +352,20 @@ __pthread_mutex_trylock (pthread_mutex_t
|
||||||
|
PTHREAD_ROBUST_MUTEX_PSHARED (mutex)),
|
||||||
|
0, 0);
|
||||||
|
|
||||||
|
+ /* To the kernel, this will be visible after the kernel has
|
||||||
|
+ acquired the mutex in the syscall. */
|
||||||
|
THREAD_SETMEM (THREAD_SELF, robust_head.list_op_pending, NULL);
|
||||||
|
return ENOTRECOVERABLE;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (robust)
|
||||||
|
{
|
||||||
|
+ /* We must not enqueue the mutex before we have acquired it.
|
||||||
|
+ Also see comments at ENQUEUE_MUTEX. */
|
||||||
|
+ __asm ("" ::: "memory");
|
||||||
|
ENQUEUE_MUTEX_PI (mutex);
|
||||||
|
+ /* We need to clear op_pending after we enqueue the mutex. */
|
||||||
|
+ __asm ("" ::: "memory");
|
||||||
|
THREAD_SETMEM (THREAD_SELF, robust_head.list_op_pending, NULL);
|
||||||
|
}
|
||||||
|
|
616
pthread-rwlock-trylock-stalls.patch
Normal file
616
pthread-rwlock-trylock-stalls.patch
Normal file
@ -0,0 +1,616 @@
|
|||||||
|
2019-01-31 Carlos O'Donell <carlos@redhat.com>
|
||||||
|
Torvald Riegel <triegel@redhat.com>
|
||||||
|
Rik Prohaska <prohaska7@gmail.com>
|
||||||
|
|
||||||
|
[BZ# 23844]
|
||||||
|
* nptl/Makefile (tests): Add tst-rwlock-tryrdlock-stall, and
|
||||||
|
tst-rwlock-trywrlock-stall.
|
||||||
|
* nptl/pthread_rwlock_tryrdlock.c (__pthread_rwlock_tryrdlock):
|
||||||
|
Wake waiters if PTHREAD_RWLOCK_FUTEX_USED is set.
|
||||||
|
* nptl/pthread_rwlock_trywrlock.c (__pthread_rwlock_trywrlock):
|
||||||
|
Set __wrphase_fute to 1 only if we started the write phase.
|
||||||
|
* nptl/tst-rwlock-tryrdlock-stall.c: New file.
|
||||||
|
* nptl/tst-rwlock-trywrlock-stall.c: New file.
|
||||||
|
* support/Makefile (libsupport-routines): Add xpthread_rwlock_destroy.
|
||||||
|
* support/xpthread_rwlock_destroy.c: New file.
|
||||||
|
* support/xthread.h: Declare xpthread_rwlock_destroy.
|
||||||
|
|
||||||
|
Index: glibc-2.29/nptl/Makefile
|
||||||
|
===================================================================
|
||||||
|
--- glibc-2.29.orig/nptl/Makefile
|
||||||
|
+++ glibc-2.29/nptl/Makefile
|
||||||
|
@@ -319,7 +319,8 @@ tests = tst-attr1 tst-attr2 tst-attr3 ts
|
||||||
|
tst-cnd-basic tst-mtx-trylock tst-cnd-broadcast \
|
||||||
|
tst-cnd-timedwait tst-thrd-detach tst-mtx-basic tst-thrd-sleep \
|
||||||
|
tst-mtx-recursive tst-tss-basic tst-call-once tst-mtx-timedlock \
|
||||||
|
- tst-rwlock-pwn
|
||||||
|
+ tst-rwlock-pwn \
|
||||||
|
+ tst-rwlock-tryrdlock-stall tst-rwlock-trywrlock-stall
|
||||||
|
|
||||||
|
tests-internal := tst-rwlock19 tst-rwlock20 \
|
||||||
|
tst-sem11 tst-sem12 tst-sem13 \
|
||||||
|
Index: glibc-2.29/nptl/pthread_rwlock_tryrdlock.c
|
||||||
|
===================================================================
|
||||||
|
--- glibc-2.29.orig/nptl/pthread_rwlock_tryrdlock.c
|
||||||
|
+++ glibc-2.29/nptl/pthread_rwlock_tryrdlock.c
|
||||||
|
@@ -94,15 +94,22 @@ __pthread_rwlock_tryrdlock (pthread_rwlo
|
||||||
|
/* Same as in __pthread_rwlock_rdlock_full:
|
||||||
|
We started the read phase, so we are also responsible for
|
||||||
|
updating the write-phase futex. Relaxed MO is sufficient.
|
||||||
|
- Note that there can be no other reader that we have to wake
|
||||||
|
- because all other readers will see the read phase started by us
|
||||||
|
- (or they will try to start it themselves); if a writer started
|
||||||
|
- the read phase, we cannot have started it. Furthermore, we
|
||||||
|
- cannot discard a PTHREAD_RWLOCK_FUTEX_USED flag because we will
|
||||||
|
- overwrite the value set by the most recent writer (or the readers
|
||||||
|
- before it in case of explicit hand-over) and we know that there
|
||||||
|
- are no waiting readers. */
|
||||||
|
- atomic_store_relaxed (&rwlock->__data.__wrphase_futex, 0);
|
||||||
|
+ We have to do the same steps as a writer would when handing over the
|
||||||
|
+ read phase to use because other readers cannot distinguish between
|
||||||
|
+ us and the writer.
|
||||||
|
+ Note that __pthread_rwlock_tryrdlock callers will not have to be
|
||||||
|
+ woken up because they will either see the read phase started by us
|
||||||
|
+ or they will try to start it themselves; however, callers of
|
||||||
|
+ __pthread_rwlock_rdlock_full just increase the reader count and then
|
||||||
|
+ check what state the lock is in, so they cannot distinguish between
|
||||||
|
+ us and a writer that acquired and released the lock in the
|
||||||
|
+ meantime. */
|
||||||
|
+ if ((atomic_exchange_relaxed (&rwlock->__data.__wrphase_futex, 0)
|
||||||
|
+ & PTHREAD_RWLOCK_FUTEX_USED) != 0)
|
||||||
|
+ {
|
||||||
|
+ int private = __pthread_rwlock_get_private (rwlock);
|
||||||
|
+ futex_wake (&rwlock->__data.__wrphase_futex, INT_MAX, private);
|
||||||
|
+ }
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
Index: glibc-2.29/nptl/pthread_rwlock_trywrlock.c
|
||||||
|
===================================================================
|
||||||
|
--- glibc-2.29.orig/nptl/pthread_rwlock_trywrlock.c
|
||||||
|
+++ glibc-2.29/nptl/pthread_rwlock_trywrlock.c
|
||||||
|
@@ -46,8 +46,15 @@ __pthread_rwlock_trywrlock (pthread_rwlo
|
||||||
|
&rwlock->__data.__readers, &r,
|
||||||
|
r | PTHREAD_RWLOCK_WRPHASE | PTHREAD_RWLOCK_WRLOCKED))
|
||||||
|
{
|
||||||
|
+ /* We have become the primary writer and we cannot have shared
|
||||||
|
+ the PTHREAD_RWLOCK_FUTEX_USED flag with someone else, so we
|
||||||
|
+ can simply enable blocking (see full wrlock code). */
|
||||||
|
atomic_store_relaxed (&rwlock->__data.__writers_futex, 1);
|
||||||
|
- atomic_store_relaxed (&rwlock->__data.__wrphase_futex, 1);
|
||||||
|
+ /* If we started a write phase, we need to enable readers to
|
||||||
|
+ wait. If we did not, we must not change it because other threads
|
||||||
|
+ may have set the PTHREAD_RWLOCK_FUTEX_USED in the meantime. */
|
||||||
|
+ if ((r & PTHREAD_RWLOCK_WRPHASE) == 0)
|
||||||
|
+ atomic_store_relaxed (&rwlock->__data.__wrphase_futex, 1);
|
||||||
|
atomic_store_relaxed (&rwlock->__data.__cur_writer,
|
||||||
|
THREAD_GETMEM (THREAD_SELF, tid));
|
||||||
|
return 0;
|
||||||
|
Index: glibc-2.29/nptl/tst-rwlock-tryrdlock-stall.c
|
||||||
|
===================================================================
|
||||||
|
--- /dev/null
|
||||||
|
+++ glibc-2.29/nptl/tst-rwlock-tryrdlock-stall.c
|
||||||
|
@@ -0,0 +1,355 @@
|
||||||
|
+/* Bug 23844: Test for pthread_rwlock_tryrdlock stalls.
|
||||||
|
+ Copyright (C) 2019 Free Software Foundation, Inc.
|
||||||
|
+ This file is part of the GNU C Library.
|
||||||
|
+
|
||||||
|
+ The GNU C Library is free software; you can redistribute it and/or
|
||||||
|
+ modify it under the terms of the GNU Lesser General Public
|
||||||
|
+ License as published by the Free Software Foundation; either
|
||||||
|
+ version 2.1 of the License, or (at your option) any later version.
|
||||||
|
+
|
||||||
|
+ The GNU C Library is distributed in the hope that it will be useful,
|
||||||
|
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
+ Lesser General Public License for more details.
|
||||||
|
+
|
||||||
|
+ You should have received a copy of the GNU Lesser General Public
|
||||||
|
+ License along with the GNU C Library; if not, see
|
||||||
|
+ <http://www.gnu.org/licenses/>. */
|
||||||
|
+
|
||||||
|
+/* For a full analysis see comment:
|
||||||
|
+ https://sourceware.org/bugzilla/show_bug.cgi?id=23844#c14
|
||||||
|
+
|
||||||
|
+ Provided here for reference:
|
||||||
|
+
|
||||||
|
+ --- Analysis of pthread_rwlock_tryrdlock() stall ---
|
||||||
|
+ A read lock begins to execute.
|
||||||
|
+
|
||||||
|
+ In __pthread_rwlock_rdlock_full:
|
||||||
|
+
|
||||||
|
+ We can attempt a read lock, but find that the lock is
|
||||||
|
+ in a write phase (PTHREAD_RWLOCK_WRPHASE, or WP-bit
|
||||||
|
+ is set), and the lock is held by a primary writer
|
||||||
|
+ (PTHREAD_RWLOCK_WRLOCKED is set). In this case we must
|
||||||
|
+ wait for explicit hand over from the writer to us or
|
||||||
|
+ one of the other waiters. The read lock threads are
|
||||||
|
+ about to execute:
|
||||||
|
+
|
||||||
|
+ 341 r = (atomic_fetch_add_acquire (&rwlock->__data.__readers,
|
||||||
|
+ 342 (1 << PTHREAD_RWLOCK_READER_SHIFT))
|
||||||
|
+ 343 + (1 << PTHREAD_RWLOCK_READER_SHIFT));
|
||||||
|
+
|
||||||
|
+ An unlock beings to execute.
|
||||||
|
+
|
||||||
|
+ Then in __pthread_rwlock_wrunlock:
|
||||||
|
+
|
||||||
|
+ 547 unsigned int r = atomic_load_relaxed (&rwlock->__data.__readers);
|
||||||
|
+ ...
|
||||||
|
+ 549 while (!atomic_compare_exchange_weak_release
|
||||||
|
+ 550 (&rwlock->__data.__readers, &r,
|
||||||
|
+ 551 ((r ^ PTHREAD_RWLOCK_WRLOCKED)
|
||||||
|
+ 552 ^ ((r >> PTHREAD_RWLOCK_READER_SHIFT) == 0 ? 0
|
||||||
|
+ 553 : PTHREAD_RWLOCK_WRPHASE))))
|
||||||
|
+ 554 {
|
||||||
|
+ ...
|
||||||
|
+ 556 }
|
||||||
|
+
|
||||||
|
+ We clear PTHREAD_RWLOCK_WRLOCKED, and if there are
|
||||||
|
+ no readers so we leave the lock in PTHRAD_RWLOCK_WRPHASE.
|
||||||
|
+
|
||||||
|
+ Back in the read lock.
|
||||||
|
+
|
||||||
|
+ The read lock adjusts __readres as above.
|
||||||
|
+
|
||||||
|
+ 383 while ((r & PTHREAD_RWLOCK_WRPHASE) != 0
|
||||||
|
+ 384 && (r & PTHREAD_RWLOCK_WRLOCKED) == 0)
|
||||||
|
+ 385 {
|
||||||
|
+ ...
|
||||||
|
+ 390 if (atomic_compare_exchange_weak_acquire (&rwlock->__data.__readers, &r,
|
||||||
|
+ 391 r ^ PTHREAD_RWLOCK_WRPHASE))
|
||||||
|
+ 392 {
|
||||||
|
+
|
||||||
|
+ And then attemps to start the read phase.
|
||||||
|
+
|
||||||
|
+ Assume there happens to be a tryrdlock at this point, noting
|
||||||
|
+ that PTHREAD_RWLOCK_WRLOCKED is clear, and PTHREAD_RWLOCK_WRPHASE
|
||||||
|
+ is 1. So the try lock attemps to start the read phase.
|
||||||
|
+
|
||||||
|
+ In __pthread_rwlock_tryrdlock:
|
||||||
|
+
|
||||||
|
+ 44 if ((r & PTHREAD_RWLOCK_WRPHASE) == 0)
|
||||||
|
+ 45 {
|
||||||
|
+ ...
|
||||||
|
+ 49 if (((r & PTHREAD_RWLOCK_WRLOCKED) != 0)
|
||||||
|
+ 50 && (rwlock->__data.__flags
|
||||||
|
+ 51 == PTHREAD_RWLOCK_PREFER_WRITER_NONRECURSIVE_NP))
|
||||||
|
+ 52 return EBUSY;
|
||||||
|
+ 53 rnew = r + (1 << PTHREAD_RWLOCK_READER_SHIFT);
|
||||||
|
+ 54 }
|
||||||
|
+ ...
|
||||||
|
+ 89 while (!atomic_compare_exchange_weak_acquire (&rwlock->__data.__readers,
|
||||||
|
+ 90 &r, rnew));
|
||||||
|
+
|
||||||
|
+ And succeeds.
|
||||||
|
+
|
||||||
|
+ Back in the write unlock:
|
||||||
|
+
|
||||||
|
+ 557 if ((r >> PTHREAD_RWLOCK_READER_SHIFT) != 0)
|
||||||
|
+ 558 {
|
||||||
|
+ ...
|
||||||
|
+ 563 if ((atomic_exchange_relaxed (&rwlock->__data.__wrphase_futex, 0)
|
||||||
|
+ 564 & PTHREAD_RWLOCK_FUTEX_USED) != 0)
|
||||||
|
+ 565 futex_wake (&rwlock->__data.__wrphase_futex, INT_MAX, private);
|
||||||
|
+ 566 }
|
||||||
|
+
|
||||||
|
+ We note that PTHREAD_RWLOCK_FUTEX_USED is non-zero
|
||||||
|
+ and don't wake anyone. This is OK because we handed
|
||||||
|
+ over to the trylock. It will be the trylock's responsibility
|
||||||
|
+ to wake any waiters.
|
||||||
|
+
|
||||||
|
+ Back in the read lock:
|
||||||
|
+
|
||||||
|
+ The read lock fails to install PTHRAD_REWLOCK_WRPHASE as 0 because
|
||||||
|
+ the __readers value was adjusted by the trylock, and so it falls through
|
||||||
|
+ to waiting on the lock for explicit handover from either a new writer
|
||||||
|
+ or a new reader.
|
||||||
|
+
|
||||||
|
+ 448 int err = futex_abstimed_wait (&rwlock->__data.__wrphase_futex,
|
||||||
|
+ 449 1 | PTHREAD_RWLOCK_FUTEX_USED,
|
||||||
|
+ 450 abstime, private);
|
||||||
|
+
|
||||||
|
+ We use PTHREAD_RWLOCK_FUTEX_USED to indicate the futex
|
||||||
|
+ is in use.
|
||||||
|
+
|
||||||
|
+ At this point we have readers waiting on the read lock
|
||||||
|
+ to unlock. The wrlock is done. The trylock is finishing
|
||||||
|
+ the installation of the read phase.
|
||||||
|
+
|
||||||
|
+ 92 if ((r & PTHREAD_RWLOCK_WRPHASE) != 0)
|
||||||
|
+ 93 {
|
||||||
|
+ ...
|
||||||
|
+ 105 atomic_store_relaxed (&rwlock->__data.__wrphase_futex, 0);
|
||||||
|
+ 106 }
|
||||||
|
+
|
||||||
|
+ The trylock does note that we were the one that
|
||||||
|
+ installed the read phase, but the comments are not
|
||||||
|
+ correct, the execution ordering above shows that
|
||||||
|
+ readers might indeed be waiting, and they are.
|
||||||
|
+
|
||||||
|
+ The atomic_store_relaxed throws away PTHREAD_RWLOCK_FUTEX_USED,
|
||||||
|
+ and the waiting reader is never worken becuase as noted
|
||||||
|
+ above it is conditional on the futex being used.
|
||||||
|
+
|
||||||
|
+ The solution is for the trylock thread to inspect
|
||||||
|
+ PTHREAD_RWLOCK_FUTEX_USED and wake the waiting readers.
|
||||||
|
+
|
||||||
|
+ --- Analysis of pthread_rwlock_trywrlock() stall ---
|
||||||
|
+
|
||||||
|
+ A write lock begins to execute, takes the write lock,
|
||||||
|
+ and then releases the lock...
|
||||||
|
+
|
||||||
|
+ In pthread_rwlock_wrunlock():
|
||||||
|
+
|
||||||
|
+ 547 unsigned int r = atomic_load_relaxed (&rwlock->__data.__readers);
|
||||||
|
+ ...
|
||||||
|
+ 549 while (!atomic_compare_exchange_weak_release
|
||||||
|
+ 550 (&rwlock->__data.__readers, &r,
|
||||||
|
+ 551 ((r ^ PTHREAD_RWLOCK_WRLOCKED)
|
||||||
|
+ 552 ^ ((r >> PTHREAD_RWLOCK_READER_SHIFT) == 0 ? 0
|
||||||
|
+ 553 : PTHREAD_RWLOCK_WRPHASE))))
|
||||||
|
+ 554 {
|
||||||
|
+ ...
|
||||||
|
+ 556 }
|
||||||
|
+
|
||||||
|
+ ... leaving it in the write phase with zero readers
|
||||||
|
+ (the case where we leave the write phase in place
|
||||||
|
+ during a write unlock).
|
||||||
|
+
|
||||||
|
+ A write trylock begins to execute.
|
||||||
|
+
|
||||||
|
+ In __pthread_rwlock_trywrlock:
|
||||||
|
+
|
||||||
|
+ 40 while (((r & PTHREAD_RWLOCK_WRLOCKED) == 0)
|
||||||
|
+ 41 && (((r >> PTHREAD_RWLOCK_READER_SHIFT) == 0)
|
||||||
|
+ 42 || (prefer_writer && ((r & PTHREAD_RWLOCK_WRPHASE) != 0))))
|
||||||
|
+ 43 {
|
||||||
|
+
|
||||||
|
+ The lock is not locked.
|
||||||
|
+
|
||||||
|
+ There are no readers.
|
||||||
|
+
|
||||||
|
+ 45 if (atomic_compare_exchange_weak_acquire (
|
||||||
|
+ 46 &rwlock->__data.__readers, &r,
|
||||||
|
+ 47 r | PTHREAD_RWLOCK_WRPHASE | PTHREAD_RWLOCK_WRLOCKED))
|
||||||
|
+
|
||||||
|
+ We atomically install the write phase and we take the
|
||||||
|
+ exclusive write lock.
|
||||||
|
+
|
||||||
|
+ 48 {
|
||||||
|
+ 49 atomic_store_relaxed (&rwlock->__data.__writers_futex, 1);
|
||||||
|
+
|
||||||
|
+ We get this far.
|
||||||
|
+
|
||||||
|
+ A reader lock begins to execute.
|
||||||
|
+
|
||||||
|
+ In pthread_rwlock_rdlock:
|
||||||
|
+
|
||||||
|
+ 437 for (;;)
|
||||||
|
+ 438 {
|
||||||
|
+ 439 while (((wpf = atomic_load_relaxed (&rwlock->__data.__wrphase_futex))
|
||||||
|
+ 440 | PTHREAD_RWLOCK_FUTEX_USED) == (1 | PTHREAD_RWLOCK_FUTEX_USED))
|
||||||
|
+ 441 {
|
||||||
|
+ 442 int private = __pthread_rwlock_get_private (rwlock);
|
||||||
|
+ 443 if (((wpf & PTHREAD_RWLOCK_FUTEX_USED) == 0)
|
||||||
|
+ 444 && (!atomic_compare_exchange_weak_relaxed
|
||||||
|
+ 445 (&rwlock->__data.__wrphase_futex,
|
||||||
|
+ 446 &wpf, wpf | PTHREAD_RWLOCK_FUTEX_USED)))
|
||||||
|
+ 447 continue;
|
||||||
|
+ 448 int err = futex_abstimed_wait (&rwlock->__data.__wrphase_futex,
|
||||||
|
+ 449 1 | PTHREAD_RWLOCK_FUTEX_USED,
|
||||||
|
+ 450 abstime, private);
|
||||||
|
+
|
||||||
|
+ We are in a write phase, so the while() on line 439 is true.
|
||||||
|
+
|
||||||
|
+ The value of wpf does not have PTHREAD_RWLOCK_FUTEX_USED set
|
||||||
|
+ since this is the first reader to lock.
|
||||||
|
+
|
||||||
|
+ The atomic operation sets wpf with PTHREAD_RELOCK_FUTEX_USED
|
||||||
|
+ on the expectation that this reader will be woken during
|
||||||
|
+ the handoff.
|
||||||
|
+
|
||||||
|
+ Back in pthread_rwlock_trywrlock:
|
||||||
|
+
|
||||||
|
+ 50 atomic_store_relaxed (&rwlock->__data.__wrphase_futex, 1);
|
||||||
|
+ 51 atomic_store_relaxed (&rwlock->__data.__cur_writer,
|
||||||
|
+ 52 THREAD_GETMEM (THREAD_SELF, tid));
|
||||||
|
+ 53 return 0;
|
||||||
|
+ 54 }
|
||||||
|
+ ...
|
||||||
|
+ 57 }
|
||||||
|
+
|
||||||
|
+ We write 1 to __wrphase_futex discarding PTHREAD_RWLOCK_FUTEX_USED,
|
||||||
|
+ and so in the unlock we will not awaken the waiting reader.
|
||||||
|
+
|
||||||
|
+ The solution to this is to realize that if we did not start the write
|
||||||
|
+ phase we need not write 1 or any other value to __wrphase_futex.
|
||||||
|
+ This ensures that any readers (which saw __wrphase_futex != 0) can
|
||||||
|
+ set PTHREAD_RWLOCK_FUTEX_USED and this can be used at unlock to
|
||||||
|
+ wake them.
|
||||||
|
+
|
||||||
|
+ If we installed the write phase then all other readers are looping
|
||||||
|
+ here:
|
||||||
|
+
|
||||||
|
+ In __pthread_rwlock_rdlock_full:
|
||||||
|
+
|
||||||
|
+ 437 for (;;)
|
||||||
|
+ 438 {
|
||||||
|
+ 439 while (((wpf = atomic_load_relaxed (&rwlock->__data.__wrphase_futex))
|
||||||
|
+ 440 | PTHREAD_RWLOCK_FUTEX_USED) == (1 | PTHREAD_RWLOCK_FUTEX_USED))
|
||||||
|
+ 441 {
|
||||||
|
+ ...
|
||||||
|
+ 508 }
|
||||||
|
+
|
||||||
|
+ waiting for the write phase to be installed or removed before they
|
||||||
|
+ can begin waiting on __wrphase_futex (part of the algorithm), or
|
||||||
|
+ taking a concurrent read lock, and thus we can safely write 1 to
|
||||||
|
+ __wrphase_futex.
|
||||||
|
+
|
||||||
|
+ If we did not install the write phase then the readers may already
|
||||||
|
+ be waiting on the futex, the original writer wrote 1 to __wrphase_futex
|
||||||
|
+ as part of starting the write phase, and we cannot also write 1
|
||||||
|
+ without loosing the PTHREAD_RWLOCK_FUTEX_USED bit.
|
||||||
|
+
|
||||||
|
+ ---
|
||||||
|
+
|
||||||
|
+ Summary for the pthread_rwlock_tryrdlock() stall:
|
||||||
|
+
|
||||||
|
+ The stall is caused by pthread_rwlock_tryrdlock failing to check
|
||||||
|
+ that PTHREAD_RWLOCK_FUTEX_USED is set in the __wrphase_futex futex
|
||||||
|
+ and then waking the futex.
|
||||||
|
+
|
||||||
|
+ The fix for bug 23844 ensures that waiters on __wrphase_futex are
|
||||||
|
+ correctly woken. Before the fix the test stalls as readers can
|
||||||
|
+ wait forever on __wrphase_futex. */
|
||||||
|
+
|
||||||
|
+#include <stdio.h>
|
||||||
|
+#include <stdlib.h>
|
||||||
|
+#include <unistd.h>
|
||||||
|
+#include <pthread.h>
|
||||||
|
+#include <support/xthread.h>
|
||||||
|
+#include <errno.h>
|
||||||
|
+
|
||||||
|
+/* We need only one lock to reproduce the issue. We will need multiple
|
||||||
|
+ threads to get the exact case where we have a read, try, and unlock
|
||||||
|
+ all interleaving to produce the case where the readers are waiting
|
||||||
|
+ and the try fails to wake them. */
|
||||||
|
+pthread_rwlock_t onelock;
|
||||||
|
+
|
||||||
|
+/* The number of threads is arbitrary but empirically chosen to have
|
||||||
|
+ enough threads that we see the condition where waiting readers are
|
||||||
|
+ not woken by a successful tryrdlock. */
|
||||||
|
+#define NTHREADS 32
|
||||||
|
+
|
||||||
|
+_Atomic int do_exit;
|
||||||
|
+
|
||||||
|
+void *
|
||||||
|
+run_loop (void *arg)
|
||||||
|
+{
|
||||||
|
+ int i = 0, ret;
|
||||||
|
+ while (!do_exit)
|
||||||
|
+ {
|
||||||
|
+ /* Arbitrarily choose if we are the writer or reader. Choose a
|
||||||
|
+ high enough ratio of readers to writers to make it likely
|
||||||
|
+ that readers block (and eventually are susceptable to
|
||||||
|
+ stalling).
|
||||||
|
+
|
||||||
|
+ If we are a writer, take the write lock, and then unlock.
|
||||||
|
+ If we are a reader, try the lock, then lock, then unlock. */
|
||||||
|
+ if ((i % 8) != 0)
|
||||||
|
+ xpthread_rwlock_wrlock (&onelock);
|
||||||
|
+ else
|
||||||
|
+ {
|
||||||
|
+ if ((ret = pthread_rwlock_tryrdlock (&onelock)) != 0)
|
||||||
|
+ {
|
||||||
|
+ if (ret == EBUSY)
|
||||||
|
+ xpthread_rwlock_rdlock (&onelock);
|
||||||
|
+ else
|
||||||
|
+ exit (EXIT_FAILURE);
|
||||||
|
+ }
|
||||||
|
+ }
|
||||||
|
+ /* Thread does some work and then unlocks. */
|
||||||
|
+ xpthread_rwlock_unlock (&onelock);
|
||||||
|
+ i++;
|
||||||
|
+ }
|
||||||
|
+ return NULL;
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+int
|
||||||
|
+do_test (void)
|
||||||
|
+{
|
||||||
|
+ int i;
|
||||||
|
+ pthread_t tids[NTHREADS];
|
||||||
|
+ xpthread_rwlock_init (&onelock, NULL);
|
||||||
|
+ for (i = 0; i < NTHREADS; i++)
|
||||||
|
+ tids[i] = xpthread_create (NULL, run_loop, NULL);
|
||||||
|
+ /* Run for some amount of time. Empirically speaking exercising
|
||||||
|
+ the stall via pthread_rwlock_tryrdlock is much harder, and on
|
||||||
|
+ a 3.5GHz 4 core x86_64 VM system it takes somewhere around
|
||||||
|
+ 20-200s to stall, approaching 100% stall past 200s. We can't
|
||||||
|
+ wait that long for a regression test so we just test for 20s,
|
||||||
|
+ and expect the stall to happen with a 5-10% chance (enough for
|
||||||
|
+ developers to see). */
|
||||||
|
+ sleep (20);
|
||||||
|
+ /* Then exit. */
|
||||||
|
+ printf ("INFO: Exiting...\n");
|
||||||
|
+ do_exit = 1;
|
||||||
|
+ /* If any readers stalled then we will timeout waiting for them. */
|
||||||
|
+ for (i = 0; i < NTHREADS; i++)
|
||||||
|
+ xpthread_join (tids[i]);
|
||||||
|
+ printf ("INFO: Done.\n");
|
||||||
|
+ xpthread_rwlock_destroy (&onelock);
|
||||||
|
+ printf ("PASS: No pthread_rwlock_tryrdlock stalls detected.\n");
|
||||||
|
+ return 0;
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+#define TIMEOUT 30
|
||||||
|
+#include <support/test-driver.c>
|
||||||
|
Index: glibc-2.29/nptl/tst-rwlock-trywrlock-stall.c
|
||||||
|
===================================================================
|
||||||
|
--- /dev/null
|
||||||
|
+++ glibc-2.29/nptl/tst-rwlock-trywrlock-stall.c
|
||||||
|
@@ -0,0 +1,108 @@
|
||||||
|
+/* Bug 23844: Test for pthread_rwlock_trywrlock stalls.
|
||||||
|
+ Copyright (C) 2019 Free Software Foundation, Inc.
|
||||||
|
+ This file is part of the GNU C Library.
|
||||||
|
+
|
||||||
|
+ The GNU C Library is free software; you can redistribute it and/or
|
||||||
|
+ modify it under the terms of the GNU Lesser General Public
|
||||||
|
+ License as published by the Free Software Foundation; either
|
||||||
|
+ version 2.1 of the License, or (at your option) any later version.
|
||||||
|
+
|
||||||
|
+ The GNU C Library is distributed in the hope that it will be useful,
|
||||||
|
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
+ Lesser General Public License for more details.
|
||||||
|
+
|
||||||
|
+ You should have received a copy of the GNU Lesser General Public
|
||||||
|
+ License along with the GNU C Library; if not, see
|
||||||
|
+ <http://www.gnu.org/licenses/>. */
|
||||||
|
+
|
||||||
|
+/* For a full analysis see comments in tst-rwlock-tryrdlock-stall.c.
|
||||||
|
+
|
||||||
|
+ Summary for the pthread_rwlock_trywrlock() stall:
|
||||||
|
+
|
||||||
|
+ The stall is caused by pthread_rwlock_trywrlock setting
|
||||||
|
+ __wrphase_futex futex to 1 and loosing the
|
||||||
|
+ PTHREAD_RWLOCK_FUTEX_USED bit.
|
||||||
|
+
|
||||||
|
+ The fix for bug 23844 ensures that waiters on __wrphase_futex are
|
||||||
|
+ correctly woken. Before the fix the test stalls as readers can
|
||||||
|
+ wait forever on __wrphase_futex. */
|
||||||
|
+
|
||||||
|
+#include <stdio.h>
|
||||||
|
+#include <stdlib.h>
|
||||||
|
+#include <unistd.h>
|
||||||
|
+#include <pthread.h>
|
||||||
|
+#include <support/xthread.h>
|
||||||
|
+#include <errno.h>
|
||||||
|
+
|
||||||
|
+/* We need only one lock to reproduce the issue. We will need multiple
|
||||||
|
+ threads to get the exact case where we have a read, try, and unlock
|
||||||
|
+ all interleaving to produce the case where the readers are waiting
|
||||||
|
+ and the try clears the PTHREAD_RWLOCK_FUTEX_USED bit and a
|
||||||
|
+ subsequent unlock fails to wake them. */
|
||||||
|
+pthread_rwlock_t onelock;
|
||||||
|
+
|
||||||
|
+/* The number of threads is arbitrary but empirically chosen to have
|
||||||
|
+ enough threads that we see the condition where waiting readers are
|
||||||
|
+ not woken by a successful unlock. */
|
||||||
|
+#define NTHREADS 32
|
||||||
|
+
|
||||||
|
+_Atomic int do_exit;
|
||||||
|
+
|
||||||
|
+void *
|
||||||
|
+run_loop (void *arg)
|
||||||
|
+{
|
||||||
|
+ int i = 0, ret;
|
||||||
|
+ while (!do_exit)
|
||||||
|
+ {
|
||||||
|
+ /* Arbitrarily choose if we are the writer or reader. Choose a
|
||||||
|
+ high enough ratio of readers to writers to make it likely
|
||||||
|
+ that readers block (and eventually are susceptable to
|
||||||
|
+ stalling).
|
||||||
|
+
|
||||||
|
+ If we are a writer, take the write lock, and then unlock.
|
||||||
|
+ If we are a reader, try the lock, then lock, then unlock. */
|
||||||
|
+ if ((i % 8) != 0)
|
||||||
|
+ {
|
||||||
|
+ if ((ret = pthread_rwlock_trywrlock (&onelock)) != 0)
|
||||||
|
+ {
|
||||||
|
+ if (ret == EBUSY)
|
||||||
|
+ xpthread_rwlock_wrlock (&onelock);
|
||||||
|
+ else
|
||||||
|
+ exit (EXIT_FAILURE);
|
||||||
|
+ }
|
||||||
|
+ }
|
||||||
|
+ else
|
||||||
|
+ xpthread_rwlock_rdlock (&onelock);
|
||||||
|
+ /* Thread does some work and then unlocks. */
|
||||||
|
+ xpthread_rwlock_unlock (&onelock);
|
||||||
|
+ i++;
|
||||||
|
+ }
|
||||||
|
+ return NULL;
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+int
|
||||||
|
+do_test (void)
|
||||||
|
+{
|
||||||
|
+ int i;
|
||||||
|
+ pthread_t tids[NTHREADS];
|
||||||
|
+ xpthread_rwlock_init (&onelock, NULL);
|
||||||
|
+ for (i = 0; i < NTHREADS; i++)
|
||||||
|
+ tids[i] = xpthread_create (NULL, run_loop, NULL);
|
||||||
|
+ /* Run for some amount of time. The pthread_rwlock_tryrwlock stall
|
||||||
|
+ is very easy to trigger and happens in seconds under the test
|
||||||
|
+ conditions. */
|
||||||
|
+ sleep (10);
|
||||||
|
+ /* Then exit. */
|
||||||
|
+ printf ("INFO: Exiting...\n");
|
||||||
|
+ do_exit = 1;
|
||||||
|
+ /* If any readers stalled then we will timeout waiting for them. */
|
||||||
|
+ for (i = 0; i < NTHREADS; i++)
|
||||||
|
+ xpthread_join (tids[i]);
|
||||||
|
+ printf ("INFO: Done.\n");
|
||||||
|
+ xpthread_rwlock_destroy (&onelock);
|
||||||
|
+ printf ("PASS: No pthread_rwlock_tryrwlock stalls detected.\n");
|
||||||
|
+ return 0;
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+#include <support/test-driver.c>
|
||||||
|
Index: glibc-2.29/support/Makefile
|
||||||
|
===================================================================
|
||||||
|
--- glibc-2.29.orig/support/Makefile
|
||||||
|
+++ glibc-2.29/support/Makefile
|
||||||
|
@@ -129,6 +129,7 @@ libsupport-routines = \
|
||||||
|
xpthread_mutexattr_settype \
|
||||||
|
xpthread_once \
|
||||||
|
xpthread_rwlock_init \
|
||||||
|
+ xpthread_rwlock_destroy \
|
||||||
|
xpthread_rwlock_rdlock \
|
||||||
|
xpthread_rwlock_unlock \
|
||||||
|
xpthread_rwlock_wrlock \
|
||||||
|
Index: glibc-2.29/support/xpthread_rwlock_destroy.c
|
||||||
|
===================================================================
|
||||||
|
--- /dev/null
|
||||||
|
+++ glibc-2.29/support/xpthread_rwlock_destroy.c
|
||||||
|
@@ -0,0 +1,26 @@
|
||||||
|
+/* pthread_rwlock_destroy with error checking.
|
||||||
|
+ Copyright (C) 2019 Free Software Foundation, Inc.
|
||||||
|
+ This file is part of the GNU C Library.
|
||||||
|
+
|
||||||
|
+ The GNU C Library is free software; you can redistribute it and/or
|
||||||
|
+ modify it under the terms of the GNU Lesser General Public
|
||||||
|
+ License as published by the Free Software Foundation; either
|
||||||
|
+ version 2.1 of the License, or (at your option) any later version.
|
||||||
|
+
|
||||||
|
+ The GNU C Library is distributed in the hope that it will be useful,
|
||||||
|
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
+ Lesser General Public License for more details.
|
||||||
|
+
|
||||||
|
+ You should have received a copy of the GNU Lesser General Public
|
||||||
|
+ License along with the GNU C Library; if not, see
|
||||||
|
+ <http://www.gnu.org/licenses/>. */
|
||||||
|
+
|
||||||
|
+#include <support/xthread.h>
|
||||||
|
+
|
||||||
|
+void
|
||||||
|
+xpthread_rwlock_destroy (pthread_rwlock_t *rwlock)
|
||||||
|
+{
|
||||||
|
+ xpthread_check_return ("pthread_rwlock_destroy",
|
||||||
|
+ pthread_rwlock_destroy (rwlock));
|
||||||
|
+}
|
||||||
|
Index: glibc-2.29/support/xthread.h
|
||||||
|
===================================================================
|
||||||
|
--- glibc-2.29.orig/support/xthread.h
|
||||||
|
+++ glibc-2.29/support/xthread.h
|
||||||
|
@@ -84,6 +84,7 @@ void xpthread_rwlockattr_setkind_np (pth
|
||||||
|
void xpthread_rwlock_wrlock (pthread_rwlock_t *rwlock);
|
||||||
|
void xpthread_rwlock_rdlock (pthread_rwlock_t *rwlock);
|
||||||
|
void xpthread_rwlock_unlock (pthread_rwlock_t *rwlock);
|
||||||
|
+void xpthread_rwlock_destroy (pthread_rwlock_t *rwlock);
|
||||||
|
|
||||||
|
__END_DECLS
|
||||||
|
|
119
riscv-clone-unwind.patch
Normal file
119
riscv-clone-unwind.patch
Normal file
@ -0,0 +1,119 @@
|
|||||||
|
2019-01-13 Jim Wilson <jimw@sifive.com>
|
||||||
|
|
||||||
|
[BZ #24040]
|
||||||
|
* elf/Makefile (CFLAGS-tst-unwind-main.c): Add -DUSE_PTHREADS=0.
|
||||||
|
* elf/tst-unwind-main.c: If USE_PTHEADS, include pthread.h and error.h
|
||||||
|
(func): New.
|
||||||
|
(main): If USE_PTHREADS, call pthread_create to run func. Otherwise
|
||||||
|
call func directly.
|
||||||
|
* nptl/Makefile (tests): Add tst-unwind-thread.
|
||||||
|
(CFLAGS-tst-unwind-thread.c): Define.
|
||||||
|
* nptl/tst-unwind-thread.c: New file.
|
||||||
|
* sysdeps/unix/sysv/linux/riscv/clone.S (__thread_start): Mark ra
|
||||||
|
as undefined.
|
||||||
|
|
||||||
|
Index: glibc-2.29/elf/Makefile
|
||||||
|
===================================================================
|
||||||
|
--- glibc-2.29.orig/elf/Makefile
|
||||||
|
+++ glibc-2.29/elf/Makefile
|
||||||
|
@@ -1497,4 +1497,4 @@ $(objpfx)tst-big-note: $(objpfx)tst-big-
|
||||||
|
|
||||||
|
$(objpfx)tst-unwind-ctor: $(objpfx)tst-unwind-ctor-lib.so
|
||||||
|
|
||||||
|
-CFLAGS-tst-unwind-main.c += -funwind-tables
|
||||||
|
+CFLAGS-tst-unwind-main.c += -funwind-tables -DUSE_PTHREADS=0
|
||||||
|
Index: glibc-2.29/elf/tst-unwind-main.c
|
||||||
|
===================================================================
|
||||||
|
--- glibc-2.29.orig/elf/tst-unwind-main.c
|
||||||
|
+++ glibc-2.29/elf/tst-unwind-main.c
|
||||||
|
@@ -20,19 +20,41 @@
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <support/test-driver.h>
|
||||||
|
|
||||||
|
+#if USE_PTHREADS
|
||||||
|
+# include <pthread.h>
|
||||||
|
+# include <error.h>
|
||||||
|
+#endif
|
||||||
|
+
|
||||||
|
static _Unwind_Reason_Code
|
||||||
|
callback (struct _Unwind_Context *ctx, void *arg)
|
||||||
|
{
|
||||||
|
return _URC_NO_REASON;
|
||||||
|
}
|
||||||
|
|
||||||
|
-int
|
||||||
|
-main (void)
|
||||||
|
+static void *
|
||||||
|
+func (void *a)
|
||||||
|
{
|
||||||
|
/* Arrange for this test to be killed if _Unwind_Backtrace runs into an
|
||||||
|
endless loop. We cannot use the test driver because the complete
|
||||||
|
call chain needs to be compiled with -funwind-tables so that
|
||||||
|
- _Unwind_Backtrace is able to reach _start. */
|
||||||
|
+ _Unwind_Backtrace is able to reach the start routine. */
|
||||||
|
alarm (DEFAULT_TIMEOUT);
|
||||||
|
_Unwind_Backtrace (callback, 0);
|
||||||
|
+ return a;
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+int
|
||||||
|
+main (void)
|
||||||
|
+{
|
||||||
|
+#if USE_PTHREADS
|
||||||
|
+ pthread_t thr;
|
||||||
|
+ int rc = pthread_create (&thr, NULL, &func, NULL);
|
||||||
|
+ if (rc)
|
||||||
|
+ error (1, rc, "pthread_create");
|
||||||
|
+ rc = pthread_join (thr, NULL);
|
||||||
|
+ if (rc)
|
||||||
|
+ error (1, rc, "pthread_join");
|
||||||
|
+#else
|
||||||
|
+ func (NULL);
|
||||||
|
+#endif
|
||||||
|
}
|
||||||
|
Index: glibc-2.29/nptl/Makefile
|
||||||
|
===================================================================
|
||||||
|
--- glibc-2.29.orig/nptl/Makefile
|
||||||
|
+++ glibc-2.29/nptl/Makefile
|
||||||
|
@@ -320,7 +320,8 @@ tests = tst-attr1 tst-attr2 tst-attr3 ts
|
||||||
|
tst-cnd-timedwait tst-thrd-detach tst-mtx-basic tst-thrd-sleep \
|
||||||
|
tst-mtx-recursive tst-tss-basic tst-call-once tst-mtx-timedlock \
|
||||||
|
tst-rwlock-pwn \
|
||||||
|
- tst-rwlock-tryrdlock-stall tst-rwlock-trywrlock-stall
|
||||||
|
+ tst-rwlock-tryrdlock-stall tst-rwlock-trywrlock-stall \
|
||||||
|
+ tst-unwind-thread
|
||||||
|
|
||||||
|
tests-internal := tst-rwlock19 tst-rwlock20 \
|
||||||
|
tst-sem11 tst-sem12 tst-sem13 \
|
||||||
|
@@ -710,6 +711,8 @@ $(objpfx)tst-audit-threads: $(objpfx)tst
|
||||||
|
$(objpfx)tst-audit-threads.out: $(objpfx)tst-audit-threads-mod1.so
|
||||||
|
tst-audit-threads-ENV = LD_AUDIT=$(objpfx)tst-audit-threads-mod1.so
|
||||||
|
|
||||||
|
+CFLAGS-tst-unwind-thread.c += -funwind-tables
|
||||||
|
+
|
||||||
|
# The tests here better do not run in parallel
|
||||||
|
ifneq ($(filter %tests,$(MAKECMDGOALS)),)
|
||||||
|
.NOTPARALLEL:
|
||||||
|
Index: glibc-2.29/nptl/tst-unwind-thread.c
|
||||||
|
===================================================================
|
||||||
|
--- /dev/null
|
||||||
|
+++ glibc-2.29/nptl/tst-unwind-thread.c
|
||||||
|
@@ -0,0 +1,2 @@
|
||||||
|
+#define USE_PTHREADS 1
|
||||||
|
+#include "../elf/tst-unwind-main.c"
|
||||||
|
Index: glibc-2.29/sysdeps/unix/sysv/linux/riscv/clone.S
|
||||||
|
===================================================================
|
||||||
|
--- glibc-2.29.orig/sysdeps/unix/sysv/linux/riscv/clone.S
|
||||||
|
+++ glibc-2.29/sysdeps/unix/sysv/linux/riscv/clone.S
|
||||||
|
@@ -69,6 +69,11 @@ L (error):
|
||||||
|
|
||||||
|
ENTRY (__thread_start)
|
||||||
|
L (thread_start):
|
||||||
|
+ /* Terminate call stack by noting ra is undefined. Use a dummy
|
||||||
|
+ .cfi_label to force starting the FDE. */
|
||||||
|
+ .cfi_label .Ldummy
|
||||||
|
+ cfi_undefined (ra)
|
||||||
|
+
|
||||||
|
/* Restore the arg for user's function. */
|
||||||
|
REG_L a1,0(sp) /* Function pointer. */
|
||||||
|
REG_L a0,SZREG(sp) /* Argument pointer. */
|
Loading…
Reference in New Issue
Block a user