Browse Source

Kernel: Snooze the NetworkTask until there are incoming packets to process.

This is accomplished using a new Alarm class and a BlockedSnoozing state.
Basically, you call Process::snooze_until(some_alarm) and then the scheduler
won't wake up the process until some_alarm.is_ringing() returns true.
Andreas Kling 6 years ago
parent
commit
bc1da7f1fd
6 changed files with 51 additions and 6 deletions
  1. 6 0
      Kernel/NetworkAdapter.cpp
  2. 17 0
      Kernel/NetworkAdapter.h
  3. 6 6
      Kernel/NetworkTask.cpp
  4. 8 0
      Kernel/Process.cpp
  5. 5 0
      Kernel/Process.h
  6. 9 0
      Kernel/Scheduler.cpp

+ 6 - 0
Kernel/NetworkAdapter.cpp

@@ -25,6 +25,7 @@ NetworkAdapter* NetworkAdapter::from_ipv4_address(const IPv4Address& address)
 }
 
 NetworkAdapter::NetworkAdapter()
+    : m_packet_queue_alarm(*this)
 {
     // FIXME: I wanna lock :(
     ASSERT_INTERRUPTS_DISABLED();
@@ -90,3 +91,8 @@ void NetworkAdapter::set_ipv4_address(const IPv4Address& address)
 {
     m_ipv4_address = address;
 }
+
+bool PacketQueueAlarm::is_ringing() const
+{
+    return m_adapter.has_queued_packets();
+}

+ 17 - 0
Kernel/NetworkAdapter.h

@@ -7,6 +7,18 @@
 #include <Kernel/IPv4.h>
 #include <Kernel/ARP.h>
 #include <Kernel/ICMP.h>
+#include <Kernel/Alarm.h>
+
+class NetworkAdapter;
+
+class PacketQueueAlarm final : public Alarm {
+public:
+    PacketQueueAlarm(NetworkAdapter& adapter) : m_adapter(adapter) { }
+    virtual ~PacketQueueAlarm() override { }
+    virtual bool is_ringing() const override;
+private:
+    NetworkAdapter& m_adapter;
+};
 
 class NetworkAdapter {
 public:
@@ -24,6 +36,10 @@ public:
 
     ByteBuffer dequeue_packet();
 
+    Alarm& packet_queue_alarm() { return m_packet_queue_alarm; }
+
+    bool has_queued_packets() const { return !m_packet_queue.is_empty(); }
+
 protected:
     NetworkAdapter();
     void set_mac_address(const MACAddress& mac_address) { m_mac_address = mac_address; }
@@ -33,5 +49,6 @@ protected:
 private:
     MACAddress m_mac_address;
     IPv4Address m_ipv4_address;
+    PacketQueueAlarm m_packet_queue_alarm;
     SinglyLinkedList<ByteBuffer> m_packet_queue;
 };

+ 6 - 6
Kernel/NetworkTask.cpp

@@ -34,16 +34,16 @@ Lockable<HashMap<IPv4Address, MACAddress>>& arp_table()
 
 void NetworkTask_main()
 {
-    auto* e1000_ptr = E1000NetworkAdapter::the();
-    ASSERT(e1000_ptr);
-    auto& e1000 = *e1000_ptr;
-    e1000.set_ipv4_address(IPv4Address(192, 168, 5, 2));
+    auto* adapter_ptr = E1000NetworkAdapter::the();
+    ASSERT(adapter_ptr);
+    auto& adapter = *adapter_ptr;
+    adapter.set_ipv4_address(IPv4Address(192, 168, 5, 2));
 
     kprintf("NetworkTask: Enter main loop.\n");
     for (;;) {
-        auto packet = e1000.dequeue_packet();
+        auto packet = adapter.dequeue_packet();
         if (packet.is_null()) {
-            sleep(100);
+            current->snooze_until(adapter.packet_queue_alarm());
             continue;
         }
         if (packet.size() < (int)(sizeof(EthernetFrameHeader))) {

+ 8 - 0
Kernel/Process.cpp

@@ -1735,6 +1735,13 @@ void Process::unblock()
     m_state = Process::Runnable;
 }
 
+void Process::snooze_until(Alarm& alarm)
+{
+    m_snoozing_alarm = &alarm;
+    block(Process::BlockedSnoozing);
+    Scheduler::yield();
+}
+
 void Process::block(Process::State new_state)
 {
     if (state() != Process::Running) {
@@ -2871,6 +2878,7 @@ const char* to_string(Process::State state)
     case Process::BlockedConnect: return "Connect";
     case Process::BlockedReceive: return "Receive";
     case Process::BeingInspected: return "Inspect";
+    case Process::BlockedSnoozing: return "Snoozing";
     }
     kprintf("to_string(Process::State): Invalid state: %u\n", state);
     ASSERT_NOT_REACHED();

+ 5 - 0
Kernel/Process.h

@@ -14,6 +14,7 @@
 #include <AK/Weakable.h>
 #include <Kernel/Lock.h>
 
+class Alarm;
 class FileDescriptor;
 class PageDirectory;
 class Region;
@@ -78,6 +79,7 @@ public:
         BlockedSelect,
         BlockedConnect,
         BlockedReceive,
+        BlockedSnoozing,
     };
 
     enum Priority {
@@ -139,6 +141,8 @@ public:
     void set_wakeup_time(dword t) { m_wakeup_time = t; }
     dword wakeup_time() const { return m_wakeup_time; }
 
+    void snooze_until(Alarm&);
+
     template<typename Callback> static void for_each(Callback);
     template<typename Callback> static void for_each_in_pgrp(pid_t, Callback);
     template<typename Callback> static void for_each_in_state(State, Callback);
@@ -382,6 +386,7 @@ private:
     dword m_pending_signals { 0 };
     dword m_signal_mask { 0 };
     RetainPtr<Socket> m_blocked_socket;
+    Alarm* m_snoozing_alarm { nullptr };
 
     byte m_termination_status { 0 };
     byte m_termination_signal { 0 };

+ 9 - 0
Kernel/Scheduler.cpp

@@ -4,6 +4,7 @@
 #include "RTC.h"
 #include "i8253.h"
 #include <AK/TemporaryChange.h>
+#include <Kernel/Alarm.h>
 
 //#define LOG_EVERY_CONTEXT_SWITCH
 //#define SCHEDULER_DEBUG
@@ -136,6 +137,14 @@ bool Scheduler::pick_next()
             return true;
         }
 
+        if (process.state() == Process::BlockedSnoozing) {
+            if (process.m_snoozing_alarm->is_ringing()) {
+                process.m_snoozing_alarm = nullptr;
+                process.unblock();
+            }
+            return true;
+        }
+
         if (process.state() == Process::Skip1SchedulerPass) {
             process.set_state(Process::Skip0SchedulerPasses);
             return true;