Files
Asuro/doc/src/driver/storage/driver.storage.mgr.md
T

11 KiB

driver.storage.mgr

Physical storage device registry and I/O dispatch manager.

Overview

The storage manager is the central hub of the Asuro storage subsystem. It maintains a global list of registered storage devices and owns the Phase 2 request-based I/O path. Hardware controller drivers call register_device when they discover a device; the manager assigns an ID, initialises the per-device CFIFO, determines whether the device is the boot device, and triggers volume discovery.

The I/O model operates as follows:

  • submit_io enqueues a TIORequest into the device's CFIFO and calls dispatch_next to send the head request to hardware.
  • complete_io is called from ISR or driver completion context to finalise a request, invoke its callback, free the heap copy, and dispatch the next queued request.
  • submit_io_wait is a blocking wrapper that parks the calling process (psAwaiting) until complete_io wakes it.
  • storage_read / storage_write are convenience wrappers that build a stack-local TIORequest and call submit_io_wait.
  • storage_read_async / storage_write_async build and submit a request with a caller-supplied callback, returning immediately.

Blocking callers use storage_read / storage_write and check the returned TError.

Dependencies

  • core.ds.cfifo
  • core.ds.types
  • driver.storage.iorequest
  • core.ds.lists
  • memory.heap
  • driver.storage.vol.mbr
  • proc.mgr
  • proc.types
  • driver.storage.types
  • io.syslog
  • debug.tracer
  • core.util, arch.x86.util
  • driver.storage.vol.mgr (implementation uses)

Boot Registration

Registered with boot.mgr as driver.storage.mgr, depending on driver.storage.vfs.

Variables

Name Type Description
storageDevices PDList Global list of all registered TStorage_Device records
nextDeviceId uint32 Monotonically increasing device ID counter
bootDriveByte uint8 BIOS drive byte set from multiboot info ($80 = first HDD)
hdCount uint32 Number of non-ATAPI devices registered so far
atapiCount uint32 Number of ATAPI devices registered so far

Functions and Procedures

init

procedure init();

Initialises the storage device list and resets all counters. Must be called before any other storage manager function.

set_boot_drive_byte

procedure set_boot_drive_byte(driveByte : uint8);

Records the BIOS drive byte obtained from the multiboot info structure. Must be called once during early boot before any controller driver registers devices.

get_boot_drive_byte

function get_boot_drive_byte() : uint8;

Returns the stored BIOS drive byte ($FF if not yet set).

register_device

procedure register_device(device : PStorage_Device);

Registers a storage device discovered by a controller driver. Assigns a device ID, initialises the request queue and Phase 2 fields, determines whether the device is the boot device by comparing against bootDriveByte, and calls driver.storage.vol.mgr.discover_volumes to find and register partitions.

ATAPI devices are identified as the boot device if the BIOS drive byte does not correspond to any known HDD index (covers standard ISO boot). HDDs are matched by position ($80 + HDD index).

get_device_list

function get_device_list() : PDList;

Returns the global device list.

get_device_count

function get_device_count() : uint32;

Returns the number of registered devices.

get_device

function get_device(index : uint32) : PStorage_Device;

Returns the device at index, or nil if out of range.

controller_type_2_string

function controller_type_2_string(controllerType : TControllerType) : pchar;

Converts a TControllerType value to a human-readable string (e.g., 'AHCI', 'ATAPI').

read_mbr

function read_mbr(device : PStorage_Device) : PMaster_Boot_Record;

Reads sector 0 from device into a heap-allocated buffer and updates the device's cachedMBR. The caller owns the returned pointer and must free it. Returns nil on I/O failure.

write_mbr

procedure write_mbr(device : PStorage_Device; mbr : PMaster_Boot_Record);

Writes an MBR record to sector 0 and updates the device cache. Does nothing if the device is not writable.

get_cached_mbr

function get_cached_mbr(device : PStorage_Device) : PMaster_Boot_Record;

Returns a pointer to the cached MBR without performing any I/O. The caller must not free the returned pointer. Returns nil if the cache is not populated or device is nil.

write_mbr_async

procedure write_mbr_async(device : PStorage_Device; mbr : PMaster_Boot_Record;
                          callback : TIOCallback; callbackData : pointer);

Updates the MBR cache and issues a non-blocking write. callback is fired from ISR context when the write completes. If the device is read-only, fires the callback immediately with eReadOnly.

submit_io

function submit_io(request : PIORequest) : TError;

Non-blocking: validates the request, enqueues it into the device's CFIFO, and calls dispatch_next. Returns eNone if successfully enqueued. The caller must have set Callback/CallbackData (async path) or Caller/UserData (blocking path) before calling. Disables interrupts around the enqueue and dispatch operations.

submit_io_wait

function submit_io_wait(request : PIORequest) : TError;

Blocking wrapper for task context only. Sets sync_io_callback as the completion callback, calls submit_io, parks the calling process in psAwaiting, and spins via pointer indirection until complete_io sets Done = 1. Returns the error code from the completed request.

complete_io

procedure complete_io(request : PIORequest; success : boolean; error : TError);

Called from ISR or driver completion context. Sets the request state and error, clears the device's activeRequest, invokes the completion callback, frees the heap copy of the request, and dispatches the next queued request.

storage_read

function storage_read(device : PStorage_Device; addr : uint32; sectors : uint32; buffer : puint32) : TError;

Blocking read. Builds a TIORequest and calls submit_io_wait. Must only be called from task context (interrupts enabled).

storage_write

function storage_write(device : PStorage_Device; addr : uint32; sectors : uint32; buffer : puint32) : TError;

Blocking write. Builds a TIORequest and calls submit_io_wait. Must only be called from task context.

storage_read_async

function storage_read_async(device : PStorage_Device; addr : uint32; sectors : uint32;
                            buffer : puint32; callback : TIOCallback; callbackData : pointer) : TError;

Non-blocking read. Enqueues a request and returns immediately. callback is fired from ISR context on completion. Safe to call from ISR or task context.

storage_write_async

function storage_write_async(device : PStorage_Device; addr : uint32; sectors : uint32;
                             buffer : puint32; callback : TIOCallback; callbackData : pointer) : TError;

Non-blocking write. Analogous to storage_read_async.

Notes

  • 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.
  • 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.