Message ID | 20201125202723.1513496-1-adhemerval.zanella@linaro.org |
---|---|
State | Committed |
Headers |
Return-Path: <libc-alpha-bounces@sourceware.org> X-Original-To: patchwork@sourceware.org Delivered-To: patchwork@sourceware.org Received: from server2.sourceware.org (localhost [IPv6:::1]) by sourceware.org (Postfix) with ESMTP id 8A670395CC49; Wed, 25 Nov 2020 20:27:35 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org 8A670395CC49 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=sourceware.org; s=default; t=1606336055; bh=BcDhopMUUpEQALVpV3DmJ1aKKd10T4ZVAymcibDjlRo=; h=To:Subject:Date:List-Id:List-Unsubscribe:List-Archive:List-Post: List-Help:List-Subscribe:From:Reply-To:Cc:From; b=pxhDGwUu0lsbYmKN2bovrnK6PDo7AvDi27aywZ0yh/ofwSWJoqzfv3nk6i152Kb9j K/wGc6kCcB4m+h2EBYdbqe+Y1f3NfBiYnMGXOIBiMloUPn34gjCm3fB8aEwIulCUGV NeSsGHKGTz296MSm6MzGKVO3IyXUry+WU9R8YGDA= X-Original-To: libc-alpha@sourceware.org Delivered-To: libc-alpha@sourceware.org Received: from mail-qt1-x842.google.com (mail-qt1-x842.google.com [IPv6:2607:f8b0:4864:20::842]) by sourceware.org (Postfix) with ESMTPS id DA41B396E044 for <libc-alpha@sourceware.org>; Wed, 25 Nov 2020 20:27:29 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.3.2 sourceware.org DA41B396E044 Received: by mail-qt1-x842.google.com with SMTP id 7so2580122qtp.1 for <libc-alpha@sourceware.org>; Wed, 25 Nov 2020 12:27:29 -0800 (PST) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:mime-version :content-transfer-encoding; bh=BcDhopMUUpEQALVpV3DmJ1aKKd10T4ZVAymcibDjlRo=; b=gA67D0YGtg+KZXWaLSi7Yl7jzmzqjsVg/C53sJ5EL/b8pBF7PGvveVZ1UqKH3JQeXw IEF26LFRWcOmr7dPwI2zuyANamTuZHa14SranzoVZTBwZRieI2E8Pt/LNb9jAWOdWpzc BVwth0BRjfQQ554cbOJeS6V/hcOuivqo/12yY/AVZ3soL9P3ZOF2FdzODZ2sTmJUg6eC c2s1KrJd2fO3VJVJo/hG8/iCzcW+ZjdAWf3tkFOt3FXPOWcMtR+zA0RlgUwgx2QjinGu 7npdX/+np5C6UhZdbkvbM/kZKU0H5xMI9hOyjae4xwGNFIqsKPgG5oRdXmqDACqLBSEw v4/Q== X-Gm-Message-State: AOAM533v7UhBiKAOmCydkMI80jnyotPllwNhunXZX1nbHno+NaSHIE5c 9vKTce0x9js5Wv6gs6MgtFJvXZMamJB9yA== X-Google-Smtp-Source: ABdhPJwweGmQNJ4WgX0CuiW576S5SSVKoEI3t+hC4fIs/oQpewyb5h+Ng5fqcr+EdONJqj9oKIoWgg== X-Received: by 2002:ac8:5308:: with SMTP id t8mr631605qtn.85.1606336049140; Wed, 25 Nov 2020 12:27:29 -0800 (PST) Received: from localhost.localdomain ([177.194.48.209]) by smtp.googlemail.com with ESMTPSA id z20sm460671qtb.31.2020.11.25.12.27.27 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 25 Nov 2020 12:27:28 -0800 (PST) To: libc-alpha@sourceware.org, Mike Crowe <mac@mcrowe.com> Subject: [PATCH] nptl: Fix PTHREAD_PRIO_PROTECT timed lock Date: Wed, 25 Nov 2020 17:27:23 -0300 Message-Id: <20201125202723.1513496-1-adhemerval.zanella@linaro.org> X-Mailer: git-send-email 2.25.1 MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Spam-Status: No, score=-13.8 required=5.0 tests=BAYES_00, DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, DKIM_VALID_EF, GIT_PATCH_0, RCVD_IN_DNSWL_NONE, SPF_HELO_NONE, SPF_PASS, TXREP autolearn=ham autolearn_force=no version=3.4.2 X-Spam-Checker-Version: SpamAssassin 3.4.2 (2018-09-13) on server2.sourceware.org X-BeenThere: libc-alpha@sourceware.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: Libc-alpha mailing list <libc-alpha.sourceware.org> List-Unsubscribe: <https://sourceware.org/mailman/options/libc-alpha>, <mailto:libc-alpha-request@sourceware.org?subject=unsubscribe> List-Archive: <https://sourceware.org/pipermail/libc-alpha/> List-Post: <mailto:libc-alpha@sourceware.org> List-Help: <mailto:libc-alpha-request@sourceware.org?subject=help> List-Subscribe: <https://sourceware.org/mailman/listinfo/libc-alpha>, <mailto:libc-alpha-request@sourceware.org?subject=subscribe> From: Adhemerval Zanella via Libc-alpha <libc-alpha@sourceware.org> Reply-To: Adhemerval Zanella <adhemerval.zanella@linaro.org> Cc: Michael Kerrisk <mtk.manpages@gmail.com> Errors-To: libc-alpha-bounces@sourceware.org Sender: "Libc-alpha" <libc-alpha-bounces@sourceware.org> |
Series |
nptl: Fix PTHREAD_PRIO_PROTECT timed lock
|
|
Commit Message
Adhemerval Zanella Netto
Nov. 25, 2020, 8:27 p.m. UTC
The 878fe624d4 changed lll_futex_timed_wait, which expects a relative timeout, with a __futex_abstimed_wait64, which expects an absolute timeout. However the code still passes a relative timeout. Also, the PTHREAD_PRIO_PROTECT support for clocks different than CLOCK_REALTIME was broken since the inclusion of pthread_mutex_clocklock (9d20e22e46) since lll_futex_timed_wait always use CLOCK_REALTIME. This patch fixes by removing the relative time calculation. It also adds some xtests that tests both thread and inter-process usage. Checked on x86_64-linux-gnu. --- nptl/Makefile | 3 ++- nptl/pthread_mutex_timedlock.c | 29 +++++------------------------ nptl/tst-mutexpp5.c | 2 ++ nptl/tst-mutexpp9.c | 2 ++ sysdeps/pthread/tst-mutex5.c | 12 +++++++++++- sysdeps/pthread/tst-mutex9.c | 13 ++++++++++++- 6 files changed, 34 insertions(+), 27 deletions(-) create mode 100644 nptl/tst-mutexpp5.c create mode 100644 nptl/tst-mutexpp9.c
Comments
On Wednesday 25 November 2020 at 17:27:23 -0300, Adhemerval Zanella wrote: > The 878fe624d4 changed lll_futex_timed_wait, which expects a relative > timeout, with a __futex_abstimed_wait64, which expects an absolute > timeout. However the code still passes a relative timeout. > > Also, the PTHREAD_PRIO_PROTECT support for clocks different than > CLOCK_REALTIME was broken since the inclusion of > pthread_mutex_clocklock (9d20e22e46) since lll_futex_timed_wait > always use CLOCK_REALTIME. > > This patch fixes by removing the relative time calculation. It > also adds some xtests that tests both thread and inter-process > usage. > > Checked on x86_64-linux-gnu. > --- > nptl/Makefile | 3 ++- > nptl/pthread_mutex_timedlock.c | 29 +++++------------------------ > nptl/tst-mutexpp5.c | 2 ++ > nptl/tst-mutexpp9.c | 2 ++ > sysdeps/pthread/tst-mutex5.c | 12 +++++++++++- > sysdeps/pthread/tst-mutex9.c | 13 ++++++++++++- > 6 files changed, 34 insertions(+), 27 deletions(-) > create mode 100644 nptl/tst-mutexpp5.c > create mode 100644 nptl/tst-mutexpp9.c > > diff --git a/nptl/Makefile b/nptl/Makefile > index a48426a396..94d805f0d4 100644 > --- a/nptl/Makefile > +++ b/nptl/Makefile > @@ -309,7 +309,8 @@ tests-internal := tst-robustpi8 tst-rwlock19 tst-rwlock20 \ > tst-setgetname \ > > xtests = tst-setuid1 tst-setuid1-static tst-setuid2 \ > - tst-mutexpp1 tst-mutexpp6 tst-mutexpp10 tst-setgroups > + tst-mutexpp1 tst-mutexpp6 tst-mutexpp10 tst-setgroups \ > + tst-mutexpp5 tst-mutexpp9 > > # This test can run into task limits because of a linux kernel bug > # and then cause the make process to fail too, see bug 24537. > diff --git a/nptl/pthread_mutex_timedlock.c b/nptl/pthread_mutex_timedlock.c > index aaaafa21ce..74adffe790 100644 > --- a/nptl/pthread_mutex_timedlock.c > +++ b/nptl/pthread_mutex_timedlock.c > @@ -547,30 +547,11 @@ __pthread_mutex_clocklock_common (pthread_mutex_t *mutex, > goto failpp; > } > > - struct __timespec64 rt; > - > - /* Get the current time. */ > - __clock_gettime64 (CLOCK_REALTIME, &rt); > - > - /* Compute relative timeout. */ > - rt.tv_sec = abstime->tv_sec - rt.tv_sec; > - rt.tv_nsec = abstime->tv_nsec - rt.tv_nsec; > - if (rt.tv_nsec < 0) > - { > - rt.tv_nsec += 1000000000; > - --rt.tv_sec; > - } > - > - /* Already timed out? */ > - if (rt.tv_sec < 0) > - { > - result = ETIMEDOUT; > - goto failpp; > - } > - > - __futex_abstimed_wait64 ( > - (unsigned int *) &mutex->__data.__lock, clockid, > - ceilval | 2, &rt, PTHREAD_MUTEX_PSHARED (mutex)); > + int e = __futex_abstimed_wait64 ( > + (unsigned int *) &mutex->__data.__lock, ceilval | 2, > + clockid, abstime, PTHREAD_MUTEX_PSHARED (mutex)); > + if (e == ETIMEDOUT) > + return ETIMEDOUT; I'm worried that futex could return other errors here which would cause a busy infinite loop. However, my attempts to provoke EINVAL have failed since the validity of abstime.tv_nsec is checked earlier. Presumably we should never get this far if EOVERFLOW could be returned? (If there is a problem here, then it also affects other mutex types which have similar code.) > } > } > while (atomic_compare_and_exchange_val_acq (&mutex->__data.__lock, > diff --git a/nptl/tst-mutexpp5.c b/nptl/tst-mutexpp5.c > new file mode 100644 > index 0000000000..a864a390ca > --- /dev/null > +++ b/nptl/tst-mutexpp5.c > @@ -0,0 +1,2 @@ > +#define ENABLE_PP 1 > +#include "tst-mutex5.c" > diff --git a/nptl/tst-mutexpp9.c b/nptl/tst-mutexpp9.c > new file mode 100644 > index 0000000000..c848c74c7e > --- /dev/null > +++ b/nptl/tst-mutexpp9.c > @@ -0,0 +1,2 @@ > +#define ENABLE_PP 1 > +#include "tst-mutex9.c" > diff --git a/sysdeps/pthread/tst-mutex5.c b/sysdeps/pthread/tst-mutex5.c > index bfe1a79fa4..dbd2c3c15f 100644 > --- a/sysdeps/pthread/tst-mutex5.c > +++ b/sysdeps/pthread/tst-mutex5.c > @@ -27,6 +27,9 @@ > #include <support/check.h> > #include <support/timespec.h> > > +#ifdef ENABLE_PP > +#include "tst-tpp.h" > +#endif > > #ifndef TYPE > # define TYPE PTHREAD_MUTEX_NORMAL > @@ -47,8 +50,11 @@ do_test_clock (clockid_t clockid, const char *fnname) > TEST_COMPARE (pthread_mutexattr_init (&a), 0); > TEST_COMPARE (pthread_mutexattr_settype (&a, TYPE), 0); > > -#ifdef ENABLE_PI > +#if defined ENABLE_PI > TEST_COMPARE (pthread_mutexattr_setprotocol (&a, PTHREAD_PRIO_INHERIT), 0); > +#elif defined ENABLE_PP > + TEST_COMPARE (pthread_mutexattr_setprotocol (&a, PTHREAD_PRIO_PROTECT), 0); > + TEST_COMPARE (pthread_mutexattr_setprioceiling (&a, 6), 0); > #endif > > int err = pthread_mutex_init (&m, &a); > @@ -110,6 +116,10 @@ do_test_clock (clockid_t clockid, const char *fnname) > > static int do_test (void) > { > +#ifdef ENABLE_PP > + init_tpp_test (); > +#endif > + > do_test_clock (CLOCK_USE_TIMEDLOCK, "timedlock"); > do_test_clock (CLOCK_REALTIME, "clocklock(realtime)"); > #ifndef ENABLE_PI > diff --git a/sysdeps/pthread/tst-mutex9.c b/sysdeps/pthread/tst-mutex9.c > index bfc01f8c75..081aeff0f6 100644 > --- a/sysdeps/pthread/tst-mutex9.c > +++ b/sysdeps/pthread/tst-mutex9.c > @@ -30,6 +30,10 @@ > #include <support/timespec.h> > #include <support/xunistd.h> > > +#ifdef ENABLE_PP > +#include "tst-tpp.h" > +#endif > + > > /* A bogus clock value that tells run_test to use pthread_mutex_timedlock > rather than pthread_mutex_clocklock. */ > @@ -73,8 +77,11 @@ do_test_clock (clockid_t clockid) > > TEST_COMPARE (pthread_mutexattr_settype (&a, PTHREAD_MUTEX_RECURSIVE), 0); > > -#ifdef ENABLE_PI > +#if defined ENABLE_PI > TEST_COMPARE (pthread_mutexattr_setprotocol (&a, PTHREAD_PRIO_INHERIT), 0); > +#elif defined ENABLE_PP > + TEST_COMPARE (pthread_mutexattr_setprotocol (&a, PTHREAD_PRIO_PROTECT), 0); > + TEST_COMPARE (pthread_mutexattr_setprioceiling (&a, 6), 0); > #endif > > int e; > @@ -131,6 +138,10 @@ do_test_clock (clockid_t clockid) > static int > do_test (void) > { > +#ifdef ENABLE_PP > + init_tpp_test (); > +#endif > + > do_test_clock (CLOCK_USE_TIMEDLOCK); > do_test_clock (CLOCK_REALTIME); > #ifndef ENABLE_PI LGTM. I have some extra timespec tests that I'll propose in a separate patch once this lands. Thanks. Mike.
On 26/11/2020 08:27, Mike Crowe wrote: > On Wednesday 25 November 2020 at 17:27:23 -0300, Adhemerval Zanella wrote: >> The 878fe624d4 changed lll_futex_timed_wait, which expects a relative >> timeout, with a __futex_abstimed_wait64, which expects an absolute >> timeout. However the code still passes a relative timeout. >> >> Also, the PTHREAD_PRIO_PROTECT support for clocks different than >> CLOCK_REALTIME was broken since the inclusion of >> pthread_mutex_clocklock (9d20e22e46) since lll_futex_timed_wait >> always use CLOCK_REALTIME. >> >> This patch fixes by removing the relative time calculation. It >> also adds some xtests that tests both thread and inter-process >> usage. >> >> Checked on x86_64-linux-gnu. >> --- >> nptl/Makefile | 3 ++- >> nptl/pthread_mutex_timedlock.c | 29 +++++------------------------ >> nptl/tst-mutexpp5.c | 2 ++ >> nptl/tst-mutexpp9.c | 2 ++ >> sysdeps/pthread/tst-mutex5.c | 12 +++++++++++- >> sysdeps/pthread/tst-mutex9.c | 13 ++++++++++++- >> 6 files changed, 34 insertions(+), 27 deletions(-) >> create mode 100644 nptl/tst-mutexpp5.c >> create mode 100644 nptl/tst-mutexpp9.c >> >> diff --git a/nptl/Makefile b/nptl/Makefile >> index a48426a396..94d805f0d4 100644 >> --- a/nptl/Makefile >> +++ b/nptl/Makefile >> @@ -309,7 +309,8 @@ tests-internal := tst-robustpi8 tst-rwlock19 tst-rwlock20 \ >> tst-setgetname \ >> >> xtests = tst-setuid1 tst-setuid1-static tst-setuid2 \ >> - tst-mutexpp1 tst-mutexpp6 tst-mutexpp10 tst-setgroups >> + tst-mutexpp1 tst-mutexpp6 tst-mutexpp10 tst-setgroups \ >> + tst-mutexpp5 tst-mutexpp9 >> >> # This test can run into task limits because of a linux kernel bug >> # and then cause the make process to fail too, see bug 24537. >> diff --git a/nptl/pthread_mutex_timedlock.c b/nptl/pthread_mutex_timedlock.c >> index aaaafa21ce..74adffe790 100644 >> --- a/nptl/pthread_mutex_timedlock.c >> +++ b/nptl/pthread_mutex_timedlock.c >> @@ -547,30 +547,11 @@ __pthread_mutex_clocklock_common (pthread_mutex_t *mutex, >> goto failpp; >> } >> >> - struct __timespec64 rt; >> - >> - /* Get the current time. */ >> - __clock_gettime64 (CLOCK_REALTIME, &rt); >> - >> - /* Compute relative timeout. */ >> - rt.tv_sec = abstime->tv_sec - rt.tv_sec; >> - rt.tv_nsec = abstime->tv_nsec - rt.tv_nsec; >> - if (rt.tv_nsec < 0) >> - { >> - rt.tv_nsec += 1000000000; >> - --rt.tv_sec; >> - } >> - >> - /* Already timed out? */ >> - if (rt.tv_sec < 0) >> - { >> - result = ETIMEDOUT; >> - goto failpp; >> - } >> - >> - __futex_abstimed_wait64 ( >> - (unsigned int *) &mutex->__data.__lock, clockid, >> - ceilval | 2, &rt, PTHREAD_MUTEX_PSHARED (mutex)); >> + int e = __futex_abstimed_wait64 ( >> + (unsigned int *) &mutex->__data.__lock, ceilval | 2, >> + clockid, abstime, PTHREAD_MUTEX_PSHARED (mutex)); >> + if (e == ETIMEDOUT) >> + return ETIMEDOUT; > > I'm worried that futex could return other errors here which would cause a > busy infinite loop. However, my attempts to provoke EINVAL have failed > since the validity of abstime.tv_nsec is checked earlier. Presumably we > should never get this far if EOVERFLOW could be returned? (If there is a > problem here, then it also affects other mutex types which have similar > code.) Revising the calls of futex-internal.h which might pass an timeout where EOVERFLOW might happen, I see nptl code requires some fixes. At nptl/pthread_mutex_timedlock.c for robust mutexes: 138 case PTHREAD_MUTEX_ROBUST_RECURSIVE_NP: 139 case PTHREAD_MUTEX_ROBUST_ERRORCHECK_NP: 140 case PTHREAD_MUTEX_ROBUST_NORMAL_NP: 141 case PTHREAD_MUTEX_ROBUST_ADAPTIVE_NP: [...] 235 /* We are about to block; check whether the timeout is invalid. */ 236 if (! valid_nanoseconds (abstime->tv_nsec)) 237 return EINVAL; 238 /* Work around the fact that the kernel rejects negative timeout 239 values despite them being valid. */ 240 if (__glibc_unlikely (abstime->tv_sec < 0)) 241 return ETIMEDOUT; The FUTEX_WAITERS will trigger a futex wake on pthread_mutex_unlock, so this check is an optimization to avoid it. It could be removed since __futex_abstimed_wait64 does the same check (it might incur in a spurious futex call). [...] 268 int err = __futex_abstimed_wait64 ( 269 (unsigned int *) &mutex->__data.__lock, 270 oldval, clockid, abstime, 271 PTHREAD_ROBUST_MUTEX_PSHARED (mutex)); 272 /* The futex call timed out. */ 273 if (err == ETIMEDOUT) 274 return err; We need to handle EOVERFLOW since without a failure it is assumed the robust mutex was locked. The priority seems to be handle as expected: 307 case PTHREAD_MUTEX_PI_RECURSIVE_NP: 308 case PTHREAD_MUTEX_PI_ERRORCHECK_NP: 309 case PTHREAD_MUTEX_PI_NORMAL_NP: 310 case PTHREAD_MUTEX_PI_ADAPTIVE_NP: 311 case PTHREAD_MUTEX_PI_ROBUST_RECURSIVE_NP: 312 case PTHREAD_MUTEX_PI_ROBUST_ERRORCHECK_NP: 313 case PTHREAD_MUTEX_PI_ROBUST_NORMAL_NP: 314 case PTHREAD_MUTEX_PI_ROBUST_ADAPTIVE_NP: [...] 380 /* The mutex is locked. The kernel will now take care of 381 everything. The timeout value must be a relative value. 382 Convert it. */ 383 int private = (robust 384 ? PTHREAD_ROBUST_MUTEX_PSHARED (mutex) 385 : PTHREAD_MUTEX_PSHARED (mutex)); 386 int e = futex_lock_pi64 (&mutex->__data.__lock, abstime, private); 387 if (e == ETIMEDOUT) 388 return ETIMEDOUT; 389 else if (e == ESRCH || e == EDEADLK) 390 { 391 assert (e != EDEADLK 392 || (kind != PTHREAD_MUTEX_ERRORCHECK_NP 393 && kind != PTHREAD_MUTEX_RECURSIVE_NP)); 394 /* ESRCH can happen only for non-robust PI mutexes where 395 the owner of the lock died. */ 396 assert (e != ESRCH || !robust); 397 398 /* Delay the thread until the timeout is reached. Then return 399 ETIMEDOUT. */ 400 do 401 e = __futex_abstimed_wait64 (&(unsigned int){0}, 0, clockid, 402 abstime, private); 403 while (e != ETIMEDOUT); 404 return ETIMEDOUT; 405 } 406 else if (e != 0) 407 return e; The EOVERFLOW for futex_lock_pi64 will be handled on the last 'e != 0', and the internal '__futex_abstimed_wait64' should use a valid abstime (since futex_lock_pi64 would fail early). As priority protected ones: 469 case PTHREAD_MUTEX_PP_RECURSIVE_NP: 470 case PTHREAD_MUTEX_PP_ERRORCHECK_NP: 471 case PTHREAD_MUTEX_PP_NORMAL_NP: 472 case PTHREAD_MUTEX_PP_ADAPTIVE_NP: [...] 550 int e = __futex_abstimed_wait64 ( 551 (unsigned int *) &mutex->__data.__lock, ceilval | 2, 552 clockid, abstime, PTHREAD_MUTEX_PSHARED (mutex)); 553 if (e == ETIMEDOUT) 554 return ETIMEDOUT; As you have noted we do need to handle EOVERFLOW here, otherwise it will trigger busy infinite loop. It does not happen now because the timers passed to __pthread_mutex_clocklock_common by 32-bit archs with 32-bit support will always fix a 32-bti timespec; but they might happen once we support the 64-bit time support on kernel older than v5.1. On pthread rw/rdlock (nptl/pthread_rwlock_common.c) there are some some issues: 328 while (((r = atomic_load_relaxed (&rwlock->__data.__readers)) 329 & PTHREAD_RWLOCK_RWAITING) != 0) 330 { 331 int private = __pthread_rwlock_get_private (rwlock); 332 int err = __futex_abstimed_wait64 (&rwlock->__data.__readers, 333 r, clockid, abstime, 334 private); 335 /* We ignore EAGAIN and EINTR. On time-outs, we can just 336 return because we don't need to clean up anything. */ 337 if (err == ETIMEDOUT) 338 return err; 339 } This also requires handle EOVERFLOW. 460 int err = __futex_abstimed_wait64 (&rwlock->__data.__wrphase_futex, 461 1 | PTHREAD_RWLOCK_FUTEX_USED, 462 clockid, abstime, private); 463 if (err == ETIMEDOUT) 464 { 465 /* If we timed out, we need to unregister. If no read phase 466 has been installed while we waited, we can just decrement 467 the number of readers. Otherwise, we just acquire the 468 lock, which is allowed because we give no precise timing 469 guarantees, and because the timeout is only required to 470 be in effect if we would have had to wait for other 471 threads (e.g., if futex_wait would time-out immediately 472 because the given absolute time is in the past). */ Same here, I think EOVERFLOW should be handled as ETIMEDOUT in this case. 730 int err = __futex_abstimed_wait64 (&rwlock->__data.__writers_futex, 731 1 | PTHREAD_RWLOCK_FUTEX_USED, 732 clockid, abstime, private); 733 if (err == ETIMEDOUT) 734 { 735 if (prefer_writer) 736 { 737 /* We need to unregister as a waiting writer. If we are the 738 last writer and writer--writer hand-over is available, 739 we must make use of it because nobody else will reset 740 WRLOCKED otherwise. (If we use it, we simply pretend 741 that this happened before the timeout; see 742 pthread_rwlock_rdlock_full for the full reasoning.) 743 Also see the similar code above. */ And 829 int err = __futex_abstimed_wait64 (&rwlock->__data.__wrphase_futex, 830 PTHREAD_RWLOCK_FUTEX_USED, 831 clockid, abstime, private); 832 if (err == ETIMEDOUT) 833 { 834 if (rwlock->__data.__flags != PTHREAD_RWLOCK_PREFER_READER_NP) 835 { 836 /* We try writer--writer hand-over. */ Also need to handle EOVERFLOW as ETIMEDOUT as also return ETIMEOUT instead of EOVERFLOW. I plan to address these on a subsequent patch.
On 26/11/2020 09:15, Adhemerval Zanella wrote: > > > On 26/11/2020 08:27, Mike Crowe wrote: >> On Wednesday 25 November 2020 at 17:27:23 -0300, Adhemerval Zanella wrote: >>> The 878fe624d4 changed lll_futex_timed_wait, which expects a relative >>> timeout, with a __futex_abstimed_wait64, which expects an absolute >>> timeout. However the code still passes a relative timeout. >>> >>> Also, the PTHREAD_PRIO_PROTECT support for clocks different than >>> CLOCK_REALTIME was broken since the inclusion of >>> pthread_mutex_clocklock (9d20e22e46) since lll_futex_timed_wait >>> always use CLOCK_REALTIME. >>> >>> This patch fixes by removing the relative time calculation. It >>> also adds some xtests that tests both thread and inter-process >>> usage. >>> >>> Checked on x86_64-linux-gnu. >>> --- >>> nptl/Makefile | 3 ++- >>> nptl/pthread_mutex_timedlock.c | 29 +++++------------------------ >>> nptl/tst-mutexpp5.c | 2 ++ >>> nptl/tst-mutexpp9.c | 2 ++ >>> sysdeps/pthread/tst-mutex5.c | 12 +++++++++++- >>> sysdeps/pthread/tst-mutex9.c | 13 ++++++++++++- >>> 6 files changed, 34 insertions(+), 27 deletions(-) >>> create mode 100644 nptl/tst-mutexpp5.c >>> create mode 100644 nptl/tst-mutexpp9.c >>> >>> diff --git a/nptl/Makefile b/nptl/Makefile >>> index a48426a396..94d805f0d4 100644 >>> --- a/nptl/Makefile >>> +++ b/nptl/Makefile >>> @@ -309,7 +309,8 @@ tests-internal := tst-robustpi8 tst-rwlock19 tst-rwlock20 \ >>> tst-setgetname \ >>> >>> xtests = tst-setuid1 tst-setuid1-static tst-setuid2 \ >>> - tst-mutexpp1 tst-mutexpp6 tst-mutexpp10 tst-setgroups >>> + tst-mutexpp1 tst-mutexpp6 tst-mutexpp10 tst-setgroups \ >>> + tst-mutexpp5 tst-mutexpp9 >>> >>> # This test can run into task limits because of a linux kernel bug >>> # and then cause the make process to fail too, see bug 24537. >>> diff --git a/nptl/pthread_mutex_timedlock.c b/nptl/pthread_mutex_timedlock.c >>> index aaaafa21ce..74adffe790 100644 >>> --- a/nptl/pthread_mutex_timedlock.c >>> +++ b/nptl/pthread_mutex_timedlock.c >>> @@ -547,30 +547,11 @@ __pthread_mutex_clocklock_common (pthread_mutex_t *mutex, >>> goto failpp; >>> } >>> >>> - struct __timespec64 rt; >>> - >>> - /* Get the current time. */ >>> - __clock_gettime64 (CLOCK_REALTIME, &rt); >>> - >>> - /* Compute relative timeout. */ >>> - rt.tv_sec = abstime->tv_sec - rt.tv_sec; >>> - rt.tv_nsec = abstime->tv_nsec - rt.tv_nsec; >>> - if (rt.tv_nsec < 0) >>> - { >>> - rt.tv_nsec += 1000000000; >>> - --rt.tv_sec; >>> - } >>> - >>> - /* Already timed out? */ >>> - if (rt.tv_sec < 0) >>> - { >>> - result = ETIMEDOUT; >>> - goto failpp; >>> - } >>> - >>> - __futex_abstimed_wait64 ( >>> - (unsigned int *) &mutex->__data.__lock, clockid, >>> - ceilval | 2, &rt, PTHREAD_MUTEX_PSHARED (mutex)); >>> + int e = __futex_abstimed_wait64 ( >>> + (unsigned int *) &mutex->__data.__lock, ceilval | 2, >>> + clockid, abstime, PTHREAD_MUTEX_PSHARED (mutex)); >>> + if (e == ETIMEDOUT) >>> + return ETIMEDOUT; >> >> I'm worried that futex could return other errors here which would cause a >> busy infinite loop. However, my attempts to provoke EINVAL have failed >> since the validity of abstime.tv_nsec is checked earlier. Presumably we >> should never get this far if EOVERFLOW could be returned? (If there is a >> problem here, then it also affects other mutex types which have similar >> code.) > > Revising the calls of futex-internal.h which might pass an timeout where > EOVERFLOW might happen, I see nptl code requires some fixes. > > At nptl/pthread_mutex_timedlock.c for robust mutexes: > > 138 case PTHREAD_MUTEX_ROBUST_RECURSIVE_NP: > 139 case PTHREAD_MUTEX_ROBUST_ERRORCHECK_NP: > 140 case PTHREAD_MUTEX_ROBUST_NORMAL_NP: > 141 case PTHREAD_MUTEX_ROBUST_ADAPTIVE_NP: > [...] > 235 /* We are about to block; check whether the timeout is invalid. */ > 236 if (! valid_nanoseconds (abstime->tv_nsec)) > 237 return EINVAL; > 238 /* Work around the fact that the kernel rejects negative timeout > 239 values despite them being valid. */ > 240 if (__glibc_unlikely (abstime->tv_sec < 0)) > 241 return ETIMEDOUT; > > The FUTEX_WAITERS will trigger a futex wake on pthread_mutex_unlock, so > this check is an optimization to avoid it. It could be removed since > __futex_abstimed_wait64 does the same check (it might incur in a spurious > futex call). > > [...] > 268 int err = __futex_abstimed_wait64 ( > 269 (unsigned int *) &mutex->__data.__lock, > 270 oldval, clockid, abstime, > 271 PTHREAD_ROBUST_MUTEX_PSHARED (mutex)); > 272 /* The futex call timed out. */ > 273 if (err == ETIMEDOUT) > 274 return err; > > We need to handle EOVERFLOW since without a failure it is assumed the robust > mutex was locked. > > > The priority seems to be handle as expected: > > 307 case PTHREAD_MUTEX_PI_RECURSIVE_NP: > 308 case PTHREAD_MUTEX_PI_ERRORCHECK_NP: > 309 case PTHREAD_MUTEX_PI_NORMAL_NP: > 310 case PTHREAD_MUTEX_PI_ADAPTIVE_NP: > 311 case PTHREAD_MUTEX_PI_ROBUST_RECURSIVE_NP: > 312 case PTHREAD_MUTEX_PI_ROBUST_ERRORCHECK_NP: > 313 case PTHREAD_MUTEX_PI_ROBUST_NORMAL_NP: > 314 case PTHREAD_MUTEX_PI_ROBUST_ADAPTIVE_NP: > [...] > 380 /* The mutex is locked. The kernel will now take care of > 381 everything. The timeout value must be a relative value. > 382 Convert it. */ > 383 int private = (robust > 384 ? PTHREAD_ROBUST_MUTEX_PSHARED (mutex) > 385 : PTHREAD_MUTEX_PSHARED (mutex)); > 386 int e = futex_lock_pi64 (&mutex->__data.__lock, abstime, private); > 387 if (e == ETIMEDOUT) > 388 return ETIMEDOUT; > 389 else if (e == ESRCH || e == EDEADLK) > 390 { > 391 assert (e != EDEADLK > 392 || (kind != PTHREAD_MUTEX_ERRORCHECK_NP > 393 && kind != PTHREAD_MUTEX_RECURSIVE_NP)); > 394 /* ESRCH can happen only for non-robust PI mutexes where > 395 the owner of the lock died. */ > 396 assert (e != ESRCH || !robust); > 397 > 398 /* Delay the thread until the timeout is reached. Then return > 399 ETIMEDOUT. */ > 400 do > 401 e = __futex_abstimed_wait64 (&(unsigned int){0}, 0, clockid, > 402 abstime, private); > 403 while (e != ETIMEDOUT); > 404 return ETIMEDOUT; > 405 } > 406 else if (e != 0) > 407 return e; > > The EOVERFLOW for futex_lock_pi64 will be handled on the last 'e != 0', > and the internal '__futex_abstimed_wait64' should use a valid abstime > (since futex_lock_pi64 would fail early). > > As priority protected ones: > > 469 case PTHREAD_MUTEX_PP_RECURSIVE_NP: > 470 case PTHREAD_MUTEX_PP_ERRORCHECK_NP: > 471 case PTHREAD_MUTEX_PP_NORMAL_NP: > 472 case PTHREAD_MUTEX_PP_ADAPTIVE_NP: > [...] > 550 int e = __futex_abstimed_wait64 ( > 551 (unsigned int *) &mutex->__data.__lock, ceilval | 2, > 552 clockid, abstime, PTHREAD_MUTEX_PSHARED (mutex)); > 553 if (e == ETIMEDOUT) > 554 return ETIMEDOUT; > > As you have noted we do need to handle EOVERFLOW here, otherwise it will > trigger busy infinite loop. It does not happen now because the timers > passed to __pthread_mutex_clocklock_common by 32-bit archs with 32-bit > support will always fix a 32-bti timespec; but they might happen once > we support the 64-bit time support on kernel older than v5.1. > > > On pthread rw/rdlock (nptl/pthread_rwlock_common.c) there are some some issues: > > 328 while (((r = atomic_load_relaxed (&rwlock->__data.__readers)) > 329 & PTHREAD_RWLOCK_RWAITING) != 0) > 330 { > 331 int private = __pthread_rwlock_get_private (rwlock); > 332 int err = __futex_abstimed_wait64 (&rwlock->__data.__readers, > 333 r, clockid, abstime, > 334 private); > 335 /* We ignore EAGAIN and EINTR. On time-outs, we can just > 336 return because we don't need to clean up anything. */ > 337 if (err == ETIMEDOUT) > 338 return err; > 339 } > > This also requires handle EOVERFLOW. > > 460 int err = __futex_abstimed_wait64 (&rwlock->__data.__wrphase_futex, > 461 1 | PTHREAD_RWLOCK_FUTEX_USED, > 462 clockid, abstime, private); > 463 if (err == ETIMEDOUT) > 464 { > 465 /* If we timed out, we need to unregister. If no read phase > 466 has been installed while we waited, we can just decrement > 467 the number of readers. Otherwise, we just acquire the > 468 lock, which is allowed because we give no precise timing > 469 guarantees, and because the timeout is only required to > 470 be in effect if we would have had to wait for other > 471 threads (e.g., if futex_wait would time-out immediately > 472 because the given absolute time is in the past). */ > > Same here, I think EOVERFLOW should be handled as ETIMEDOUT in this case. > > 730 int err = __futex_abstimed_wait64 (&rwlock->__data.__writers_futex, > 731 1 | PTHREAD_RWLOCK_FUTEX_USED, > 732 clockid, abstime, private); > 733 if (err == ETIMEDOUT) > 734 { > 735 if (prefer_writer) > 736 { > 737 /* We need to unregister as a waiting writer. If we are the > 738 last writer and writer--writer hand-over is available, > 739 we must make use of it because nobody else will reset > 740 WRLOCKED otherwise. (If we use it, we simply pretend > 741 that this happened before the timeout; see > 742 pthread_rwlock_rdlock_full for the full reasoning.) > 743 Also see the similar code above. */ > > And > > 829 int err = __futex_abstimed_wait64 (&rwlock->__data.__wrphase_futex, > 830 PTHREAD_RWLOCK_FUTEX_USED, > 831 clockid, abstime, private); > 832 if (err == ETIMEDOUT) > 833 { > 834 if (rwlock->__data.__flags != PTHREAD_RWLOCK_PREFER_READER_NP) > 835 { > 836 /* We try writer--writer hand-over. */ > > Also need to handle EOVERFLOW as ETIMEDOUT as also return ETIMEOUT instead of > EOVERFLOW. > > I plan to address these on a subsequent patch. > Revising the __futex_clocklock64 usage, they also seems to already handle EOVERFLOW: 32 #ifndef lll_clocklock_elision 33 #define lll_clocklock_elision(futex, adapt_count, clockid, abstime, private) \ 34 __futex_clocklock64 (&(futex), clockid, abstime, private) 35 #endif 78 /* We have to get the mutex. */ 79 result = __futex_clocklock64 (&mutex->__data.__lock, clockid, abstime, 80 PTHREAD_MUTEX_PSHARED (mutex)); 81 82 if (result != 0) 83 goto out; This already handles EOVERFLOW 100 simple: 101 /* Normal mutex. */ 102 result = __futex_clocklock64 (&mutex->__data.__lock, clockid, abstime, 103 PTHREAD_MUTEX_PSHARED (mutex)); 104 break; This also handled EOVERFLOW (it will bail out to line 583). 106 case PTHREAD_MUTEX_TIMED_ELISION_NP: 107 elision: __attribute__((unused)) 108 /* Don't record ownership */ 109 return lll_clocklock_elision (mutex->__data.__lock, 110 mutex->__data.__spins, 111 clockid, abstime, 112 PTHREAD_MUTEX_PSHARED (mutex)); All the target overriden lll_clocklock_elision call target provided __lll_lock_elision and this return __futex_clocklock64, so it already handles EOVERFLOW. 115 case PTHREAD_MUTEX_ADAPTIVE_NP: 116 if (lll_trylock (mutex->__data.__lock) != 0) 117 { 118 int cnt = 0; 119 int max_cnt = MIN (max_adaptive_count (), 120 mutex->__data.__spins * 2 + 10); 121 do 122 { 123 if (cnt++ >= max_cnt) 124 { 125 result = __futex_clocklock64 (&mutex->__data.__lock, 126 clockid, abstime, 127 PTHREAD_MUTEX_PSHARED (mutex)); 128 break; 129 } 130 atomic_spin_nop (); 131 } 132 while (lll_trylock (mutex->__data.__lock) != 0); 133 134 mutex->__data.__spins += (cnt - mutex->__data.__spins) / 8; 135 } This should be safe as well (it will break once cnt reaches max_cnt). It could bail early if result is EOVERFLOW but it would require to adjust the __spins as well.
On 26/11/2020 09:51, Adhemerval Zanella wrote: > > > On 26/11/2020 09:15, Adhemerval Zanella wrote: >> >> >> On 26/11/2020 08:27, Mike Crowe wrote: >>> On Wednesday 25 November 2020 at 17:27:23 -0300, Adhemerval Zanella wrote: >>>> The 878fe624d4 changed lll_futex_timed_wait, which expects a relative >>>> timeout, with a __futex_abstimed_wait64, which expects an absolute >>>> timeout. However the code still passes a relative timeout. >>>> >>>> Also, the PTHREAD_PRIO_PROTECT support for clocks different than >>>> CLOCK_REALTIME was broken since the inclusion of >>>> pthread_mutex_clocklock (9d20e22e46) since lll_futex_timed_wait >>>> always use CLOCK_REALTIME. >>>> >>>> This patch fixes by removing the relative time calculation. It >>>> also adds some xtests that tests both thread and inter-process >>>> usage. >>>> >>>> Checked on x86_64-linux-gnu. >>>> --- >>>> nptl/Makefile | 3 ++- >>>> nptl/pthread_mutex_timedlock.c | 29 +++++------------------------ >>>> nptl/tst-mutexpp5.c | 2 ++ >>>> nptl/tst-mutexpp9.c | 2 ++ >>>> sysdeps/pthread/tst-mutex5.c | 12 +++++++++++- >>>> sysdeps/pthread/tst-mutex9.c | 13 ++++++++++++- >>>> 6 files changed, 34 insertions(+), 27 deletions(-) >>>> create mode 100644 nptl/tst-mutexpp5.c >>>> create mode 100644 nptl/tst-mutexpp9.c >>>> >>>> diff --git a/nptl/Makefile b/nptl/Makefile >>>> index a48426a396..94d805f0d4 100644 >>>> --- a/nptl/Makefile >>>> +++ b/nptl/Makefile >>>> @@ -309,7 +309,8 @@ tests-internal := tst-robustpi8 tst-rwlock19 tst-rwlock20 \ >>>> tst-setgetname \ >>>> >>>> xtests = tst-setuid1 tst-setuid1-static tst-setuid2 \ >>>> - tst-mutexpp1 tst-mutexpp6 tst-mutexpp10 tst-setgroups >>>> + tst-mutexpp1 tst-mutexpp6 tst-mutexpp10 tst-setgroups \ >>>> + tst-mutexpp5 tst-mutexpp9 >>>> >>>> # This test can run into task limits because of a linux kernel bug >>>> # and then cause the make process to fail too, see bug 24537. >>>> diff --git a/nptl/pthread_mutex_timedlock.c b/nptl/pthread_mutex_timedlock.c >>>> index aaaafa21ce..74adffe790 100644 >>>> --- a/nptl/pthread_mutex_timedlock.c >>>> +++ b/nptl/pthread_mutex_timedlock.c >>>> @@ -547,30 +547,11 @@ __pthread_mutex_clocklock_common (pthread_mutex_t *mutex, >>>> goto failpp; >>>> } >>>> >>>> - struct __timespec64 rt; >>>> - >>>> - /* Get the current time. */ >>>> - __clock_gettime64 (CLOCK_REALTIME, &rt); >>>> - >>>> - /* Compute relative timeout. */ >>>> - rt.tv_sec = abstime->tv_sec - rt.tv_sec; >>>> - rt.tv_nsec = abstime->tv_nsec - rt.tv_nsec; >>>> - if (rt.tv_nsec < 0) >>>> - { >>>> - rt.tv_nsec += 1000000000; >>>> - --rt.tv_sec; >>>> - } >>>> - >>>> - /* Already timed out? */ >>>> - if (rt.tv_sec < 0) >>>> - { >>>> - result = ETIMEDOUT; >>>> - goto failpp; >>>> - } >>>> - >>>> - __futex_abstimed_wait64 ( >>>> - (unsigned int *) &mutex->__data.__lock, clockid, >>>> - ceilval | 2, &rt, PTHREAD_MUTEX_PSHARED (mutex)); >>>> + int e = __futex_abstimed_wait64 ( >>>> + (unsigned int *) &mutex->__data.__lock, ceilval | 2, >>>> + clockid, abstime, PTHREAD_MUTEX_PSHARED (mutex)); >>>> + if (e == ETIMEDOUT) >>>> + return ETIMEDOUT; >>> >>> I'm worried that futex could return other errors here which would cause a >>> busy infinite loop. However, my attempts to provoke EINVAL have failed >>> since the validity of abstime.tv_nsec is checked earlier. Presumably we >>> should never get this far if EOVERFLOW could be returned? (If there is a >>> problem here, then it also affects other mutex types which have similar >>> code.) >> >> Revising the calls of futex-internal.h which might pass an timeout where >> EOVERFLOW might happen, I see nptl code requires some fixes. >> >> At nptl/pthread_mutex_timedlock.c for robust mutexes: >> >> 138 case PTHREAD_MUTEX_ROBUST_RECURSIVE_NP: >> 139 case PTHREAD_MUTEX_ROBUST_ERRORCHECK_NP: >> 140 case PTHREAD_MUTEX_ROBUST_NORMAL_NP: >> 141 case PTHREAD_MUTEX_ROBUST_ADAPTIVE_NP: >> [...] >> 235 /* We are about to block; check whether the timeout is invalid. */ >> 236 if (! valid_nanoseconds (abstime->tv_nsec)) >> 237 return EINVAL; >> 238 /* Work around the fact that the kernel rejects negative timeout >> 239 values despite them being valid. */ >> 240 if (__glibc_unlikely (abstime->tv_sec < 0)) >> 241 return ETIMEDOUT; >> >> The FUTEX_WAITERS will trigger a futex wake on pthread_mutex_unlock, so >> this check is an optimization to avoid it. It could be removed since >> __futex_abstimed_wait64 does the same check (it might incur in a spurious >> futex call). >> >> [...] >> 268 int err = __futex_abstimed_wait64 ( >> 269 (unsigned int *) &mutex->__data.__lock, >> 270 oldval, clockid, abstime, >> 271 PTHREAD_ROBUST_MUTEX_PSHARED (mutex)); >> 272 /* The futex call timed out. */ >> 273 if (err == ETIMEDOUT) >> 274 return err; >> >> We need to handle EOVERFLOW since without a failure it is assumed the robust >> mutex was locked. >> >> >> The priority seems to be handle as expected: >> >> 307 case PTHREAD_MUTEX_PI_RECURSIVE_NP: >> 308 case PTHREAD_MUTEX_PI_ERRORCHECK_NP: >> 309 case PTHREAD_MUTEX_PI_NORMAL_NP: >> 310 case PTHREAD_MUTEX_PI_ADAPTIVE_NP: >> 311 case PTHREAD_MUTEX_PI_ROBUST_RECURSIVE_NP: >> 312 case PTHREAD_MUTEX_PI_ROBUST_ERRORCHECK_NP: >> 313 case PTHREAD_MUTEX_PI_ROBUST_NORMAL_NP: >> 314 case PTHREAD_MUTEX_PI_ROBUST_ADAPTIVE_NP: >> [...] >> 380 /* The mutex is locked. The kernel will now take care of >> 381 everything. The timeout value must be a relative value. >> 382 Convert it. */ >> 383 int private = (robust >> 384 ? PTHREAD_ROBUST_MUTEX_PSHARED (mutex) >> 385 : PTHREAD_MUTEX_PSHARED (mutex)); >> 386 int e = futex_lock_pi64 (&mutex->__data.__lock, abstime, private); >> 387 if (e == ETIMEDOUT) >> 388 return ETIMEDOUT; >> 389 else if (e == ESRCH || e == EDEADLK) >> 390 { >> 391 assert (e != EDEADLK >> 392 || (kind != PTHREAD_MUTEX_ERRORCHECK_NP >> 393 && kind != PTHREAD_MUTEX_RECURSIVE_NP)); >> 394 /* ESRCH can happen only for non-robust PI mutexes where >> 395 the owner of the lock died. */ >> 396 assert (e != ESRCH || !robust); >> 397 >> 398 /* Delay the thread until the timeout is reached. Then return >> 399 ETIMEDOUT. */ >> 400 do >> 401 e = __futex_abstimed_wait64 (&(unsigned int){0}, 0, clockid, >> 402 abstime, private); >> 403 while (e != ETIMEDOUT); >> 404 return ETIMEDOUT; >> 405 } >> 406 else if (e != 0) >> 407 return e; >> >> The EOVERFLOW for futex_lock_pi64 will be handled on the last 'e != 0', >> and the internal '__futex_abstimed_wait64' should use a valid abstime >> (since futex_lock_pi64 would fail early). >> >> As priority protected ones: >> >> 469 case PTHREAD_MUTEX_PP_RECURSIVE_NP: >> 470 case PTHREAD_MUTEX_PP_ERRORCHECK_NP: >> 471 case PTHREAD_MUTEX_PP_NORMAL_NP: >> 472 case PTHREAD_MUTEX_PP_ADAPTIVE_NP: >> [...] >> 550 int e = __futex_abstimed_wait64 ( >> 551 (unsigned int *) &mutex->__data.__lock, ceilval | 2, >> 552 clockid, abstime, PTHREAD_MUTEX_PSHARED (mutex)); >> 553 if (e == ETIMEDOUT) >> 554 return ETIMEDOUT; >> >> As you have noted we do need to handle EOVERFLOW here, otherwise it will >> trigger busy infinite loop. It does not happen now because the timers >> passed to __pthread_mutex_clocklock_common by 32-bit archs with 32-bit >> support will always fix a 32-bti timespec; but they might happen once >> we support the 64-bit time support on kernel older than v5.1. >> >> >> On pthread rw/rdlock (nptl/pthread_rwlock_common.c) there are some some issues: >> >> 328 while (((r = atomic_load_relaxed (&rwlock->__data.__readers)) >> 329 & PTHREAD_RWLOCK_RWAITING) != 0) >> 330 { >> 331 int private = __pthread_rwlock_get_private (rwlock); >> 332 int err = __futex_abstimed_wait64 (&rwlock->__data.__readers, >> 333 r, clockid, abstime, >> 334 private); >> 335 /* We ignore EAGAIN and EINTR. On time-outs, we can just >> 336 return because we don't need to clean up anything. */ >> 337 if (err == ETIMEDOUT) >> 338 return err; >> 339 } >> >> This also requires handle EOVERFLOW. >> >> 460 int err = __futex_abstimed_wait64 (&rwlock->__data.__wrphase_futex, >> 461 1 | PTHREAD_RWLOCK_FUTEX_USED, >> 462 clockid, abstime, private); >> 463 if (err == ETIMEDOUT) >> 464 { >> 465 /* If we timed out, we need to unregister. If no read phase >> 466 has been installed while we waited, we can just decrement >> 467 the number of readers. Otherwise, we just acquire the >> 468 lock, which is allowed because we give no precise timing >> 469 guarantees, and because the timeout is only required to >> 470 be in effect if we would have had to wait for other >> 471 threads (e.g., if futex_wait would time-out immediately >> 472 because the given absolute time is in the past). */ >> >> Same here, I think EOVERFLOW should be handled as ETIMEDOUT in this case. >> >> 730 int err = __futex_abstimed_wait64 (&rwlock->__data.__writers_futex, >> 731 1 | PTHREAD_RWLOCK_FUTEX_USED, >> 732 clockid, abstime, private); >> 733 if (err == ETIMEDOUT) >> 734 { >> 735 if (prefer_writer) >> 736 { >> 737 /* We need to unregister as a waiting writer. If we are the >> 738 last writer and writer--writer hand-over is available, >> 739 we must make use of it because nobody else will reset >> 740 WRLOCKED otherwise. (If we use it, we simply pretend >> 741 that this happened before the timeout; see >> 742 pthread_rwlock_rdlock_full for the full reasoning.) >> 743 Also see the similar code above. */ >> >> And >> >> 829 int err = __futex_abstimed_wait64 (&rwlock->__data.__wrphase_futex, >> 830 PTHREAD_RWLOCK_FUTEX_USED, >> 831 clockid, abstime, private); >> 832 if (err == ETIMEDOUT) >> 833 { >> 834 if (rwlock->__data.__flags != PTHREAD_RWLOCK_PREFER_READER_NP) >> 835 { >> 836 /* We try writer--writer hand-over. */ >> >> Also need to handle EOVERFLOW as ETIMEDOUT as also return ETIMEOUT instead of >> EOVERFLOW. >> >> I plan to address these on a subsequent patch. >> > > Revising the __futex_clocklock64 usage, they also seems to already handle > EOVERFLOW: > > 32 #ifndef lll_clocklock_elision > 33 #define lll_clocklock_elision(futex, adapt_count, clockid, abstime, private) \ > 34 __futex_clocklock64 (&(futex), clockid, abstime, private) > 35 #endif > > 78 /* We have to get the mutex. */ > 79 result = __futex_clocklock64 (&mutex->__data.__lock, clockid, abstime, > 80 PTHREAD_MUTEX_PSHARED (mutex)); > 81 > 82 if (result != 0) > 83 goto out; > > This already handles EOVERFLOW > > > 100 simple: > 101 /* Normal mutex. */ > 102 result = __futex_clocklock64 (&mutex->__data.__lock, clockid, abstime, > 103 PTHREAD_MUTEX_PSHARED (mutex)); > 104 break; > > This also handled EOVERFLOW (it will bail out to line 583). > > > 106 case PTHREAD_MUTEX_TIMED_ELISION_NP: > 107 elision: __attribute__((unused)) > 108 /* Don't record ownership */ > 109 return lll_clocklock_elision (mutex->__data.__lock, > 110 mutex->__data.__spins, > 111 clockid, abstime, > 112 PTHREAD_MUTEX_PSHARED (mutex)); > > All the target overriden lll_clocklock_elision call target provided > __lll_lock_elision and this return __futex_clocklock64, so it already handles > EOVERFLOW. > > > 115 case PTHREAD_MUTEX_ADAPTIVE_NP: > 116 if (lll_trylock (mutex->__data.__lock) != 0) > 117 { > 118 int cnt = 0; > 119 int max_cnt = MIN (max_adaptive_count (), > 120 mutex->__data.__spins * 2 + 10); > 121 do > 122 { > 123 if (cnt++ >= max_cnt) > 124 { > 125 result = __futex_clocklock64 (&mutex->__data.__lock, > 126 clockid, abstime, > 127 PTHREAD_MUTEX_PSHARED (mutex)); > 128 break; > 129 } > 130 atomic_spin_nop (); > 131 } > 132 while (lll_trylock (mutex->__data.__lock) != 0); > 133 > 134 mutex->__data.__spins += (cnt - mutex->__data.__spins) / 8; > 135 } > > This should be safe as well (it will break once cnt reaches max_cnt). It could > bail early if result is EOVERFLOW but it would require to adjust the > __spins as well. > I forgot to analyze the usages of __futex_abstimed_wait_cancelable64 as well. On nptl/pthread_cond_wait.c: 504 err = __futex_abstimed_wait_cancelable64 ( 505 cond->__data.__g_signals + g, 0, clockid, abstime, private); 506 507 __pthread_cleanup_pop (&buffer, 0); 508 509 if (__glibc_unlikely (err == ETIMEDOUT)) 510 { This also requires handle EOVERFLOW. The nptl/pthread_join_common.c is safe: 102 int ret = __futex_abstimed_wait_cancelable64 ( 103 (unsigned int *) &pd->tid, tid, clockid, abstime, LLL_SHARED); 104 if (ret == ETIMEDOUT || ret == EOVERFLOW) 105 { 106 result = ret; 107 break; 108 } The nptl/sem_waitcommon.c also requires fixing: 104 static int 105 __attribute__ ((noinline)) 106 do_futex_wait (struct new_sem *sem, clockid_t clockid, 107 const struct __timespec64 *abstime) 108 { 109 int err; 110 111 #if __HAVE_64B_ATOMICS 112 err = __futex_abstimed_wait_cancelable64 ( 113 (unsigned int *) &sem->data + SEM_VALUE_OFFSET, 0, 114 clockid, abstime, 115 sem->private); 116 #else 117 err = __futex_abstimed_wait_cancelable64 (&sem->value, SEM_NWAITERS_MASK, 118 clockid, abstime, sem->private); 119 #endif 120 121 return err; 122 } 79 for (;;) 180 { 181 /* If there is no token available, sleep until there is. */ 182 if ((d & SEM_VALUE_MASK) == 0) 183 { 184 err = do_futex_wait (sem, clockid, abstime); [...] 194 if (err == ETIMEDOUT || err == EINTR) 195 { 196 __set_errno (err); 197 err = -1;
diff --git a/nptl/Makefile b/nptl/Makefile index a48426a396..94d805f0d4 100644 --- a/nptl/Makefile +++ b/nptl/Makefile @@ -309,7 +309,8 @@ tests-internal := tst-robustpi8 tst-rwlock19 tst-rwlock20 \ tst-setgetname \ xtests = tst-setuid1 tst-setuid1-static tst-setuid2 \ - tst-mutexpp1 tst-mutexpp6 tst-mutexpp10 tst-setgroups + tst-mutexpp1 tst-mutexpp6 tst-mutexpp10 tst-setgroups \ + tst-mutexpp5 tst-mutexpp9 # This test can run into task limits because of a linux kernel bug # and then cause the make process to fail too, see bug 24537. diff --git a/nptl/pthread_mutex_timedlock.c b/nptl/pthread_mutex_timedlock.c index aaaafa21ce..74adffe790 100644 --- a/nptl/pthread_mutex_timedlock.c +++ b/nptl/pthread_mutex_timedlock.c @@ -547,30 +547,11 @@ __pthread_mutex_clocklock_common (pthread_mutex_t *mutex, goto failpp; } - struct __timespec64 rt; - - /* Get the current time. */ - __clock_gettime64 (CLOCK_REALTIME, &rt); - - /* Compute relative timeout. */ - rt.tv_sec = abstime->tv_sec - rt.tv_sec; - rt.tv_nsec = abstime->tv_nsec - rt.tv_nsec; - if (rt.tv_nsec < 0) - { - rt.tv_nsec += 1000000000; - --rt.tv_sec; - } - - /* Already timed out? */ - if (rt.tv_sec < 0) - { - result = ETIMEDOUT; - goto failpp; - } - - __futex_abstimed_wait64 ( - (unsigned int *) &mutex->__data.__lock, clockid, - ceilval | 2, &rt, PTHREAD_MUTEX_PSHARED (mutex)); + int e = __futex_abstimed_wait64 ( + (unsigned int *) &mutex->__data.__lock, ceilval | 2, + clockid, abstime, PTHREAD_MUTEX_PSHARED (mutex)); + if (e == ETIMEDOUT) + return ETIMEDOUT; } } while (atomic_compare_and_exchange_val_acq (&mutex->__data.__lock, diff --git a/nptl/tst-mutexpp5.c b/nptl/tst-mutexpp5.c new file mode 100644 index 0000000000..a864a390ca --- /dev/null +++ b/nptl/tst-mutexpp5.c @@ -0,0 +1,2 @@ +#define ENABLE_PP 1 +#include "tst-mutex5.c" diff --git a/nptl/tst-mutexpp9.c b/nptl/tst-mutexpp9.c new file mode 100644 index 0000000000..c848c74c7e --- /dev/null +++ b/nptl/tst-mutexpp9.c @@ -0,0 +1,2 @@ +#define ENABLE_PP 1 +#include "tst-mutex9.c" diff --git a/sysdeps/pthread/tst-mutex5.c b/sysdeps/pthread/tst-mutex5.c index bfe1a79fa4..dbd2c3c15f 100644 --- a/sysdeps/pthread/tst-mutex5.c +++ b/sysdeps/pthread/tst-mutex5.c @@ -27,6 +27,9 @@ #include <support/check.h> #include <support/timespec.h> +#ifdef ENABLE_PP +#include "tst-tpp.h" +#endif #ifndef TYPE # define TYPE PTHREAD_MUTEX_NORMAL @@ -47,8 +50,11 @@ do_test_clock (clockid_t clockid, const char *fnname) TEST_COMPARE (pthread_mutexattr_init (&a), 0); TEST_COMPARE (pthread_mutexattr_settype (&a, TYPE), 0); -#ifdef ENABLE_PI +#if defined ENABLE_PI TEST_COMPARE (pthread_mutexattr_setprotocol (&a, PTHREAD_PRIO_INHERIT), 0); +#elif defined ENABLE_PP + TEST_COMPARE (pthread_mutexattr_setprotocol (&a, PTHREAD_PRIO_PROTECT), 0); + TEST_COMPARE (pthread_mutexattr_setprioceiling (&a, 6), 0); #endif int err = pthread_mutex_init (&m, &a); @@ -110,6 +116,10 @@ do_test_clock (clockid_t clockid, const char *fnname) static int do_test (void) { +#ifdef ENABLE_PP + init_tpp_test (); +#endif + do_test_clock (CLOCK_USE_TIMEDLOCK, "timedlock"); do_test_clock (CLOCK_REALTIME, "clocklock(realtime)"); #ifndef ENABLE_PI diff --git a/sysdeps/pthread/tst-mutex9.c b/sysdeps/pthread/tst-mutex9.c index bfc01f8c75..081aeff0f6 100644 --- a/sysdeps/pthread/tst-mutex9.c +++ b/sysdeps/pthread/tst-mutex9.c @@ -30,6 +30,10 @@ #include <support/timespec.h> #include <support/xunistd.h> +#ifdef ENABLE_PP +#include "tst-tpp.h" +#endif + /* A bogus clock value that tells run_test to use pthread_mutex_timedlock rather than pthread_mutex_clocklock. */ @@ -73,8 +77,11 @@ do_test_clock (clockid_t clockid) TEST_COMPARE (pthread_mutexattr_settype (&a, PTHREAD_MUTEX_RECURSIVE), 0); -#ifdef ENABLE_PI +#if defined ENABLE_PI TEST_COMPARE (pthread_mutexattr_setprotocol (&a, PTHREAD_PRIO_INHERIT), 0); +#elif defined ENABLE_PP + TEST_COMPARE (pthread_mutexattr_setprotocol (&a, PTHREAD_PRIO_PROTECT), 0); + TEST_COMPARE (pthread_mutexattr_setprioceiling (&a, 6), 0); #endif int e; @@ -131,6 +138,10 @@ do_test_clock (clockid_t clockid) static int do_test (void) { +#ifdef ENABLE_PP + init_tpp_test (); +#endif + do_test_clock (CLOCK_USE_TIMEDLOCK); do_test_clock (CLOCK_REALTIME); #ifndef ENABLE_PI