Skip to content

Commit 24769a5

Browse files
committed
sys/interrupts: reschedule thread when exiting interrupt
Instead of rescheduling a thread only during the timer interrupt, always try to reschedule a thread when exiting an interrupt. This time is the best time to do this since an expensive context switch to kernel mode has been performed already. The user can afford to wait for a bit more. This commit removes the scheduling logic from the timer interrupt handler, as well as the running field in struct thread, and relies on the timer's frequency to define the maximum timeslice given to each processes. While at it remove HZ, and rename CLOCK_PER_SECOND to TICKS_PER_SECOND since those are timer ticks and not clock cycles.
1 parent f833abc commit 24769a5

8 files changed

Lines changed: 41 additions & 37 deletions

File tree

include/kernel/process.h

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -145,11 +145,6 @@ typedef struct thread {
145145

146146
/** Information relative to the current state of the thread */
147147
union {
148-
/** For running threads only */
149-
struct {
150-
/** End of the currently running thread's timeslice */
151-
clock_t preempt;
152-
} running;
153148
/** For sleeping threads only */
154149
struct {
155150
clock_t wakeup; /*!< Time when it should wakeup (in ticks) */
@@ -160,7 +155,8 @@ typedef struct thread {
160155

161156
/** @enum thread_flags */
162157
typedef enum thread_flags {
163-
THREAD_KERNEL = BIT(0), ///< This thread runs in kernel mode
158+
THREAD_KERNEL = BIT(0), ///< This is a kernel thread
159+
THREAD_RESCHED = BIT(1), ///< Reschedule when exiting interrupt
164160
} process_flags_t;
165161

166162
/***/

include/kernel/time.h

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,14 @@
88
#ifndef KERNEL_TIME_H
99
#define KERNEL_TIME_H
1010

11-
/* The number of clock ticks in a second (1KHz). */
12-
#define CLOCK_PER_SECOND 1000
11+
/*
12+
* The number of clock ticks in a second (100Hz, 1 tick = 100ms).
13+
*
14+
* This is also used as the maximum time a thread is allowed to run for
15+
* before being rescheduled (thread will be forcefully rescheduled
16+
* when a timer interrupt eventually occurs).
17+
*/
18+
#define TICKS_PER_SECOND 100
1319

1420
/** @brief Used for conversions from seconds to another time unit @{ */
1521
#define SEC(_x) (_x)

include/kernel/timer.h

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -20,18 +20,15 @@
2020
#include <kernel/time.h>
2121
#include <kernel/types.h>
2222

23-
/** The frequency used for the timer (in Hz) */
24-
#define HZ CLOCK_PER_SECOND
25-
2623
/** @brief Compute the number of ticks in a given time frame @{ */
27-
#define SEC_TO_TICKS(_time) SEC((_time) * HZ)
24+
#define SEC_TO_TICKS(_time) SEC((_time) * TICKS_PER_SECOND)
2825
#define MS_TO_TICKS(_time) MS_TO_SEC(SEC_TO_TICKS(_time))
2926
#define US_TO_TICKS(_time) US_TO_SEC(SEC_TO_TICKS(_time))
3027
#define NS_TO_TICKS(_time) NS_TO_SEC(SEC_TO_TICKS(_time))
3128
/** @} */
3229

3330
/** @brief Convert a number of ticks into a regular time unit @{ */
34-
#define TICKS_TO_SEC(_ticks) SEC((_ticks) / HZ)
31+
#define TICKS_TO_SEC(_ticks) SEC((_ticks) / TICKS_PER_SECOND)
3532
#define TICKS_TO_MS(_ticks) MS(TICKS_TO_SEC(_ticks))
3633
#define TICKS_TO_US(_ticks) US(TICKS_TO_SEC(_ticks))
3734
#define TICKS_TO_NS(_ticks) NS(TICKS_TO_SEC(_ticks))

kernel/arch/i686/mmu.c

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -783,6 +783,12 @@ static INTERRUPT_HANDLER_FUNCTION(page_fault)
783783

784784
UNUSED(data);
785785

786+
/*
787+
* Starting a new process can generate a lot of page faults. Avoid
788+
* rescheduling for page faults to speed up process startup.
789+
*/
790+
current->flags &= ~THREAD_RESCHED;
791+
786792
if (!error.present || is_cow) {
787793
as = IS_KERNEL_ADDRESS(faulty_address) ? &kernel_address_space
788794
: current->process->as;

kernel/arch/i686/timer.c

Lines changed: 1 addition & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -15,11 +15,11 @@
1515

1616
#define LOG_DOMAIN "timer"
1717

18-
#include <kernel/timer.h>
1918
#include <kernel/error.h>
2019
#include <kernel/interrupts.h>
2120
#include <kernel/logger.h>
2221
#include <kernel/sched.h>
22+
#include <kernel/timer.h>
2323

2424
#include <kernel/arch/i686/devices/pic.h>
2525
#include <kernel/arch/i686/devices/pit.h>
@@ -55,25 +55,11 @@ static INTERRUPT_HANDLER_FUNCTION(irq_timer)
5555

5656
pic_eoi(IRQ_TIMER);
5757

58-
if (!scheduler_initialized)
59-
return INTERRUPT_HANDLED;
60-
61-
// TODO: Use a separate and more modern timer for scheduler (LAPIC, HPET)
62-
//
63-
// We could be setting the next timer interrupt dynamically to match the
64-
// next due task, but:
65-
// * this is our main timekeeping source, and it would make it less accurate
66-
// * the PIT is too slow to re-program dynamically like this
67-
68-
// TODO: Don't mingle IRQ and scheduling
69-
7058
/*
7159
* Unblock waiting threads whose deadline has been reached, and preempt
7260
* the current thread if we reached the end of its timeslice.
7361
*/
7462
sched_unblock_waiting_before(timer_ticks_counter);
75-
if (current->running.preempt <= timer_ticks_counter)
76-
schedule();
7763

7864
return INTERRUPT_HANDLED;
7965
}

kernel/main.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -190,7 +190,7 @@ void kernel_main(struct multiboot_info *mbt, unsigned int magic)
190190
* IRQs and controllers are setup, we can safely enable interrupts.
191191
*/
192192
interrupts_enable();
193-
timer_start(HZ);
193+
timer_start(TICKS_PER_SECOND);
194194

195195
mbt_info = kmalloc(mbt_tmp.mbt.total_size, KMALLOC_KERNEL);
196196
memcpy(mbt_info, mbt_tmp.raw, mbt_tmp.mbt.total_size);

kernel/sys/interrupts.c

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
#include <kernel/interrupts.h>
66
#include <kernel/kmalloc.h>
77
#include <kernel/logger.h>
8+
#include <kernel/sched.h>
89

910
#include <libalgo/linked_list.h>
1011

@@ -42,7 +43,22 @@ interrupt_chip_interrupt_handle(const struct interrupt_chip *chip,
4243
*/
4344
error_t interrupt_handle(unsigned int nr)
4445
{
45-
return interrupt_chip_interrupt_handle(&interrupt_root_chip, nr);
46+
error_t err;
47+
48+
current->flags |= THREAD_RESCHED;
49+
50+
err = interrupt_chip_interrupt_handle(&interrupt_root_chip, nr);
51+
52+
/*
53+
* Try to reschedule the current thread. This is the best time to do so.
54+
*
55+
* TODO: Make sure that a user thread does not return to userland with
56+
* preemption disabled.
57+
*/
58+
if (current->flags & THREAD_RESCHED)
59+
schedule();
60+
61+
return err;
4662
}
4763

4864
/*

kernel/sys/sched.c

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,6 @@ bool scheduler_initialized = false;
1414

1515
static DECLARE_LLIST(sleeping_tasks);
1616

17-
/** The maximum timeslice given to a thread by the scheduler */
18-
#define SCHED_TIMESLICE MS_TO_TICKS(2ULL) // 2MS
19-
2017
typedef struct scheduler {
2118

2219
/** The runqueue
@@ -50,6 +47,9 @@ static void schedule_locked(bool preempt, bool reschedule)
5047
{
5148
node_t *next_node;
5249

50+
if (unlikely(!scheduler_initialized))
51+
return;
52+
5353
if (atomic_read(&scheduler.sync.preemption_level) > 1 && !preempt)
5454
return;
5555

@@ -79,11 +79,8 @@ static void schedule_locked(bool preempt, bool reschedule)
7979
}
8080
}
8181

82-
next->running.preempt = timer_gettick() + SCHED_TIMESLICE;
83-
84-
if (!thread_switch(next)) {
82+
if (!thread_switch(next))
8583
schedule_locked(preempt, false);
86-
}
8784
}
8885

8986
void schedule(void)

0 commit comments

Comments
 (0)