diff options
-rw-r--r-- | librt/Makefile.in | 23 | ||||
-rw-r--r-- | librt/clock_getcpuclockid.c | 104 | ||||
-rw-r--r-- | librt/clock_gettime.c | 301 | ||||
-rw-r--r-- | librt/clock_nanosleep.c | 96 | ||||
-rw-r--r-- | librt/kernel-posix-timers.h | 14 | ||||
-rw-r--r-- | librt/mq_receive.c | 2 | ||||
-rw-r--r-- | librt/mq_send.c | 2 | ||||
-rw-r--r-- | librt/mq_timedreceive.S | 10 | ||||
-rw-r--r-- | librt/mq_timedsend.S | 10 |
9 files changed, 558 insertions, 4 deletions
diff --git a/librt/Makefile.in b/librt/Makefile.in index 8a30a18e2..c63f52065 100644 --- a/librt/Makefile.in +++ b/librt/Makefile.in @@ -7,7 +7,12 @@ CFLAGS-librt := -DNOT_IN_libc -DIS_IN_librt $(SSP_ALL_CFLAGS) +ifeq ($(UCLIBC_HAS_THREADS_NATIVE),y) +LDFLAGS-librt.so := $(LDFLAGS) $(top_builddir)lib/libdl.so \ + $(top_builddir)lib/libpthread.so +else LDFLAGS-librt.so := $(LDFLAGS) +endif LIBS-librt.so := $(LIBS) @@ -16,21 +21,31 @@ librt_FULL_NAME := librt-$(MAJOR_VERSION).$(MINOR_VERSION).$(SUBLEVEL).so librt_DIR := $(top_srcdir)librt librt_OUT := $(top_builddir)librt -librt_SRC := $(wildcard $(librt_DIR)/*.c) +ifeq ($(UCLIBC_HAS_THREADS_NATIVE),y) +librt_CSRC := $(filter-out mq_notify.c timer_create.c timer_delete.c \ + timer_getoverr.c timer_gettime.c timer_settime.c, \ + $(notdir $(wildcard $(librt_DIR)/*.c))) +librt_SSRC := $(wildcard $(librt_DIR)/*.S) +librt_OBJ := $(patsubst %.c,$(librt_OUT)/%.o,$(librt_CSRC)) +librt_OBJ += $(patsubst $(librt_DIR)/%.S,$(librt_OUT)/%.o,$(librt_SSRC)) +else +librt_SRC := $(filter-out clock_nanosleep.c clock_getcpuclockid.c, \ + $(notdir $(wildcard $(librt_DIR)/*.c))) librt_OBJ := $(patsubst $(librt_DIR)/%.c,$(librt_OUT)/%.o,$(librt_SRC)) +endif ifeq ($(DOPIC),y) librt-a-y += $(librt_OBJ:.o=.os) else librt-a-y += $(librt_OBJ) endif -librt-so-y += $(librt_OBJ:.o=.os) +librt-so-y += $(librt_OBJ:.o=.oS) lib-a-y += $(top_builddir)lib/librt.a lib-so-y += $(top_builddir)lib/librt.so objclean-y += librt_clean -ifeq ($(DOPIC),y) +ifeq ($(DOPIC)$(UCLIBC_HAS_THREADS_NATIVE),yn) $(top_builddir)lib/librt.so: $(top_builddir)lib/librt.a $(libc) else $(top_builddir)lib/librt.so: $(librt_OUT)/librt_so.a $(libc) @@ -49,4 +64,4 @@ $(top_builddir)lib/librt.a: $(librt-a-y) $(do_ar) librt_clean: - $(RM) $(librt_OUT)/*.{o,os,a} + $(RM) $(librt_OUT)/*.{o,os,oS,a} diff --git a/librt/clock_getcpuclockid.c b/librt/clock_getcpuclockid.c new file mode 100644 index 000000000..e6f669a7c --- /dev/null +++ b/librt/clock_getcpuclockid.c @@ -0,0 +1,104 @@ +/* clock_getcpuclockid -- Get a clockid_t for process CPU time. Linux version. + Copyright (C) 2004, 2005 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, write to the Free + Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + 02111-1307 USA. */ + +#include <errno.h> +#include <time.h> +#include <sysdep.h> +#include <unistd.h> +#include "kernel-features.h" +#include "kernel-posix-cpu-timers.h" + +#ifndef HAS_CPUCLOCK +# define HAS_CPUCLOCK 1 +#endif + +int +clock_getcpuclockid (pid_t pid, clockid_t *clock_id) +{ +#ifdef __NR_clock_getres + /* The clockid_t value is a simple computation from the PID. + But we do a clock_getres call to validate it. */ + + const clockid_t pidclock = MAKE_PROCESS_CPUCLOCK (pid, CPUCLOCK_SCHED); + +# if !(__ASSUME_POSIX_CPU_TIMERS > 0) + extern int __libc_missing_posix_cpu_timers attribute_hidden; +# if !(__ASSUME_POSIX_TIMERS > 0) + extern int __libc_missing_posix_timers attribute_hidden; + if (__libc_missing_posix_timers && !__libc_missing_posix_cpu_timers) + __libc_missing_posix_cpu_timers = 1; +# endif + if (!__libc_missing_posix_cpu_timers) +# endif + { + INTERNAL_SYSCALL_DECL (err); + int r = INTERNAL_SYSCALL (clock_getres, err, 2, pidclock, NULL); + if (!INTERNAL_SYSCALL_ERROR_P (r, err)) + { + *clock_id = pidclock; + return 0; + } + +# if !(__ASSUME_POSIX_TIMERS > 0) + if (INTERNAL_SYSCALL_ERRNO (r, err) == ENOSYS) + { + /* The kernel doesn't support these calls at all. */ + __libc_missing_posix_timers = 1; + __libc_missing_posix_cpu_timers = 1; + } + else +# endif + if (INTERNAL_SYSCALL_ERRNO (r, err) == EINVAL) + { +# if !(__ASSUME_POSIX_CPU_TIMERS > 0) + if (pidclock == MAKE_PROCESS_CPUCLOCK (0, CPUCLOCK_SCHED) + || INTERNAL_SYSCALL_ERROR_P (INTERNAL_SYSCALL + (clock_getres, err, 2, + MAKE_PROCESS_CPUCLOCK + (0, CPUCLOCK_SCHED), NULL), + err)) + /* The kernel doesn't support these clocks at all. */ + __libc_missing_posix_cpu_timers = 1; + else +# endif + /* The clock_getres system call checked the PID for us. */ + return ESRCH; + } + else + return INTERNAL_SYSCALL_ERRNO (r, err); + } +#endif + + /* We don't allow any process ID but our own. */ + if (pid != 0 && pid != getpid ()) + return EPERM; + +#ifdef CLOCK_PROCESS_CPUTIME_ID + if (HAS_CPUCLOCK) + { + /* Store the number. */ + *clock_id = CLOCK_PROCESS_CPUTIME_ID; + + return 0; + } +#endif + + /* We don't have a timer for that. */ + return ENOENT; +} diff --git a/librt/clock_gettime.c b/librt/clock_gettime.c new file mode 100644 index 000000000..80de9f651 --- /dev/null +++ b/librt/clock_gettime.c @@ -0,0 +1,301 @@ +/* clock_gettime -- Get current time from a POSIX clockid_t. Linux version. + Copyright (C) 2003, 2004 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, write to the Free + Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + 02111-1307 USA. */ + +#include <sysdep.h> +#include <errno.h> +#include <time.h> +#include "kernel-posix-cpu-timers.h" +#include "kernel-features.h" + + +#define SYSCALL_GETTIME \ + retval = INLINE_SYSCALL (clock_gettime, 2, clock_id, tp); \ + break + +#ifdef __ASSUME_POSIX_TIMERS + +/* This means the REALTIME and MONOTONIC clock are definitely + supported in the kernel. */ +# define SYSDEP_GETTIME \ + SYSDEP_GETTIME_CPUTIME \ + case CLOCK_REALTIME: \ + case CLOCK_MONOTONIC: \ + SYSCALL_GETTIME + +# define __libc_missing_posix_timers 0 +#elif defined __NR_clock_gettime +/* Is the syscall known to exist? */ +int __libc_missing_posix_timers attribute_hidden; + +static inline int +maybe_syscall_gettime (clockid_t clock_id, struct timespec *tp) +{ + int e = EINVAL; + + if (!__libc_missing_posix_timers) + { + INTERNAL_SYSCALL_DECL (err); + int r = INTERNAL_SYSCALL (clock_gettime, err, 2, clock_id, tp); + if (!INTERNAL_SYSCALL_ERROR_P (r, err)) + return 0; + + e = INTERNAL_SYSCALL_ERRNO (r, err); + if (e == ENOSYS) + { + __libc_missing_posix_timers = 1; + e = EINVAL; + } + } + + return e; +} + +/* The REALTIME and MONOTONIC clock might be available. Try the + syscall first. */ +# define SYSDEP_GETTIME \ + SYSDEP_GETTIME_CPUTIME \ + case CLOCK_REALTIME: \ + case CLOCK_MONOTONIC: \ + retval = maybe_syscall_gettime (clock_id, tp); \ + if (retval == 0) \ + break; \ + /* Fallback code. */ \ + if (retval == EINVAL && clock_id == CLOCK_REALTIME) \ + retval = realtime_gettime (tp); \ + else \ + { \ + __set_errno (retval); \ + retval = -1; \ + } \ + break; +#endif + +#ifdef __NR_clock_gettime +/* We handled the REALTIME clock here. */ +# define HANDLED_REALTIME 1 +# define HANDLED_CPUTIME 1 + +# if __ASSUME_POSIX_CPU_TIMERS > 0 + +# define SYSDEP_GETTIME_CPU SYSCALL_GETTIME +# define SYSDEP_GETTIME_CPUTIME /* Default catches them too. */ + +# else + +int __libc_missing_posix_cpu_timers attribute_hidden; + +static int +maybe_syscall_gettime_cpu (clockid_t clock_id, struct timespec *tp) +{ + int e = EINVAL; + + if (!__libc_missing_posix_cpu_timers) + { + INTERNAL_SYSCALL_DECL (err); + int r = INTERNAL_SYSCALL (clock_gettime, err, 2, clock_id, tp); + if (!INTERNAL_SYSCALL_ERROR_P (r, err)) + return 0; + + e = INTERNAL_SYSCALL_ERRNO (r, err); +# ifndef __ASSUME_POSIX_TIMERS + if (e == ENOSYS) + { + __libc_missing_posix_timers = 1; + __libc_missing_posix_cpu_timers = 1; + e = EINVAL; + } + else +# endif + { + if (e == EINVAL) + { + /* Check whether the kernel supports CPU clocks at all. + If not, record it for the future. */ + r = INTERNAL_SYSCALL (clock_getres, err, 2, + MAKE_PROCESS_CPUCLOCK (0, CPUCLOCK_SCHED), + NULL); + if (INTERNAL_SYSCALL_ERROR_P (r, err)) + __libc_missing_posix_cpu_timers = 1; + } + } + } + + return e; +} + +# define SYSDEP_GETTIME_CPU \ + retval = maybe_syscall_gettime_cpu (clock_id, tp); \ + if (retval == 0) \ + break; \ + if (retval != EINVAL || !__libc_missing_posix_cpu_timers) \ + { \ + __set_errno (retval); \ + retval = -1; \ + break; \ + } \ + retval = -1 /* Otherwise continue on to the HP_TIMING version. */; + +static inline int +maybe_syscall_gettime_cputime (clockid_t clock_id, struct timespec *tp) +{ + return maybe_syscall_gettime_cpu + (clock_id == CLOCK_THREAD_CPUTIME_ID + ? MAKE_THREAD_CPUCLOCK (0, CPUCLOCK_SCHED) + : MAKE_PROCESS_CPUCLOCK (0, CPUCLOCK_SCHED), + tp); +} + +# define SYSDEP_GETTIME_CPUTIME \ + case CLOCK_PROCESS_CPUTIME_ID: \ + case CLOCK_THREAD_CPUTIME_ID: \ + retval = maybe_syscall_gettime_cputime (clock_id, tp); \ + if (retval == 0) \ + break; \ + if (retval != EINVAL || !__libc_missing_posix_cpu_timers) \ + { \ + __set_errno (retval); \ + retval = -1; \ + break; \ + } \ + retval = hp_timing_gettime (clock_id, tp); \ + break; +# if !HP_TIMING_AVAIL +# define hp_timing_gettime(clock_id, tp) (__set_errno (EINVAL), -1) +# endif + +# endif +#endif + +#include <errno.h> +#include <stdint.h> +#include <time.h> +#include <sys/time.h> +#include <libc-internal.h> +#include <ldsodefs.h> + + +#if HP_TIMING_AVAIL +/* Clock frequency of the processor. We make it a 64-bit variable + because some jokers are already playing with processors with more + than 4GHz. */ +static hp_timing_t freq; + + +/* This function is defined in the thread library. */ +extern int __pthread_clock_gettime (clockid_t clock_id, hp_timing_t freq, + struct timespec *tp) + __attribute__ ((__weak__)); + +static int +hp_timing_gettime (clockid_t clock_id, struct timespec *tp) +{ + hp_timing_t tsc; + + if (__builtin_expect (freq == 0, 0)) + { + /* This can only happen if we haven't initialized the `freq' + variable yet. Do this now. We don't have to protect this + code against multiple execution since all of them should + lead to the same result. */ + freq = __get_clockfreq (); + if (__builtin_expect (freq == 0, 0)) + /* Something went wrong. */ + return -1; + } + + if (clock_id != CLOCK_PROCESS_CPUTIME_ID + && __pthread_clock_gettime != NULL) + return __pthread_clock_gettime (clock_id, freq, tp); + + /* Get the current counter. */ + HP_TIMING_NOW (tsc); + + /* Compute the offset since the start time of the process. */ + tsc -= GL(dl_cpuclock_offset); + + /* Compute the seconds. */ + tp->tv_sec = tsc / freq; + + /* And the nanoseconds. This computation should be stable until + we get machines with about 16GHz frequency. */ + tp->tv_nsec = ((tsc % freq) * UINT64_C (1000000000)) / freq; + + return 0; +} +#endif + + +static inline int +realtime_gettime (struct timespec *tp) +{ + struct timeval tv; + int retval = gettimeofday (&tv, NULL); + if (retval == 0) + /* Convert into `timespec'. */ + TIMEVAL_TO_TIMESPEC (&tv, tp); + return retval; +} + + +/* Get current value of CLOCK and store it in TP. */ +int +clock_gettime (clockid_t clock_id, struct timespec *tp) +{ + int retval = -1; +#ifndef HANDLED_REALTIME + struct timeval tv; +#endif + + switch (clock_id) + { +#ifdef SYSDEP_GETTIME + SYSDEP_GETTIME; +#endif + +#ifndef HANDLED_REALTIME + case CLOCK_REALTIME: + retval = gettimeofday (&tv, NULL); + if (retval == 0) + TIMEVAL_TO_TIMESPEC (&tv, tp); + break; +#endif + + default: +#ifdef SYSDEP_GETTIME_CPU + SYSDEP_GETTIME_CPU; +#endif +#if HP_TIMING_AVAIL + if ((clock_id & ((1 << CLOCK_IDFIELD_SIZE) - 1)) + == CLOCK_THREAD_CPUTIME_ID) + retval = hp_timing_gettime (clock_id, tp); + else +#endif + __set_errno (EINVAL); + break; + +#if HP_TIMING_AVAIL && !defined HANDLED_CPUTIME + case CLOCK_PROCESS_CPUTIME_ID: + retval = hp_timing_gettime (clock_id, tp); + break; +#endif + } + + return retval; +} +librt_hidden_def (clock_gettime) diff --git a/librt/clock_nanosleep.c b/librt/clock_nanosleep.c new file mode 100644 index 000000000..3fb14b48a --- /dev/null +++ b/librt/clock_nanosleep.c @@ -0,0 +1,96 @@ +/* Copyright (C) 2003, 2004, 2005 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, write to the Free + Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + 02111-1307 USA. */ + +#include <time.h> +#include <errno.h> + +#include <sysdep-cancel.h> +#include "kernel-features.h" +#include "kernel-posix-cpu-timers.h" + + +#ifdef __ASSUME_POSIX_TIMERS +/* We can simply use the syscall. The CPU clocks are not supported + with this function. */ +int +clock_nanosleep (clockid_t clock_id, int flags, const struct timespec *req, + struct timespec *rem) +{ + INTERNAL_SYSCALL_DECL (err); + int r; + + if (clock_id == CLOCK_THREAD_CPUTIME_ID) + return EINVAL; + if (clock_id == CLOCK_PROCESS_CPUTIME_ID) + clock_id = MAKE_PROCESS_CPUCLOCK (0, CPUCLOCK_SCHED); + + if (SINGLE_THREAD_P) + r = INTERNAL_SYSCALL (clock_nanosleep, err, 4, clock_id, flags, req, rem); + else + { + int oldstate = LIBC_CANCEL_ASYNC (); + + r = INTERNAL_SYSCALL (clock_nanosleep, err, 4, clock_id, flags, req, + rem); + + LIBC_CANCEL_RESET (oldstate); + } + + return (INTERNAL_SYSCALL_ERROR_P (r, err) + ? INTERNAL_SYSCALL_ERRNO (r, err) : 0); +} + +#else +# ifdef __NR_clock_nanosleep +/* Is the syscall known to exist? */ +extern int __libc_missing_posix_timers attribute_hidden; + +/* The REALTIME and MONOTONIC clock might be available. Try the + syscall first. */ +# define SYSDEP_NANOSLEEP \ + if (!__libc_missing_posix_timers) \ + { \ + clockid_t syscall_clockid; \ + INTERNAL_SYSCALL_DECL (err); \ + \ + if (clock_id == CLOCK_THREAD_CPUTIME_ID) \ + return EINVAL; \ + if (clock_id == CLOCK_PROCESS_CPUTIME_ID) \ + syscall_clockid = MAKE_PROCESS_CPUCLOCK (0, CPUCLOCK_SCHED); \ + else \ + syscall_clockid = clock_id; \ + \ + int oldstate = LIBC_CANCEL_ASYNC (); \ + \ + int r = INTERNAL_SYSCALL (clock_nanosleep, err, 4, \ + syscall_clockid, flags, req, rem); \ + \ + LIBC_CANCEL_RESET (oldstate); \ + \ + if (!INTERNAL_SYSCALL_ERROR_P (r, err)) \ + return 0; \ + \ + if (INTERNAL_SYSCALL_ERRNO (r, err) != ENOSYS) \ + return INTERNAL_SYSCALL_ERRNO (r, err); \ + \ + __libc_missing_posix_timers = 1; \ + } +# endif + +# include <sysdeps/unix/clock_nanosleep.c> +#endif diff --git a/librt/kernel-posix-timers.h b/librt/kernel-posix-timers.h index bf246c925..27d3f1fd7 100644 --- a/librt/kernel-posix-timers.h +++ b/librt/kernel-posix-timers.h @@ -10,6 +10,20 @@ #include <pthread.h> #endif +#ifdef __UCLIBC_HAS_THREADS_NATIVE__ +/* Nonzero if the system calls are not available. */ +extern int __no_posix_timers attribute_hidden; + +/* Callback to start helper thread. */ +extern void __start_helper_thread (void) attribute_hidden; + +/* Control variable for helper thread creation. */ +extern pthread_once_t __helper_once attribute_hidden; + +/* TID of the helper thread. */ +extern pid_t __helper_tid attribute_hidden; +#endif + /* Type of timers in the kernel */ typedef int kernel_timer_t; diff --git a/librt/mq_receive.c b/librt/mq_receive.c index c75d9ecb7..922f0c039 100644 --- a/librt/mq_receive.c +++ b/librt/mq_receive.c @@ -9,6 +9,7 @@ librt_hidden_proto(mq_timedreceive) +#ifndef __UCLIBC_HAS_THREADS_NATIVE__ #ifdef __NR_mq_timedreceive #define __NR___syscall_mq_timedreceive __NR_mq_timedreceive static inline _syscall5(int, __syscall_mq_timedreceive, int, mqdes, @@ -32,6 +33,7 @@ ssize_t mq_timedreceive(mqd_t mqdes, char *msg_ptr, size_t msg_len, #endif } librt_hidden_def(mq_timedreceive) +#endif /* Receive the oldest from highest priority messages */ ssize_t mq_receive(mqd_t mqdes, char *msg_ptr, size_t msg_len, diff --git a/librt/mq_send.c b/librt/mq_send.c index 1dd7db62b..660743719 100644 --- a/librt/mq_send.c +++ b/librt/mq_send.c @@ -9,6 +9,7 @@ librt_hidden_proto(mq_timedsend) +#ifndef __UCLIBC_HAS_THREADS_NATIVE__ #ifdef __NR_mq_timedsend #define __NR___syscall_mq_timedsend __NR_mq_timedsend static inline _syscall5(int, __syscall_mq_timedsend, int, mqdes, @@ -32,6 +33,7 @@ int mq_timedsend(mqd_t mqdes, const char *msg_ptr, size_t msg_len, #endif } librt_hidden_def(mq_timedsend) +#endif /* Add a message to queue */ int mq_send(mqd_t mqdes, const char *msg_ptr, size_t msg_len, diff --git a/librt/mq_timedreceive.S b/librt/mq_timedreceive.S new file mode 100644 index 000000000..71acc4dd4 --- /dev/null +++ b/librt/mq_timedreceive.S @@ -0,0 +1,10 @@ +#include <sysdep-cancel.h> +#ifndef __NR_mq_timedreceive +#error Missing definition of NR_timedreceive needed for cancellation. +#endif +PSEUDO (__mq_timedreceive, mq_timedreceive, 5) +ret +PSEUDO_END(__mq_timedreceive) +libc_hidden_def (__mq_timedreceive) +weak_alias (__mq_timedreceive, mq_timedreceive) +libc_hidden_weak (mq_timedreceive) diff --git a/librt/mq_timedsend.S b/librt/mq_timedsend.S new file mode 100644 index 000000000..dee894677 --- /dev/null +++ b/librt/mq_timedsend.S @@ -0,0 +1,10 @@ +#include <sysdep-cancel.h> +#ifndef __NR_mq_timedsend +#error Missing definition of NR_timedsend needed for cancellation. +#endif +PSEUDO (__mq_timedsend, mq_timedsend, 5) +ret +PSEUDO_END(__mq_timedsend) +libc_hidden_def (__mq_timedsend) +weak_alias (__mq_timedsend, mq_timedsend) +libc_hidden_weak (mq_timedsend) |