diff options
author | David S. Miller <davem@davemloft.net> | 2015-01-31 18:59:40 -0800 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2015-01-31 23:39:50 -0800 |
commit | d4abeca50400747402a5a33f3a8aa0941be076d5 (patch) | |
tree | 0af10d77c2be123e4592654038d4bc3ac7a5dab5 /sysdeps/sparc/sparc32 | |
parent | 5f3d0b78e011d2a72f9e88b0e9ef5bc081d18f97 (diff) | |
download | glibc-d4abeca50400747402a5a33f3a8aa0941be076d5.tar.gz glibc-d4abeca50400747402a5a33f3a8aa0941be076d5.tar.xz glibc-d4abeca50400747402a5a33f3a8aa0941be076d5.zip |
Fix sparc semaphore implementation after recent changes.
* sysdeps/sparc/nptl/sem_init.c: Delete. * sysdeps/sparc/nptl/sem_post.c: Delete. * sysdeps/sparc/nptl/sem_timedwait.c: Delete. * sysdeps/sparc/nptl/sem_wait.c: Delete. * sysdeps/sparc/sparc32/sem_init.c: New file. * sysdeps/sparc/sparc32/sem_waitcommon.c: New file. * sysdeps/sparc/sparc32/sem_open.c: Generic nptl version with padding explicitly initialized. * sysdeps/sparc/sparc32/sem_post.c: Generic nptl version using padding for in-semaphore spinlock. * sysdeps/sparc/sparc32/sem_wait.c: Likewise. * sysdeps/sparc/sparc32/sem_trywait.c: Delete. * sysdeps/sparc/sparc32/sem_timedwait.c: Delete. * sysdeps/sparc/sparc32/sparcv9/sem_init.c: New file. * sysdeps/sparc/sparc32/sparcv9/sem_open.c: New file. * sysdeps/sparc/sparc32/sparcv9/sem_post.c: New file. * sysdeps/sparc/sparc32/sparcv9/sem_waitcommon.c: New file. * sysdeps/sparc/sparc32/sparcv9/sem_wait.c: Redirect to nptl version. * sysdeps/sparc/sparc32/sparcv9/sem_timedwait.c: Delete. * sysdeps/sparc/sparc32/sparcv9/sem_trywait.c: Delete.
Diffstat (limited to 'sysdeps/sparc/sparc32')
-rw-r--r-- | sysdeps/sparc/sparc32/sem_init.c | 95 | ||||
-rw-r--r-- | sysdeps/sparc/sparc32/sem_open.c | 298 | ||||
-rw-r--r-- | sysdeps/sparc/sparc32/sem_post.c | 87 | ||||
-rw-r--r-- | sysdeps/sparc/sparc32/sem_timedwait.c | 153 | ||||
-rw-r--r-- | sysdeps/sparc/sparc32/sem_trywait.c | 58 | ||||
-rw-r--r-- | sysdeps/sparc/sparc32/sem_wait.c | 160 | ||||
-rw-r--r-- | sysdeps/sparc/sparc32/sem_waitcommon.c | 245 | ||||
-rw-r--r-- | sysdeps/sparc/sparc32/sparcv9/sem_init.c | 1 | ||||
-rw-r--r-- | sysdeps/sparc/sparc32/sparcv9/sem_open.c | 1 | ||||
-rw-r--r-- | sysdeps/sparc/sparc32/sparcv9/sem_post.c | 2 | ||||
-rw-r--r-- | sysdeps/sparc/sparc32/sparcv9/sem_timedwait.c | 1 | ||||
-rw-r--r-- | sysdeps/sparc/sparc32/sparcv9/sem_trywait.c | 1 | ||||
-rw-r--r-- | sysdeps/sparc/sparc32/sparcv9/sem_wait.c | 2 | ||||
-rw-r--r-- | sysdeps/sparc/sparc32/sparcv9/sem_waitcommon.c | 1 |
14 files changed, 735 insertions, 370 deletions
diff --git a/sysdeps/sparc/sparc32/sem_init.c b/sysdeps/sparc/sparc32/sem_init.c new file mode 100644 index 0000000000..7c46cee900 --- /dev/null +++ b/sysdeps/sparc/sparc32/sem_init.c @@ -0,0 +1,95 @@ +/* Copyright (C) 2002-2015 Free Software Foundation, Inc. + This file is part of the GNU C Library. + Contributed by Ulrich Drepper <drepper@redhat.com>, 2002. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + <http://www.gnu.org/licenses/>. */ + +#include <errno.h> +#include <semaphore.h> +#include <shlib-compat.h> +#include "semaphoreP.h" +#include <kernel-features.h> + +/* Returns FUTEX_PRIVATE if pshared is zero and private futexes are supported; + returns FUTEX_SHARED otherwise. + TODO Remove when cleaning up the futex API throughout glibc. */ +static __always_inline int +futex_private_if_supported (int pshared) +{ + if (pshared != 0) + return LLL_SHARED; +#ifdef __ASSUME_PRIVATE_FUTEX + return LLL_PRIVATE; +#else + return THREAD_GETMEM (THREAD_SELF, header.private_futex) + ^ FUTEX_PRIVATE_FLAG; +#endif +} + + +int +__new_sem_init (sem_t *sem, int pshared, unsigned int value) +{ + /* Parameter sanity check. */ + if (__glibc_unlikely (value > SEM_VALUE_MAX)) + { + __set_errno (EINVAL); + return -1; + } + + /* Map to the internal type. */ + struct new_sem *isem = (struct new_sem *) sem; + + /* Use the values the caller provided. */ + isem->value = value << SEM_VALUE_SHIFT; + isem->pad = 0; + isem->nwaiters = 0; + + isem->private = futex_private_if_supported (pshared); + + return 0; +} +versioned_symbol (libpthread, __new_sem_init, sem_init, GLIBC_2_1); + + + +#if SHLIB_COMPAT(libpthread, GLIBC_2_0, GLIBC_2_1) +int +attribute_compat_text_section +__old_sem_init (sem, pshared, value) + sem_t *sem; + int pshared; + unsigned int value; +{ + /* Parameter sanity check. */ + if (__glibc_unlikely (value > SEM_VALUE_MAX)) + { + __set_errno (EINVAL); + return -1; + } + + /* Map to the internal type. */ + struct old_sem *isem = (struct old_sem *) sem; + + /* Use the value the user provided. */ + isem->value = value; + + /* We cannot store the PSHARED attribute. So we always use the + operations needed for shared semaphores. */ + + return 0; +} +compat_symbol (libpthread, __old_sem_init, sem_init, GLIBC_2_0); +#endif diff --git a/sysdeps/sparc/sparc32/sem_open.c b/sysdeps/sparc/sparc32/sem_open.c new file mode 100644 index 0000000000..af9233c653 --- /dev/null +++ b/sysdeps/sparc/sparc32/sem_open.c @@ -0,0 +1,298 @@ +/* Copyright (C) 2002-2015 Free Software Foundation, Inc. + This file is part of the GNU C Library. + Contributed by Ulrich Drepper <drepper@redhat.com>, 2002. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + <http://www.gnu.org/licenses/>. */ + +#include <errno.h> +#include <fcntl.h> +#include <pthread.h> +#include <search.h> +#include <semaphore.h> +#include <stdarg.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <sys/mman.h> +#include <sys/stat.h> +#include "semaphoreP.h" +#include <shm-directory.h> + + +/* Comparison function for search of existing mapping. */ +int +attribute_hidden +__sem_search (const void *a, const void *b) +{ + const struct inuse_sem *as = (const struct inuse_sem *) a; + const struct inuse_sem *bs = (const struct inuse_sem *) b; + + if (as->ino != bs->ino) + /* Cannot return the difference the type is larger than int. */ + return as->ino < bs->ino ? -1 : (as->ino == bs->ino ? 0 : 1); + + if (as->dev != bs->dev) + /* Cannot return the difference the type is larger than int. */ + return as->dev < bs->dev ? -1 : (as->dev == bs->dev ? 0 : 1); + + return strcmp (as->name, bs->name); +} + + +/* The search tree for existing mappings. */ +void *__sem_mappings attribute_hidden; + +/* Lock to protect the search tree. */ +int __sem_mappings_lock attribute_hidden = LLL_LOCK_INITIALIZER; + + +/* Search for existing mapping and if possible add the one provided. */ +static sem_t * +check_add_mapping (const char *name, size_t namelen, int fd, sem_t *existing) +{ + sem_t *result = SEM_FAILED; + + /* Get the information about the file. */ + struct stat64 st; + if (__fxstat64 (_STAT_VER, fd, &st) == 0) + { + /* Get the lock. */ + lll_lock (__sem_mappings_lock, LLL_PRIVATE); + + /* Search for an existing mapping given the information we have. */ + struct inuse_sem *fake; + fake = (struct inuse_sem *) alloca (sizeof (*fake) + namelen); + memcpy (fake->name, name, namelen); + fake->dev = st.st_dev; + fake->ino = st.st_ino; + + struct inuse_sem **foundp = tfind (fake, &__sem_mappings, __sem_search); + if (foundp != NULL) + { + /* There is already a mapping. Use it. */ + result = (*foundp)->sem; + ++(*foundp)->refcnt; + } + else + { + /* We haven't found a mapping. Install ione. */ + struct inuse_sem *newp; + + newp = (struct inuse_sem *) malloc (sizeof (*newp) + namelen); + if (newp != NULL) + { + /* If the caller hasn't provided any map it now. */ + if (existing == SEM_FAILED) + existing = (sem_t *) mmap (NULL, sizeof (sem_t), + PROT_READ | PROT_WRITE, MAP_SHARED, + fd, 0); + + newp->dev = st.st_dev; + newp->ino = st.st_ino; + newp->refcnt = 1; + newp->sem = existing; + memcpy (newp->name, name, namelen); + + /* Insert the new value. */ + if (existing != MAP_FAILED + && tsearch (newp, &__sem_mappings, __sem_search) != NULL) + /* Successful. */ + result = existing; + else + /* Something went wrong while inserting the new + value. We fail completely. */ + free (newp); + } + } + + /* Release the lock. */ + lll_unlock (__sem_mappings_lock, LLL_PRIVATE); + } + + if (result != existing && existing != SEM_FAILED && existing != MAP_FAILED) + { + /* Do not disturb errno. */ + int save = errno; + munmap (existing, sizeof (sem_t)); + errno = save; + } + + return result; +} + + +sem_t * +sem_open (const char *name, int oflag, ...) +{ + int fd; + sem_t *result; + + /* Create the name of the final file in local variable SHM_NAME. */ + SHM_GET_NAME (EINVAL, SEM_FAILED, SEM_SHM_PREFIX); + + /* If the semaphore object has to exist simply open it. */ + if ((oflag & O_CREAT) == 0 || (oflag & O_EXCL) == 0) + { + try_again: + fd = __libc_open (shm_name, + (oflag & ~(O_CREAT|O_ACCMODE)) | O_NOFOLLOW | O_RDWR); + + if (fd == -1) + { + /* If we are supposed to create the file try this next. */ + if ((oflag & O_CREAT) != 0 && errno == ENOENT) + goto try_create; + + /* Return. errno is already set. */ + } + else + /* Check whether we already have this semaphore mapped and + create one if necessary. */ + result = check_add_mapping (name, namelen, fd, SEM_FAILED); + } + else + { + /* We have to open a temporary file first since it must have the + correct form before we can start using it. */ + char *tmpfname; + mode_t mode; + unsigned int value; + va_list ap; + + try_create: + va_start (ap, oflag); + + mode = va_arg (ap, mode_t); + value = va_arg (ap, unsigned int); + + va_end (ap); + + if (value > SEM_VALUE_MAX) + { + __set_errno (EINVAL); + return SEM_FAILED; + } + + /* Create the initial file content. */ + union + { + sem_t initsem; + struct new_sem newsem; + } sem; + + sem.newsem.value = value << SEM_VALUE_SHIFT; + sem.newsem.pad = 0; + sem.newsem.nwaiters = 0; + + /* This always is a shared semaphore. */ + sem.newsem.private = LLL_SHARED; + + /* Initialize the remaining bytes as well. */ + memset ((char *) &sem.initsem + sizeof (struct new_sem), '\0', + sizeof (sem_t) - sizeof (struct new_sem)); + + tmpfname = __alloca (shm_dirlen + sizeof SEM_SHM_PREFIX + 6); + char *xxxxxx = __mempcpy (tmpfname, shm_dir, shm_dirlen); + + int retries = 0; +#define NRETRIES 50 + while (1) + { + /* Add the suffix for mktemp. */ + strcpy (xxxxxx, "XXXXXX"); + + /* We really want to use mktemp here. We cannot use mkstemp + since the file must be opened with a specific mode. The + mode cannot later be set since then we cannot apply the + file create mask. */ + if (__mktemp (tmpfname) == NULL) + return SEM_FAILED; + + /* Open the file. Make sure we do not overwrite anything. */ + fd = __libc_open (tmpfname, O_RDWR | O_CREAT | O_EXCL, mode); + if (fd == -1) + { + if (errno == EEXIST) + { + if (++retries < NRETRIES) + continue; + + __set_errno (EAGAIN); + } + + return SEM_FAILED; + } + + /* We got a file. */ + break; + } + + if (TEMP_FAILURE_RETRY (__libc_write (fd, &sem.initsem, sizeof (sem_t))) + == sizeof (sem_t) + /* Map the sem_t structure from the file. */ + && (result = (sem_t *) mmap (NULL, sizeof (sem_t), + PROT_READ | PROT_WRITE, MAP_SHARED, + fd, 0)) != MAP_FAILED) + { + /* Create the file. Don't overwrite an existing file. */ + if (link (tmpfname, shm_name) != 0) + { + /* Undo the mapping. */ + (void) munmap (result, sizeof (sem_t)); + + /* Reinitialize 'result'. */ + result = SEM_FAILED; + + /* This failed. If O_EXCL is not set and the problem was + that the file exists, try again. */ + if ((oflag & O_EXCL) == 0 && errno == EEXIST) + { + /* Remove the file. */ + (void) unlink (tmpfname); + + /* Close the file. */ + (void) __libc_close (fd); + + goto try_again; + } + } + else + /* Insert the mapping into the search tree. This also + determines whether another thread sneaked by and already + added such a mapping despite the fact that we created it. */ + result = check_add_mapping (name, namelen, fd, result); + } + + /* Now remove the temporary name. This should never fail. If + it fails we leak a file name. Better fix the kernel. */ + (void) unlink (tmpfname); + } + + /* Map the mmap error to the error we need. */ + if (MAP_FAILED != (void *) SEM_FAILED && result == MAP_FAILED) + result = SEM_FAILED; + + /* We don't need the file descriptor anymore. */ + if (fd != -1) + { + /* Do not disturb errno. */ + int save = errno; + __libc_close (fd); + errno = save; + } + + return result; +} diff --git a/sysdeps/sparc/sparc32/sem_post.c b/sysdeps/sparc/sparc32/sem_post.c index 7ca189810e..64cd851ffa 100644 --- a/sysdeps/sparc/sparc32/sem_post.c +++ b/sysdeps/sparc/sparc32/sem_post.c @@ -1,4 +1,4 @@ -/* sem_post -- post to a POSIX semaphore. SPARC version. +/* sem_post -- post to a POSIX semaphore. Generic futex-using version. Copyright (C) 2003-2015 Free Software Foundation, Inc. This file is part of the GNU C Library. Contributed by Jakub Jelinek <jakub@redhat.com>, 2003. @@ -17,6 +17,7 @@ License along with the GNU C Library; if not, see <http://www.gnu.org/licenses/>. */ +#include <atomic.h> #include <errno.h> #include <sysdep.h> #include <lowlevellock.h> @@ -24,33 +25,58 @@ #include <semaphore.h> #include <shlib-compat.h> -#include <sparc-nptl.h> +/* Wrapper for lll_futex_wake, with error checking. + TODO Remove when cleaning up the futex API throughout glibc. */ +static __always_inline void +futex_wake (unsigned int* futex, int processes_to_wake, int private) +{ + int res = lll_futex_wake (futex, processes_to_wake, private); + /* No error. Ignore the number of woken processes. */ + if (res >= 0) + return; + switch (res) + { + case -EFAULT: /* Could have happened due to memory reuse. */ + case -EINVAL: /* Could be either due to incorrect alignment (a bug in + glibc or in the application) or due to memory being + reused for a PI futex. We cannot distinguish between the + two causes, and one of them is correct use, so we do not + act in this case. */ + return; + case -ENOSYS: /* Must have been caused by a glibc bug. */ + /* No other errors are documented at this time. */ + default: + abort (); + } +} + + +/* See sem_wait for an explanation of the algorithm. */ int __new_sem_post (sem_t *sem) { - struct sparc_new_sem *isem = (struct sparc_new_sem *) sem; - int nr; + struct new_sem *isem = (struct new_sem *) sem; + int private = isem->private; + unsigned int v; - if (__atomic_is_v9) - nr = atomic_increment_val (&isem->value); - else - { - __sparc32_atomic_do_lock24 (&isem->lock); - nr = ++(isem->value); - __sparc32_atomic_do_unlock24 (&isem->lock); - } - atomic_full_barrier (); - if (isem->nwaiters > 0) + __sparc32_atomic_do_lock24(&isem->pad); + + v = isem->value; + if ((v << SEM_VALUE_SHIFT) == SEM_VALUE_MAX) { - int err = lll_futex_wake (&isem->value, 1, - isem->private ^ FUTEX_PRIVATE_FLAG); - if (__builtin_expect (err, 0) < 0) - { - __set_errno (-err); - return -1; - } + __sparc32_atomic_do_unlock24(&isem->pad); + + __set_errno (EOVERFLOW); + return -1; } + isem->value = v + (1 << SEM_VALUE_SHIFT); + + __sparc32_atomic_do_unlock24(&isem->pad); + + if ((v & SEM_NWAITERS_MASK) != 0) + futex_wake (&isem->value, 1, private); + return 0; } versioned_symbol (libpthread, __new_sem_post, sem_post, GLIBC_2_1); @@ -61,19 +87,14 @@ int attribute_compat_text_section __old_sem_post (sem_t *sem) { - struct sparc_old_sem *isem = (struct sparc_old_sem *) sem; - int nr; + int *futex = (int *) sem; - if (__atomic_is_v9) - nr = atomic_increment_val (&isem->value); - else - { - __sparc32_atomic_do_lock24 (&isem->lock); - nr = ++(isem->value); - __sparc32_atomic_do_unlock24 (&isem->lock); - } - int err = lll_futex_wake (&isem->value, 1, - isem->private ^ FUTEX_PRIVATE_FLAG); + /* We must need to synchronize with consumers of this token, so the atomic + increment must have release MO semantics. */ + atomic_write_barrier (); + (void) atomic_increment_val (futex); + /* We always have to assume it is a shared semaphore. */ + int err = lll_futex_wake (futex, 1, LLL_SHARED); if (__builtin_expect (err, 0) < 0) { __set_errno (-err); diff --git a/sysdeps/sparc/sparc32/sem_timedwait.c b/sysdeps/sparc/sparc32/sem_timedwait.c deleted file mode 100644 index 4884fad98f..0000000000 --- a/sysdeps/sparc/sparc32/sem_timedwait.c +++ /dev/null @@ -1,153 +0,0 @@ -/* sem_timedwait -- wait on a semaphore. SPARC version. - Copyright (C) 2003-2015 Free Software Foundation, Inc. - This file is part of the GNU C Library. - Contributed by Paul Mackerras <paulus@au.ibm.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 - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. - - The GNU C Library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with the GNU C Library; if not, see - <http://www.gnu.org/licenses/>. */ - -#include <errno.h> -#include <sysdep.h> -#include <lowlevellock.h> -#include <internaltypes.h> -#include <semaphore.h> - -#include <pthreadP.h> -#include <shlib-compat.h> -#include <sparc-nptl.h> - -extern void __sem_wait_cleanup (void *arg) attribute_hidden; - -/* This is in a seperate function in order to make sure gcc - puts the call site into an exception region, and thus the - cleanups get properly run. */ -static int -__attribute__ ((noinline)) -do_futex_timed_wait (struct sparc_new_sem *isem, struct timespec *rt) -{ - int err, oldtype = __pthread_enable_asynccancel (); - - err = lll_futex_timed_wait (&isem->value, 0, rt, - isem->private ^ FUTEX_PRIVATE_FLAG); - - __pthread_disable_asynccancel (oldtype); - return err; -} - -int -sem_timedwait (sem_t *sem, const struct timespec *abstime) -{ - struct sparc_new_sem *isem = (struct sparc_new_sem *) sem; - int err; - int val; - - if (__atomic_is_v9) - val = atomic_decrement_if_positive (&isem->value); - else - { - __sparc32_atomic_do_lock24 (&isem->lock); - val = isem->value; - if (val > 0) - isem->value = val - 1; - __sparc32_atomic_do_unlock24 (&isem->lock); - } - - if (val > 0) - return 0; - - if (abstime->tv_nsec < 0 || abstime->tv_nsec >= 1000000000) - { - __set_errno (EINVAL); - return -1; - } - - if (__atomic_is_v9) - atomic_increment (&isem->nwaiters); - else - { - __sparc32_atomic_do_lock24 (&isem->lock); - isem->nwaiters++; - __sparc32_atomic_do_unlock24 (&isem->lock); - } - - pthread_cleanup_push (__sem_wait_cleanup, isem); - - while (1) - { - struct timeval tv; - struct timespec rt; - int sec, nsec; - - /* Get the current time. */ - __gettimeofday (&tv, NULL); - - /* Compute relative timeout. */ - sec = abstime->tv_sec - tv.tv_sec; - nsec = abstime->tv_nsec - tv.tv_usec * 1000; - if (nsec < 0) - { - nsec += 1000000000; - --sec; - } - - /* Already timed out? */ - if (sec < 0) - { - __set_errno (ETIMEDOUT); - err = -1; - break; - } - - /* Do wait. */ - rt.tv_sec = sec; - rt.tv_nsec = nsec; - err = do_futex_timed_wait(isem, &rt); - if (err != 0 && err != -EWOULDBLOCK) - { - __set_errno (-err); - err = -1; - break; - } - - if (__atomic_is_v9) - val = atomic_decrement_if_positive (&isem->value); - else - { - __sparc32_atomic_do_lock24 (&isem->lock); - val = isem->value; - if (val > 0) - isem->value = val - 1; - __sparc32_atomic_do_unlock24 (&isem->lock); - } - - if (val > 0) - { - err = 0; - break; - } - } - - pthread_cleanup_pop (0); - - if (__atomic_is_v9) - atomic_decrement (&isem->nwaiters); - else - { - __sparc32_atomic_do_lock24 (&isem->lock); - isem->nwaiters--; - __sparc32_atomic_do_unlock24 (&isem->lock); - } - - return err; -} diff --git a/sysdeps/sparc/sparc32/sem_trywait.c b/sysdeps/sparc/sparc32/sem_trywait.c deleted file mode 100644 index 15af72bee3..0000000000 --- a/sysdeps/sparc/sparc32/sem_trywait.c +++ /dev/null @@ -1,58 +0,0 @@ -/* sem_trywait -- wait on a semaphore. SPARC version. - Copyright (C) 2003-2015 Free Software Foundation, Inc. - This file is part of the GNU C Library. - Contributed by Paul Mackerras <paulus@au.ibm.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 - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. - - The GNU C Library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with the GNU C Library; if not, see - <http://www.gnu.org/licenses/>. */ - -#include <errno.h> -#include <sysdep.h> -#include <lowlevellock.h> -#include <internaltypes.h> -#include <semaphore.h> - -#include <shlib-compat.h> - - -int -__new_sem_trywait (sem_t *sem) -{ - struct sparc_old_sem *isem = (struct sparc_old_sem *) sem; - int val; - - if (isem->value > 0) - { - if (__atomic_is_v9) - val = atomic_decrement_if_positive (&isem->value); - else - { - __sparc32_atomic_do_lock24 (&isem->lock); - val = isem->value; - if (val > 0) - isem->value = val - 1; - __sparc32_atomic_do_unlock24 (&isem->lock); - } - if (val > 0) - return 0; - } - - __set_errno (EAGAIN); - return -1; -} -versioned_symbol (libpthread, __new_sem_trywait, sem_trywait, GLIBC_2_1); -#if SHLIB_COMPAT (libpthread, GLIBC_2_0, GLIBC_2_1) -strong_alias (__new_sem_trywait, __old_sem_trywait) -compat_symbol (libpthread, __old_sem_trywait, sem_trywait, GLIBC_2_0); -#endif diff --git a/sysdeps/sparc/sparc32/sem_wait.c b/sysdeps/sparc/sparc32/sem_wait.c index 9495e5ea88..c1fd10c9d0 100644 --- a/sysdeps/sparc/sparc32/sem_wait.c +++ b/sysdeps/sparc/sparc32/sem_wait.c @@ -17,151 +17,36 @@ License along with the GNU C Library; if not, see <http://www.gnu.org/licenses/>. */ -#include <errno.h> -#include <sysdep.h> -#include <lowlevellock.h> -#include <internaltypes.h> -#include <semaphore.h> - -#include <pthreadP.h> -#include <shlib-compat.h> -#include <sparc-nptl.h> - -void -attribute_hidden -__sem_wait_cleanup (void *arg) -{ - struct sparc_new_sem *isem = (struct sparc_new_sem *) arg; - - if (__atomic_is_v9) - atomic_decrement (&isem->nwaiters); - else - { - __sparc32_atomic_do_lock24 (&isem->lock); - isem->nwaiters--; - __sparc32_atomic_do_unlock24 (&isem->lock); - } -} - -/* This is in a seperate function in order to make sure gcc - puts the call site into an exception region, and thus the - cleanups get properly run. */ -static int -__attribute__ ((noinline)) -do_futex_wait (struct sparc_new_sem *isem) -{ - int err, oldtype = __pthread_enable_asynccancel (); - - err = lll_futex_wait (&isem->value, 0, isem->private ^ FUTEX_PRIVATE_FLAG); - - __pthread_disable_asynccancel (oldtype); - return err; -} +#include "sem_waitcommon.c" int __new_sem_wait (sem_t *sem) { - struct sparc_new_sem *isem = (struct sparc_new_sem *) sem; - int err; - int val; - - if (__atomic_is_v9) - val = atomic_decrement_if_positive (&isem->value); - else - { - __sparc32_atomic_do_lock24 (&isem->lock); - val = isem->value; - if (val > 0) - isem->value = val - 1; - else - isem->nwaiters++; - __sparc32_atomic_do_unlock24 (&isem->lock); - } - - if (val > 0) + if (__new_sem_wait_fast ((struct new_sem *) sem, 0) == 0) return 0; - - if (__atomic_is_v9) - atomic_increment (&isem->nwaiters); - else - /* Already done above while still holding isem->lock. */; - - pthread_cleanup_push (__sem_wait_cleanup, isem); - - while (1) - { - err = do_futex_wait(isem); - if (err != 0 && err != -EWOULDBLOCK) - { - __set_errno (-err); - err = -1; - break; - } - - if (__atomic_is_v9) - val = atomic_decrement_if_positive (&isem->value); - else - { - __sparc32_atomic_do_lock24 (&isem->lock); - val = isem->value; - if (val > 0) - isem->value = val - 1; - __sparc32_atomic_do_unlock24 (&isem->lock); - } - - if (val > 0) - { - err = 0; - break; - } - } - - pthread_cleanup_pop (0); - - if (__atomic_is_v9) - atomic_decrement (&isem->nwaiters); else - { - __sparc32_atomic_do_lock24 (&isem->lock); - isem->nwaiters--; - __sparc32_atomic_do_unlock24 (&isem->lock); - } - - return err; + return __new_sem_wait_slow((struct new_sem *) sem, NULL); } versioned_symbol (libpthread, __new_sem_wait, sem_wait, GLIBC_2_1); - #if SHLIB_COMPAT (libpthread, GLIBC_2_0, GLIBC_2_1) int attribute_compat_text_section __old_sem_wait (sem_t *sem) { - struct sparc_old_sem *isem = (struct sparc_old_sem *) sem; + int *futex = (int *) sem; int err; - int val; do { - if (__atomic_is_v9) - val = atomic_decrement_if_positive (&isem->value); - else - { - __sparc32_atomic_do_lock24 (&isem->lock); - val = isem->value; - if (val > 0) - isem->value = val - 1; - __sparc32_atomic_do_unlock24 (&isem->lock); - } - - if (val > 0) + if (atomic_decrement_if_positive (futex) > 0) return 0; /* Enable asynchronous cancellation. Required by the standard. */ int oldtype = __pthread_enable_asynccancel (); - err = lll_futex_wait (&isem->value, 0, - isem->private ^ FUTEX_PRIVATE_FLAG); + /* Always assume the semaphore is shared. */ + err = lll_futex_wait (futex, 0, LLL_SHARED); /* Disable asynchronous cancellation. */ __pthread_disable_asynccancel (oldtype); @@ -174,3 +59,34 @@ __old_sem_wait (sem_t *sem) compat_symbol (libpthread, __old_sem_wait, sem_wait, GLIBC_2_0); #endif + +int +__new_sem_trywait (sem_t *sem) +{ + /* We must not fail spuriously, so require a definitive result even if this + may lead to a long execution time. */ + if (__new_sem_wait_fast ((struct new_sem *) sem, 1) == 0) + return 0; + __set_errno (EAGAIN); + return -1; +} +versioned_symbol (libpthread, __new_sem_trywait, sem_trywait, GLIBC_2_1); +#if SHLIB_COMPAT (libpthread, GLIBC_2_0, GLIBC_2_1) +int +__old_sem_trywait (sem_t *sem) +{ + int *futex = (int *) sem; + int val; + + if (*futex > 0) + { + val = atomic_decrement_if_positive (futex); + if (val > 0) + return 0; + } + + __set_errno (EAGAIN); + return -1; +} +compat_symbol (libpthread, __old_sem_trywait, sem_trywait, GLIBC_2_0); +#endif diff --git a/sysdeps/sparc/sparc32/sem_waitcommon.c b/sysdeps/sparc/sparc32/sem_waitcommon.c new file mode 100644 index 0000000000..9c1c6a5af7 --- /dev/null +++ b/sysdeps/sparc/sparc32/sem_waitcommon.c @@ -0,0 +1,245 @@ +/* sem_waitcommon -- wait on a semaphore, shared code. + Copyright (C) 2003-2015 Free Software Foundation, Inc. + This file is part of the GNU C Library. + Contributed by Paul Mackerras <paulus@au.ibm.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 + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + <http://www.gnu.org/licenses/>. */ + +#include <errno.h> +#include <sysdep.h> +#include <lowlevellock.h> +#include <internaltypes.h> +#include <semaphore.h> +#include <sys/time.h> + +#include <pthreadP.h> +#include <shlib-compat.h> +#include <atomic.h> + +/* Wrapper for lll_futex_wait with absolute timeout and error checking. + TODO Remove when cleaning up the futex API throughout glibc. */ +static __always_inline int +futex_abstimed_wait (unsigned int* futex, unsigned int expected, + const struct timespec* abstime, int private, bool cancel) +{ + int err, oldtype; + if (abstime == NULL) + { + if (cancel) + oldtype = __pthread_enable_asynccancel (); + err = lll_futex_wait (futex, expected, private); + if (cancel) + __pthread_disable_asynccancel (oldtype); + } + else + { + struct timeval tv; + struct timespec rt; + int sec, nsec; + + /* Get the current time. */ + __gettimeofday (&tv, NULL); + + /* Compute relative timeout. */ + sec = abstime->tv_sec - tv.tv_sec; + nsec = abstime->tv_nsec - tv.tv_usec * 1000; + if (nsec < 0) + { + nsec += 1000000000; + --sec; + } + + /* Already timed out? */ + if (sec < 0) + return ETIMEDOUT; + + /* Do wait. */ + rt.tv_sec = sec; + rt.tv_nsec = nsec; + if (cancel) + oldtype = __pthread_enable_asynccancel (); + err = lll_futex_timed_wait (futex, expected, &rt, private); + if (cancel) + __pthread_disable_asynccancel (oldtype); + } + switch (err) + { + case 0: + case -EAGAIN: + case -EINTR: + case -ETIMEDOUT: + return -err; + + case -EFAULT: /* Must have been caused by a glibc or application bug. */ + case -EINVAL: /* Either due to wrong alignment or due to the timeout not + being normalized. Must have been caused by a glibc or + application bug. */ + case -ENOSYS: /* Must have been caused by a glibc bug. */ + /* No other errors are documented at this time. */ + default: + abort (); + } +} + +/* Wrapper for lll_futex_wake, with error checking. + TODO Remove when cleaning up the futex API throughout glibc. */ +static __always_inline void +futex_wake (unsigned int* futex, int processes_to_wake, int private) +{ + int res = lll_futex_wake (futex, processes_to_wake, private); + /* No error. Ignore the number of woken processes. */ + if (res >= 0) + return; + switch (res) + { + case -EFAULT: /* Could have happened due to memory reuse. */ + case -EINVAL: /* Could be either due to incorrect alignment (a bug in + glibc or in the application) or due to memory being + reused for a PI futex. We cannot distinguish between the + two causes, and one of them is correct use, so we do not + act in this case. */ + return; + case -ENOSYS: /* Must have been caused by a glibc bug. */ + /* No other errors are documented at this time. */ + default: + abort (); + } +} + + +/* Set this to true if you assume that, in contrast to current Linux futex + documentation, lll_futex_wake can return -EINTR only if interrupted by a + signal, not spuriously due to some other reason. + TODO Discuss EINTR conditions with the Linux kernel community. For + now, we set this to true to not change behavior of semaphores compared + to previous glibc builds. */ +static const int sem_assume_only_signals_cause_futex_EINTR = 1; + +static void +__sem_wait_32_finish (struct new_sem *sem); + +static void +__sem_wait_cleanup (void *arg) +{ + struct new_sem *sem = (struct new_sem *) arg; + + __sem_wait_32_finish (sem); +} + +/* Wait until at least one token is available, possibly with a timeout. + This is in a separate function in order to make sure gcc + puts the call site into an exception region, and thus the + cleanups get properly run. TODO still necessary? Other futex_wait + users don't seem to need it. */ +static int +__attribute__ ((noinline)) +do_futex_wait (struct new_sem *sem, const struct timespec *abstime) +{ + int err; + + err = futex_abstimed_wait (&sem->value, SEM_NWAITERS_MASK, abstime, + sem->private, true); + + return err; +} + +/* Fast path: Try to grab a token without blocking. */ +static int +__new_sem_wait_fast (struct new_sem *sem, int definitive_result) +{ + unsigned int v; + int ret = 0; + + __sparc32_atomic_do_lock24(&sem->pad); + + v = sem->value; + if ((v >> SEM_VALUE_SHIFT) == 0) + ret = -1; + else + sem->value = v - (1 << SEM_VALUE_SHIFT); + + __sparc32_atomic_do_unlock24(&sem->pad); + + return ret; +} + +/* Slow path that blocks. */ +static int +__attribute__ ((noinline)) +__new_sem_wait_slow (struct new_sem *sem, const struct timespec *abstime) +{ + unsigned int v; + int err = 0; + + __sparc32_atomic_do_lock24(&sem->pad); + + sem->nwaiters++; + + pthread_cleanup_push (__sem_wait_cleanup, sem); + + /* Wait for a token to be available. Retry until we can grab one. */ + v = sem->value; + do + { + if (!(v & SEM_NWAITERS_MASK)) + sem->value = v | SEM_NWAITERS_MASK; + + /* If there is no token, wait. */ + if ((v >> SEM_VALUE_SHIFT) == 0) + { + __sparc32_atomic_do_unlock24(&sem->pad); + + err = do_futex_wait(sem, abstime); + if (err == ETIMEDOUT || + (err == EINTR && sem_assume_only_signals_cause_futex_EINTR)) + { + __set_errno (err); + err = -1; + goto error; + } + err = 0; + + __sparc32_atomic_do_lock24(&sem->pad); + + /* We blocked, so there might be a token now. */ + v = sem->value; + } + } + /* If there is no token, we must not try to grab one. */ + while ((v >> SEM_VALUE_SHIFT) == 0); + + sem->value = v - (1 << SEM_VALUE_SHIFT); + + __sparc32_atomic_do_unlock24(&sem->pad); + +error: + pthread_cleanup_pop (0); + + __sem_wait_32_finish (sem); + + return err; +} + +/* Stop being a registered waiter (non-64b-atomics code only). */ +static void +__sem_wait_32_finish (struct new_sem *sem) +{ + __sparc32_atomic_do_lock24(&sem->pad); + + if (--sem->nwaiters == 0) + sem->value &= ~SEM_NWAITERS_MASK; + + __sparc32_atomic_do_unlock24(&sem->pad); +} diff --git a/sysdeps/sparc/sparc32/sparcv9/sem_init.c b/sysdeps/sparc/sparc32/sparcv9/sem_init.c new file mode 100644 index 0000000000..c90d1b599a --- /dev/null +++ b/sysdeps/sparc/sparc32/sparcv9/sem_init.c @@ -0,0 +1 @@ +#include <nptl/sem_init.c> diff --git a/sysdeps/sparc/sparc32/sparcv9/sem_open.c b/sysdeps/sparc/sparc32/sparcv9/sem_open.c new file mode 100644 index 0000000000..bff2d2db6a --- /dev/null +++ b/sysdeps/sparc/sparc32/sparcv9/sem_open.c @@ -0,0 +1 @@ +#include <nptl/sem_open.c> diff --git a/sysdeps/sparc/sparc32/sparcv9/sem_post.c b/sysdeps/sparc/sparc32/sparcv9/sem_post.c index 3c4b940fe6..6a2813caee 100644 --- a/sysdeps/sparc/sparc32/sparcv9/sem_post.c +++ b/sysdeps/sparc/sparc32/sparcv9/sem_post.c @@ -1 +1 @@ -#include <sysdeps/sparc/nptl/sem_post.c> +#include <nptl/sem_post.c> diff --git a/sysdeps/sparc/sparc32/sparcv9/sem_timedwait.c b/sysdeps/sparc/sparc32/sparcv9/sem_timedwait.c deleted file mode 100644 index f19b2c5487..0000000000 --- a/sysdeps/sparc/sparc32/sparcv9/sem_timedwait.c +++ /dev/null @@ -1 +0,0 @@ -#include <sysdeps/sparc/nptl/sem_timedwait.c> diff --git a/sysdeps/sparc/sparc32/sparcv9/sem_trywait.c b/sysdeps/sparc/sparc32/sparcv9/sem_trywait.c deleted file mode 100644 index a8d4acc3f0..0000000000 --- a/sysdeps/sparc/sparc32/sparcv9/sem_trywait.c +++ /dev/null @@ -1 +0,0 @@ -#include <nptl/sem_trywait.c> diff --git a/sysdeps/sparc/sparc32/sparcv9/sem_wait.c b/sysdeps/sparc/sparc32/sparcv9/sem_wait.c index b6d8287d92..bccdaed92e 100644 --- a/sysdeps/sparc/sparc32/sparcv9/sem_wait.c +++ b/sysdeps/sparc/sparc32/sparcv9/sem_wait.c @@ -1 +1 @@ -#include <sysdeps/sparc/nptl/sem_wait.c> +#include <nptl/sem_wait.c> diff --git a/sysdeps/sparc/sparc32/sparcv9/sem_waitcommon.c b/sysdeps/sparc/sparc32/sparcv9/sem_waitcommon.c new file mode 100644 index 0000000000..d4a139572b --- /dev/null +++ b/sysdeps/sparc/sparc32/sparcv9/sem_waitcommon.c @@ -0,0 +1 @@ +#include <nptl/sem_waitcommon.c> |