aboutsummaryrefslogtreecommitdiffstats
path: root/arch/um/kernel/time.c
blob: 11f518a7e1562538e1cf75e5b87af1c609d50009 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
/* 
 * Copyright (C) 2000, 2001, 2002 Jeff Dike (jdike@karaya.com)
 * Licensed under the GPL
 */

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <time.h>
#include <sys/time.h>
#include <signal.h>
#include <errno.h>
#include "user_util.h"
#include "kern_util.h"
#include "user.h"
#include "process.h"
#include "time_user.h"
#include "kern_constants.h"
#include "os.h"

/* XXX This really needs to be declared and initialized in a kernel file since
 * it's in <linux/time.h>
 */
extern struct timespec wall_to_monotonic;

extern struct timeval xtime;

struct timeval local_offset = { 0, 0 };

void timer(void)
{
	gettimeofday(&xtime, NULL);
	timeradd(&xtime, &local_offset, &xtime);
}

static void set_interval(int timer_type)
{
	int usec = 1000000/hz();
	struct itimerval interval = ((struct itimerval) { { 0, usec },
							  { 0, usec } });

	if(setitimer(timer_type, &interval, NULL) == -1)
		panic("setitimer failed - errno = %d\n", errno);
}

void enable_timer(void)
{
	set_interval(ITIMER_VIRTUAL);
}

void prepare_timer(void * ptr)
{
	int usec = 1000000/hz();
	*(struct itimerval *)ptr = ((struct itimerval) { { 0, usec },
							 { 0, usec }});
}

void disable_timer(void)
{
	struct itimerval disable = ((struct itimerval) { { 0, 0 }, { 0, 0 }});
	if((setitimer(ITIMER_VIRTUAL, &disable, NULL) < 0) ||
	   (setitimer(ITIMER_REAL, &disable, NULL) < 0))
		printk("disnable_timer - setitimer failed, errno = %d\n",
		       errno);
	/* If there are signals already queued, after unblocking ignore them */
	set_handler(SIGALRM, SIG_IGN, 0, -1);
	set_handler(SIGVTALRM, SIG_IGN, 0, -1);
}

void switch_timers(int to_real)
{
	struct itimerval disable = ((struct itimerval) { { 0, 0 }, { 0, 0 }});
	struct itimerval enable = ((struct itimerval) { { 0, 1000000/hz() },
							{ 0, 1000000/hz() }});
	int old, new;

	if(to_real){
		old = ITIMER_VIRTUAL;
		new = ITIMER_REAL;
	}
	else {
		old = ITIMER_REAL;
		new = ITIMER_VIRTUAL;
	}

	if((setitimer(old, &disable, NULL) < 0) ||
	   (setitimer(new, &enable, NULL)))
		printk("switch_timers - setitimer failed, errno = %d\n",
		       errno);
}

void uml_idle_timer(void)
{
	if(signal(SIGVTALRM, SIG_IGN) == SIG_ERR)
		panic("Couldn't unset SIGVTALRM handler");
	
	set_handler(SIGALRM, (__sighandler_t) alarm_handler, 
		    SA_RESTART, SIGUSR1, SIGIO, SIGWINCH, SIGVTALRM, -1);
	set_interval(ITIMER_REAL);
}

extern int do_posix_clock_monotonic_gettime(struct timespec *tp);

void time_init(void)
{
	struct timespec now;

	if(signal(SIGVTALRM, boot_timer_handler) == SIG_ERR)
		panic("Couldn't set SIGVTALRM handler");
	set_interval(ITIMER_VIRTUAL);

	do_posix_clock_monotonic_gettime(&now);
	wall_to_monotonic.tv_sec = -now.tv_sec;
	wall_to_monotonic.tv_nsec = -now.tv_nsec;
}

/* Declared in linux/time.h, which can't be included here */
extern void clock_was_set(void);

void do_gettimeofday(struct timeval *tv)
{
	unsigned long flags;

	flags = time_lock();
	gettimeofday(tv, NULL);
	timeradd(tv, &local_offset, tv);
	time_unlock(flags);
	clock_was_set();
}

int do_settimeofday(struct timespec *tv)
{
	struct timeval now;
	unsigned long flags;
	struct timeval tv_in;

	if ((unsigned long) tv->tv_nsec >= UM_NSEC_PER_SEC)
		return -EINVAL;

	tv_in.tv_sec = tv->tv_sec;
	tv_in.tv_usec = tv->tv_nsec / 1000;

	flags = time_lock();
	gettimeofday(&now, NULL);
	timersub(&tv_in, &now, &local_offset);
	time_unlock(flags);

	return(0);
}

void idle_sleep(int secs)
{
	struct timespec ts;

	ts.tv_sec = secs;
	ts.tv_nsec = 0;
	nanosleep(&ts, NULL);
}

/* XXX This partly duplicates init_irq_signals */

void user_time_init(void)
{
	set_handler(SIGVTALRM, (__sighandler_t) alarm_handler,
		    SA_ONSTACK | SA_RESTART, SIGUSR1, SIGIO, SIGWINCH,
		    SIGALRM, SIGUSR2, -1);
	set_handler(SIGALRM, (__sighandler_t) alarm_handler,
		    SA_ONSTACK | SA_RESTART, SIGUSR1, SIGIO, SIGWINCH,
		    SIGVTALRM, SIGUSR2, -1);
	set_interval(ITIMER_VIRTUAL);
}