aboutsummaryrefslogtreecommitdiff
path: root/nptl
diff options
context:
space:
mode:
authorFlorian Weimer <fweimer@redhat.com>2020-05-15 11:26:37 +0200
committerFlorian Weimer <fweimer@redhat.com>2020-05-20 20:22:31 +0200
commit714da1d4eac32400187255254dd40334b48b80f3 (patch)
tree71304ad87efc30ceaa5b16b8475670c9d9d76270 /nptl
parentb6ad64b907ab0066902215cf28eadc1e97307e84 (diff)
downloadglibc-714da1d4eac32400187255254dd40334b48b80f3.tar
glibc-714da1d4eac32400187255254dd40334b48b80f3.tar.gz
glibc-714da1d4eac32400187255254dd40334b48b80f3.tar.bz2
glibc-714da1d4eac32400187255254dd40334b48b80f3.zip
nptl: Replace some stubs with the Linux implementation
The stubs for pthread_getaffinity_np, pthread_getname_np, pthread_setaffinity_np, pthread_setname_np are replaced, and corresponding tests are moved. After the removal of the NaCl port, nptl is Linux-specific, and the stubs are no longer needed. This effectively reverts commit c76d1ff5149bd03210f2bb8cd64446c51618d016 ("NPTL: Add stubs for Linux-only extension functions."). Reviewed-by: Carlos O'Donell <carlos@redhat.com>
Diffstat (limited to 'nptl')
-rw-r--r--nptl/Makefile9
-rw-r--r--nptl/pthread_getaffinity.c42
-rw-r--r--nptl/pthread_getname.c49
-rw-r--r--nptl/pthread_setaffinity.c37
-rw-r--r--nptl/pthread_setname.c43
-rw-r--r--nptl/tst-setgetname.c296
-rw-r--r--nptl/tst-thread-affinity-pthread.c49
-rw-r--r--nptl/tst-thread-affinity-pthread2.c95
-rw-r--r--nptl/tst-thread-affinity-sched.c36
9 files changed, 626 insertions, 30 deletions
diff --git a/nptl/Makefile b/nptl/Makefile
index ef9a6eb561..5fc45b224b 100644
--- a/nptl/Makefile
+++ b/nptl/Makefile
@@ -321,14 +321,19 @@ tests = tst-attr2 tst-attr3 tst-default-attr \
tst-thread-exit-clobber tst-minstack-cancel tst-minstack-exit \
tst-minstack-throw \
tst-rwlock-pwn \
- tst-unwind-thread
+ tst-unwind-thread \
+ tst-thread-affinity-pthread \
+ tst-thread-affinity-pthread2 \
+ tst-thread-affinity-sched \
+
tests-container = tst-pthread-getattr
tests-internal := tst-robustpi8 tst-rwlock19 tst-rwlock20 \
tst-sem11 tst-sem12 tst-sem13 \
tst-barrier5 tst-signal7 tst-mutex8 tst-mutex8-static \
- tst-mutexpi8 tst-mutexpi8-static tst-cancel25
+ tst-mutexpi8 tst-mutexpi8-static tst-cancel25 \
+ tst-setgetname \
xtests = tst-setuid1 tst-setuid1-static tst-setuid2 \
tst-mutexpp1 tst-mutexpp6 tst-mutexpp10
diff --git a/nptl/pthread_getaffinity.c b/nptl/pthread_getaffinity.c
index 2ba05b5b92..cf6ecfe01f 100644
--- a/nptl/pthread_getaffinity.c
+++ b/nptl/pthread_getaffinity.c
@@ -1,6 +1,6 @@
-/* Get the processor affinity of a thread. Stub version.
- Copyright (C) 2014-2020 Free Software Foundation, Inc.
+/* Copyright (C) 2003-2020 Free Software Foundation, Inc.
This file is part of the GNU C Library.
+ Contributed by Ulrich Drepper <drepper@redhat.com>, 2003.
The GNU C Library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
@@ -16,17 +16,43 @@
License along with the GNU C Library; if not, see
<https://www.gnu.org/licenses/>. */
+#include <errno.h>
+#include <limits.h>
#include <pthreadP.h>
+#include <string.h>
+#include <sysdep.h>
+#include <sys/param.h>
+#include <sys/types.h>
+#include <shlib-compat.h>
+
int
-__pthread_getaffinity_np (pthread_t th, size_t cpusetsize, cpu_set_t *cpuset)
+__pthread_getaffinity_new (pthread_t th, size_t cpusetsize, cpu_set_t *cpuset)
{
const struct pthread *pd = (const struct pthread *) th;
- if (INVALID_TD_P (pd))
- return ESRCH;
+ int res = INTERNAL_SYSCALL_CALL (sched_getaffinity, pd->tid,
+ MIN (INT_MAX, cpusetsize), cpuset);
+ if (INTERNAL_SYSCALL_ERROR_P (res))
+ return INTERNAL_SYSCALL_ERRNO (res);
+
+ /* Clean the rest of the memory the kernel didn't do. */
+ memset ((char *) cpuset + res, '\0', cpusetsize - res);
+
+ return 0;
+}
+strong_alias (__pthread_getaffinity_new, __pthread_getaffinity_np)
+versioned_symbol (libpthread, __pthread_getaffinity_new,
+ pthread_getaffinity_np, GLIBC_2_3_4);
- return ENOSYS;
+
+#if SHLIB_COMPAT (libpthread, GLIBC_2_3_3, GLIBC_2_3_4)
+int
+__pthread_getaffinity_old (pthread_t th, cpu_set_t *cpuset)
+{
+ /* The old interface by default assumed a 1024 processor bitmap. */
+ return __pthread_getaffinity_new (th, 128, cpuset);
}
-weak_alias (__pthread_getaffinity_np, pthread_getaffinity_np)
-stub_warning (pthread_getaffinity_np)
+compat_symbol (libpthread, __pthread_getaffinity_old, pthread_getaffinity_np,
+ GLIBC_2_3_3);
+#endif
diff --git a/nptl/pthread_getname.c b/nptl/pthread_getname.c
index b771f2ef76..c78cccffd4 100644
--- a/nptl/pthread_getname.c
+++ b/nptl/pthread_getname.c
@@ -1,5 +1,5 @@
-/* pthread_getname_np -- Get thread name. Stub version.
- Copyright (C) 2014-2020 Free Software Foundation, Inc.
+/* pthread_getname_np -- Get thread name. Linux version
+ Copyright (C) 2010-2020 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
@@ -17,16 +17,53 @@
not, see <https://www.gnu.org/licenses/>. */
#include <errno.h>
+#include <fcntl.h>
#include <pthreadP.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/prctl.h>
+
+#include <not-cancel.h>
+
int
pthread_getname_np (pthread_t th, char *buf, size_t len)
{
const struct pthread *pd = (const struct pthread *) th;
- if (INVALID_TD_P (pd))
- return ESRCH;
+ /* Unfortunately the kernel headers do not export the TASK_COMM_LEN
+ macro. So we have to define it here. */
+#define TASK_COMM_LEN 16
+ if (len < TASK_COMM_LEN)
+ return ERANGE;
+
+ if (pd == THREAD_SELF)
+ return prctl (PR_GET_NAME, buf) ? errno : 0;
+
+#define FMT "/proc/self/task/%u/comm"
+ char fname[sizeof (FMT) + 8];
+ sprintf (fname, FMT, (unsigned int) pd->tid);
+
+ int fd = __open64_nocancel (fname, O_RDONLY);
+ if (fd == -1)
+ return errno;
+
+ int res = 0;
+ ssize_t n = TEMP_FAILURE_RETRY (__read_nocancel (fd, buf, len));
+ if (n < 0)
+ res = errno;
+ else
+ {
+ if (buf[n - 1] == '\n')
+ buf[n - 1] = '\0';
+ else if (n == len)
+ res = ERANGE;
+ else
+ buf[n] = '\0';
+ }
+
+ __close_nocancel_nostatus (fd);
- return ENOSYS;
+ return res;
}
-stub_warning (pthread_getname_np)
diff --git a/nptl/pthread_setaffinity.c b/nptl/pthread_setaffinity.c
index 29bbfb4fb2..b0bd90c324 100644
--- a/nptl/pthread_setaffinity.c
+++ b/nptl/pthread_setaffinity.c
@@ -1,6 +1,6 @@
-/* Set the processor affinity of a thread. Stub version.
- Copyright (C) 2014-2020 Free Software Foundation, Inc.
+/* Copyright (C) 2003-2020 Free Software Foundation, Inc.
This file is part of the GNU C Library.
+ Contributed by Ulrich Drepper <drepper@redhat.com>, 2003.
The GNU C Library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
@@ -16,17 +16,38 @@
License along with the GNU C Library; if not, see
<https://www.gnu.org/licenses/>. */
+#include <errno.h>
#include <pthreadP.h>
+#include <sysdep.h>
+#include <sys/types.h>
+#include <shlib-compat.h>
+
int
-pthread_setaffinity_np (pthread_t th,
- size_t cpusetsize, const cpu_set_t *cpuset)
+__pthread_setaffinity_new (pthread_t th, size_t cpusetsize,
+ const cpu_set_t *cpuset)
{
const struct pthread *pd = (const struct pthread *) th;
+ int res;
+
+ res = INTERNAL_SYSCALL_CALL (sched_setaffinity, pd->tid, cpusetsize,
+ cpuset);
+
+ return (INTERNAL_SYSCALL_ERROR_P (res)
+ ? INTERNAL_SYSCALL_ERRNO (res)
+ : 0);
+}
+versioned_symbol (libpthread, __pthread_setaffinity_new,
+ pthread_setaffinity_np, GLIBC_2_3_4);
- if (INVALID_TD_P (pd))
- return ESRCH;
- return ENOSYS;
+#if SHLIB_COMPAT (libpthread, GLIBC_2_3_3, GLIBC_2_3_4)
+int
+__pthread_setaffinity_old (pthread_t th, cpu_set_t *cpuset)
+{
+ /* The old interface by default assumed a 1024 processor bitmap. */
+ return __pthread_setaffinity_new (th, 128, cpuset);
}
-stub_warning (pthread_setaffinity_np)
+compat_symbol (libpthread, __pthread_setaffinity_old, pthread_setaffinity_np,
+ GLIBC_2_3_3);
+#endif
diff --git a/nptl/pthread_setname.c b/nptl/pthread_setname.c
index adfb3e828f..29df46b77c 100644
--- a/nptl/pthread_setname.c
+++ b/nptl/pthread_setname.c
@@ -1,5 +1,5 @@
-/* pthread_setname_np -- Set thread name. Stub version.
- Copyright (C) 2014-2020 Free Software Foundation, Inc.
+/* pthread_setname_np -- Set thread name. Linux version
+ Copyright (C) 2010-2020 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
@@ -17,16 +17,47 @@
not, see <https://www.gnu.org/licenses/>. */
#include <errno.h>
+#include <fcntl.h>
#include <pthreadP.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/prctl.h>
+
+#include <not-cancel.h>
+
int
pthread_setname_np (pthread_t th, const char *name)
{
const struct pthread *pd = (const struct pthread *) th;
- if (INVALID_TD_P (pd))
- return ESRCH;
+ /* Unfortunately the kernel headers do not export the TASK_COMM_LEN
+ macro. So we have to define it here. */
+#define TASK_COMM_LEN 16
+ size_t name_len = strlen (name);
+ if (name_len >= TASK_COMM_LEN)
+ return ERANGE;
+
+ if (pd == THREAD_SELF)
+ return prctl (PR_SET_NAME, name) ? errno : 0;
+
+#define FMT "/proc/self/task/%u/comm"
+ char fname[sizeof (FMT) + 8];
+ sprintf (fname, FMT, (unsigned int) pd->tid);
+
+ int fd = __open64_nocancel (fname, O_RDWR);
+ if (fd == -1)
+ return errno;
+
+ int res = 0;
+ ssize_t n = TEMP_FAILURE_RETRY (__write_nocancel (fd, name, name_len));
+ if (n < 0)
+ res = errno;
+ else if (n != name_len)
+ res = EIO;
+
+ __close_nocancel_nostatus (fd);
- return ENOSYS;
+ return res;
}
-stub_warning (pthread_setname_np)
diff --git a/nptl/tst-setgetname.c b/nptl/tst-setgetname.c
new file mode 100644
index 0000000000..c9a6fafb96
--- /dev/null
+++ b/nptl/tst-setgetname.c
@@ -0,0 +1,296 @@
+/* Test pthread_setname_np and pthread_getname_np.
+ Copyright (C) 2013-2020 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; see the file COPYING.LIB. If
+ not, see <https://www.gnu.org/licenses/>. */
+#include <stdio.h>
+#include <stdlib.h>
+#include <pthread.h>
+#include <string.h>
+#include <sys/syscall.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <errno.h>
+
+/* New name of process. */
+#define NEW_NAME "setname"
+
+/* Name of process which is one byte too big
+ e.g. 17 bytes including null-terminator */
+#define BIG_NAME "....V....X....XV"
+
+/* Longest name of a process
+ e.g. 16 bytes including null-terminator. */
+#define LONGEST_NAME "....V....X....X"
+
+/* One less than longest name with unique
+ characters to detect modification. */
+#define CANARY_NAME "abcdefghijklmn"
+
+/* On Linux the maximum length of the name of a task *including* the null
+ terminator. */
+#define TASK_COMM_LEN 16
+
+/* On Linux we can read this task's name from /proc. */
+int
+get_self_comm (long tid, char *buf, size_t len)
+{
+ int res = 0;
+#define FMT "/proc/self/task/%lu/comm"
+ char fname[sizeof (FMT) + 32];
+ sprintf (fname, FMT, (unsigned long) tid);
+
+ int fd = open (fname, O_RDONLY);
+ if (fd == -1)
+ return errno;
+
+ ssize_t n = read (fd, (void *) buf, len);
+ if (n < 0)
+ res = errno;
+ else
+ {
+ if (buf[n - 1] == '\n')
+ buf[n - 1] = '\0';
+ else if (n == len)
+ res = ERANGE;
+ else
+ buf[n] = '\0';
+ }
+
+ close (fd);
+ return res;
+}
+
+int
+do_test (int argc, char **argv)
+{
+ pthread_t self;
+ int res;
+ int ret = 0;
+ char name[TASK_COMM_LEN];
+ char name_check[TASK_COMM_LEN];
+
+ memset (name, '\0', TASK_COMM_LEN);
+ memset (name_check, '\0', TASK_COMM_LEN);
+
+ /* Test 1: Get the name of the task via pthread_getname_np and /proc
+ and verify that they both match. */
+ self = pthread_self ();
+ res = pthread_getname_np (self, name, TASK_COMM_LEN);
+
+ if (res == 0)
+ {
+ res = get_self_comm (gettid (), name_check, TASK_COMM_LEN);
+
+ if (res == 0)
+ {
+ if (strncmp (name, name_check, strlen (BIG_NAME)) == 0)
+ printf ("PASS: Test 1 - pthread_getname_np and /proc agree.\n");
+ else
+ {
+ printf ("FAIL: Test 1 - pthread_getname_np and /proc differ"
+ " i.e. %s != %s\n", name, name_check);
+ ret++;
+ }
+ }
+ else
+ {
+ printf ("FAIL: Test 1 - unable read task name via proc.\n");
+ ret++;
+ }
+ }
+ else
+ {
+ printf ("FAIL: Test 1 - pthread_getname_np failed with error %d\n", res);
+ ret++;
+ }
+
+ /* Test 2: Test setting the name and then independently verify it
+ was set. */
+ res = pthread_setname_np (self, NEW_NAME);
+
+ if (res == 0)
+ {
+ res = get_self_comm (gettid (), name_check, TASK_COMM_LEN);
+ if (res == 0)
+ {
+ if (strncmp (NEW_NAME, name_check, strlen (BIG_NAME)) == 0)
+ printf ("PASS: Test 2 - Value used in pthread_setname_np and"
+ " /proc agree.\n");
+ else
+ {
+ printf ("FAIL: Test 2 - Value used in pthread_setname_np"
+ " and /proc differ i.e. %s != %s\n",
+ NEW_NAME, name_check);
+ ret++;
+ }
+ }
+ else
+ {
+ printf ("FAIL: Test 2 - unable to read task name via proc.\n");
+ ret++;
+ }
+ }
+ else
+ {
+ printf ("FAIL: Test 2 - pthread_setname_np failed with error %d\n", res);
+ ret++;
+ }
+
+ /* Test 3: Test setting a name that is one-byte too big. */
+ res = pthread_getname_np (self, name, TASK_COMM_LEN);
+
+ if (res == 0)
+ {
+ res = pthread_setname_np (self, BIG_NAME);
+ if (res != 0)
+ {
+ if (res == ERANGE)
+ {
+ printf ("PASS: Test 3 - pthread_setname_np returned ERANGE"
+ " for a process name that was too long.\n");
+
+ /* Verify the old name didn't change. */
+ res = get_self_comm (gettid (), name_check, TASK_COMM_LEN);
+ if (res == 0)
+ {
+ if (strncmp (name, name_check, strlen (BIG_NAME)) == 0)
+ printf ("PASS: Test 3 - Original name unchanged after"
+ " pthread_setname_np returned ERANGE.\n");
+ else
+ {
+ printf ("FAIL: Test 3 - Original name changed after"
+ " pthread_setname_np returned ERANGE"
+ " i.e. %s != %s\n",
+ name, name_check);
+ ret++;
+ }
+ }
+ else
+ {
+ printf ("FAIL: Test 3 - unable to read task name.\n");
+ ret++;
+ }
+ }
+ else
+ {
+ printf ("FAIL: Test 3 - Wrong error returned"
+ " i.e. ERANGE != %d\n", res);
+ ret++;
+ }
+ }
+ else
+ {
+ printf ("FAIL: Test 3 - Too-long name accepted by"
+ " pthread_setname_np.\n");
+ ret++;
+ }
+ }
+ else
+ {
+ printf ("FAIL: Test 3 - Unable to get original name.\n");
+ ret++;
+ }
+
+ /* Test 4: Verify that setting the longest name works. */
+ res = pthread_setname_np (self, LONGEST_NAME);
+
+ if (res == 0)
+ {
+ res = get_self_comm (gettid (), name_check, TASK_COMM_LEN);
+ if (res == 0)
+ {
+ if (strncmp (LONGEST_NAME, name_check, strlen (BIG_NAME)) == 0)
+ printf ("PASS: Test 4 - Longest name set via pthread_setname_np"
+ " agrees with /proc.\n");
+ else
+ {
+ printf ("FAIL: Test 4 - Value used in pthread_setname_np and /proc"
+ " differ i.e. %s != %s\n", LONGEST_NAME, name_check);
+ ret++;
+ }
+ }
+ else
+ {
+ printf ("FAIL: Test 4 - unable to read task name via proc.\n");
+ ret++;
+ }
+ }
+ else
+ {
+ printf ("FAIL: Test 4 - pthread_setname_np failed with error %d\n", res);
+ ret++;
+ }
+
+ /* Test 5: Verify that getting a long name into a small buffer fails. */
+ strncpy (name, CANARY_NAME, strlen (CANARY_NAME) + 1);
+
+ /* Claim the buffer length is strlen (LONGEST_NAME). This is one character
+ too small to hold LONGEST_NAME *and* the null terminator. We should get
+ back ERANGE and name should be unmodified. */
+ res = pthread_getname_np (self, name, strlen (LONGEST_NAME));
+
+ if (res != 0)
+ {
+ if (res == ERANGE)
+ {
+ if (strncmp (CANARY_NAME, name, strlen (BIG_NAME)) == 0)
+ {
+ printf ("PASS: Test 5 - ERANGE and buffer unmodified.\n");
+ }
+ else
+ {
+ printf ("FAIL: Test 5 - Original buffer modified.\n");
+ ret++;
+ }
+ }
+ else
+ {
+ printf ("FAIL: Test 5 - Did not return ERANGE for small buffer.\n");
+ ret++;
+ }
+ }
+ else
+ {
+ printf ("FAIL: Test 5 - Returned name longer than buffer.\n");
+ ret++;
+ }
+
+ /* Test 6: Lastly make sure we can read back the longest name. */
+ res = pthread_getname_np (self, name, strlen (LONGEST_NAME) + 1);
+
+ if (res == 0)
+ {
+ if (strncmp (LONGEST_NAME, name, strlen (BIG_NAME)) == 0)
+ {
+ printf ("PASS: Test 6 - Read back longest name correctly.\n");
+ }
+ else
+ {
+ printf ("FAIL: Test 6 - Read \"%s\" instead of longest name.\n",
+ name);
+ ret++;
+ }
+ }
+ else
+ {
+ printf ("FAIL: Test 6 - pthread_getname_np failed with error %d\n", res);
+ ret++;
+ }
+
+ return ret;
+}
+
+#include <test-skeleton.c>
diff --git a/nptl/tst-thread-affinity-pthread.c b/nptl/tst-thread-affinity-pthread.c
new file mode 100644
index 0000000000..388457763d
--- /dev/null
+++ b/nptl/tst-thread-affinity-pthread.c
@@ -0,0 +1,49 @@
+/* Multi-threaded test for pthread_getaffinity_np, pthread_setaffinity_np.
+ Copyright (C) 2015-2020 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
+ <https://www.gnu.org/licenses/>. */
+
+#include <errno.h>
+#include <pthread.h>
+
+/* Defined for the benefit of tst-skeleton-thread-affinity.c, included
+ below. */
+
+static int
+setaffinity (size_t size, const cpu_set_t *set)
+{
+ int ret = pthread_setaffinity_np (pthread_self (), size, set);
+ if (ret != 0)
+ {
+ errno = ret;
+ return -1;
+ }
+ return 0;
+}
+
+static int
+getaffinity (size_t size, cpu_set_t *set)
+{
+ int ret = pthread_getaffinity_np (pthread_self (), size, set);
+ if (ret != 0)
+ {
+ errno = ret;
+ return -1;
+ }
+ return 0;
+}
+
+#include "tst-skeleton-thread-affinity.c"
diff --git a/nptl/tst-thread-affinity-pthread2.c b/nptl/tst-thread-affinity-pthread2.c
new file mode 100644
index 0000000000..3dd126fa60
--- /dev/null
+++ b/nptl/tst-thread-affinity-pthread2.c
@@ -0,0 +1,95 @@
+/* Separate thread test for pthread_getaffinity_np, pthread_setaffinity_np.
+ Copyright (C) 2015-2020 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
+ <https://www.gnu.org/licenses/>. */
+
+#include <errno.h>
+#include <pthread.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+/* Defined for the benefit of tst-skeleton-thread-affinity.c, included
+ below. This variant runs the functions on a separate thread. */
+
+struct affinity_access_task
+{
+ pthread_t thread;
+ cpu_set_t *set;
+ size_t size;
+ bool get;
+ int result;
+};
+
+static void *
+affinity_access_thread (void *closure)
+{
+ struct affinity_access_task *task = closure;
+ if (task->get)
+ task->result = pthread_getaffinity_np
+ (task->thread, task->size, task->set);
+ else
+ task->result = pthread_setaffinity_np
+ (task->thread, task->size, task->set);
+ return NULL;
+}
+
+static int
+run_affinity_access_thread (cpu_set_t *set, size_t size, bool get)
+{
+ struct affinity_access_task task =
+ {
+ .thread = pthread_self (),
+ .set = set,
+ .size = size,
+ .get = get
+ };
+ pthread_t thr;
+ int ret = pthread_create (&thr, NULL, affinity_access_thread, &task);
+ if (ret != 0)
+ {
+ errno = ret;
+ printf ("error: could not create affinity access thread: %m\n");
+ abort ();
+ }
+ ret = pthread_join (thr, NULL);
+ if (ret != 0)
+ {
+ errno = ret;
+ printf ("error: could not join affinity access thread: %m\n");
+ abort ();
+ }
+ if (task.result != 0)
+ {
+ errno = task.result;
+ return -1;
+ }
+ return 0;
+}
+
+static int
+setaffinity (size_t size, const cpu_set_t *set)
+{
+ return run_affinity_access_thread ((cpu_set_t *) set, size, false);
+}
+
+static int
+getaffinity (size_t size, cpu_set_t *set)
+{
+ return run_affinity_access_thread (set, size, true);
+}
+
+#include "tst-skeleton-thread-affinity.c"
diff --git a/nptl/tst-thread-affinity-sched.c b/nptl/tst-thread-affinity-sched.c
new file mode 100644
index 0000000000..9fe4613a8f
--- /dev/null
+++ b/nptl/tst-thread-affinity-sched.c
@@ -0,0 +1,36 @@
+/* Multi-threaded test for sched_getaffinity_np, sched_setaffinity_np.
+ Copyright (C) 2015-2020 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
+ <https://www.gnu.org/licenses/>. */
+
+#include <sched.h>
+
+/* Defined for the benefit of tst-skeleton-thread-affinity.c, included
+ below. */
+
+static int
+getaffinity (size_t size, cpu_set_t *set)
+{
+ return sched_getaffinity (0, size, set);
+}
+
+static int
+setaffinity (size_t size, const cpu_set_t *set)
+{
+ return sched_setaffinity (0, size, set);
+}
+
+#include "tst-skeleton-thread-affinity.c"