summaryrefslogtreecommitdiffstats
path: root/kernel/itimer.c
blob: 4d5fa0f671161cbac6dbd93b59d74cd1ebc4a7f5 (plain)
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
/*
 * linux/kernel/itimer.c
 *
 * Copyright (C) 1992 Darren Senn
 */

/* These are all the functions necessary to implement itimers */

#include <linux/signal.h>
#include <linux/sched.h>
#include <linux/string.h>
#include <linux/errno.h>
#include <linux/time.h>

#include <asm/segment.h>

static unsigned long tvtojiffies(struct timeval *value)
{
	return((unsigned long )value->tv_sec * HZ +
		(unsigned long )(value->tv_usec + (1000000 / HZ - 1)) /
		(1000000 / HZ));
}

static void jiffiestotv(unsigned long jiffies, struct timeval *value)
{
	value->tv_usec = (jiffies % HZ) * (1000000 / HZ);
	value->tv_sec = jiffies / HZ;
	return;
}

int _getitimer(int which, struct itimerval *value)
{
	register unsigned long val, interval;

	switch (which) {
	case ITIMER_REAL:
		val = current->it_real_value;
		interval = current->it_real_incr;
		break;
	case ITIMER_VIRTUAL:
		val = current->it_virt_value;
		interval = current->it_virt_incr;
		break;
	case ITIMER_PROF:
		val = current->it_prof_value;
		interval = current->it_prof_incr;
		break;
	default:
		return(-EINVAL);
	}
	jiffiestotv(val, &value->it_value);
	jiffiestotv(interval, &value->it_interval);
	return(0);
}

asmlinkage int sys_getitimer(int which, struct itimerval *value)
{
	int error;
	struct itimerval get_buffer;

	if (!value)
		return -EFAULT;
	error = _getitimer(which, &get_buffer);
	if (error)
		return error;
	error = verify_area(VERIFY_WRITE, value, sizeof(struct itimerval));
	if (error)
		return error;
	memcpy_tofs(value, &get_buffer, sizeof(get_buffer));
	return 0;
}

int _setitimer(int which, struct itimerval *value, struct itimerval *ovalue)
{
	register unsigned long i, j;
	int k;

	i = tvtojiffies(&value->it_interval);
	j = tvtojiffies(&value->it_value);
	if (ovalue && (k = _getitimer(which, ovalue)) < 0)
		return k;
	switch (which) {
		case ITIMER_REAL:
			if (j) {
				j += 1+itimer_ticks;
				if (j < itimer_next)
					itimer_next = j;
			}
			current->it_real_value = j;
			current->it_real_incr = i;
			break;
		case ITIMER_VIRTUAL:
			if (j)
				j++;
			current->it_virt_value = j;
			current->it_virt_incr = i;
			break;
		case ITIMER_PROF:
			if (j)
				j++;
			current->it_prof_value = j;
			current->it_prof_incr = i;
			break;
		default:
			return -EINVAL;
	}
	return 0;
}

asmlinkage int sys_setitimer(int which, struct itimerval *value, struct itimerval *ovalue)
{
	int error;
	struct itimerval set_buffer, get_buffer;

	if (value) {
		error = verify_area(VERIFY_READ, value, sizeof(*value));
		if (error)
			return error;
		memcpy_fromfs(&set_buffer, value, sizeof(set_buffer));
	} else
		memset((char *) &set_buffer, 0, sizeof(set_buffer));

	if (ovalue) {
		error = verify_area(VERIFY_WRITE, ovalue, sizeof(struct itimerval));
		if (error)
			return error;
	}

	error = _setitimer(which, &set_buffer, ovalue ? &get_buffer : 0);
	if (error || !ovalue)
		return error;

	memcpy_tofs(ovalue, &get_buffer, sizeof(get_buffer));
	return error;
}