Skip to content

Commit e1f9653

Browse files
committed
fix: Proper network wakers
This refactors our timer interrupt handling into a separate module that all timer interrupts are configured through and handled by. It allows us to track why a timer interrupt was fired. We make use of this to now only conditionally wake the network task's waker when necessary. This is either because we sent some network packets, a network device driver received an interrupt, or because a timer interrupt that we configured so we can poll smoltcp in the future was handled.
1 parent 4e2eb9e commit e1f9653

File tree

14 files changed

+231
-214
lines changed

14 files changed

+231
-214
lines changed

src/arch/aarch64/kernel/interrupts.rs

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ use crate::drivers::pci::get_interrupt_handlers;
2424
use crate::drivers::{InterruptHandlerQueue, InterruptLine};
2525
use crate::kernel::serial::handle_uart_interrupt;
2626
use crate::mm::{PageAlloc, PageRangeAllocator};
27-
use crate::scheduler::{self, CoreId};
27+
use crate::scheduler::{self, CoreId, timer_interrupts};
2828
use crate::{core_id, core_scheduler, env};
2929

3030
/// The ID of the first Private Peripheral Interrupt.
@@ -93,10 +93,7 @@ pub(crate) fn install_handlers() {
9393

9494
fn timer_handler() {
9595
debug!("Handle timer interrupt");
96-
97-
// disable timer
98-
CNTP_CVAL_EL0.set(0);
99-
CNTP_CTL_EL0.write(CNTP_CTL_EL0::ENABLE::CLEAR);
96+
timer_interrupts::clear_active_and_set_next();
10097
}
10198

10299
for (key, value) in get_interrupt_handlers().into_iter() {

src/arch/riscv64/kernel/scheduler.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,10 @@ use free_list::{PageLayout, PageRange};
55
use memory_addresses::{PhysAddr, VirtAddr};
66

77
use crate::arch::riscv64::kernel::core_local::core_scheduler;
8-
use crate::arch::riscv64::kernel::processor::set_oneshot_timer;
98
use crate::arch::riscv64::mm::paging::{BasePageSize, PageSize, PageTableEntryFlags};
109
use crate::mm::{FrameAlloc, PageAlloc, PageRangeAllocator};
1110
use crate::scheduler::task::{Task, TaskFrame};
11+
use crate::scheduler::timer_interrupts;
1212
use crate::{DEFAULT_STACK_SIZE, KERNEL_STACK_SIZE};
1313

1414
#[repr(C, packed)]
@@ -360,9 +360,9 @@ unsafe extern "C" fn task_start(func: extern "C" fn(usize), arg: usize, user_sta
360360
}
361361

362362
pub fn timer_handler() {
363-
//increment_irq_counter(apic::TIMER_INTERRUPT_NUMBER.into());
363+
debug!("Handle timer interrupt");
364+
timer_interrupts::clear_active_and_set_next();
364365
core_scheduler().handle_waiting_tasks();
365-
set_oneshot_timer(None);
366366
core_scheduler().scheduler();
367367
}
368368

src/arch/x86_64/kernel/scheduler.rs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,8 @@ use crate::arch::x86_64::mm::paging::{
1616
use crate::config::*;
1717
use crate::env;
1818
use crate::mm::{FrameAlloc, PageAlloc, PageRangeAllocator};
19-
use crate::scheduler::PerCoreSchedulerExt;
2019
use crate::scheduler::task::{Task, TaskFrame};
20+
use crate::scheduler::{PerCoreSchedulerExt, timer_interrupts};
2121

2222
#[repr(C, packed)]
2323
struct State {
@@ -318,6 +318,10 @@ impl TaskFrame for Task {
318318

319319
extern "x86-interrupt" fn timer_handler(_stack_frame: interrupts::ExceptionStackFrame) {
320320
increment_irq_counter(apic::TIMER_INTERRUPT_NUMBER);
321+
322+
debug!("Handle timer interrupt");
323+
timer_interrupts::clear_active_and_set_next();
324+
321325
core_scheduler().handle_waiting_tasks();
322326
apic::eoi();
323327
core_scheduler().reschedule();

src/drivers/net/gem.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ use crate::drivers::net::{NetworkDriver, mtu};
3232
#[cfg(feature = "pci")]
3333
use crate::drivers::pci as hardware;
3434
use crate::drivers::{Driver, InterruptLine};
35+
use crate::executor::network::NETWORK_WAKER;
3536
use crate::mm::device_alloc::DeviceAlloc;
3637
use crate::{BasePageSize, PageSize};
3738

@@ -276,6 +277,9 @@ impl NetworkDriver for GEMDriver {
276277

277278
fn handle_interrupt(&mut self) {
278279
self.tx_fields.handle_interrupt();
280+
281+
trace!("Waking network waker");
282+
NETWORK_WAKER.lock().wake();
279283
}
280284
}
281285

src/drivers/net/loopback.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ use smoltcp::time::Instant;
88

99
use crate::drivers::net::NetworkDriver;
1010
use crate::drivers::{Driver, InterruptLine};
11+
use crate::executor::network::NETWORK_WAKER;
1112
use crate::mm::device_alloc::DeviceAlloc;
1213

1314
pub(crate) struct LoopbackDriver {
@@ -122,7 +123,8 @@ impl NetworkDriver for LoopbackDriver {
122123
}
123124

124125
fn handle_interrupt(&mut self) {
125-
// no-op
126+
trace!("Waking network waker");
127+
NETWORK_WAKER.lock().wake();
126128
}
127129
}
128130

src/drivers/net/rtl8139.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ use crate::drivers::Driver;
2121
use crate::drivers::error::DriverError;
2222
use crate::drivers::net::{NetworkDriver, mtu};
2323
use crate::drivers::pci::PciDevice;
24+
use crate::executor::network::NETWORK_WAKER;
2425
use crate::mm::device_alloc::DeviceAlloc;
2526

2627
/// size of the receive buffer
@@ -687,6 +688,9 @@ impl NetworkDriver for RTL8139Driver {
687688
self.regs.as_mut_ptr().isr().write(le16::from(
688689
isr_contents & (ISR_RXOVW | ISR_TER | ISR_RER | ISR_TOK | ISR_ROK),
689690
));
691+
692+
trace!("Waking network waker");
693+
NETWORK_WAKER.lock().wake();
690694
}
691695
}
692696

src/drivers/net/virtio/mod.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ use crate::drivers::virtio::virtqueue::{
4242
AvailBufferToken, BufferElem, BufferType, UsedBufferToken, VirtQueue, Virtq,
4343
};
4444
use crate::drivers::{Driver, InterruptLine};
45+
use crate::executor::network::NETWORK_WAKER;
4546
use crate::mm::device_alloc::DeviceAlloc;
4647

4748
/// A wrapper struct for the raw configuration structure.
@@ -417,6 +418,9 @@ impl NetworkDriver for VirtioNetDriver<Init> {
417418
}
418419

419420
self.isr_stat.acknowledge();
421+
422+
trace!("Waking network waker");
423+
NETWORK_WAKER.lock().wake();
420424
}
421425
}
422426

src/executor/mod.rs

Lines changed: 8 additions & 87 deletions
Original file line numberDiff line numberDiff line change
@@ -18,15 +18,11 @@ use core::time::Duration;
1818

1919
use crossbeam_utils::Backoff;
2020
use hermit_sync::without_interrupts;
21-
#[cfg(feature = "net")]
22-
use smoltcp::time::Instant;
2321

2422
use crate::arch::core_local;
2523
use crate::errno::Errno;
2624
use crate::executor::task::AsyncTask;
2725
use crate::io;
28-
#[cfg(feature = "net")]
29-
use crate::scheduler::PerCoreSchedulerExt;
3026
use crate::synch::futex::*;
3127

3228
/// WakerRegistration is derived from smoltcp's
@@ -155,101 +151,26 @@ where
155151

156152
let now = crate::arch::kernel::systemtime::now_micros();
157153
if let Poll::Ready(t) = result {
158-
// allow network interrupts
159-
#[cfg(feature = "net")]
160-
{
161-
if let Some(mut guard) = crate::executor::network::NIC.try_lock() {
162-
let delay = if let Ok(nic) = guard.as_nic_mut() {
163-
nic.set_polling_mode(false);
164-
165-
nic.poll_delay(Instant::from_micros_const(now.try_into().unwrap()))
166-
.map(|d| d.total_micros())
167-
} else {
168-
None
169-
};
170-
core_local::core_scheduler().add_network_timer(
171-
delay.map(|d| crate::arch::processor::get_timer_ticks() + d),
172-
);
173-
}
174-
}
175-
176154
return t;
177155
}
178156

179157
if let Some(duration) = timeout
180158
&& Duration::from_micros(now - start) >= duration
181159
{
182-
// allow network interrupts
183-
#[cfg(feature = "net")]
184-
{
185-
if let Some(mut guard) = crate::executor::network::NIC.try_lock() {
186-
let delay = if let Ok(nic) = guard.as_nic_mut() {
187-
nic.set_polling_mode(false);
188-
189-
nic.poll_delay(Instant::from_micros_const(now.try_into().unwrap()))
190-
.map(|d| d.total_micros())
191-
} else {
192-
None
193-
};
194-
core_local::core_scheduler().add_network_timer(
195-
delay.map(|d| crate::arch::processor::get_timer_ticks() + d),
196-
);
197-
}
198-
}
199-
200160
return Err(Errno::Time);
201161
}
202162

203-
#[cfg(feature = "net")]
204163
if backoff.is_completed() {
205-
let delay = if let Some(mut guard) = crate::executor::network::NIC.try_lock() {
206-
if let Ok(nic) = guard.as_nic_mut() {
207-
nic.set_polling_mode(false);
208-
209-
nic.poll_delay(Instant::from_micros_const(now.try_into().unwrap()))
210-
.map(|d| d.total_micros())
211-
} else {
212-
None
213-
}
214-
} else {
215-
None
216-
};
217-
218-
if delay.unwrap_or(10_000_000) > 10_000 {
219-
core_local::core_scheduler().add_network_timer(
220-
delay.map(|d| crate::arch::processor::get_timer_ticks() + d),
221-
);
222-
let wakeup_time =
223-
timeout.map(|duration| start + u64::try_from(duration.as_micros()).unwrap());
224-
225-
// switch to another task
226-
task_notify.wait(wakeup_time);
227-
228-
// restore default values
229-
if let Ok(nic) = crate::executor::network::NIC.lock().as_nic_mut() {
230-
nic.set_polling_mode(true);
231-
}
232-
233-
backoff.reset();
234-
}
235-
} else {
236-
backoff.snooze();
237-
}
238-
239-
#[cfg(not(feature = "net"))]
240-
{
241-
if backoff.is_completed() {
242-
let wakeup_time =
243-
timeout.map(|duration| start + u64::try_from(duration.as_micros()).unwrap());
164+
let wakeup_time =
165+
timeout.map(|duration| start + u64::try_from(duration.as_micros()).unwrap());
244166

245-
// switch to another task
246-
task_notify.wait(wakeup_time);
167+
// switch to another task
168+
task_notify.wait(wakeup_time);
247169

248-
// restore default values
249-
backoff.reset();
250-
} else {
251-
backoff.snooze();
252-
}
170+
// restore default values
171+
backoff.reset();
172+
} else {
173+
backoff.snooze();
253174
}
254175
}
255176
}

src/executor/network.rs

Lines changed: 25 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -26,10 +26,10 @@ use crate::arch;
2626
use crate::drivers::net::{NetworkDevice, NetworkDriver};
2727
#[cfg(feature = "dns")]
2828
use crate::errno::Errno;
29-
use crate::executor::spawn;
29+
use crate::executor::{WakerRegistration, spawn};
3030
#[cfg(feature = "dns")]
3131
use crate::io;
32-
use crate::scheduler::PerCoreSchedulerExt;
32+
use crate::scheduler::timer_interrupts::{Source, create_timer};
3333

3434
pub(crate) enum NetworkState<'a> {
3535
Missing,
@@ -189,14 +189,33 @@ async fn dhcpv4_run() {
189189
.await;
190190
}
191191

192+
pub(crate) static NETWORK_WAKER: InterruptTicketMutex<WakerRegistration> =
193+
InterruptTicketMutex::new(WakerRegistration::new());
194+
192195
async fn network_run() {
193196
future::poll_fn(|cx| {
194197
if let Some(mut guard) = NIC.try_lock() {
195198
match &mut *guard {
196199
NetworkState::Initialized(nic) => {
197-
nic.poll_common(now());
198-
// FIXME: only wake when progress can be made
199-
cx.waker().wake_by_ref();
200+
let now = now();
201+
202+
match nic.poll_common(now) {
203+
PollResult::SocketStateChanged => {
204+
// Progress was made
205+
cx.waker().wake_by_ref();
206+
}
207+
PollResult::None => {
208+
// Very likely no progress can be made, so set up a timer interrupt to wake the waker
209+
NETWORK_WAKER.lock().register(cx.waker());
210+
nic.set_polling_mode(false);
211+
if let Some(wakeup_time) = nic.poll_delay(now).map(|d| d.total_micros())
212+
{
213+
create_timer(Source::Network, wakeup_time);
214+
trace!("Configured an interrupt for {wakeup_time:?}");
215+
}
216+
}
217+
}
218+
200219
Poll::Pending
201220
}
202221
_ => Poll::Ready(()),
@@ -254,14 +273,7 @@ pub(crate) fn init() {
254273

255274
*guard = NetworkInterface::create();
256275

257-
if let NetworkState::Initialized(nic) = &mut *guard {
258-
let time = now();
259-
nic.poll_common(time);
260-
let wakeup_time = nic
261-
.poll_delay(time)
262-
.map(|d| crate::arch::processor::get_timer_ticks() + d.total_micros());
263-
crate::core_scheduler().add_network_timer(wakeup_time);
264-
276+
if let NetworkState::Initialized(_) = &mut *guard {
265277
spawn(network_run());
266278
#[cfg(feature = "dhcpv4")]
267279
spawn(dhcpv4_run());

src/fd/socket/tcp.rs

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ use smoltcp::wire::{IpEndpoint, Ipv4Address, Ipv6Address};
1313

1414
use crate::errno::Errno;
1515
use crate::executor::block_on;
16-
use crate::executor::network::{Handle, NIC};
16+
use crate::executor::network::{Handle, NETWORK_WAKER, NIC};
1717
use crate::fd::{self, Endpoint, ListenEndpoint, ObjectInterface, PollEvent, SocketOption};
1818
use crate::syscalls::socket::Af;
1919
use crate::{DEFAULT_KEEP_ALIVE_INTERVAL, io};
@@ -64,14 +64,18 @@ impl Socket {
6464
fn with<R>(&self, f: impl FnOnce(&mut tcp::Socket<'_>) -> R) -> R {
6565
let mut guard = NIC.lock();
6666
let nic = guard.as_nic_mut().unwrap();
67-
f(nic.get_mut_socket::<tcp::Socket<'_>>(*self.handle.first().unwrap()))
67+
let r = f(nic.get_mut_socket::<tcp::Socket<'_>>(*self.handle.first().unwrap()));
68+
NETWORK_WAKER.lock().wake();
69+
r
6870
}
6971

7072
fn with_context<R>(&self, f: impl FnOnce(&mut tcp::Socket<'_>, &mut iface::Context) -> R) -> R {
7173
let mut guard = NIC.lock();
7274
let nic = guard.as_nic_mut().unwrap();
7375
let (s, cx) = nic.get_socket_and_context::<tcp::Socket<'_>>(*self.handle.first().unwrap());
74-
f(s, cx)
76+
let r = f(s, cx);
77+
NETWORK_WAKER.lock().wake();
78+
r
7579
}
7680

7781
async fn close(&self) -> io::Result<()> {

0 commit comments

Comments
 (0)