226 lines
5.1 KiB
C
226 lines
5.1 KiB
C
// SPDX-License-Identifier: GPL-2.0-or-later
|
|
/*
|
|
* Copyright (c) Crackerjack Project., 2007-2008 ,Hitachi, Ltd
|
|
* Author(s): Takahiro Yasui <takahiro.yasui.mp@hitachi.com>,
|
|
* Yumiko Sugita <yumiko.sugita.yf@hitachi.com>,
|
|
* Satoshi Fujiwara <sa-fuji@sdl.hitachi.co.jp>
|
|
* Copyright (c) 2016 Linux Test Project
|
|
*/
|
|
/*
|
|
* test status of errors on man page
|
|
* EINTR v (function was interrupted by a signal)
|
|
* EINVAL v (invalid tv_nsec, etc.)
|
|
* ENOTSUP v (sleep not supported against the specified clock_id)
|
|
* EFAULT v (Invalid request pointer)
|
|
* EFAULT V (Invalid remain pointer when interrupted by a signal)
|
|
*/
|
|
|
|
#include <limits.h>
|
|
|
|
#include "time64_variants.h"
|
|
#include "tst_safe_clocks.h"
|
|
#include "tst_sig_proc.h"
|
|
#include "tst_timer.h"
|
|
|
|
static void sighandler(int sig LTP_ATTRIBUTE_UNUSED)
|
|
{
|
|
}
|
|
|
|
enum test_type {
|
|
NORMAL,
|
|
SEND_SIGINT,
|
|
BAD_TS_ADDR_REQ,
|
|
BAD_TS_ADDR_REM,
|
|
};
|
|
|
|
#define TYPE_NAME(x) .ttype = x, .desc = #x
|
|
|
|
static void *bad_addr;
|
|
|
|
struct test_case {
|
|
clockid_t clk_id; /* clock_* clock type parameter */
|
|
int ttype; /* test type (enum) */
|
|
const char *desc; /* test description (name) */
|
|
int flags; /* clock_nanosleep flags parameter */
|
|
long tv_sec;
|
|
long tv_nsec;
|
|
int exp_ret;
|
|
int exp_err;
|
|
};
|
|
|
|
static struct test_case tcase[] = {
|
|
{
|
|
TYPE_NAME(NORMAL),
|
|
.clk_id = CLOCK_REALTIME,
|
|
.flags = 0,
|
|
.tv_sec = 0,
|
|
.tv_nsec = -1,
|
|
.exp_ret = -1,
|
|
.exp_err = EINVAL,
|
|
},
|
|
{
|
|
TYPE_NAME(NORMAL),
|
|
.clk_id = CLOCK_REALTIME,
|
|
.flags = 0,
|
|
.tv_sec = 0,
|
|
.tv_nsec = 1000000000,
|
|
.exp_ret = -1,
|
|
.exp_err = EINVAL,
|
|
},
|
|
{
|
|
TYPE_NAME(NORMAL),
|
|
.clk_id = CLOCK_THREAD_CPUTIME_ID,
|
|
.flags = 0,
|
|
.tv_sec = 0,
|
|
.tv_nsec = 500000000,
|
|
.exp_ret = -1,
|
|
.exp_err = ENOTSUP,
|
|
},
|
|
{
|
|
TYPE_NAME(SEND_SIGINT),
|
|
.clk_id = CLOCK_REALTIME,
|
|
.flags = 0,
|
|
.tv_sec = 10,
|
|
.tv_nsec = 0,
|
|
.exp_ret = -1,
|
|
.exp_err = EINTR,
|
|
},
|
|
{
|
|
TYPE_NAME(BAD_TS_ADDR_REQ),
|
|
.clk_id = CLOCK_REALTIME,
|
|
.flags = 0,
|
|
.exp_ret = -1,
|
|
.exp_err = EFAULT,
|
|
},
|
|
{
|
|
TYPE_NAME(BAD_TS_ADDR_REM),
|
|
.clk_id = CLOCK_REALTIME,
|
|
.flags = 0,
|
|
.tv_sec = 10,
|
|
.tv_nsec = 0,
|
|
.exp_ret = -1,
|
|
.exp_err = EFAULT,
|
|
},
|
|
};
|
|
|
|
static struct tst_ts *rq;
|
|
static struct tst_ts *rm;
|
|
|
|
static struct time64_variants variants[] = {
|
|
{ .clock_nanosleep = libc_clock_nanosleep, .ts_type = TST_LIBC_TIMESPEC, .desc = "vDSO or syscall with libc spec"},
|
|
|
|
#if (__NR_clock_nanosleep != __LTP__NR_INVALID_SYSCALL)
|
|
{ .clock_nanosleep = sys_clock_nanosleep, .ts_type = TST_KERN_OLD_TIMESPEC, .desc = "syscall with old kernel spec"},
|
|
#endif
|
|
|
|
#if (__NR_clock_nanosleep_time64 != __LTP__NR_INVALID_SYSCALL)
|
|
{ .clock_nanosleep = sys_clock_nanosleep64, .ts_type = TST_KERN_TIMESPEC, .desc = "syscall time64 with kernel spec"},
|
|
#endif
|
|
};
|
|
|
|
void setup(void)
|
|
{
|
|
rq->type = variants[tst_variant].ts_type;
|
|
tst_res(TINFO, "Testing variant: %s", variants[tst_variant].desc);
|
|
SAFE_SIGNAL(SIGINT, sighandler);
|
|
bad_addr = tst_get_bad_addr(NULL);
|
|
}
|
|
|
|
static void do_test(unsigned int i)
|
|
{
|
|
struct time64_variants *tv = &variants[tst_variant];
|
|
struct test_case *tc = &tcase[i];
|
|
pid_t pid = 0;
|
|
void *request, *remain;
|
|
|
|
memset(rm, 0, sizeof(*rm));
|
|
rm->type = rq->type;
|
|
|
|
tst_res(TINFO, "case %s", tc->desc);
|
|
|
|
if (tc->ttype == SEND_SIGINT || tc->ttype == BAD_TS_ADDR_REM)
|
|
pid = create_sig_proc(SIGINT, 40, 500000);
|
|
|
|
tst_ts_set_sec(rq, tc->tv_sec);
|
|
tst_ts_set_nsec(rq, tc->tv_nsec);
|
|
|
|
if (tc->ttype == BAD_TS_ADDR_REQ)
|
|
request = bad_addr;
|
|
else
|
|
request = tst_ts_get(rq);
|
|
|
|
if (tc->ttype == BAD_TS_ADDR_REM)
|
|
remain = bad_addr;
|
|
else
|
|
remain = tst_ts_get(rm);
|
|
|
|
TEST(tv->clock_nanosleep(tc->clk_id, tc->flags, request, remain));
|
|
|
|
if (tv->clock_nanosleep == libc_clock_nanosleep) {
|
|
/*
|
|
* The return value and error number are differently set for
|
|
* libc syscall as compared to kernel syscall.
|
|
*/
|
|
if (TST_RET) {
|
|
TST_ERR = TST_RET;
|
|
TST_RET = -1;
|
|
}
|
|
|
|
/*
|
|
* nsleep isn't implemented by kernelf or
|
|
* CLOCK_THREAD_CPUTIME_ID and it returns ENOTSUP, but libc
|
|
* changes that error value to EINVAL.
|
|
*/
|
|
if (tc->clk_id == CLOCK_THREAD_CPUTIME_ID)
|
|
tc->exp_err = EINVAL;
|
|
}
|
|
|
|
if (pid) {
|
|
SAFE_KILL(pid, SIGTERM);
|
|
SAFE_WAIT(NULL);
|
|
}
|
|
|
|
if (tc->ttype == SEND_SIGINT) {
|
|
long long expect_ms = tst_ts_to_ms(*rq);
|
|
long long remain_ms = tst_ts_to_ms(*rm);
|
|
|
|
if (tst_ts_valid(rm)) {
|
|
tst_res(TFAIL | TTERRNO,
|
|
"The clock_nanosleep() haven't updated"
|
|
" timespec or it's not valid");
|
|
return;
|
|
}
|
|
|
|
if (remain_ms > expect_ms) {
|
|
tst_res(TFAIL| TTERRNO,
|
|
"remaining time > requested time (%lld > %lld)",
|
|
remain_ms, expect_ms);
|
|
return;
|
|
}
|
|
|
|
tst_res(TPASS, "Timespec updated correctly");
|
|
}
|
|
|
|
if ((TST_RET != tc->exp_ret) || (TST_ERR != tc->exp_err)) {
|
|
tst_res(TFAIL | TTERRNO, "returned %ld, expected %d,"
|
|
" expected errno: %s (%d)", TST_RET,
|
|
tc->exp_ret, tst_strerrno(tc->exp_err), tc->exp_err);
|
|
return;
|
|
}
|
|
|
|
tst_res(TPASS | TTERRNO, "clock_nanosleep() failed with");
|
|
}
|
|
|
|
static struct tst_test test = {
|
|
.tcnt = ARRAY_SIZE(tcase),
|
|
.test = do_test,
|
|
.test_variants = ARRAY_SIZE(variants),
|
|
.setup = setup,
|
|
.forks_child = 1,
|
|
.bufs = (struct tst_buffers []) {
|
|
{&rq, .size = sizeof(*rq)},
|
|
{&rm, .size = sizeof(*rm)},
|
|
{},
|
|
}
|
|
};
|