Commit 5dbab1a7 authored by Vladimir Davydov's avatar Vladimir Davydov Committed by Pavel Emelyanov

zdtm: check one-shot posix timers

With the patch zdtm will check not only periodic timers, but also
one-shot ones.

 Set one-shot timer to expire in a very long interval (INT_MAX in the
 test), and check its remaining value after checkpoint/restore. The
 following relationship must hold:

 initial_value - remaining_value = time_passed
Signed-off-by: 's avatarVladimir Davydov <vdavydov@parallels.com>
Acked-by: 's avatarAndrew Vagin <avagin@parallels.com>
Signed-off-by: 's avatarPavel Emelyanov <xemul@parallels.com>
parent fc07b9ae
...@@ -3,6 +3,8 @@ ...@@ -3,6 +3,8 @@
#include <signal.h> #include <signal.h>
#include <time.h> #include <time.h>
#include <sys/time.h> #include <sys/time.h>
#include <limits.h>
#include <string.h>
#include "zdtmtst.h" #include "zdtmtst.h"
...@@ -17,14 +19,24 @@ sigset_t mask; ...@@ -17,14 +19,24 @@ sigset_t mask;
#define MAX_TIMER_DISPLACEMENT 10 #define MAX_TIMER_DISPLACEMENT 10
static void realtime_handler(int sig, siginfo_t *si, void *uc); static void realtime_periodic_handler(int sig, siginfo_t *si, void *uc);
static void monotonic_handler(int sig, siginfo_t *si, void *uc); static void monotonic_periodic_handler(int sig, siginfo_t *si, void *uc);
static void realtime_oneshot_handler(int sig, siginfo_t *si, void *uc);
static void monotonic_oneshot_handler(int sig, siginfo_t *si, void *uc);
enum {
REALTIME_PERIODIC_INFO,
MONOTONIC_PERIODIC_INFO,
REALTIME_ONESHOT_INFO,
MONOTONIC_ONESHOT_INFO,
};
static struct posix_timers_info { static struct posix_timers_info {
char clock; char clock;
char *name; char *name;
void (*handler)(int sig, siginfo_t *si, void *uc); void (*handler)(int sig, siginfo_t *si, void *uc);
int sig; int sig;
int oneshot;
int ms_int; int ms_int;
struct sigaction sa; struct sigaction sa;
int handler_status; int handler_status;
...@@ -33,17 +45,24 @@ static struct posix_timers_info { ...@@ -33,17 +45,24 @@ static struct posix_timers_info {
int overrun; int overrun;
struct timespec start, end; struct timespec start, end;
} posix_timers[] = { } posix_timers[] = {
[CLOCK_REALTIME] = {CLOCK_REALTIME, "REALTIME", realtime_handler, SIGALRM, 1}, [REALTIME_PERIODIC_INFO] = {CLOCK_REALTIME, "REALTIME (periodic)",
[CLOCK_MONOTONIC] = {CLOCK_MONOTONIC, "MONOTONIC", monotonic_handler, SIGINT, 3}, realtime_periodic_handler, SIGALRM, 0, 1},
[MONOTONIC_PERIODIC_INFO] = {CLOCK_MONOTONIC, "MONOTONIC (periodic)",
monotonic_periodic_handler, SIGINT, 0, 3},
[REALTIME_ONESHOT_INFO] = {CLOCK_REALTIME, "REALTIME (oneshot)",
realtime_oneshot_handler, SIGUSR1, 1, INT_MAX},
[MONOTONIC_ONESHOT_INFO] = {CLOCK_MONOTONIC, "MONOTONIC (oneshot)",
monotonic_oneshot_handler, SIGUSR2, 1, INT_MAX},
{ } { }
}; };
static int check_handler_status(struct posix_timers_info *info, int ms_passed) static int check_handler_status(struct posix_timers_info *info,
struct itimerspec *its, int ms_passed)
{ {
int displacement; int displacement;
int timer_ms; int timer_ms;
if (!info->handler_cnt) { if (!info->handler_cnt && !info->oneshot) {
fail("%s: Signal handler wasn't called\n", info->name); fail("%s: Signal handler wasn't called\n", info->name);
return -EINVAL; return -EINVAL;
} }
...@@ -58,7 +77,41 @@ static int check_handler_status(struct posix_timers_info *info, int ms_passed) ...@@ -58,7 +77,41 @@ static int check_handler_status(struct posix_timers_info *info, int ms_passed)
return -1; return -1;
} }
timer_ms = (info->overrun + info->handler_cnt) * info->ms_int; if (!info->oneshot && !its->it_value.tv_sec && !its->it_value.tv_nsec) {
fail("%s: timer became unset\n", info->name);
return -EFAULT;
}
if (info->oneshot && (its->it_interval.tv_sec || its->it_interval.tv_nsec)) {
fail("%s: timer became periodic\n", info->name);
return -EFAULT;
}
if (!info->oneshot && !its->it_interval.tv_sec && !its->it_interval.tv_nsec) {
fail("%s: timer became oneshot\n", info->name);
return -EFAULT;
}
if (info->oneshot) {
int val = its->it_value.tv_sec * 1000 + its->it_value.tv_nsec / 1000 / 1000;
if (info->handler_cnt) {
if (val != 0) {
fail("%s: timer continues ticking after expiration\n", info->name);
return -EFAULT;
}
if (info->handler_cnt > 1) {
fail("%s: timer expired %d times\n", info->name, info->handler_cnt);
return -EFAULT;
}
if (info->ms_int > ms_passed) {
fail("%s: timer expired too early\n", info->name);
return -EFAULT;
}
return 0;
}
timer_ms = info->ms_int - val;
} else
timer_ms = (info->overrun + info->handler_cnt) * info->ms_int;
displacement = abs(ms_passed - timer_ms) * 100 / ms_passed; displacement = abs(ms_passed - timer_ms) * 100 / ms_passed;
if (displacement > MAX_TIMER_DISPLACEMENT) { if (displacement > MAX_TIMER_DISPLACEMENT) {
...@@ -76,6 +129,7 @@ static int check_timers(void) ...@@ -76,6 +129,7 @@ static int check_timers(void)
struct posix_timers_info *info = posix_timers; struct posix_timers_info *info = posix_timers;
int ms_passed; int ms_passed;
int status = 0; int status = 0;
struct itimerspec val, oldval;
if (sigprocmask(SIG_UNBLOCK, &mask, NULL) == -1) { if (sigprocmask(SIG_UNBLOCK, &mask, NULL) == -1) {
fail("Failed to unlock signal\n"); fail("Failed to unlock signal\n");
...@@ -83,8 +137,9 @@ static int check_timers(void) ...@@ -83,8 +137,9 @@ static int check_timers(void)
} }
while (info->handler) { while (info->handler) {
if (timer_delete(info->timerid) == -1) { memset(&val, 0, sizeof(val));
fail("%s: Failed to delete timer\n", info->name); if (timer_settime(info->timerid, 0, &val, &oldval) == -1) {
fail("%s: failed to reset timer\n", info->name);
return -errno; return -errno;
} }
...@@ -96,7 +151,7 @@ static int check_timers(void) ...@@ -96,7 +151,7 @@ static int check_timers(void)
ms_passed = (info->end.tv_sec - info->start.tv_sec) * 1000 + ms_passed = (info->end.tv_sec - info->start.tv_sec) * 1000 +
(info->end.tv_nsec - info->start.tv_nsec) / (1000 * 1000); (info->end.tv_nsec - info->start.tv_nsec) / (1000 * 1000);
if (check_handler_status(info, ms_passed)) if (check_handler_status(info, &oldval, ms_passed))
status--; status--;
info++; info++;
} }
...@@ -124,14 +179,28 @@ static void generic_handler(struct posix_timers_info *info, ...@@ -124,14 +179,28 @@ static void generic_handler(struct posix_timers_info *info,
info->handler_cnt++; info->handler_cnt++;
} }
static void monotonic_handler(int sig, siginfo_t *si, void *uc) static void monotonic_periodic_handler(int sig, siginfo_t *si, void *uc)
{
generic_handler(si->si_value.sival_ptr,
&posix_timers[MONOTONIC_PERIODIC_INFO], sig);
}
static void monotonic_oneshot_handler(int sig, siginfo_t *si, void *uc)
{
generic_handler(si->si_value.sival_ptr,
&posix_timers[MONOTONIC_ONESHOT_INFO], sig);
}
static void realtime_periodic_handler(int sig, siginfo_t *si, void *uc)
{ {
generic_handler(si->si_value.sival_ptr, &posix_timers[CLOCK_MONOTONIC], sig); generic_handler(si->si_value.sival_ptr,
&posix_timers[REALTIME_PERIODIC_INFO], sig);
} }
static void realtime_handler(int sig, siginfo_t *si, void *uc) static void realtime_oneshot_handler(int sig, siginfo_t *si, void *uc)
{ {
generic_handler(si->si_value.sival_ptr, &posix_timers[CLOCK_REALTIME], sig); generic_handler(si->si_value.sival_ptr,
&posix_timers[REALTIME_ONESHOT_INFO], sig);
} }
static int setup_timers(void) static int setup_timers(void)
...@@ -171,10 +240,13 @@ static int setup_timers(void) ...@@ -171,10 +240,13 @@ static int setup_timers(void)
return -errno; return -errno;
} }
its.it_value.tv_sec = 0; its.it_value.tv_sec = info->ms_int / 1000;
its.it_value.tv_nsec = info->ms_int * 1000 * 1000; its.it_value.tv_nsec = info->ms_int % 1000 * 1000 * 1000;
its.it_interval.tv_sec = its.it_value.tv_sec; if (!info->oneshot) {
its.it_interval.tv_nsec = its.it_value.tv_nsec; its.it_interval.tv_sec = its.it_value.tv_sec;
its.it_interval.tv_nsec = its.it_value.tv_nsec;
} else
its.it_interval.tv_sec = its.it_interval.tv_nsec = 0;
if (clock_gettime(info->clock, &info->start) == -1) { if (clock_gettime(info->clock, &info->start) == -1) {
err("Can't get %s start time\n", info->name); err("Can't get %s start time\n", info->name);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment