implement generic I/O async request object to replace file-specific async requests, refactor lwIP network stack to dispatch requests through IRPs with cancellation support, add cancellation polling to lwIP accept and receive operations using LwipSocketWaitRecvNetbuf and LwipSocketWaitAccept, introduce NetSubmitSocketIrp for routing socket operations via device IRPs, track network IRP statistics with SYS_NET_IRP_STATS, add process module lock spinlock and reference counting for process modules, implement dynamic module loading and unloading via user module handles, add POSIX dynamic loader APIs including dlopen, dlsym, dlerror, and dlclose, introduce PosixStart entry point for posix applications, add new native smoke tests for net IRPs.
This commit is contained in:
1 parent
ed3d065abd
commit
1f5477cf66
32 files changed
+3713
-551
No files matched your search
+77
-166
@@ -32,31 +32,12 @@ typedef enum IO_ASYNC_FILE_OP {
|
||||
IoAsyncFileWrite = 1,
|
||||
} IO_ASYNC_FILE_OP;
|
||||
|
||||
typedef enum IO_ASYNC_FILE_STATE {
|
||||
IoAsyncStateQueued = 0,
|
||||
IoAsyncStateRunning = 1,
|
||||
IoAsyncStateCompleted = 2,
|
||||
IoAsyncStateCancelled = 3,
|
||||
} IO_ASYNC_FILE_STATE;
|
||||
|
||||
struct IO_ASYNC_FILE_REQUEST_OBJECT {
|
||||
OBJECT Header; // ObjectTypeAsyncIo
|
||||
LIST_ENTRY Link; // protected by gAsyncFileQueueLock
|
||||
IO_ASYNC_REQUEST_OBJECT Async;
|
||||
|
||||
IO_ASYNC_FILE_OP Op;
|
||||
IO_ASYNC_FILE_STATE State;
|
||||
uint8_t CancelRequested;
|
||||
|
||||
FILE_OBJECT* File; // referenced until object destruction
|
||||
EVENT* WaitEvent; // internal completion event
|
||||
EVENT* CompletionEvent; // optional, referenced until object destruction
|
||||
KAPC* CompletionApc; // caller-owned APC storage
|
||||
IO_STATUS_BLOCK* Iosb; // caller/user-owned completion block
|
||||
IO_STATUS_BLOCK IosbSnapshot;
|
||||
MM_LOCKED_USER_BUFFER* LockedBuffer;
|
||||
MM_LOCKED_USER_BUFFER* LockedIosb;
|
||||
|
||||
void* Buffer; // caller-owned; user buffers must be locked until completion/cancel
|
||||
FILE_OBJECT* File;
|
||||
void* Buffer;
|
||||
size_t Size;
|
||||
uint64_t Offset;
|
||||
};
|
||||
@@ -228,72 +209,45 @@ static void KiIoSignalFileLockWaitersLocked(const IO_FILE_IDENTITY* identity) {
|
||||
}
|
||||
}
|
||||
|
||||
// Implements the io Async File Request Destroy operation.
|
||||
static void KiIoAsyncFileRequestDestroy(OBJECT* obj) {
|
||||
// Releases file-specific references owned by an async file request.
|
||||
static void KiIoAsyncFileRequestCleanup(IO_ASYNC_REQUEST_OBJECT* async, void* context) {
|
||||
(void)context;
|
||||
IO_ASYNC_FILE_REQUEST_OBJECT* req =
|
||||
CONTAINING_RECORD(obj, IO_ASYNC_FILE_REQUEST_OBJECT, Header);
|
||||
(IO_ASYNC_FILE_REQUEST_OBJECT*)async;
|
||||
|
||||
if (req->WaitEvent) {
|
||||
ObDereferenceObject(&req->WaitEvent->Header);
|
||||
req->WaitEvent = (EVENT*)0;
|
||||
}
|
||||
if (req->CompletionEvent) {
|
||||
ObDereferenceObject(&req->CompletionEvent->Header);
|
||||
req->CompletionEvent = (EVENT*)0;
|
||||
}
|
||||
if (req->File) {
|
||||
ObDereferenceObject(&req->File->Header);
|
||||
req->File = (FILE_OBJECT*)0;
|
||||
}
|
||||
if (req->LockedBuffer) {
|
||||
MmUnlockUserBuffer(req->LockedBuffer);
|
||||
req->LockedBuffer = (MM_LOCKED_USER_BUFFER*)0;
|
||||
}
|
||||
if (req->LockedIosb) {
|
||||
MmUnlockUserBuffer(req->LockedIosb);
|
||||
req->LockedIosb = (MM_LOCKED_USER_BUFFER*)0;
|
||||
}
|
||||
}
|
||||
|
||||
static void KiIoCompleteAsyncFileRequest(IO_ASYNC_FILE_REQUEST_OBJECT* req,
|
||||
KSTATUS status,
|
||||
size_t information) {
|
||||
if (!req) return;
|
||||
|
||||
req->IosbSnapshot.Status = status;
|
||||
req->IosbSnapshot.Information = information;
|
||||
// Cancels an async file request from the common async request surface.
|
||||
static KSTATUS KiIoCancelAsyncFileRequest(IO_ASYNC_REQUEST_OBJECT* async, void* context) {
|
||||
(void)context;
|
||||
if (!async) return STATUS_INVALID_PARAMETER;
|
||||
|
||||
uintptr_t flags = KeAcquireSpinlock(&gAsyncFileQueueLock);
|
||||
req->State = (status == STATUS_CANCELLED) ? IoAsyncStateCancelled : IoAsyncStateCompleted;
|
||||
IO_ASYNC_REQUEST_STATE state = IoAsyncRequestGetState(async);
|
||||
|
||||
if (state == IoAsyncRequestQueued) {
|
||||
ListRemove(&async->Link);
|
||||
IoAsyncRequestRequestCancel(async);
|
||||
IoAsyncRequestSetState(async, IoAsyncRequestCancelled);
|
||||
KeReleaseSpinlock(&gAsyncFileQueueLock, flags);
|
||||
|
||||
IoCompleteAsyncRequest(async, STATUS_CANCELLED, 0);
|
||||
ObDereferenceObject(&async->Header);
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
if (state == IoAsyncRequestRunning) {
|
||||
IoAsyncRequestRequestCancel(async);
|
||||
KeReleaseSpinlock(&gAsyncFileQueueLock, flags);
|
||||
return STATUS_PENDING;
|
||||
}
|
||||
|
||||
KeReleaseSpinlock(&gAsyncFileQueueLock, flags);
|
||||
|
||||
if (req->Iosb) {
|
||||
req->Iosb->Status = status;
|
||||
req->Iosb->Information = information;
|
||||
}
|
||||
|
||||
// Match IRP completion ordering: queue APC first, then signal the event.
|
||||
if (req->CompletionApc) {
|
||||
if (!KeInsertQueueApc(req->CompletionApc, req->Iosb, (void*)(uintptr_t)status)) {
|
||||
if (req->CompletionApc->RundownRoutine)
|
||||
req->CompletionApc->RundownRoutine(req->CompletionApc);
|
||||
}
|
||||
}
|
||||
|
||||
if (req->CompletionEvent)
|
||||
KeSignalEvent(req->CompletionEvent);
|
||||
|
||||
if (req->WaitEvent)
|
||||
KeSignalEvent(req->WaitEvent);
|
||||
|
||||
if (req->LockedBuffer) {
|
||||
MmUnlockUserBuffer(req->LockedBuffer);
|
||||
req->LockedBuffer = (MM_LOCKED_USER_BUFFER*)0;
|
||||
}
|
||||
if (req->LockedIosb) {
|
||||
MmUnlockUserBuffer(req->LockedIosb);
|
||||
req->LockedIosb = (MM_LOCKED_USER_BUFFER*)0;
|
||||
}
|
||||
return STATUS_OBJECT_DEAD;
|
||||
}
|
||||
|
||||
// Implements the io Async File Dequeue operation.
|
||||
@@ -306,12 +260,12 @@ static IO_ASYNC_FILE_REQUEST_OBJECT* KiIoAsyncFileDequeue(void) {
|
||||
}
|
||||
|
||||
IO_ASYNC_FILE_REQUEST_OBJECT* req =
|
||||
CONTAINING_RECORD(node, IO_ASYNC_FILE_REQUEST_OBJECT, Link);
|
||||
(IO_ASYNC_FILE_REQUEST_OBJECT*)CONTAINING_RECORD(node, IO_ASYNC_REQUEST_OBJECT, Link);
|
||||
|
||||
if (req->CancelRequested) {
|
||||
req->State = IoAsyncStateCancelled;
|
||||
if (IoAsyncRequestIsCancelRequested(&req->Async)) {
|
||||
IoAsyncRequestSetState(&req->Async, IoAsyncRequestCancelled);
|
||||
} else {
|
||||
req->State = IoAsyncStateRunning;
|
||||
IoAsyncRequestSetState(&req->Async, IoAsyncRequestRunning);
|
||||
}
|
||||
KeReleaseSpinlock(&gAsyncFileQueueLock, flags);
|
||||
return req;
|
||||
@@ -331,9 +285,9 @@ static void KiIoAsyncFileWorker(void* arg) {
|
||||
IO_ASYNC_FILE_REQUEST_OBJECT* req = KiIoAsyncFileDequeue();
|
||||
if (!req) continue;
|
||||
|
||||
if (req->CancelRequested || req->State == IoAsyncStateCancelled) {
|
||||
KiIoCompleteAsyncFileRequest(req, STATUS_CANCELLED, 0);
|
||||
ObDereferenceObject(&req->Header); // queue/worker reference
|
||||
if (IoAsyncRequestIsCancelRequested(&req->Async) || IoAsyncRequestGetState(&req->Async) == IoAsyncRequestCancelled) {
|
||||
IoCompleteAsyncRequest(&req->Async, STATUS_CANCELLED, 0);
|
||||
ObDereferenceObject(&req->Async.Header); // queue/worker reference
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -346,7 +300,7 @@ static void KiIoAsyncFileWorker(void* arg) {
|
||||
req->Size, &done);
|
||||
}
|
||||
|
||||
if (req->CancelRequested) {
|
||||
if (IoAsyncRequestIsCancelRequested(&req->Async)) {
|
||||
// The operation may already have reached the filesystem. The
|
||||
// completion contract still reports cancellation so waiters/APCs do
|
||||
// not mistake a cancelled request for useful data.
|
||||
@@ -354,8 +308,8 @@ static void KiIoAsyncFileWorker(void* arg) {
|
||||
done = 0;
|
||||
}
|
||||
|
||||
KiIoCompleteAsyncFileRequest(req, s, done);
|
||||
ObDereferenceObject(&req->Header); // queue/worker reference
|
||||
IoCompleteAsyncRequest(&req->Async, s, done);
|
||||
ObDereferenceObject(&req->Async.Header); // queue/worker reference
|
||||
}
|
||||
}
|
||||
|
||||
@@ -404,13 +358,13 @@ static void KiIoNativeFsAsyncComplete(void* completeContext,
|
||||
(IO_ASYNC_FILE_REQUEST_OBJECT*)completeContext;
|
||||
if (!req) return;
|
||||
|
||||
if (req->CancelRequested) {
|
||||
if (IoAsyncRequestIsCancelRequested(&req->Async)) {
|
||||
status = STATUS_CANCELLED;
|
||||
information = 0;
|
||||
}
|
||||
|
||||
KiIoCompleteAsyncFileRequest(req, status, information);
|
||||
ObDereferenceObject(&req->Header); // native filesystem provider reference
|
||||
IoCompleteAsyncRequest(&req->Async, status, information);
|
||||
ObDereferenceObject(&req->Async.Header); // native filesystem provider reference
|
||||
}
|
||||
|
||||
static KSTATUS KiIoSubmitAsyncFileRequest(IO_ASYNC_FILE_OP op,
|
||||
@@ -443,45 +397,30 @@ static KSTATUS KiIoSubmitAsyncFileRequest(IO_ASYNC_FILE_OP op,
|
||||
iosb->Status = STATUS_PENDING;
|
||||
iosb->Information = 0;
|
||||
|
||||
IO_ASYNC_FILE_REQUEST_OBJECT* req =
|
||||
(IO_ASYNC_FILE_REQUEST_OBJECT*)ObAllocateObject(ObjectTypeAsyncIo,
|
||||
sizeof(IO_ASYNC_FILE_REQUEST_OBJECT));
|
||||
if (!req) {
|
||||
iosb->Status = STATUS_NO_MEMORY;
|
||||
IO_ASYNC_REQUEST_OBJECT* async = (IO_ASYNC_REQUEST_OBJECT*)0;
|
||||
KSTATUS createStatus = IoCreateAsyncRequest(sizeof(IO_ASYNC_FILE_REQUEST_OBJECT),
|
||||
KiIoAsyncFileRequestCleanup,
|
||||
(void*)0,
|
||||
KiIoCancelAsyncFileRequest,
|
||||
(void*)0,
|
||||
&async);
|
||||
if (!KSUCCESS(createStatus)) {
|
||||
iosb->Status = createStatus;
|
||||
MmUnlockUserBuffer(lockedBuffer);
|
||||
MmUnlockUserBuffer(lockedIosb);
|
||||
return STATUS_NO_MEMORY;
|
||||
return createStatus;
|
||||
}
|
||||
|
||||
req->Header.Destroy = KiIoAsyncFileRequestDestroy;
|
||||
LIST_INIT(&req->Link);
|
||||
IO_ASYNC_FILE_REQUEST_OBJECT* req = (IO_ASYNC_FILE_REQUEST_OBJECT*)async;
|
||||
req->Op = op;
|
||||
req->State = IoAsyncStateQueued;
|
||||
req->CancelRequested = 0;
|
||||
KSTATUS waitStatus = KeEventCreate(EventManualReset, 0, &req->WaitEvent);
|
||||
if (!KSUCCESS(waitStatus)) {
|
||||
iosb->Status = waitStatus;
|
||||
MmUnlockUserBuffer(lockedBuffer);
|
||||
MmUnlockUserBuffer(lockedIosb);
|
||||
ObDereferenceObject(&req->Header);
|
||||
return waitStatus;
|
||||
}
|
||||
|
||||
req->File = file;
|
||||
req->CompletionEvent = completionEvent;
|
||||
req->CompletionApc = completionApc;
|
||||
req->Iosb = iosb;
|
||||
req->IosbSnapshot.Status = STATUS_PENDING;
|
||||
req->IosbSnapshot.Information = 0;
|
||||
req->LockedBuffer = lockedBuffer;
|
||||
req->LockedIosb = lockedIosb;
|
||||
req->Buffer = buffer;
|
||||
req->Size = size;
|
||||
req->Offset = offset;
|
||||
|
||||
IoInitializeAsyncRequestCompletion(&req->Async, iosb, completionEvent,
|
||||
completionApc, lockedBuffer, lockedIosb);
|
||||
ObReferenceObject(&file->Header);
|
||||
if (completionEvent)
|
||||
ObReferenceObject(&completionEvent->Header);
|
||||
|
||||
IO_FS_ASYNC_COMPLETE complete = KiIoNativeFsAsyncComplete;
|
||||
KSTATUS ns = STATUS_NOT_SUPPORTED;
|
||||
@@ -490,10 +429,10 @@ static KSTATUS KiIoSubmitAsyncFileRequest(IO_ASYNC_FILE_OP op,
|
||||
|
||||
if (hasNative) {
|
||||
uintptr_t flags = KeAcquireSpinlock(&gAsyncFileQueueLock);
|
||||
req->State = IoAsyncStateRunning;
|
||||
IoAsyncRequestSetState(&req->Async, IoAsyncRequestRunning);
|
||||
KeReleaseSpinlock(&gAsyncFileQueueLock, flags);
|
||||
|
||||
ObReferenceObject(&req->Header); // native filesystem provider reference
|
||||
ObReferenceObject(&req->Async.Header); // native filesystem provider reference
|
||||
if (op == IoAsyncFileRead) {
|
||||
ns = file->Fs->ReadAsync(file->Fs, file->FileCtx, offset, buffer,
|
||||
size, complete, req);
|
||||
@@ -505,34 +444,34 @@ static KSTATUS KiIoSubmitAsyncFileRequest(IO_ASYNC_FILE_OP op,
|
||||
if (requestOut) {
|
||||
*requestOut = req;
|
||||
} else {
|
||||
ObDereferenceObject(&req->Header);
|
||||
ObDereferenceObject(&req->Async.Header);
|
||||
}
|
||||
return STATUS_PENDING;
|
||||
}
|
||||
|
||||
ObDereferenceObject(&req->Header); // provider rejected synchronously
|
||||
ObDereferenceObject(&req->Async.Header); // provider rejected synchronously
|
||||
iosb->Status = ns;
|
||||
req->IosbSnapshot.Status = ns;
|
||||
req->IosbSnapshot.Information = 0;
|
||||
ObDereferenceObject(&req->Header); // creator reference
|
||||
req->Async.IosbSnapshot.Status = ns;
|
||||
req->Async.IosbSnapshot.Information = 0;
|
||||
ObDereferenceObject(&req->Async.Header); // creator reference
|
||||
return ns;
|
||||
}
|
||||
|
||||
KSTATUS s = KiIoEnsureAsyncFileIoStarted();
|
||||
if (!KSUCCESS(s)) {
|
||||
iosb->Status = s;
|
||||
req->IosbSnapshot.Status = s;
|
||||
req->IosbSnapshot.Information = 0;
|
||||
ObDereferenceObject(&req->Header);
|
||||
req->Async.IosbSnapshot.Status = s;
|
||||
req->Async.IosbSnapshot.Information = 0;
|
||||
ObDereferenceObject(&req->Async.Header);
|
||||
return s;
|
||||
}
|
||||
|
||||
// Queue ownership keeps the request alive until the worker completes or a
|
||||
// successful queued cancellation completes it from IoCancelAsyncFileRequest.
|
||||
ObReferenceObject(&req->Header);
|
||||
ObReferenceObject(&req->Async.Header);
|
||||
|
||||
uintptr_t qflags = KeAcquireSpinlock(&gAsyncFileQueueLock);
|
||||
ListInsertTail(&gAsyncFileQueue, &req->Link);
|
||||
ListInsertTail(&gAsyncFileQueue, &req->Async.Link);
|
||||
KeReleaseSpinlock(&gAsyncFileQueueLock, qflags);
|
||||
|
||||
KeSignalSemaphore(gAsyncFileSemaphore);
|
||||
@@ -540,7 +479,7 @@ static KSTATUS KiIoSubmitAsyncFileRequest(IO_ASYNC_FILE_OP op,
|
||||
if (requestOut) {
|
||||
*requestOut = req; // creator reference returned to caller
|
||||
} else {
|
||||
ObDereferenceObject(&req->Header);
|
||||
ObDereferenceObject(&req->Async.Header);
|
||||
}
|
||||
|
||||
return STATUS_PENDING;
|
||||
@@ -574,56 +513,28 @@ __declspec(dllexport) KSTATUS IoWriteFileAsyncKernel(FILE_OBJECT* file, const vo
|
||||
|
||||
// Implements the async File Request Object operation.
|
||||
__declspec(dllexport) OBJECT* IoAsyncFileRequestObject(IO_ASYNC_FILE_REQUEST_OBJECT* req) {
|
||||
return req ? &req->Header : (OBJECT*)0;
|
||||
return req ? &req->Async.Header : (OBJECT*)0;
|
||||
}
|
||||
|
||||
// Returns the wait event owned by an async file request.
|
||||
__declspec(dllexport) EVENT* IoGetAsyncFileRequestWaitEvent(IO_ASYNC_FILE_REQUEST_OBJECT* req) {
|
||||
return req ? req->WaitEvent : (EVENT*)0;
|
||||
return req ? IoGetAsyncRequestWaitEvent(&req->Async) : (EVENT*)0;
|
||||
}
|
||||
|
||||
// Waits for an async file request to complete according to wait flags.
|
||||
__declspec(dllexport) KSTATUS IoWaitAsyncFileRequest(IO_ASYNC_FILE_REQUEST_OBJECT* req, uint32_t flags) {
|
||||
if (!req || !req->WaitEvent) return STATUS_INVALID_PARAMETER;
|
||||
if (flags & ~SYS_WAIT_FLAG_ALERTABLE) return STATUS_INVALID_PARAMETER;
|
||||
return KeWaitForEvent(req->WaitEvent, (flags & SYS_WAIT_FLAG_ALERTABLE) ? 1 : 0);
|
||||
return IoWaitAsyncRequest(req ? &req->Async : (IO_ASYNC_REQUEST_OBJECT*)0, flags);
|
||||
}
|
||||
|
||||
// Cancels async File Request.
|
||||
// Cancels an async file request through the common async I/O object.
|
||||
__declspec(dllexport) KSTATUS IoCancelAsyncFileRequest(IO_ASYNC_FILE_REQUEST_OBJECT* req) {
|
||||
if (!req) return STATUS_INVALID_PARAMETER;
|
||||
|
||||
IO_ASYNC_FILE_STATE state;
|
||||
uintptr_t flags = KeAcquireSpinlock(&gAsyncFileQueueLock);
|
||||
state = req->State;
|
||||
|
||||
if (state == IoAsyncStateQueued) {
|
||||
ListRemove(&req->Link);
|
||||
req->CancelRequested = 1;
|
||||
req->State = IoAsyncStateCancelled;
|
||||
KeReleaseSpinlock(&gAsyncFileQueueLock, flags);
|
||||
|
||||
KiIoCompleteAsyncFileRequest(req, STATUS_CANCELLED, 0);
|
||||
ObDereferenceObject(&req->Header); // queue reference removed above
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
if (state == IoAsyncStateRunning) {
|
||||
req->CancelRequested = 1;
|
||||
KeReleaseSpinlock(&gAsyncFileQueueLock, flags);
|
||||
return STATUS_PENDING;
|
||||
}
|
||||
|
||||
KeReleaseSpinlock(&gAsyncFileQueueLock, flags);
|
||||
return STATUS_OBJECT_DEAD;
|
||||
return IoCancelAsyncRequest(req ? &req->Async : (IO_ASYNC_REQUEST_OBJECT*)0);
|
||||
}
|
||||
|
||||
__declspec(dllexport) KSTATUS IoQueryAsyncFileRequest(IO_ASYNC_FILE_REQUEST_OBJECT* req,
|
||||
IO_STATUS_BLOCK* iosbOut) {
|
||||
if (!req || !iosbOut) return STATUS_INVALID_PARAMETER;
|
||||
iosbOut->Status = req->IosbSnapshot.Status;
|
||||
iosbOut->Information = req->IosbSnapshot.Information;
|
||||
return STATUS_SUCCESS;
|
||||
return IoQueryAsyncRequest(req ? &req->Async : (IO_ASYNC_REQUEST_OBJECT*)0,
|
||||
iosbOut);
|
||||
}
|
||||
|
||||
// Current Working Directory
|
||||
|
||||
Reference in new issue
Block a user