Compare commits

...
3 Commits
20 changed files with 2078 additions and 819 deletions

No files matched your search

@@ -219,3 +219,37 @@ Drop-in procedure replacements for callers using the old void-returning API. For
- `dispatch_next` is an internal procedure called with interrupts disabled. It dequeues the head of the CFIFO, allocates a heap copy via `ioreq_copy` (for ISR safety), and invokes the appropriate `dispatchRead` or `dispatchWrite` on the device. If neither dispatch pointer is set, the request is failed with `eDeviceNotFound`. - `dispatch_next` is an internal procedure called with interrupts disabled. It dequeues the head of the CFIFO, allocates a heap copy via `ioreq_copy` (for ISR safety), and invokes the appropriate `dispatchRead` or `dispatchWrite` on the device. If neither dispatch pointer is set, the request is failed with `eDeviceNotFound`.
- The blocking path relies on pointer-dereferenced spin (`puint32(@wait.Done)^`) to prevent FPC from caching the `Done` flag in a register across the `hlt` instruction. - The blocking path relies on pointer-dereferenced spin (`puint32(@wait.Done)^`) to prevent FPC from caching the `Done` flag in a register across the `hlt` instruction.
- Devices without `dispatchRead`/`dispatchWrite` set (i.e., drivers not yet migrated to Phase 2) will have `submit_io` return `eDeviceNotFound`. Legacy drivers must be migrated before they can use the new I/O path. - Devices without `dispatchRead`/`dispatchWrite` set (i.e., drivers not yet migrated to Phase 2) will have `submit_io` return `eDeviceNotFound`. Legacy drivers must be migrated before they can use the new I/O path.
## Bounded performance path
The manager merges up to 32 consecutive queued requests when operation, device,
LBA range, and virtual buffer range are contiguous. It never searches past the
queue head or delays a lone request to create a merge. Drivers opt in through
`mergeMaxSectors`; SATA AHCI advertises 65,536 sectors, while other drivers default
to zero (disabled). A merged command delivers one completion to each original
request, in FIFO order. A command error fails every member conservatively.
`storage_get_stats` takes an interrupt-protected snapshot of the device counters.
`DISK info <index>` displays accepted/completed/failed logical requests, hardware
commands, merged requests, queue-full rejections, queue and active high-water
marks, successful read/write sectors, request-size buckets, queue/service ticks,
and FAT metadata cache hits/misses. Queue ticks are accumulated per logical
request; service ticks are accumulated per hardware command. Tick values use
`Counters.c32`, not milliseconds. Counters are unsigned 32-bit values, start at
device registration, and wrap; use snapshot differences over bounded benchmark
intervals. Size buckets are <=8, 9..256, and >256 sectors.
FAT32 blocking and async reads share a pooled transfer engine with batches of up
to four outstanding reads, capped by device queue depth. Destination slices are
disjoint. On any error, the engine drains every accepted request before releasing
its context and reports only the contiguous successful byte prefix. Unaligned
edges and odd DMA addresses use the context's private scratch sector. Writes
remain sequential. Run maps store cumulative file-cluster offsets; an open-file
cursor handles sequential locality and binary search handles random seeks.
`STORPERF` runs memory-backed regression checks (also included in boot unit tests)
for reverse completion order, failures, unaligned reads, run lookup, queue
rejection, merging limits, and callback accounting. It does not access real disks
or measure hardware throughput. Compare counters alongside `STORBENCH` and file
benchmarks on contiguous and fragmented files for actual performance validation.
@@ -58,3 +58,111 @@ Registers the `STORTEST` and `DISKTEST` shell commands with `io.stdio`. Must be
- `run_tests` exercises `makeAbsolutePathFrom`, `resolvePathFrom`, `changeDirectoryFrom`, `GetDirectoryListingFrom`, `newVirtualDirectory`, and `PathValid` including `.` and `..` traversal. - `run_tests` exercises `makeAbsolutePathFrom`, `resolvePathFrom`, `changeDirectoryFrom`, `GetDirectoryListingFrom`, `newVirtualDirectory`, and `PathValid` including `.` and `..` traversal.
- `run_disk_tests` is destructive and will irreversibly wipe the partition table of disk 0. It is never called automatically. - `run_disk_tests` is destructive and will irreversibly wipe the partition table of disk 0. It is never called automatically.
- The `Assert` helper used in both procedures increments `passed` or `failed` counters and logs the test name on failure; passing tests produce no output to avoid noise. - The `Assert` helper used in both procedures increments `passed` or `failed` counters and logs the test name on failure; passing tests produce no output to avoid noise.
## Raw read benchmark
`STORBENCH` is implemented and registered by `driver.storage.bench`. It performs
read-only raw-device I/O; it does not invoke the destructive `DISKTEST` path.
```text
STORBENCH [disk] [MiB]
STORBENCH list
STORBENCH run [disk] [MiB] [KiB/request] [depth] [seq|random] [start_lba]
STORBENCH compare [disk] [MiB]
STORBENCH help
```
Manual (`run`) defaults are disk 0, 128 MiB, 1024 KiB/request, sequential reads, and a depth of
`min(4, hardware depth)` (at least one). Examples:
```text
STORBENCH 0 128 64 4
STORBENCH 0 128 4 16 random
STORBENCH 0 256 1024 1 seq 2048
STORBENCH compare 0 128
```
The comparison runs sequential reads at 4, 64, and 1024 KiB/request, each at depths
1, 4, 16, and 32. The MiB argument applies to each of the twelve cases. Queue depth
may exceed hardware depth to expose software queuing and request merging.
Limits are 1..16384 MiB, 1..1024 KiB/request, and depth 1..32. Request size must be
an exact number of device sectors. Buffers are allocated once per case, with a
separate slice for every outstanding request and a maximum total size of 32 MiB.
The transfer range is clamped to the device. Without an explicit LBA, reads start
at sector 2048 when the full requested range fits there; otherwise they start at
zero. Random mode uses a fixed xorshift seed and samples that range with replacement,
so total bytes read are bounded but unique coverage is not guaranteed.
Reports include successful bytes, elapsed microseconds, MiB/s, IOPS, accepted and
completed request counts, peak outstanding requests, average/p95/max completion
latency, and per-run storage-counter deltas. Latencies are measured from submission
to callback. The p95 is an upper bound from logarithmic microsecond buckets, not an exact
microsecond percentile. Runs shorter than 10 ms are excluded from suite speed comparisons.
No console output occurs inside the measured loop. Any failure stops new work;
already accepted DMA is drained before buffers are freed, and failed requests
contribute no bytes or successful IOPS.
Timing prefers the Hyper-V-compatible VM reference counter (100 ns ticks), detected
through its Hv#1 interface and reference-counter privilege. It needs no calibration.
Other platforms use an invariant TSC calibrated against two complete RTC seconds,
with up to three attempts to tolerate transient RTC updates or scheduling delays.
Calibration requires agreement within 1%; failures include the specific reason.
Unlike delivered timer interrupts, this clock advances while interrupts are masked.
Queue submission restores interrupts between requests. Filesystem caches are
bypassed, but drive and virtual-machine/host caches may still affect results.
Virtual or sparse disks can report throughput above physical-media speed.
Device counter deltas may include concurrent I/O from other processes;
benchmark-owned byte, request, and latency counters do not.
`driver.storage.bench.UnitTest` runs memory-only parser, arithmetic, range,
queue-depth, completion-order, and error-accounting tests at boot. It never reads
or writes a real disk. The clock tests also verify that an advertised VM reference
counter advances with interrupts masked; they never calibrate the RTC at boot.
### Automatic suite and table output
`STORBENCH` without arguments, or `STORBENCH auto [disk] [MiB]`, runs six
read-only profiles. Auto defaults to disk 0 and **32 MiB per profile** (up to
192 MiB total before device-size clamping). One or two numeric arguments select the auto suite: `STORBENCH 2 512` tests
disk 2 with 512 MiB per profile. `STORBENCH list` lists disk indices, capacities,
controllers, sector sizes, and hardware queue depths. Three or more numeric
arguments retain the legacy manual syntax; `run` selects manual mode explicitly.
| Profile | Request size | Queue depth |
| --- | --- | --- |
| Seq large | 1024 KiB | 1 |
| Seq queued | 1024 KiB | queued |
| Seq medium | 64 KiB | queued |
| Random small | 4 KiB | 1 |
| Random queued | 4 KiB | queued |
| Random medium | 64 KiB | queued |
Queued depth is the device hardware depth clamped to 4..16, allowing software
queue testing even on serial devices. A request smaller than a device sector is
increased to one sector. Each profile samples the same byte-span size, with the
existing fixed random seed and range clamping rules.
Auto and compare modes print an aligned ASCII table with request KiB, queue
depth, MiB/s, IOPS, average latency, p95 latency upper bound, and status. Rows are
printed outside the measured loop. Successful runs shorter than 10 ms show `-` metrics and
`SHORT`, and are excluded from best-result and queue-gain summaries. Errors show
`FAILED`, stop the suite after accepted DMA has drained, and leave remaining
profiles unrun. The summary includes successful profiles, bytes actually read,
measured time, best sequential throughput, peak random IOPS, and (in auto mode)
queued/QD1 throughput ratios. These ratios describe the measured cases, including
any controller-cache effects; they are not a hardware rating.
```text
STORBENCH
STORBENCH list
STORBENCH 2 512
STORBENCH auto 0 64
STORBENCH compare 0 32
```
Profiles reuse the same raw device range. Host/drive caches are not flushed, so
later profiles may read warm data. These results are observed raw read throughput,
not a claim of uncached physical-media performance.
+11
View File
@@ -150,6 +150,7 @@ var
device : PStorage_Device; device : PStorage_Device;
volCount : uint32; volCount : uint32;
freeSec : uint32; freeSec : uint32;
stats : TStorageStats;
begin begin
if paramCount(params) < 2 then begin if paramCount(params) < 2 then begin
println('Usage: DISK info <disk>'); println('Usage: DISK info <disk>');
@@ -191,6 +192,16 @@ begin
else else
volCount := 0; volCount := 0;
print('Partitions: '); printint(volCount); println(''); print('Partitions: '); printint(volCount); println('');
storage_get_stats(device,stats);
print('I/O submitted/completed/failed: '); printint(stats.Submitted); print(' / '); printint(stats.Completed); print(' / '); printint(stats.Failed); println('');
print('Commands / merged requests: '); printint(stats.Commands); print(' / '); printint(stats.Merged); println('');
print('Queue peak / active peak / queue full: '); printint(stats.QueuePeak); print(' / '); printint(stats.ActivePeak); print(' / '); printint(stats.QueueFull); println('');
print('Read / written sectors: '); printint(stats.ReadSectors); print(' / '); printint(stats.WriteSectors); println('');
print('Requests <=8 / <=256 / >256 sectors: '); printint(stats.SmallRequests); print(' / '); printint(stats.MediumRequests); print(' / '); printint(stats.LargeRequests); println('');
print('Queue ticks total / max: '); printint(stats.QueueTicks); print(' / '); printint(stats.MaxQueueTicks); println('');
print('Service ticks total / max: '); printint(stats.ServiceTicks); print(' / '); printint(stats.MaxServiceTicks); println('');
print('FAT cache hits / misses: '); printint(stats.FatCacheHits); print(' / '); printint(stats.FatCacheMisses); println('');
end; end;
{ ---------- DISK WIPE ---------- } { ---------- DISK WIPE ---------- }
+5 -1
View File
@@ -27,6 +27,10 @@ uses
arch.x86.isr.mgr, arch.x86.isr.mgr,
arch.x86.idt; arch.x86.idt;
const
PIT_INPUT_HZ = 1193182;
PIT_DIVISOR = 149;
procedure register(); procedure register();
procedure hook(hook_method : uint32); procedure hook(hook_method : uint32);
procedure unhook(hook_method : uint32); procedure unhook(hook_method : uint32);
@@ -54,7 +58,7 @@ procedure register();
begin begin
if not registered then begin if not registered then begin
asm asm
mov ax, 149 mov ax, PIT_DIVISOR
out $40, al out $40, al
mov al, ah mov al, ah
out $40, al out $40, al
File diff suppressed because it is too large. Load diff
@@ -21,6 +21,7 @@ unit driver.storage.ctl.ahci.types;
interface interface
uses uses
io.syslog, io.syslog,
driver.storage.types,
driver.mgr, driver.mgr,
driver.types, driver.types,
driver.storage.ctl.ide.types, driver.storage.ctl.ide.types,
@@ -251,11 +252,13 @@ type
{ Callback signature fired from IRQ context when an async I/O command completes. { Callback signature fired from IRQ context when an async I/O command completes.
Must be very short — set a flag or post to a queue. Must be very short — set a flag or post to a queue.
Never call kalloc/kfree/VFS from within this callback. } Never call kalloc/kfree/VFS from within this callback. }
TIOCompletion = procedure(success : boolean; userdata : puint32); TIOCompletion = procedure(error : TError; userdata : puint32);
{ Per-slot pending operation — one entry per HBA command slot } { Per-slot pending operation — one entry per HBA command slot }
TPendingOp = record TPendingOp = record
inUse : boolean; inUse : boolean;
issued : boolean;
started : uint32;
completion : TIOCompletion; completion : TIOCompletion;
userdata : puint32; userdata : puint32;
end; end;
@@ -267,6 +270,11 @@ type
} }
TAHCI_Device = record TAHCI_Device = record
port : PHBA_Port; port : PHBA_Port;
owner : pointer; { stable PAHCI_Controller }
offline : boolean;
recovering : boolean;
recoveryTick : uint32;
recoveryError: TError;
device_type : TDeviceType; device_type : TDeviceType;
ata_info : TIdentResponse; ata_info : TIdentResponse;
supportsNCQ : boolean; supportsNCQ : boolean;
@@ -288,6 +296,7 @@ type
} }
TAHCI_Controller = record TAHCI_Controller = record
pci_device : PPCI_Device; pci_device : PPCI_Device;
offline : boolean;
mio: PHBA_Memory; mio: PHBA_Memory;
ata_info : TIdentResponse; ata_info : TIdentResponse;
devices : array[0..31] of TAHCI_Device; devices : array[0..31] of TAHCI_Device;
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
@@ -39,8 +39,8 @@ function ioreq_alloc(reqType : TIORequestType;
procedure ioreq_free(req : PIORequest); procedure ioreq_free(req : PIORequest);
{ Copy an existing TIORequest into a new pooled allocation (deep copy of the { Copy an existing TIORequest into a new pooled allocation (deep copy of the
record, NOT the buffer it points to). Used by dispatch_next to create an record, NOT the buffer it points to). Used by submit_io before enqueueing
ISR-safe copy from the CFIFO value-element. } the owned pointer. Manager-only merge/timing fields are initialised here. }
function ioreq_copy(src : PIORequest) : PIORequest; function ioreq_copy(src : PIORequest) : PIORequest;
implementation implementation
@@ -132,6 +132,9 @@ begin
ioreq_alloc := nil; ioreq_alloc := nil;
exit; exit;
end; end;
req^.MergeNext := nil;
req^.OriginalSectors := sectors;
req^.QueuedTick := 0; req^.DispatchTick := 0;
req^.RequestType := reqType; req^.RequestType := reqType;
req^.State := iosPending; req^.State := iosPending;
req^.Device := device; req^.Device := device;
@@ -161,6 +164,9 @@ begin
ioreq_copy := nil; ioreq_copy := nil;
exit; exit;
end; end;
dst^.MergeNext := nil;
dst^.OriginalSectors := src^.SectorCount;
dst^.QueuedTick := 0; dst^.DispatchTick := 0;
dst^.RequestType := src^.RequestType; dst^.RequestType := src^.RequestType;
dst^.State := src^.State; dst^.State := src^.State;
dst^.Device := src^.Device; dst^.Device := src^.Device;
+16 -3
View File
@@ -14,7 +14,7 @@ type
PMock = ^TMock; PMock = ^TMock;
TMock = record TMock = record
Data : puint8; Data : puint8;
Writes, FailWrite, Callbacks : uint32; Writes, FailWrite, Callbacks, Depth, MaxDepth : uint32;
Error : TError; Error : TError;
OutOfBounds, FailRead : boolean; OutOfBounds, FailRead : boolean;
end; end;
@@ -27,7 +27,10 @@ begin
(request^.SectorCount > device^.maxSectorCount-request^.LBA) then begin (request^.SectorCount > device^.maxSectorCount-request^.LBA) then begin
m^.OutOfBounds := true; complete_io(request, false, eInvalidArgument); exit; m^.OutOfBounds := true; complete_io(request, false, eInvalidArgument); exit;
end; end;
if (request^.RequestType <> ioWrite) and m^.FailRead then begin complete_io(request, false, eIOError); exit; end; if (request^.RequestType <> ioWrite) and m^.FailRead then begin
Inc(m^.Depth); if m^.Depth > m^.MaxDepth then m^.MaxDepth := m^.Depth;
complete_io(request,false,eIOError); Dec(m^.Depth); exit;
end;
offset := request^.LBA*512; size := request^.SectorCount*512; offset := request^.LBA*512; size := request^.SectorCount*512;
if request^.RequestType = ioWrite then begin if request^.RequestType = ioWrite then begin
Inc(m^.Writes); Inc(m^.Writes);
@@ -81,7 +84,17 @@ begin
device^.controllerId0 := uint32(mock); device^.sectorSize := 512; device^.controllerId0 := uint32(mock); device^.sectorSize := 512;
device^.maxSectorCount := 256; device^.writable := true; device^.maxActive := 1; device^.maxSectorCount := 256; device^.writable := true; device^.maxActive := 1;
device^.dispatchRead := @dispatch; device^.dispatchWrite := @dispatch; device^.dispatchRead := @dispatch; device^.dispatchWrite := @dispatch;
device^.requestQueue := CFIFO_New(sizeof(Pointer),4); device^.requestQueue := CFIFO_New(sizeof(Pointer),8);
device^.maxActive := 0; mock^.FailRead := true;
for i := 1 to 4 do
Assert(storage_read_async(device,0,1,puint32(mock^.Data),@completion,mock)=eNone,'queued fault request accepted');
Assert(mock^.Callbacks=0,'queued requests wait for dispatch');
device^.maxActive := 1;
Assert(storage_read_async(device,0,1,puint32(mock^.Data),@completion,mock)=eNone,'fault queue drain accepted');
Assert((mock^.Callbacks=5) and (mock^.Error=eIOError),'every queued error completes once');
Assert((mock^.MaxDepth=1) and (device^.activeCount=0) and (CFIFO_Size(device^.requestQueue)=0),
'inline failures drain without recursive driver dispatch');
mock^.FailRead := false; mock^.Callbacks := 0;
device^.cachedMBR := kalloc(512); device^.cachedMBR := kalloc(512);
mbr := PMaster_Boot_Record(device^.cachedMBR); mbr := PMaster_Boot_Record(device^.cachedMBR);
memset(uint32(mbr),0,512); mbr^.boot_sector := $AA55; mbr^.signature := $12345678; memset(uint32(mbr),0,512); mbr^.boot_sector := $AA55; mbr^.signature := $12345678;
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
@@ -252,11 +252,22 @@ type
UserData : pointer; { points back to caller's stack-local request (blocking path) } UserData : pointer; { points back to caller's stack-local request (blocking path) }
Callback : TIOCallback; { async completion callback (nil for blocking path) } Callback : TIOCallback; { async completion callback (nil for blocking path) }
CallbackData : pointer; { opaque data passed to Callback } CallbackData : pointer; { opaque data passed to Callback }
{ Manager-owned fields; callers need not initialise these. }
MergeNext : PIORequest;
OriginalSectors, QueuedTick, DispatchTick : uint32;
end; end;
byteArray8 = array[0..7] of char; byteArray8 = array[0..7] of char;
PByteArray8 = ^byteArray8; PByteArray8 = ^byteArray8;
TStorageStats = record
Submitted, Completed, Failed, Commands, Merged, QueueFull : uint32;
ReadSectors, WriteSectors, QueuePeak, ActivePeak : uint32;
QueueTicks, ServiceTicks, MaxQueueTicks, MaxServiceTicks : uint32;
SmallRequests, MediumRequests, LargeRequests : uint32;
FatCacheHits, FatCacheMisses : uint32;
end;
{ Generic storage device } { Generic storage device }
TStorage_Device = record TStorage_Device = record
id : uint8; id : uint8;
@@ -270,7 +281,9 @@ type
dispatchRead : TDriverDispatch; { nil if driver not yet migrated } dispatchRead : TDriverDispatch; { nil if driver not yet migrated }
dispatchWrite : TDriverDispatch; { nil if driver not yet migrated } dispatchWrite : TDriverDispatch; { nil if driver not yet migrated }
requestQueue : PCFIFOQueue; { per-device I/O request CFIFO } requestQueue : PCFIFOQueue; { per-device I/O request CFIFO }
dispatching : boolean; { prevents recursive synchronous-error queue draining }
activeCount : uint32; { number of commands currently dispatched to HW } activeCount : uint32; { number of commands currently dispatched to HW }
mergeMaxSectors : uint32; { zero disables merging; driver sets safe command limit }
maxActive : uint32; { max concurrent commands (NCQ depth) } maxActive : uint32; { max concurrent commands (NCQ depth) }
maxSectorCount : uint32; maxSectorCount : uint32;
@@ -280,6 +293,7 @@ type
mbrWriteBusy : boolean; { serializes staged sector-zero writes } mbrWriteBusy : boolean; { serializes staged sector-zero writes }
cachedMBR : pointer; { cached mbr data, nil if not yet read } cachedMBR : pointer; { cached mbr data, nil if not yet read }
isBootDevice : boolean; { true if this is the device GRUB booted from } isBootDevice : boolean; { true if this is the device GRUB booted from }
Stats : TStorageStats; { cold tail: wrapping counters, timer ticks }
end; end;
{ Filesystem driver descriptor } { Filesystem driver descriptor }
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
@@ -99,6 +99,7 @@ type
TFATRun = record TFATRun = record
StartCluster : uint32; StartCluster : uint32;
ClusterCount : uint32; ClusterCount : uint32;
FileClusterStart : uint32;
end; end;
TFATAllocHint = record TFATAllocHint = record
@@ -183,6 +184,13 @@ type
PFATVolumeCtx = ^TFATVolumeCtx; PFATVolumeCtx = ^TFATVolumeCtx;
PFATTransferCtx = ^TFATTransferCtx; PFATTransferCtx = ^TFATTransferCtx;
PFATReadSlot = ^TFATReadSlot;
TFATReadSlot = record
Owner : PFATTransferCtx;
Bytes : uint32;
Error : TError;
end;
TFATTransferCtx = record TFATTransferCtx = record
Next : PFATTransferCtx; Next : PFATTransferCtx;
VolumeCtx : PFATVolumeCtx; VolumeCtx : PFATVolumeCtx;
@@ -209,6 +217,10 @@ type
RunHintIndex : uint32; RunHintIndex : uint32;
RunHintFileCluster : uint32; RunHintFileCluster : uint32;
LastError : TError; LastError : TError;
Pumping, Resume, Finished : boolean;
ReadSubmitting : boolean;
ReadPending, ReadCount : uint32;
ReadSlots : array[0..3] of TFATReadSlot;
end; end;
TFATVolumeCtx = record TFATVolumeCtx = record
+4
View File
@@ -486,6 +486,10 @@ var
begin begin
search := params; search := params;
for i := 0 to index do begin for i := 0 to index do begin
if search = nil then begin
getParam := nil;
exit;
end;
if search^.next = nil then begin if search^.next = nil then begin
getParam := nil; getParam := nil;
exit; exit;
+20 -7
View File
@@ -107,10 +107,10 @@ procedure process_trampoline; forward;
{ ---- Process creation ---- } { ---- Process creation ---- }
function create(name : pchar; function createInternal(name : pchar;
entry : TProcessEntryPoint; entry : TProcessEntryPoint;
localData : void; localData : void;
priority : uint8) : PProcessContext; priority : uint8; runnable : boolean) : PProcessContext;
var var
ctx : PProcessContext; ctx : PProcessContext;
slotPtr : ^uint32; slotPtr : ^uint32;
@@ -119,7 +119,7 @@ var
prio : uint8; prio : uint8;
begin begin
push_trace('proc.mgr.create'); push_trace('proc.mgr.create');
create := nil; createInternal := nil;
if priority = 0 then prio := 1 else prio := priority; if priority = 0 then prio := 1 else prio := priority;
@@ -140,7 +140,7 @@ begin
ctx^.ParentID := CurrentProcess^.ProcessID; ctx^.ParentID := CurrentProcess^.ProcessID;
{ State } { State }
ctx^.State := psCreated; ctx^.State := psSuspended;
ctx^.ExitCode := 0; ctx^.ExitCode := 0;
{ Entry point } { Entry point }
@@ -269,14 +269,22 @@ begin
slotPtr^ := uint32(ctx); slotPtr^ := uint32(ctx);
{ Mark as ready for scheduling } { Mark as ready for scheduling }
ctx^.State := psReady; if runnable then ctx^.State := psReady;
asm popf end; asm popf end;
io.syslog.log('PROCMGR', 'Created process: '); io.syslog.log('PROCMGR', 'Created process: ');
io.syslog.writestringln(name); io.syslog.writestringln(name);
pop_trace; pop_trace;
create := ctx; createInternal := ctx;
end;
{ Public creation retains its ready-on-return behavior. Commands need their
arguments and streams installed before the scheduler may enter them. }
function create(name : pchar; entry : TProcessEntryPoint;
localData : void; priority : uint8) : PProcessContext;
begin
create := createInternal(name, entry, localData, priority, true);
end; end;
{ ---- Process trampoline ---- } { ---- Process trampoline ---- }
@@ -543,12 +551,17 @@ function runCommand(name : pchar; method : TCommandMethod; params : PParamList;
var var
ctx : PProcessContext; ctx : PProcessContext;
begin begin
ctx := create(name, @legacy_entry, void(method), 1); ctx := createInternal(name, @legacy_entry, void(method), 1, false);
if ctx <> nil then begin if ctx <> nil then begin
ctx^.MsgData := void(params); ctx^.MsgData := void(params);
ctx^.StdIn := stdin_buf; ctx^.StdIn := stdin_buf;
ctx^.StdOut := stdout_buf; ctx^.StdOut := stdout_buf;
ctx^.StdErr := stderr_buf; ctx^.StdErr := stderr_buf;
{ Publish only after all fields read by legacy_entry are initialized.
psCreated is also runnable, so creation must use psSuspended. }
asm pushf; cli end;
ctx^.State := psReady;
asm popf end;
end; end;
runCommand := ctx; runCommand := ctx;
end; end;
+6
View File
@@ -33,7 +33,10 @@ uses
core.ds.prio, core.ds.prio,
driver.storage.vfs, driver.storage.vfs,
driver.storage.test, driver.storage.test,
driver.storage.bench,
driver.storage.metatest, driver.storage.metatest,
driver.storage.perftest,
driver.storage.ctl.ahci,
core.enc.fnv1a, core.enc.fnv1a,
core.enc.djb2, core.enc.djb2,
core.ds.bloom, core.ds.bloom,
@@ -69,7 +72,10 @@ begin
core.ds.prio.UnitTest; core.ds.prio.UnitTest;
driver.storage.vfs.UnitTest; driver.storage.vfs.UnitTest;
driver.storage.test.UnitTest; driver.storage.test.UnitTest;
driver.storage.bench.UnitTest;
driver.storage.metatest.UnitTest; driver.storage.metatest.UnitTest;
driver.storage.perftest.UnitTest;
driver.storage.ctl.ahci.UnitTest;
core.enc.fnv1a.UnitTest; core.enc.fnv1a.UnitTest;
core.enc.djb2.UnitTest; core.enc.djb2.UnitTest;
core.ds.bloom.UnitTest; core.ds.bloom.UnitTest;