162306a36Sopenharmony_ci/* Demo leapsecond deadlock
262306a36Sopenharmony_ci *              by: John Stultz (john.stultz@linaro.org)
362306a36Sopenharmony_ci *              (C) Copyright IBM 2012
462306a36Sopenharmony_ci *              (C) Copyright 2013, 2015 Linaro Limited
562306a36Sopenharmony_ci *              Licensed under the GPL
662306a36Sopenharmony_ci *
762306a36Sopenharmony_ci * This test demonstrates leapsecond deadlock that is possible
862306a36Sopenharmony_ci * on kernels from 2.6.26 to 3.3.
962306a36Sopenharmony_ci *
1062306a36Sopenharmony_ci * WARNING: THIS WILL LIKELY HARD HANG SYSTEMS AND MAY LOSE DATA
1162306a36Sopenharmony_ci * RUN AT YOUR OWN RISK!
1262306a36Sopenharmony_ci *  To build:
1362306a36Sopenharmony_ci *	$ gcc leapcrash.c -o leapcrash -lrt
1462306a36Sopenharmony_ci */
1562306a36Sopenharmony_ci
1662306a36Sopenharmony_ci
1762306a36Sopenharmony_ci
1862306a36Sopenharmony_ci#include <stdio.h>
1962306a36Sopenharmony_ci#include <stdlib.h>
2062306a36Sopenharmony_ci#include <time.h>
2162306a36Sopenharmony_ci#include <sys/time.h>
2262306a36Sopenharmony_ci#include <sys/timex.h>
2362306a36Sopenharmony_ci#include <string.h>
2462306a36Sopenharmony_ci#include <signal.h>
2562306a36Sopenharmony_ci#include "../kselftest.h"
2662306a36Sopenharmony_ci
2762306a36Sopenharmony_ci/* clear NTP time_status & time_state */
2862306a36Sopenharmony_ciint clear_time_state(void)
2962306a36Sopenharmony_ci{
3062306a36Sopenharmony_ci	struct timex tx;
3162306a36Sopenharmony_ci	int ret;
3262306a36Sopenharmony_ci
3362306a36Sopenharmony_ci	/*
3462306a36Sopenharmony_ci	 * We have to call adjtime twice here, as kernels
3562306a36Sopenharmony_ci	 * prior to 6b1859dba01c7 (included in 3.5 and
3662306a36Sopenharmony_ci	 * -stable), had an issue with the state machine
3762306a36Sopenharmony_ci	 * and wouldn't clear the STA_INS/DEL flag directly.
3862306a36Sopenharmony_ci	 */
3962306a36Sopenharmony_ci	tx.modes = ADJ_STATUS;
4062306a36Sopenharmony_ci	tx.status = STA_PLL;
4162306a36Sopenharmony_ci	ret = adjtimex(&tx);
4262306a36Sopenharmony_ci
4362306a36Sopenharmony_ci	tx.modes = ADJ_STATUS;
4462306a36Sopenharmony_ci	tx.status = 0;
4562306a36Sopenharmony_ci	ret = adjtimex(&tx);
4662306a36Sopenharmony_ci
4762306a36Sopenharmony_ci	return ret;
4862306a36Sopenharmony_ci}
4962306a36Sopenharmony_ci
5062306a36Sopenharmony_ci/* Make sure we cleanup on ctrl-c */
5162306a36Sopenharmony_civoid handler(int unused)
5262306a36Sopenharmony_ci{
5362306a36Sopenharmony_ci	clear_time_state();
5462306a36Sopenharmony_ci	exit(0);
5562306a36Sopenharmony_ci}
5662306a36Sopenharmony_ci
5762306a36Sopenharmony_ci
5862306a36Sopenharmony_ciint main(void)
5962306a36Sopenharmony_ci{
6062306a36Sopenharmony_ci	struct timex tx;
6162306a36Sopenharmony_ci	struct timespec ts;
6262306a36Sopenharmony_ci	time_t next_leap;
6362306a36Sopenharmony_ci	int count = 0;
6462306a36Sopenharmony_ci
6562306a36Sopenharmony_ci	setbuf(stdout, NULL);
6662306a36Sopenharmony_ci
6762306a36Sopenharmony_ci	signal(SIGINT, handler);
6862306a36Sopenharmony_ci	signal(SIGKILL, handler);
6962306a36Sopenharmony_ci	printf("This runs for a few minutes. Press ctrl-c to stop\n");
7062306a36Sopenharmony_ci
7162306a36Sopenharmony_ci	clear_time_state();
7262306a36Sopenharmony_ci
7362306a36Sopenharmony_ci
7462306a36Sopenharmony_ci	/* Get the current time */
7562306a36Sopenharmony_ci	clock_gettime(CLOCK_REALTIME, &ts);
7662306a36Sopenharmony_ci
7762306a36Sopenharmony_ci	/* Calculate the next possible leap second 23:59:60 GMT */
7862306a36Sopenharmony_ci	next_leap = ts.tv_sec;
7962306a36Sopenharmony_ci	next_leap += 86400 - (next_leap % 86400);
8062306a36Sopenharmony_ci
8162306a36Sopenharmony_ci	for (count = 0; count < 20; count++) {
8262306a36Sopenharmony_ci		struct timeval tv;
8362306a36Sopenharmony_ci
8462306a36Sopenharmony_ci
8562306a36Sopenharmony_ci		/* set the time to 2 seconds before the leap */
8662306a36Sopenharmony_ci		tv.tv_sec = next_leap - 2;
8762306a36Sopenharmony_ci		tv.tv_usec = 0;
8862306a36Sopenharmony_ci		if (settimeofday(&tv, NULL)) {
8962306a36Sopenharmony_ci			printf("Error: You're likely not running with proper (ie: root) permissions\n");
9062306a36Sopenharmony_ci			return ksft_exit_fail();
9162306a36Sopenharmony_ci		}
9262306a36Sopenharmony_ci		tx.modes = 0;
9362306a36Sopenharmony_ci		adjtimex(&tx);
9462306a36Sopenharmony_ci
9562306a36Sopenharmony_ci		/* hammer on adjtime w/ STA_INS */
9662306a36Sopenharmony_ci		while (tx.time.tv_sec < next_leap + 1) {
9762306a36Sopenharmony_ci			/* Set the leap second insert flag */
9862306a36Sopenharmony_ci			tx.modes = ADJ_STATUS;
9962306a36Sopenharmony_ci			tx.status = STA_INS;
10062306a36Sopenharmony_ci			adjtimex(&tx);
10162306a36Sopenharmony_ci		}
10262306a36Sopenharmony_ci		clear_time_state();
10362306a36Sopenharmony_ci		printf(".");
10462306a36Sopenharmony_ci		fflush(stdout);
10562306a36Sopenharmony_ci	}
10662306a36Sopenharmony_ci	printf("[OK]\n");
10762306a36Sopenharmony_ci	return ksft_exit_pass();
10862306a36Sopenharmony_ci}
109