From: Thomas Gleixner <tglx@linutronix.de>
To: "André Almeida" <andrealmeid@igalia.com>,
"Ingo Molnar" <mingo@redhat.com>,
"Peter Zijlstra" <peterz@infradead.org>,
"Darren Hart" <dvhart@infradead.org>,
"Davidlohr Bueso" <dave@stgolabs.net>,
"Shuah Khan" <shuah@kernel.org>, "Arnd Bergmann" <arnd@arndb.de>,
"Sebastian Andrzej Siewior" <bigeasy@linutronix.de>,
"Waiman Long" <longman@redhat.com>
Cc: linux-kernel@vger.kernel.org, linux-kselftest@vger.kernel.org,
linux-api@vger.kernel.org, kernel-dev@igalia.com,
"André Almeida" <andrealmeid@igalia.com>
Subject: Re: [PATCH v5 2/7] selftests/futex: Create test for robust list
Date: Fri, 27 Jun 2025 00:36:37 +0200 [thread overview]
Message-ID: <878qlep1u2.ffs@tglx> (raw)
In-Reply-To: <20250626-tonyk-robust_futex-v5-2-179194dbde8f@igalia.com>
On Thu, Jun 26 2025 at 14:11, André Almeida wrote:
> +
> +int set_robust_list(struct robust_list_head *head, size_t len)
This function and the get() counterpart are global because they can?
> +{
> + return syscall(SYS_set_robust_list, head, len);
> +}
> +/*
> + * Basic lock struct, contains just the futex word and the robust list element
> + * Real implementations have also a *prev to easily walk in the list
> + */
> +struct lock_struct {
> + _Atomic(unsigned int) futex;
> + struct robust_list list;
tabular arrangement please.
> + pthread_barrier_wait(&barrier);
> +
> + /*
> + * There's a race here: the parent thread needs to be inside
> + * futex_wait() before the child thread dies, otherwise it will miss the
> + * wakeup from handle_futex_death() that this child will emit. We wait a
> + * little bit just to make sure that this happens.
> + */
> + sleep(1);
One second is quite a little bit. :)
> + /*
> + * futex_wait() should return 0 and the futex word should be marked with
> + * FUTEX_OWNER_DIED
> + */
> + ASSERT_EQ(ret, 0);
> + if (ret != 0)
> + printf("futex wait returned %d", errno);
What's the purpose of the extra printf() after the assert here? This
code is not even reached when ret != 0, no?
> + ASSERT_TRUE(*futex | FUTEX_OWNER_DIED);
That's always true no matter what the content of the futex variable is, no?
> +/*
> + * The only valid value for len is sizeof(*head)
> + */
> +static void test_set_robust_list_invalid_size(void)
> +{
> + struct robust_list_head head;
> + size_t head_size = sizeof(struct robust_list_head);
Groan. You already define the robust_list_head variable ahead of
head_size and violate the reverse fir tree ordering, so why don't you
use the obvious and actually robust 'sizeof(head)'?
> +/*
> + * Test get_robust_list with pid = 0, getting the list of the running thread
> + */
> +static void test_get_robust_list_self(void)
> +{
> + struct robust_list_head head, head2, *get_head;
> + size_t head_size = sizeof(struct robust_list_head), len_ptr;
Ditto.
> +static int child_list(void *arg)
> +{
> + struct robust_list_head *head = (struct robust_list_head *) arg;
void pointers really don't require type casts
> + int ret;
> +
> + ret = set_robust_list(head, sizeof(struct robust_list_head));
sizeof(*head)
> + if (ret)
> + ksft_test_result_fail("set_robust_list error\n");
> +
> + pthread_barrier_wait(&barrier);
> + pthread_barrier_wait(&barrier2);
Lacks a comment what this waits for
> + return 0;
> +}
> +
> +/*
> + * Test get_robust_list from another thread. We use two barriers here to ensure
> + * that:
> + * 1) the child thread set the list before we try to get it from the
> + * parent
> + * 2) the child thread still alive when we try to get the list from it
> + */
> +static void test_get_robust_list_child(void)
> +{
> + pid_t tid;
> + int ret;
> + struct robust_list_head head, *get_head;
> + size_t len_ptr;
Reverse fir tree ordering please.
> + ret = pthread_barrier_init(&barrier, NULL, 2);
> + ret = pthread_barrier_init(&barrier2, NULL, 2);
> + ASSERT_EQ(ret, 0);
> +
> + tid = create_child(&child_list, &head);
> + ASSERT_NE(tid, -1);
> +
> + pthread_barrier_wait(&barrier);
> +
> + ret = get_robust_list(tid, &get_head, &len_ptr);
> + ASSERT_EQ(ret, 0);
> + ASSERT_EQ(&head, get_head);
> +
> + pthread_barrier_wait(&barrier2);
> +
> + wait(NULL);
> + pthread_barrier_destroy(&barrier);
> + pthread_barrier_destroy(&barrier2);
> +
> + ksft_test_result_pass("%s\n", __func__);
> +}
> +
> +static int child_fn_lock_with_error(void *arg)
> +{
> + struct lock_struct *lock = (struct lock_struct *) arg;
See above
> + struct robust_list_head head;
> + int ret;
> +
> + ret = set_list(&head);
> + if (ret)
> + ksft_test_result_fail("set_robust_list error\n");
So you fail the test and continue to produce more fails or what? Why
does this not use one of these ASSERT thingies or return?
> + ret = mutex_lock(lock, &head, true);
> + if (ret)
> + ksft_test_result_fail("mutex_lock error\n");
> +
> + pthread_barrier_wait(&barrier);
> +
> + sleep(1);
> +
> + return 0;
> +}
> +
> +/*
> + * Same as robustness test, but inject an error where the mutex_lock() exits
> + * earlier, just after setting list_op_pending and taking the lock, to test the
> + * list_op_pending mechanism
> + */
> +static void test_set_list_op_pending(void)
> +{
> + struct lock_struct lock = { .futex = 0 };
> + struct robust_list_head head;
> + _Atomic(unsigned int) *futex = &lock.futex;
> + int ret;
See above
> + ASSERT_EQ(ret, 0);
> + if (ret != 0)
> + printf("futex wait returned %d", errno);
The random insertion of completely pointless printf()'s is stunning.
> + ASSERT_TRUE(*futex | FUTEX_OWNER_DIED);
Yet another always true assert which is happily optimized out by the
compiler.
> + wait(NULL);
> + pthread_barrier_destroy(&barrier);
> +
> + ksft_test_result_pass("%s\n", __func__);
> +}
> +static int child_wait_lock(void *arg)
> +{
> + struct lock_struct *lock = (struct lock_struct *) arg;
> + struct robust_list_head head;
> + int ret;
> +
> + pthread_barrier_wait(&barrier2);
> + ret = mutex_lock(lock, &head, false);
> +
> + if (ret)
> + ksft_test_result_fail("mutex_lock error\n");
> +
> + if (!(lock->futex | FUTEX_OWNER_DIED))
> + ksft_test_result_fail("futex not marked with FUTEX_OWNER_DIED\n");
Now I kinda understand this insanity. The child emits a fail and
exits. Then the parent ...
> + for (i = 0; i < CHILD_NR; i++)
> + create_child(&child_wait_lock, &locks[i]);
> +
> + /* Wait for all children to return */
> + while (wait(NULL) > 0);
> +
> + pthread_barrier_destroy(&barrier);
> + pthread_barrier_destroy(&barrier2);
> +
> + ksft_test_result_pass("%s\n", __func__);
... happily claims that the test passed.
Seriously?
Thread functions have a return value for a reason and wait(2) has a
wstatus argument for the very same reason.
> +static int child_circular_list(void *arg)
> +{
> + static struct robust_list_head head;
> + struct lock_struct a, b, c;
> + int ret;
> +
> + ret = set_list(&head);
> + if (ret)
> + ksft_test_result_fail("set_list error\n");
Yet another instance of the same ....
Thanks,
tglx
next prev parent reply other threads:[~2025-06-26 22:36 UTC|newest]
Thread overview: 18+ messages / expand[flat|nested] mbox.gz Atom feed top
2025-06-26 17:11 [PATCH v5 0/7] futex: Create set_robust_list2 André Almeida
2025-06-26 17:11 ` [PATCH v5 1/7] selftests/futex: Add ASSERT_ macros André Almeida
2025-06-26 22:07 ` Thomas Gleixner
2025-06-26 22:09 ` Thomas Gleixner
2025-06-27 20:23 ` André Almeida
2025-07-01 9:20 ` Thomas Gleixner
2025-06-26 17:11 ` [PATCH v5 2/7] selftests/futex: Create test for robust list André Almeida
2025-06-26 22:36 ` Thomas Gleixner [this message]
2025-06-26 17:11 ` [PATCH v5 3/7] futex: Use explicit sizes for compat_exit_robust_list André Almeida
2025-06-26 22:56 ` Thomas Gleixner
2025-06-28 14:27 ` kernel test robot
2025-06-26 17:11 ` [PATCH v5 4/7] futex: Create set_robust_list2 André Almeida
2025-06-27 12:06 ` Thomas Gleixner
2025-06-26 17:11 ` [PATCH v5 5/7] futex: Remove the limit of elements for sys_set_robust_list2 lists André Almeida
2025-06-27 12:22 ` Thomas Gleixner
2025-06-26 17:11 ` [PATCH v5 6/7] futex: Wire up set_robust_list2 syscall André Almeida
2025-06-26 17:11 ` [PATCH v5 7/7] selftests: futex: Expand robust list test for the new interface André Almeida
2025-06-27 12:48 ` Thomas Gleixner
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=878qlep1u2.ffs@tglx \
--to=tglx@linutronix.de \
--cc=andrealmeid@igalia.com \
--cc=arnd@arndb.de \
--cc=bigeasy@linutronix.de \
--cc=dave@stgolabs.net \
--cc=dvhart@infradead.org \
--cc=kernel-dev@igalia.com \
--cc=linux-api@vger.kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-kselftest@vger.kernel.org \
--cc=longman@redhat.com \
--cc=mingo@redhat.com \
--cc=peterz@infradead.org \
--cc=shuah@kernel.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.