diff --git a/test/zdtm/live/static/Makefile b/test/zdtm/live/static/Makefile index 06087fcde..698d726b3 100644 --- a/test/zdtm/live/static/Makefile +++ b/test/zdtm/live/static/Makefile @@ -66,6 +66,7 @@ TST_NOFILE = \ socket-ext \ unhashed_proc \ cow00 \ + posix_timers \ # jobctl00 \ TST_FILE = \ @@ -193,6 +194,7 @@ pthread00: override LDLIBS += -pthread shm: override CFLAGS += -DNEW_IPC_NS msgque: override CFLAGS += -DNEW_IPC_NS sem: override CFLAGS += -DNEW_IPC_NS +posix_timers: override LDLIBS += -lrt $(LIB): force $(MAKE) -C $(LIBDIR) diff --git a/test/zdtm/live/static/posix_timers.c b/test/zdtm/live/static/posix_timers.c new file mode 100644 index 000000000..dd035f51c --- /dev/null +++ b/test/zdtm/live/static/posix_timers.c @@ -0,0 +1,220 @@ +#include +#include +#include +#include +#include + +#include "zdtmtst.h" + +const char *test_doc ="Posix timers migration check"; +const char *test_author = "Kinsbursky Stanislav "; + +struct timespec start; +sigset_t mask; + +#define WRONG_SIGNAL 1 +#define WRONG_SI_PTR 2 +#define FAIL_OVERRUN 4 + +#define MAX_TIMER_DISPLACEMENT 10 + +static void realtime_handler(int sig, siginfo_t *si, void *uc); +static void monotonic_handler(int sig, siginfo_t *si, void *uc); + +static struct posix_timers_info { + char clock; + char *name; + void (*handler)(int sig, siginfo_t *si, void *uc); + int sig; + int ms_int; + struct sigaction sa; + int handler_status; + int handler_cnt; + timer_t timerid; + int overrun; +} posix_timers[] = { + [CLOCK_REALTIME] = {CLOCK_REALTIME, "REALTIME", realtime_handler, SIGALRM, 1}, + [CLOCK_MONOTONIC] = {CLOCK_MONOTONIC, "MONOTONIC", monotonic_handler, SIGINT, 3}, + { } +}; + +static int check_handler_status(struct posix_timers_info *info, int ms_passed) +{ + int displacement; + int timer_ms; + + if (!info->handler_cnt) { + fail("%s: Signal handler wasn't called\n", info->name); + return -EINVAL; + } + + if (info->handler_status) { + if (info->handler_status & WRONG_SIGNAL) + fail("%s: Handler: wrong signal received\n", info->name); + if (info->handler_status & WRONG_SI_PTR) + fail("%s: Handler: wrong timer address\n", info->name); + if (info->handler_status & FAIL_OVERRUN) + fail("%s: Handler: failed to get overrun count\n", info->name); + return -1; + } + + if (info->overrun + info->handler_cnt > ms_passed) { + fail("%s: Overrun (%d) is greater than time passed (%d). " + "Timers problems?\n", info->name, info->overrun + info->handler_cnt, + ms_passed); + return -E2BIG; + } + + timer_ms = (info->overrun + info->handler_cnt) * info->ms_int; + displacement = (ms_passed - timer_ms) * 100 / ms_passed; + + if (displacement > MAX_TIMER_DISPLACEMENT) { + test_msg("%s: Time passed (ms) : %d msec\n", info->name, ms_passed); + test_msg("%s: Timer results : %d msec\n", info->name, timer_ms); + test_msg("%s: Handler count : %d\n", info->name, info->handler_cnt); + fail("%s: Time displacement: %d%% (max alloved: %d%%)\n", info->name, displacement, MAX_TIMER_DISPLACEMENT); + return -EFAULT; + } + return 0; +} + +static int check_timers(void) +{ + struct posix_timers_info *info = posix_timers; + struct timespec end; + int ms_passed; + int status = 0; + + if (sigprocmask(SIG_UNBLOCK, &mask, NULL) == -1) { + fail("Failed to unlock signal\n"); + return -errno; + } + + if (clock_gettime(CLOCK_REALTIME, &end) == -1) { + fail("Can't get end time\n"); + return -errno; + } + + ms_passed = (end.tv_sec - start.tv_sec) * 1000 + (end.tv_nsec - start.tv_nsec) / (1000 * 1000); + + while (info->handler) { + if (timer_delete(info->timerid) == -1) { + fail("%s: Failed to delete timer\n", info->name); + return -errno; + } + if (check_handler_status(info, ms_passed)) + status--; + info++; + } + return status; +} + +static void generic_handler(struct posix_timers_info *info, + struct posix_timers_info *real, int sig) +{ + int overrun; + + if (info != real) { + real->handler_status |= WRONG_SI_PTR; + return; + } + + if (sig != info->sig) + info->handler_status |= WRONG_SIGNAL; + + overrun = timer_getoverrun(info->timerid); + if (overrun == -1) + info->handler_status |= FAIL_OVERRUN; + else + info->overrun += overrun; + info->handler_cnt++; +} + +static void monotonic_handler(int sig, siginfo_t *si, void *uc) +{ + generic_handler(si->si_value.sival_ptr, &posix_timers[CLOCK_MONOTONIC], sig); +} + +static void realtime_handler(int sig, siginfo_t *si, void *uc) +{ + generic_handler(si->si_value.sival_ptr, &posix_timers[CLOCK_REALTIME], sig); +} + +static int setup_timers(void) +{ + struct posix_timers_info *info = posix_timers; + struct sigevent sev; + struct itimerspec its; + + if (clock_gettime(CLOCK_REALTIME, &start) == -1) { + err("Can't get start time\n"); + return -errno; + } + + sigemptyset(&mask); + while(info->handler) { + sigaddset(&mask, info->sig); + info++; + } + + if (sigprocmask(SIG_SETMASK, &mask, NULL) == -1) { + err("Failed to unlock signal\n"); + return -errno; + } + + info = posix_timers; + while(info->handler) { + info->sa.sa_flags = SA_SIGINFO; + info->sa.sa_sigaction = info->handler; + sigemptyset(&info->sa.sa_mask); + + if (sigaction(info->sig, &info->sa, NULL) == -1) { + err("Failed to set SIGALRM handler\n"); + return -errno; + } + + sev.sigev_notify = SIGEV_SIGNAL; + sev.sigev_signo = info->sig; + sev.sigev_value.sival_ptr = info; + + if (timer_create(info->clock, &sev, &info->timerid) == -1) { + err("Can't create timer\n"); + return -errno; + } + + its.it_value.tv_sec = 0; + its.it_value.tv_nsec = info->ms_int * 1000 * 1000; + its.it_interval.tv_sec = its.it_value.tv_sec; + its.it_interval.tv_nsec = its.it_value.tv_nsec; + + if (timer_settime(info->timerid, 0, &its, NULL) == -1) { + err("Can't set timer\n"); + return -errno; + } + info++; + } + return 0; +} + +int main(int argc, char **argv) +{ + int err; + + test_init(argc, argv); + + err = setup_timers(); + if (err) + return err; + + usleep(500 * 1000); + + test_daemon(); + test_waitsig(); + + err = check_timers(); + if (err) + return err; + + pass(); + return 0; +}