162306a36Sopenharmony_ci/* Clocksource change test
262306a36Sopenharmony_ci *		by: john stultz (johnstul@us.ibm.com)
362306a36Sopenharmony_ci *		(C) Copyright IBM 2012
462306a36Sopenharmony_ci *		Licensed under the GPLv2
562306a36Sopenharmony_ci *
662306a36Sopenharmony_ci *  NOTE: This is a meta-test which quickly changes the clocksource and
762306a36Sopenharmony_ci *  then uses other tests to detect problems. Thus this test requires
862306a36Sopenharmony_ci *  that the inconsistency-check and nanosleep tests be present in the
962306a36Sopenharmony_ci *  same directory it is run from.
1062306a36Sopenharmony_ci *
1162306a36Sopenharmony_ci *  To build:
1262306a36Sopenharmony_ci *	$ gcc clocksource-switch.c -o clocksource-switch -lrt
1362306a36Sopenharmony_ci *
1462306a36Sopenharmony_ci *   This program is free software: you can redistribute it and/or modify
1562306a36Sopenharmony_ci *   it under the terms of the GNU General Public License as published by
1662306a36Sopenharmony_ci *   the Free Software Foundation, either version 2 of the License, or
1762306a36Sopenharmony_ci *   (at your option) any later version.
1862306a36Sopenharmony_ci *
1962306a36Sopenharmony_ci *   This program is distributed in the hope that it will be useful,
2062306a36Sopenharmony_ci *   but WITHOUT ANY WARRANTY; without even the implied warranty of
2162306a36Sopenharmony_ci *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
2262306a36Sopenharmony_ci *   GNU General Public License for more details.
2362306a36Sopenharmony_ci */
2462306a36Sopenharmony_ci
2562306a36Sopenharmony_ci
2662306a36Sopenharmony_ci#include <fcntl.h>
2762306a36Sopenharmony_ci#include <stdio.h>
2862306a36Sopenharmony_ci#include <stdlib.h>
2962306a36Sopenharmony_ci#include <string.h>
3062306a36Sopenharmony_ci#include <sys/stat.h>
3162306a36Sopenharmony_ci#include <sys/time.h>
3262306a36Sopenharmony_ci#include <sys/timex.h>
3362306a36Sopenharmony_ci#include <sys/types.h>
3462306a36Sopenharmony_ci#include <sys/wait.h>
3562306a36Sopenharmony_ci#include <time.h>
3662306a36Sopenharmony_ci#include <unistd.h>
3762306a36Sopenharmony_ci#include "../kselftest.h"
3862306a36Sopenharmony_ci
3962306a36Sopenharmony_ci
4062306a36Sopenharmony_ciint get_clocksources(char list[][30])
4162306a36Sopenharmony_ci{
4262306a36Sopenharmony_ci	int fd, i;
4362306a36Sopenharmony_ci	size_t size;
4462306a36Sopenharmony_ci	char buf[512];
4562306a36Sopenharmony_ci	char *head, *tmp;
4662306a36Sopenharmony_ci
4762306a36Sopenharmony_ci	fd = open("/sys/devices/system/clocksource/clocksource0/available_clocksource", O_RDONLY);
4862306a36Sopenharmony_ci
4962306a36Sopenharmony_ci	size = read(fd, buf, 512);
5062306a36Sopenharmony_ci
5162306a36Sopenharmony_ci	close(fd);
5262306a36Sopenharmony_ci
5362306a36Sopenharmony_ci	for (i = 0; i < 10; i++)
5462306a36Sopenharmony_ci		list[i][0] = '\0';
5562306a36Sopenharmony_ci
5662306a36Sopenharmony_ci	head = buf;
5762306a36Sopenharmony_ci	i = 0;
5862306a36Sopenharmony_ci	while (head - buf < size) {
5962306a36Sopenharmony_ci		/* Find the next space */
6062306a36Sopenharmony_ci		for (tmp = head; *tmp != ' '; tmp++) {
6162306a36Sopenharmony_ci			if (*tmp == '\n')
6262306a36Sopenharmony_ci				break;
6362306a36Sopenharmony_ci			if (*tmp == '\0')
6462306a36Sopenharmony_ci				break;
6562306a36Sopenharmony_ci		}
6662306a36Sopenharmony_ci		*tmp = '\0';
6762306a36Sopenharmony_ci		strcpy(list[i], head);
6862306a36Sopenharmony_ci		head = tmp + 1;
6962306a36Sopenharmony_ci		i++;
7062306a36Sopenharmony_ci	}
7162306a36Sopenharmony_ci
7262306a36Sopenharmony_ci	return i-1;
7362306a36Sopenharmony_ci}
7462306a36Sopenharmony_ci
7562306a36Sopenharmony_ciint get_cur_clocksource(char *buf, size_t size)
7662306a36Sopenharmony_ci{
7762306a36Sopenharmony_ci	int fd;
7862306a36Sopenharmony_ci
7962306a36Sopenharmony_ci	fd = open("/sys/devices/system/clocksource/clocksource0/current_clocksource", O_RDONLY);
8062306a36Sopenharmony_ci
8162306a36Sopenharmony_ci	size = read(fd, buf, size);
8262306a36Sopenharmony_ci
8362306a36Sopenharmony_ci	return 0;
8462306a36Sopenharmony_ci}
8562306a36Sopenharmony_ci
8662306a36Sopenharmony_ciint change_clocksource(char *clocksource)
8762306a36Sopenharmony_ci{
8862306a36Sopenharmony_ci	int fd;
8962306a36Sopenharmony_ci	ssize_t size;
9062306a36Sopenharmony_ci
9162306a36Sopenharmony_ci	fd = open("/sys/devices/system/clocksource/clocksource0/current_clocksource", O_WRONLY);
9262306a36Sopenharmony_ci
9362306a36Sopenharmony_ci	if (fd < 0)
9462306a36Sopenharmony_ci		return -1;
9562306a36Sopenharmony_ci
9662306a36Sopenharmony_ci	size = write(fd, clocksource, strlen(clocksource));
9762306a36Sopenharmony_ci
9862306a36Sopenharmony_ci	if (size < 0)
9962306a36Sopenharmony_ci		return -1;
10062306a36Sopenharmony_ci
10162306a36Sopenharmony_ci	close(fd);
10262306a36Sopenharmony_ci	return 0;
10362306a36Sopenharmony_ci}
10462306a36Sopenharmony_ci
10562306a36Sopenharmony_ci
10662306a36Sopenharmony_ciint run_tests(int secs)
10762306a36Sopenharmony_ci{
10862306a36Sopenharmony_ci	int ret;
10962306a36Sopenharmony_ci	char buf[255];
11062306a36Sopenharmony_ci
11162306a36Sopenharmony_ci	sprintf(buf, "./inconsistency-check -t %i", secs);
11262306a36Sopenharmony_ci	ret = system(buf);
11362306a36Sopenharmony_ci	if (WIFEXITED(ret) && WEXITSTATUS(ret))
11462306a36Sopenharmony_ci		return WEXITSTATUS(ret);
11562306a36Sopenharmony_ci	ret = system("./nanosleep");
11662306a36Sopenharmony_ci	return WIFEXITED(ret) ? WEXITSTATUS(ret) : 0;
11762306a36Sopenharmony_ci}
11862306a36Sopenharmony_ci
11962306a36Sopenharmony_ci
12062306a36Sopenharmony_cichar clocksource_list[10][30];
12162306a36Sopenharmony_ci
12262306a36Sopenharmony_ciint main(int argc, char **argv)
12362306a36Sopenharmony_ci{
12462306a36Sopenharmony_ci	char orig_clk[512];
12562306a36Sopenharmony_ci	int count, i, status, opt;
12662306a36Sopenharmony_ci	int do_sanity_check = 1;
12762306a36Sopenharmony_ci	int runtime = 60;
12862306a36Sopenharmony_ci	pid_t pid;
12962306a36Sopenharmony_ci
13062306a36Sopenharmony_ci	/* Process arguments */
13162306a36Sopenharmony_ci	while ((opt = getopt(argc, argv, "st:")) != -1) {
13262306a36Sopenharmony_ci		switch (opt) {
13362306a36Sopenharmony_ci		case 's':
13462306a36Sopenharmony_ci			do_sanity_check = 0;
13562306a36Sopenharmony_ci			break;
13662306a36Sopenharmony_ci		case 't':
13762306a36Sopenharmony_ci			runtime = atoi(optarg);
13862306a36Sopenharmony_ci			break;
13962306a36Sopenharmony_ci		default:
14062306a36Sopenharmony_ci			printf("Usage: %s [-s] [-t <secs>]\n", argv[0]);
14162306a36Sopenharmony_ci			printf("	-s: skip sanity checks\n");
14262306a36Sopenharmony_ci			printf("	-t: Number of seconds to run\n");
14362306a36Sopenharmony_ci			exit(-1);
14462306a36Sopenharmony_ci		}
14562306a36Sopenharmony_ci	}
14662306a36Sopenharmony_ci
14762306a36Sopenharmony_ci	get_cur_clocksource(orig_clk, 512);
14862306a36Sopenharmony_ci
14962306a36Sopenharmony_ci	count = get_clocksources(clocksource_list);
15062306a36Sopenharmony_ci
15162306a36Sopenharmony_ci	if (change_clocksource(clocksource_list[0])) {
15262306a36Sopenharmony_ci		printf("Error: You probably need to run this as root\n");
15362306a36Sopenharmony_ci		return -1;
15462306a36Sopenharmony_ci	}
15562306a36Sopenharmony_ci
15662306a36Sopenharmony_ci	/* Check everything is sane before we start switching asynchronously */
15762306a36Sopenharmony_ci	if (do_sanity_check) {
15862306a36Sopenharmony_ci		for (i = 0; i < count; i++) {
15962306a36Sopenharmony_ci			printf("Validating clocksource %s\n",
16062306a36Sopenharmony_ci				clocksource_list[i]);
16162306a36Sopenharmony_ci			if (change_clocksource(clocksource_list[i])) {
16262306a36Sopenharmony_ci				status = -1;
16362306a36Sopenharmony_ci				goto out;
16462306a36Sopenharmony_ci			}
16562306a36Sopenharmony_ci			if (run_tests(5)) {
16662306a36Sopenharmony_ci				status = -1;
16762306a36Sopenharmony_ci				goto out;
16862306a36Sopenharmony_ci			}
16962306a36Sopenharmony_ci		}
17062306a36Sopenharmony_ci	}
17162306a36Sopenharmony_ci
17262306a36Sopenharmony_ci	printf("Running Asynchronous Switching Tests...\n");
17362306a36Sopenharmony_ci	pid = fork();
17462306a36Sopenharmony_ci	if (!pid)
17562306a36Sopenharmony_ci		return run_tests(runtime);
17662306a36Sopenharmony_ci
17762306a36Sopenharmony_ci	while (pid != waitpid(pid, &status, WNOHANG))
17862306a36Sopenharmony_ci		for (i = 0; i < count; i++)
17962306a36Sopenharmony_ci			if (change_clocksource(clocksource_list[i])) {
18062306a36Sopenharmony_ci				status = -1;
18162306a36Sopenharmony_ci				goto out;
18262306a36Sopenharmony_ci			}
18362306a36Sopenharmony_ciout:
18462306a36Sopenharmony_ci	change_clocksource(orig_clk);
18562306a36Sopenharmony_ci
18662306a36Sopenharmony_ci	/* Print at the end to not mix output with child process */
18762306a36Sopenharmony_ci	ksft_print_header();
18862306a36Sopenharmony_ci	ksft_set_plan(1);
18962306a36Sopenharmony_ci	ksft_test_result(!status, "clocksource-switch\n");
19062306a36Sopenharmony_ci	ksft_exit(!status);
19162306a36Sopenharmony_ci}
192