Sixty-five headers sat side by side in include/, so `#include "irq.h"` said nothing about where irq.h was or what it belonged to, and errno.h, stat.h and fcntl.h at the top shadowed the system's for any host tool built with -I./include. They are in folders now -- arch/ core/ fs/ dev/ gfx/ net/ uapi/ (and ldls/ for the Linux driver layer) -- and every include names the folder, as Linux's do: "core/proc.h". Done mechanically and checked the hard way: the kernel built from this tree disassembles identically to the one built from the tree before it. The full image boots, and userland is the same set of programs. Paths in docs and comments follow; docs/guides/layout.md has a table of what is where, and tools/fix-includes.py rewrites old-style includes from the folders in the tree, for code written before this. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01S1giNvrHTrPMPw7V6x1kQ4
121 lines
3.9 KiB
C
121 lines
3.9 KiB
C
// SPDX-License-Identifier: GPL-2.0-or-later
|
|
//
|
|
// The lwIP port layer: everything lwIP needs from the system it runs on.
|
|
//
|
|
// With NO_SYS=1 that is a short list -- a clock, a source of randomness, and a
|
|
// lock -- because lwIP is not creating threads or mailboxes of its own. The
|
|
// lock is the interesting one.
|
|
//
|
|
// ---------------------------------------------------------------------------
|
|
// The concurrency model, in full
|
|
//
|
|
// lwIP with NO_SYS=1 is not re-entrant. It is entered from exactly two kinds of
|
|
// place here: the network thread (kernel/net/net.c), which feeds it received
|
|
// frames and runs its timers, and the socket syscalls, which call into it on
|
|
// behalf of a process. Those can run on different CPUs at the same time, so
|
|
// something has to serialise them, and lwIP's own SYS_ARCH_PROTECT hooks are
|
|
// the sanctioned place to put it.
|
|
//
|
|
// A sleeplock rather than a spinlock, because a call into lwIP can reach the
|
|
// driver and the driver can sleep. That makes this lock unusable from an
|
|
// interrupt handler -- which is why the e1000 interrupt does not feed lwIP
|
|
// directly and the network thread polls instead.
|
|
//
|
|
// It is recursive by necessity: lwIP brackets small critical sections inside
|
|
// larger ones and expects nesting to be free.
|
|
|
|
#include "core/types.h"
|
|
#include "core/defs.h"
|
|
#include "core/param.h"
|
|
#include "core/spinlock.h"
|
|
#include "core/sleeplock.h"
|
|
#include "core/proc.h"
|
|
|
|
#include "lwip/opt.h"
|
|
#include "lwip/sys.h"
|
|
|
|
static struct sleeplock lwip_lock;
|
|
static struct proc *lwip_owner;
|
|
static int lwip_depth;
|
|
static int lwip_lock_ready;
|
|
|
|
void
|
|
lwip_sys_init(void)
|
|
{
|
|
initsleeplock(&lwip_lock, "lwip");
|
|
lwip_owner = 0;
|
|
lwip_depth = 0;
|
|
lwip_lock_ready = 1;
|
|
}
|
|
|
|
// Reading `lwip_owner` without holding anything is safe in the one direction it
|
|
// is asked: only the process inside the lock ever writes its own proc pointer
|
|
// there, and it holds the lock the whole time. No other process can see its own
|
|
// pointer here, so a stale read cannot produce a false "I hold it". The null
|
|
// check matters because myproc() is 0 in early boot, and 0 == 0 would otherwise
|
|
// claim the lock for a process that does not exist.
|
|
static int
|
|
lwip_lock_mine(void)
|
|
{
|
|
return lwip_owner != 0 && lwip_owner == myproc();
|
|
}
|
|
|
|
void
|
|
lwip_sys_lock(void)
|
|
{
|
|
// Before lwip_sys_init runs there is exactly one context in existence, so
|
|
// there is nothing to serialise against and the sleeplock is not yet valid.
|
|
if (!lwip_lock_ready)
|
|
return;
|
|
if (lwip_lock_mine()) {
|
|
lwip_depth++;
|
|
return;
|
|
}
|
|
acquiresleep(&lwip_lock);
|
|
lwip_owner = myproc();
|
|
lwip_depth = 1;
|
|
}
|
|
|
|
void
|
|
lwip_sys_unlock(void)
|
|
{
|
|
if (!lwip_lock_ready)
|
|
return;
|
|
if (!lwip_lock_mine())
|
|
return; // not ours; releasing would free someone else's
|
|
if (--lwip_depth > 0)
|
|
return;
|
|
lwip_owner = 0;
|
|
releasesleep(&lwip_lock);
|
|
}
|
|
|
|
// Milliseconds since boot. lwIP drives every timer off this: retransmission,
|
|
// TIME_WAIT, ARP expiry, DHCP renewal. It need not match wall time, but it must
|
|
// advance monotonically -- a clock that goes backwards makes lwIP believe every
|
|
// timer is due at once.
|
|
//
|
|
// `ticks` is the timer interrupt count at 100 Hz, so one tick is 10 ms. Taken
|
|
// without the lock: it is a single aligned word, and a torn read would cost
|
|
// lwIP one early or late timer rather than anything worse.
|
|
u32_t
|
|
sys_now(void)
|
|
{
|
|
return (u32_t)ticks * 10u;
|
|
}
|
|
|
|
// lwIP wants randomness for initial TCP sequence numbers and for the DHCP
|
|
// transaction id. Sequence numbers that are predictable are a real weakness on
|
|
// a network with anyone hostile on it; this is not a CSPRNG and does not claim
|
|
// to be, but it is seeded from a clock that an outsider cannot observe.
|
|
u32_t
|
|
lwip_rand_seed(void)
|
|
{
|
|
static u32_t s;
|
|
if (s == 0)
|
|
s = (u32_t)ticks ^ 0x9E3779B9u;
|
|
// xorshift32
|
|
s ^= s << 13;
|
|
s ^= s >> 17;
|
|
s ^= s << 5;
|
|
return s;
|
|
}
|