Rework the Linux-compat timer code
Our alarm code is mostly sufficient to do the things Linux's timers do.
The old code was unable to cancel timers and was just a huge hack. This
is just a moderate hack.
Signed-off-by: Barret Rhoden <brho@cs.berkeley.edu>
diff --git a/kern/include/linux/compat_todo.h b/kern/include/linux/compat_todo.h
index 32836ca..a1eed08 100644
--- a/kern/include/linux/compat_todo.h
+++ b/kern/include/linux/compat_todo.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 1991-2015, the Linux Kernel authors
+ * Copyright (C) 1991-2019, the Linux Kernel authors
*
* This source code is licensed under the GNU General Public License,
* Version 2. See the file COPYING for more details.
@@ -300,7 +300,7 @@
return timeout;
}
-#define jiffies tsc2msec(read_tsc())
+#define jiffies (unsigned long)tsc2msec(read_tsc())
static inline unsigned long round_jiffies(unsigned long j)
{
@@ -317,73 +317,88 @@
return m;
}
-#define time_after_eq(a, b) ((b) <= (a))
+/* These time macros are from Linux 5.2 */
+#define time_after(a,b) \
+ (typecheck(unsigned long, a) && \
+ typecheck(unsigned long, b) && \
+ ((long)((b) - (a)) < 0))
+#define time_before(a,b) time_after(b,a)
+
+#define time_after_eq(a,b) \
+ (typecheck(unsigned long, a) && \
+ typecheck(unsigned long, b) && \
+ ((long)((a) - (b)) >= 0))
+#define time_before_eq(a,b) time_after_eq(b,a)
+
+#define time_is_before_jiffies(a) time_after(jiffies, a)
+#define time_is_after_jiffies(a) time_before(jiffies, a)
+#define time_is_before_eq_jiffies(a) time_after_eq(jiffies, a)
+#define time_is_after_eq_jiffies(a) time_before_eq(jiffies, a)
+
struct timer_list {
- spinlock_t lock;
- bool scheduled;
- unsigned long expires;
- void (*function)(unsigned long);
+ struct alarm_waiter alarm;
+ unsigned long expires; // jiffies
+ union {
+ void (*func_a)(unsigned long);
+ void (*func)(struct timer_list*);
+ };
+ bool has_arg;
unsigned long data;
};
-static inline void init_timer(struct timer_list *timer)
+/* Intermediate layer that handles setup_timer-style functions with arguments,
+ * until we can change the older Linux drivers. */
+static inline void timer_fired(struct alarm_waiter *waiter)
{
- spinlock_init_irqsave(&timer->lock);
- timer->scheduled = false;
- timer->expires = -1;
- timer->function = 0;
- timer->data = 0;
+ struct timer_list *timer = container_of(waiter, struct timer_list,
+ alarm);
+ if (timer->has_arg)
+ timer->func_a(timer->data);
+ else
+ timer->func(timer);
}
static inline void setup_timer(struct timer_list *timer,
void (*func)(unsigned long), unsigned long data)
{
- init_timer(timer);
- timer->function = func;
+ init_awaiter(&timer->alarm, timer_fired);
+
+ timer->func_a = func;
+ timer->has_arg = true;
timer->data = data;
}
-static void __timer_wrapper(uint32_t srcid, long a0, long a1, long a2)
+static inline void timer_setup(struct timer_list *timer,
+ void (*func)(struct timer_list *), int flags)
{
- struct timer_list *timer = (struct timer_list *)a0;
- unsigned long expires, now_j;
+ init_awaiter(&timer->alarm, timer_fired);
- spin_lock_irqsave(&timer->lock);
- expires = timer->expires;
- timer->scheduled = false;
- spin_unlock_irqsave(&timer->lock);
-
- now_j = jiffies;
- if (expires > now_j)
- kthread_usleep((expires - now_j) * 1000);
- timer->function(timer->data);
+ timer->func = func;
+ timer->has_arg = false;
}
static inline void add_timer(struct timer_list *timer)
{
- timer->scheduled = true;
- send_kernel_message(core_id(), __timer_wrapper, (long)timer, 0, 0,
- KMSG_ROUTINE);
+ struct timer_chain *tchain = &per_cpu_info[0].tchain;
+ struct alarm_waiter *waiter = &timer->alarm;
+
+ set_awaiter_abs(waiter, msec2tsc(timer->expires));
+ set_alarm(tchain, waiter);
}
static inline void mod_timer(struct timer_list *timer, unsigned long expires)
{
- spin_lock_irqsave(&timer->lock);
+ struct timer_chain *tchain = &per_cpu_info[0].tchain;
+ struct alarm_waiter *waiter = &timer->alarm;
+
timer->expires = expires;
- if (timer->scheduled) {
- spin_unlock_irqsave(&timer->lock);
- return;
- }
- timer->scheduled = true;
- spin_unlock_irqsave(&timer->lock);
- send_kernel_message(core_id(), __timer_wrapper, (long)timer, 0, 0,
- KMSG_ROUTINE);
+ reset_alarm_abs(tchain, waiter, msec2tsc(expires));
}
static inline void del_timer_sync(struct timer_list *timer)
{
- panic("del_timer_sync unimplemented");
+ unset_alarm(&per_cpu_info[0].tchain, &timer->alarm);
}
static inline void setup_timer_on_stack(struct timer_list *timer,
@@ -397,14 +412,14 @@
{
}
-/* TODO: This is nasty (all of the timer stuff). We don't know if a timer
- * actually finished or not. Someone could mod_timer repeatedly and have the
- * same handler running concurrently. */
static inline bool timer_pending(struct timer_list *timer)
{
- return timer->expires >= jiffies;
+ return !alarm_expired(&timer->alarm);
}
+#define from_timer(var, callback_timer, timer_fieldname) \
+ container_of(callback_timer, typeof(*var), timer_fieldname)
+
struct cpu_rmap {
};