xenomai.lists.linux.dev archive mirror
 help / color / mirror / Atom feed
From: Jan Kiszka <jan.kiszka@siemens.com>
To: Fabian Scheler <Fabian.Scheler@siemens.com>, xenomai@lists.linux.dev
Subject: Re: [PATCH v2] testsuite: smokey - new testcase pi_nesting_contend_multicore
Date: Wed, 20 Mar 2024 08:42:21 +0100	[thread overview]
Message-ID: <f3da1356-8a59-4bc7-b10c-aafa615e4c9e@siemens.com> (raw)
In-Reply-To: <20240314131927.27417-1-Fabian.Scheler@siemens.com>

On 14.03.24 14:19, Fabian Scheler wrote:
> The testcase ensures that the correct handling of priority boosts in a nested
> priority-inheritance scenario in a multi-core environment by assigning different
> threads to different cores if this is possible. On single-core targets all
> threads are assigned to the same core. In contrast to protect_nesting_protect
> and protect_nesting_pi only priority-inheritance is involved here.
> 
> For more details please refer to the detailed comment before
> pi_nesting_contend_multicore.
> 
> Signed-off-by: Fabian Scheler <Fabian.Scheler@siemens.com>
> ---
>  testsuite/smokey/posix-mutex/posix-mutex.c | 303 ++++++++++++++++++++-
>  1 file changed, 296 insertions(+), 7 deletions(-)
> 
> diff --git a/testsuite/smokey/posix-mutex/posix-mutex.c b/testsuite/smokey/posix-mutex/posix-mutex.c
> index 4aad24964..5cca0d2a1 100644
> --- a/testsuite/smokey/posix-mutex/posix-mutex.c
> +++ b/testsuite/smokey/posix-mutex/posix-mutex.c
> @@ -86,11 +86,12 @@ static int get_effective_prio(void)
>  	return stat.cprio;
>  }
>  
> -static int create_thread(pthread_t *tid, int policy, int prio,
> +static int create_thread(pthread_t *tid, int policy, int prio, int cpu,
>  			 void *(*thread)(void *), void *arg)
>  {
>  	struct sched_param param;
>  	pthread_attr_t thattr;
> +	cpu_set_t cpuset;
>  	int ret;
>  
>  	pthread_attr_init(&thattr);
> @@ -99,6 +100,16 @@ static int create_thread(pthread_t *tid, int policy, int prio,
>  	pthread_attr_setschedparam(&thattr, &param);
>  	pthread_attr_setinheritsched(&thattr, PTHREAD_EXPLICIT_SCHED);
>  
> +	if (cpu != -1) {
> +		CPU_ZERO(&cpuset);
> +		CPU_SET(cpu, &cpuset);
> +
> +		if (!__T(ret, pthread_attr_setaffinity_np(&thattr,
> +							  sizeof(cpu_set_t),
> +							  &cpuset)))
> +			return ret;
> +	}
> +
>  	if (!__T(ret, pthread_create(tid, &thattr, thread, arg)))
>  		return ret;
>  
> @@ -194,7 +205,7 @@ static int do_timed_contend(pthread_mutex_t *mutex, int prio)
>  		return ret;
>  
>  	args.mutex = mutex;
> -	ret = create_thread(&tid, SCHED_FIFO, prio,
> +	ret = create_thread(&tid, SCHED_FIFO, prio, -1,
>  			    mutex_timed_locker, &args);
>  	if (ret)
>  		return ret;
> @@ -256,7 +267,7 @@ static int do_contend(pthread_mutex_t *mutex, int type)
>  	smokey_barrier_init(&barrier);
>  	args.barrier = &barrier;
>  	args.lock_acquired = 0;
> -	ret = create_thread(&tid, SCHED_FIFO, THREAD_PRIO_MEDIUM,
> +	ret = create_thread(&tid, SCHED_FIFO, THREAD_PRIO_MEDIUM, -1,
>  			    mutex_locker, &args);
>  	if (ret)
>  		return ret;
> @@ -496,7 +507,7 @@ static int do_pi_contend(int prio)
>  	args.mutex = &mutex;
>  	smokey_barrier_init(&barrier);
>  	args.barrier = &barrier;
> -	ret = create_thread(&tid, SCHED_FIFO, prio,
> +	ret = create_thread(&tid, SCHED_FIFO, prio, -1,
>  			    mutex_timed_locker, &args);
>  	if (ret)
>  		return ret;
> @@ -533,6 +544,283 @@ static int pi_contend(void)
>  	return do_pi_contend(THREAD_PRIO_HIGH);
>  }
>  
> +struct multicore_low_context {
> +	pthread_mutex_t *mutex1;
> +	pthread_mutex_t *mutex2;
> +	struct smokey_barrier *low_barrier;
> +	struct smokey_barrier *locker1_barrier;
> +	struct smokey_barrier *locker2_barrier;
> +	struct smokey_barrier *medium_barrier;
> +	int *medium_flag;
> +};
> +
> +struct multicore_mutex_locker_context {
> +	pthread_mutex_t *mutex;
> +	struct smokey_barrier *barrier;
> +};
> +
> +struct multicore_medium_context {
> +	int *flag;
> +	struct smokey_barrier *barrier;
> +};
> +
> +static void *pi_nesting_contend_multicore_low(void *arg)
> +{
> +	struct multicore_low_context *context =
> +		(struct multicore_low_context *)arg;
> +	int ret;
> +
> +	if (!__T(ret, smokey_barrier_wait(context->low_barrier)))
> +		return (void *)(long)ret;
> +
> +	if (!__T(ret, pthread_mutex_lock(context->mutex1)))
> +		return (void *)(long)ret;
> +
> +	if (!__T(ret, pthread_mutex_lock(context->mutex2)))
> +		return (void *)(long)ret;
> +
> +	smokey_barrier_release(context->locker1_barrier);
> +	smokey_barrier_release(context->locker2_barrier);
> +	smokey_barrier_release(context->medium_barrier);
> +
> +	if (!__T(ret, pthread_mutex_unlock(context->mutex1)))
> +		return (void *)(long)ret;
> +
> +	if (!__Tassert(*(context->medium_flag) == 0))
> +		return (void *)(long)-10;
> +
> +	if (!__T(ret, pthread_mutex_unlock(context->mutex2)))
> +		return (void *)(long)ret;
> +
> +	if (!__Tassert(*(context->medium_flag) == 1))
> +		return (void *)(long)-20;
> +
> +	return NULL;
> +}
> +
> +static void *pi_nesting_contend_multicore_mutex_locker(void *arg)
> +{
> +	struct multicore_mutex_locker_context *context =
> +		(struct multicore_mutex_locker_context *)arg;
> +	int ret;
> +
> +	if (!__T(ret, smokey_barrier_wait(context->barrier)))
> +		return (void *)(long)ret;
> +
> +	if (!__T(ret, pthread_mutex_lock(context->mutex)))
> +		return (void *)(long)ret;
> +
> +	if (!__T(ret, pthread_mutex_unlock(context->mutex)))
> +		return (void *)(long)ret;
> +
> +	return NULL;
> +}
> +
> +static void *pi_nesting_contend_multicore_medium(void *arg)
> +{
> +	struct multicore_medium_context *context =
> +		(struct multicore_medium_context *)arg;
> +	int ret;
> +
> +	if (!__T(ret, smokey_barrier_wait(context->barrier)))
> +		return (void *)(long)ret;
> +
> +	*(context->flag) = 1;
> +
> +	return NULL;
> +}
> +
> +static int pi_nesting_contend_multicore_getcpu(cpu_set_t *rt_cpus,
> +					       cpu_set_t *online_cpus,
> +					       int num_cpus, int start_cpu)
> +{
> +	int tmp_cpu = start_cpu;
> +	int result_cpu = 0;
> +	do {
> +		result_cpu = ++tmp_cpu % num_cpus;
> +		if (result_cpu == start_cpu) {
> +			break;
> +		}
> +	} while (CPU_ISSET(tmp_cpu, rt_cpus) == 0 ||
> +		 CPU_ISSET(tmp_cpu, online_cpus));
> +
> +	return result_cpu;
> +}
> +
> +/*
> + * This testcase exercises a nested priority-inheritance-scenario preventing
> + * uncrontrolled prioritoty-inversion caused by a thread with a medium
> + * priority.
> + *
> + * In this test there are four threads involved:
> + *
> + * - low (prio THREAD_PRIO_LOW): holds mutexes mutex1 and mutex2
> + * - locker1 (prio THREAD_PRIO_HIGH): blocked by mutex1
> + * - locker2 (prio THREAD_PRIO_VERY_HIGH): blocked by mutex2
> + * - medium (prio THREAD_PRIO_MEDIUM): "tries" to preempt low
> + *
> + * The test ensures that the priority of the low-prio thread is correctly
> + * restored when it unlocks the pi-mutex and thus is deboosted. This test also
> + * checks this behavior in a multi-core environment by assigning different
> + * threads to different cores if this is possible. If only a single core is
> + * available, all threads are assigned to that core, i.e. the test also works on
> + * single-core targets.
> + *
> + * The threads are allocated to processor cores as follows:
> + * - low & medium: same core => low_medium_cpu, this is necessary, otherwise
> + *   medium could not prevent low from running
> + * - locker1: locker1_cpu != low_medium_cpu if possible
> + * - locker2: locker2_cpu != low_medium_cpu && locker1_cpu != locker2_cpu if possible
> + *
> + * (rough) execution sequence:
> + * 1.  setup test
> + * 2.  unblock thread low
> + * 3.  low: lock mutex1 and mutex2
> + * 4.  low: unblock locker1, locker2 and medium (in that order)
> + * 5.  locker1 and locker2: block when locking mutex1 and mutex2 respectively
> + * 6.  medium: blocked by low due to priority inheritance
> + * 7.  low: unlock mutex1 => locker1 can finish, medium => still blocked
> + * 8.  low: unlock mutex2 => locker2 can finish
> + * 9.  medium: run to completion
> + * 10. low: run to completion
> + */
> +static int pi_nesting_contend_multicore(void)
> +{
> +	struct multicore_mutex_locker_context locker1_args, locker2_args;
> +	int cpu, num_cpus, low_medium_cpu, locker1_cpu, locker2_cpu;
> +	struct smokey_barrier locker1_barrier, locker2_barrier;
> +	struct smokey_barrier low_barrier, medium_barrier;
> +	struct multicore_medium_context medium_args;
> +	pthread_t low, locker1, locker2, medium;
> +	struct multicore_low_context low_args;
> +	pthread_mutex_t mutex1, mutex2;
> +	cpu_set_t rt_cpus, online_cpus;
> +	int medium_flag = 0;
> +	void *status;
> +	int ret;
> +
> +	/* setup test */
> +
> +	cpu = get_current_cpu();
> +
> +	num_cpus = sysconf(_SC_NPROCESSORS_ONLN);
> +
> +	if (!__T(ret, get_realtime_cpu_set(&rt_cpus)))
> +		return ret;
> +
> +	if (!__T(ret, get_online_cpu_set(&online_cpus)))
> +		return ret;
> +
> +	low_medium_cpu = pi_nesting_contend_multicore_getcpu(
> +		&rt_cpus, &online_cpus, num_cpus, cpu);
> +	locker1_cpu = pi_nesting_contend_multicore_getcpu(
> +		&rt_cpus, &online_cpus, num_cpus, low_medium_cpu);
> +	locker2_cpu = pi_nesting_contend_multicore_getcpu(
> +		&rt_cpus, &online_cpus, num_cpus, locker1_cpu);
> +
> +	if (!__T(ret, smokey_barrier_init(&low_barrier)))
> +		return ret;
> +
> +	if (!__T(ret, smokey_barrier_init(&locker1_barrier)))
> +		return ret;
> +
> +	if (!__T(ret, smokey_barrier_init(&locker2_barrier)))
> +		return ret;
> +
> +	if (!__T(ret, smokey_barrier_init(&medium_barrier)))
> +		return ret;
> +
> +	ret = do_init_mutex(&mutex1, PTHREAD_MUTEX_NORMAL,
> +			    PTHREAD_PRIO_INHERIT);
> +	if (ret)
> +		return ret;
> +
> +	ret = do_init_mutex(&mutex2, PTHREAD_MUTEX_NORMAL,
> +			    PTHREAD_PRIO_INHERIT);
> +	if (ret)
> +		return ret;
> +
> +	low_args.mutex1 = &mutex1;
> +	low_args.mutex2 = &mutex2;
> +	low_args.low_barrier = &low_barrier;
> +	low_args.locker1_barrier = &locker1_barrier;
> +	low_args.locker2_barrier = &locker2_barrier;
> +	low_args.medium_barrier = &medium_barrier;
> +	low_args.medium_flag = &medium_flag;
> +
> +	locker1_args.mutex = &mutex1;
> +	locker1_args.barrier = &locker1_barrier;
> +
> +	locker2_args.mutex = &mutex2;
> +	locker2_args.barrier = &locker2_barrier;
> +
> +	medium_args.barrier = &medium_barrier;
> +	medium_args.flag = &medium_flag;
> +
> +	if (!__T(ret, create_thread(
> +			      &low, SCHED_FIFO, THREAD_PRIO_LOW, low_medium_cpu,
> +			      pi_nesting_contend_multicore_low, &low_args)))
> +		return ret;
> +
> +	if (!__T(ret, create_thread(&locker1, SCHED_FIFO, THREAD_PRIO_HIGH,
> +				    locker1_cpu,
> +				    pi_nesting_contend_multicore_mutex_locker,
> +				    &locker1_args)))
> +		return ret;
> +
> +	if (!__T(ret, create_thread(&locker2, SCHED_FIFO, THREAD_PRIO_VERY_HIGH,
> +				    locker2_cpu,
> +				    pi_nesting_contend_multicore_mutex_locker,
> +				    &locker2_args)))
> +		return ret;
> +
> +	if (!__T(ret, create_thread(&medium, SCHED_FIFO, THREAD_PRIO_MEDIUM,
> +				    low_medium_cpu,
> +				    pi_nesting_contend_multicore_medium,
> +				    &medium_args)))
> +		return ret;
> +
> +	/* start test */
> +
> +	smokey_barrier_release(&low_barrier);
> +
> +	/* wait for threads to finish */
> +
> +	if (!__T(ret, pthread_join(low, &status)))
> +		return ret;
> +	if (!__Tassert(status == NULL))
> +		return -EINVAL;
> +
> +	if (!__T(ret, pthread_join(locker1, &status)))
> +		return ret;
> +	if (!__Tassert(status == NULL))
> +		return -EINVAL;
> +
> +	if (!__T(ret, pthread_join(locker2, &status)))
> +		return ret;
> +	if (!__Tassert(status == NULL))
> +		return -EINVAL;
> +
> +	if (!__T(ret, pthread_join(medium, &status)))
> +		return ret;
> +	if (!__Tassert(status == NULL))
> +		return -EINVAL;
> +
> +	/* cleanup test */
> +
> +	smokey_barrier_destroy(&low_barrier);
> +	smokey_barrier_destroy(&locker1_barrier);
> +	smokey_barrier_destroy(&locker2_barrier);
> +	smokey_barrier_destroy(&medium_barrier);
> +
> +	if (!__T(ret, pthread_mutex_destroy(&mutex1)))
> +		return ret;
> +	if (!__T(ret, pthread_mutex_destroy(&mutex2)))
> +		return ret;
> +
> +	return 0;
> +}
> +
>  static void *mutex_locker_steal(void *arg)
>  {
>  	struct locker_context *p = arg;
> @@ -572,7 +860,7 @@ static int do_steal(int may_steal)
>  	smokey_barrier_init(&barrier);
>  	args.barrier = &barrier;
>  	args.lock_acquired = 0;
> -	ret = create_thread(&tid, SCHED_FIFO, THREAD_PRIO_LOW,
> +	ret = create_thread(&tid, SCHED_FIFO, THREAD_PRIO_LOW, -1,
>  			    mutex_locker_steal, &args);
>  	if (ret)
>  		return ret;
> @@ -974,7 +1262,7 @@ static int protect_handover(void)
>  	smokey_barrier_init(&barrier);
>  	args.barrier = &barrier;
>  	args.lock_acquired = 0;
> -	ret = create_thread(&tid, SCHED_FIFO, THREAD_PRIO_LOW,
> +	ret = create_thread(&tid, SCHED_FIFO, THREAD_PRIO_LOW, -1,
>  			    mutex_locker, &args);
>  	if (ret)
>  		return ret;
> @@ -1040,7 +1328,7 @@ static int check_timedlock_abstime_validation(void)
>  	 * validate the (invalid) timeout
>  	 */
>  	args.mutex = &mutex;
> -	ret = create_thread(&tid, SCHED_FIFO, THREAD_PRIO_LOW,
> +	ret = create_thread(&tid, SCHED_FIFO, THREAD_PRIO_LOW, -1,
>  			    mutex_timed_locker_inv_timeout, &args);
>  
>  	if (ret)
> @@ -1114,6 +1402,7 @@ static int run_posix_mutex(struct smokey_test *t, int argc, char *const argv[])
>  	do_test(timed_contend, MAX_100_MS);
>  	do_test(weak_mode_switch, MAX_100_MS);
>  	do_test(pi_contend, MAX_100_MS);
> +	do_test(pi_nesting_contend_multicore, MAX_100_MS);
>  	do_test(steal, MAX_100_MS);
>  	do_test(no_steal, MAX_100_MS);
>  	do_test(protect_raise, MAX_100_MS);

Thanks, applied.

Jan

-- 
Siemens AG, Technology
Linux Expert Center


      reply	other threads:[~2024-03-20  7:42 UTC|newest]

Thread overview: 6+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2024-03-12  7:23 [PATCH] testsuite: smokey - new testcase pi_nesting_contend_multicore Fabian Scheler
2024-03-12  7:44 ` Jan Kiszka
2024-03-12  8:29   ` Scheler, Fabian (T CED SES-DE)
2024-03-12 10:44 ` Jan Kiszka
2024-03-14 13:19   ` [PATCH v2] " Fabian Scheler
2024-03-20  7:42     ` Jan Kiszka [this message]

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=f3da1356-8a59-4bc7-b10c-aafa615e4c9e@siemens.com \
    --to=jan.kiszka@siemens.com \
    --cc=Fabian.Scheler@siemens.com \
    --cc=xenomai@lists.linux.dev \
    /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 a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).