8#include "pedigree/kernel/Atomic.h"
9#include "pedigree/kernel/Log.h"
10#include "pedigree/kernel/machine/IrqHandler.h"
11#include "pedigree/kernel/machine/IrqHandlerRegistry.h"
12#include "pedigree/kernel/process/Scheduler.h"
13#include "pedigree/kernel/process/Semaphore.h"
14#include "pedigree/kernel/process/Thread.h"
15#include "pedigree/kernel/processor/Processor.h"
16#include "pedigree/kernel/processor/state.h"
17#include "pedigree/kernel/time/Time.h"
18#include "pedigree/kernel/utilities/new"
25#include "modules/system/network-stack/NetworkStack.h"
28class HostedNetworkDevice final :
public Network {
30 bool send(
size_t, uintptr_t)
override {
39bool check(
bool condition,
const char* test,
const char* detail) {
44 ERROR(
"HOSTED-NETWORK-TEST: FAIL " << test <<
": " << detail);
53bool waitUntilQueued(
Thread* thread,
size_t debugState) {
54 const Time::Timestamp deadline = Time::getTicks() + (500 * Time::Multiplier::Millisecond);
55 while (Time::getTicks() < deadline) {
57 uintptr_t debugAddress = 0;
67struct DeviceDeregisterContext {
69 : stack(stack), device(device), entered(0), returned(0) {}
77int deregisterLeasedDevice(
void* parameter) {
78 DeviceDeregisterContext* context =
reinterpret_cast<DeviceDeregisterContext*
>(parameter);
79 context->entered += 1;
80 context->stack->deRegisterDevice(context->device);
81 context->returned += 1;
85bool deviceLeaseDeregisterDrain() {
86 static const char* Test =
"device-lease-deregister-drain";
89 alignas(HostedNetworkDevice) uint8_t deviceStorage[
sizeof(HostedNetworkDevice)];
90 HostedNetworkDevice* original =
new (deviceStorage) HostedNetworkDevice();
95 struct netif* originalInterface = acquired ? held.interface() :
nullptr;
96 DeviceDeregisterContext context(&stack, original);
98 bool drainPublished =
false;
99 bool lateRejected =
false;
100 bool usableWhileDraining =
false;
104 nullptr,
false,
true);
105 remover->setName(
"hosted network device deregister");
106 drainPublished = waitUntilQueued(remover, Thread::CallbackDrain);
114 usableWhileDraining = held.device() == original && held.interface() == originalInterface &&
115 originalInterface && originalInterface->
state == original &&
120 const bool removerJoined = remover && remover->
join();
121 const bool unregistered = !isRegistered(stack, original);
125 original->~HostedNetworkDevice();
127 HostedNetworkDevice* replacement =
new (deviceStorage) HostedNetworkDevice();
130 const bool replacementRegistered = stack.
acquireDevice(replacement, replacementLease);
133 replacement->~HostedNetworkDevice();
135 const bool passed = check(
136 acquired && context.entered == 1 && drainPublished && context.returned == 1 && lateRejected &&
137 usableWhileDraining && removerJoined && unregistered && replacementRegistered,
138 Test,
"deregistration did not unpublish, drain, and retire one held registration");
140 NOTICE(
"HOSTED-NETWORK-TEST: PASS " << Test);
147 MailboxHardIrqProbe(
sys_mbox_t* mailbox,
void* payload)
148 : mailbox(mailbox), payload(payload), result(
ERR_OK) {}
152 return HardIrqDisposition::Handled;
160bool mailboxTryPostRejectsHardIrq() {
161 static const char* Test =
"mailbox-hard-irq-trypost";
163 constexpr uint8_t TestIrq = 42;
167 return check(
false, Test,
"mailbox creation failed");
171 MailboxHardIrqProbe probe(&mailbox, &payload);
174 return check(
false, Test,
"hard IRQ registration failed");
177 alignas(InterruptState) uint8_t stateStorage[
sizeof(InterruptState)] = {};
178 InterruptState& state = *
reinterpret_cast<InterruptState*
>(stateStorage);
182 const bool dispatched = registry.
dispatchHard(TestIrq, state, disposition);
185 IrqHandlerRegistry::UnregisterResult::Completed;
187 void* fetched =
nullptr;
192 check(dispatched && disposition == HardIrqDisposition::Handled && removed &&
194 Test,
"the ISR call enqueued data or entered a blocking notification path");
196 NOTICE(
"HOSTED-NETWORK-TEST: PASS " << Test);
201struct ReceiveContext {
205 interface(interface),
206 removal(stack, device),
219 struct netif* interface;
220 DeviceDeregisterContext removal;
232ReceiveContext* g_ReceiveContext =
nullptr;
234void holdTcpipWorker(
void* parameter) {
235 ReceiveContext* context =
reinterpret_cast<ReceiveContext*
>(parameter);
236 context->coreEntered.release();
237 if (!context->allowCore.acquireForCompletion()) {
238 context->failures += 1;
242err_t receiveQueuedPacket(
struct pbuf* packet,
struct netif* interface) {
243 ReceiveContext* context = g_ReceiveContext;
244 if (context->removal.returned || interface != context->interface ||
245 interface->
state != context->device || packet->
tot_len != 64 ||
247 context->failures += 1;
250 context->delivered += 1;
254err_t holdReceiveInput(
struct pbuf* packet,
struct netif* interface) {
255 ReceiveContext* context = g_ReceiveContext;
256 if ((context->inputs += 1) == 1) {
257 context->inputEntered.release();
258 if (!context->allowInput.acquireForCompletion()) {
259 context->failures += 1;
262 if (!context->admitInput) {
269 context->enqueued += 1;
271 context->failures += 1;
276int receivePacketBatch(
void* parameter) {
277 ReceiveContext* context =
reinterpret_cast<ReceiveContext*
>(parameter);
279 if (!context->stack->acquireDevice(context->device, batch)) {
280 context->failures += 1;
283 uint8_t packet[68] = {};
286 for (
size_t i = 0; i < 2; ++i) {
287 context->stack->receive(64,
reinterpret_cast<uintptr_t
>(packet), batch, 4);
292bool receiveInterfaceRetirement() {
293 static const char* Test =
"receive-interface-retirement";
296 HostedNetworkDevice device;
301 return check(
false, Test,
"could not acquire the registered interface");
304 ReceiveContext context(&stack, &device, setup.interface());
305 setup.interface()->
input = holdReceiveInput;
307 g_ReceiveContext = &context;
310 const bool coreHeld = corePosted && context.coreEntered.acquire(1, 2);
311 Thread* receiver =
nullptr;
312 Thread* remover =
nullptr;
313 bool inputHeld =
false;
314 bool leaseDraining =
false;
315 bool lateRejected =
false;
316 bool receiverJoined =
false;
317 bool queueDraining =
false;
321 nullptr,
false,
true);
322 receiver->setName(
"hosted network receive");
323 inputHeld = context.inputEntered.acquire(1, 2);
326 &context.removal,
nullptr,
false,
true);
327 remover->setName(
"hosted network receive retirement");
328 leaseDraining = waitUntilQueued(remover, Thread::CallbackDrain);
330 uint8_t latePacket[64] = {};
331 stack.
receive(
sizeof(latePacket),
reinterpret_cast<uintptr_t
>(latePacket), &device, 0);
332 lateRejected = !isRegistered(stack, &device) && context.inputs == 1;
333 context.admitInput = 1;
341 context.allowInput.release();
342 receiverJoined = receiver && receiver->
join();
343 if (remover && leaseDraining) {
344 queueDraining = waitUntilQueued(remover, Thread::SemWait) && !context.removal.returned &&
345 !context.delivered && context.enqueued == 2;
348 context.allowCore.release();
349 const bool removerJoined = remover && remover->
join();
353 g_ReceiveContext =
nullptr;
356 passed &= check(coreHeld && inputHeld && leaseDraining && lateRejected && receiverJoined, Test,
357 "receive batch did not pin its interface through input admission");
358 passed &= check(queueDraining && removerJoined && context.removal.returned == 1 &&
359 context.delivered == 2 && !context.failures && !isRegistered(stack, &device),
360 Test,
"queued input did not finish before interface retirement");
362 NOTICE(
"HOSTED-NETWORK-TEST: PASS " << Test);
368bool runHostedNetworkStackRegressions() {
369 bool passed = mailboxTryPostRejectsHardIrq();
370 passed &= deviceLeaseDeregisterDrain();
371 passed &= receiveInterfaceRetirement();
virtual HardIrqDisposition irq(irq_id_t number, InterruptState &state)=0
UnregisterResult unregisterHandler(uint8_t irq, IrqHandlerBase *handler)
bool dispatchHard(uint8_t irq, InterruptState &state, HardIrqDisposition &disposition, HardIrqHandler *onlyHandler=nullptr, size_t dispatchGeneration=0, irq_id_t callbackId=0)
bool registerHardHandler(uint8_t irq, HardIrqHandler *handler)
void deRegisterDevice(Network *pDevice)
static NetworkStack & instance()
void receive(size_t nBytes, uintptr_t packet, Network *pCard, uint32_t offset)
MUST_USE_RESULT bool acquireDevice(size_t n, DeviceLease &lease)
void registerDevice(Network *pDevice)
virtual const StationInfo & getStationInfo()
virtual bool send(size_t nBytes, uintptr_t buffer)=0
virtual void getName(String &str)
static bool getInterrupts()
static void setInterrupts(bool bEnable)
static Scheduler & instance()
bool getWaitDebugInfo(WaitDebugInfo &info)
DebugState getDebugState(uintptr_t &address)
u8_t pbuf_get_at(const struct pbuf *p, u16_t offset)
u8_t pbuf_free(struct pbuf *p)
err_t sys_mbox_trypost(sys_mbox_t *mbox, void *msg)
err_t sys_mbox_new(sys_mbox_t *mbox, int size)
void sys_mbox_free(sys_mbox_t *mbox)
u32_t sys_arch_mbox_tryfetch(sys_mbox_t *mbox, void **msg)
err_t tcpip_inpkt(struct pbuf *p, struct netif *inp, netif_input_fn input_fn)
#define tcpip_callback(f, ctx)