summaryrefslogtreecommitdiff
path: root/linux/sched.c
blob: 1c7198d279bc9f534c946f2f56b65011a88ff452 (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

#include <stdio.h>
#include <string.h>
#include <sys/mman.h>
#include <linux/futex.h>

/* hack for mips: */
#define CONFIG_RCU_HAVE_FUTEX 1
#include <urcu/futex.h>

#include <linux/rcupdate.h>
#include <linux/sched.h>
#include <linux/timer.h>

__thread struct task_struct *current;

void __put_task_struct(struct task_struct *t)
{
	pthread_join(t->thread, NULL);
	free(t);
}

/* returns true if process was woken up, false if it was already running */
int wake_up_process(struct task_struct *p)
{
	int ret = p->state != TASK_RUNNING;

	p->state = TASK_RUNNING;
	futex(&p->state, FUTEX_WAKE|FUTEX_PRIVATE_FLAG,
	      INT_MAX, NULL, NULL, 0);
	return ret;
}

void schedule(void)
{
	int v;

	rcu_quiescent_state();

	while ((v = READ_ONCE(current->state)) != TASK_RUNNING)
		futex(&current->state, FUTEX_WAIT|FUTEX_PRIVATE_FLAG,
		      v, NULL, NULL, 0);
}

struct process_timer {
	struct timer_list timer;
	struct task_struct *task;
};

static void process_timeout(struct timer_list *t)
{
	struct process_timer *timeout =
		container_of(t, struct process_timer, timer);

	wake_up_process(timeout->task);
}

long schedule_timeout(long timeout)
{
	struct process_timer timer;
	unsigned long expire;

	switch (timeout)
	{
	case MAX_SCHEDULE_TIMEOUT:
		/*
		 * These two special cases are useful to be comfortable
		 * in the caller. Nothing more. We could take
		 * MAX_SCHEDULE_TIMEOUT from one of the negative value
		 * but I' d like to return a valid offset (>=0) to allow
		 * the caller to do everything it want with the retval.
		 */
		schedule();
		goto out;
	default:
		/*
		 * Another bit of PARANOID. Note that the retval will be
		 * 0 since no piece of kernel is supposed to do a check
		 * for a negative retval of schedule_timeout() (since it
		 * should never happens anyway). You just have the printk()
		 * that will tell you if something is gone wrong and where.
		 */
		if (timeout < 0) {
			fprintf(stderr, "schedule_timeout: wrong timeout "
				"value %lx\n", timeout);
			current->state = TASK_RUNNING;
			goto out;
		}
	}

	expire = timeout + jiffies;

	timer.task = current;
	timer_setup_on_stack(&timer.timer, process_timeout, 0);
	mod_timer(&timer.timer, expire);
	schedule();
	del_timer_sync(&timer.timer);

	timeout = expire - jiffies;
out:
	return timeout < 0 ? 0 : timeout;
}

__attribute__((constructor(101)))
static void sched_init(void)
{
	struct task_struct *p = malloc(sizeof(*p));

	memset(p, 0, sizeof(*p));

	p->state	= TASK_RUNNING;
	atomic_set(&p->usage, 1);
	init_completion(&p->exited);

	current = p;

	rcu_init();
	rcu_register_thread();
}

#ifndef SYS_getrandom
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/types.h>
int urandom_fd;

__attribute__((constructor(101)))
static void rand_init(void)
{
	urandom_fd = open("/dev/urandom", O_RDONLY);
	BUG_ON(urandom_fd < 0);
}
#endif