You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
638 lines
11 KiB
638 lines
11 KiB
|
|
//
|
|
// Test/simulation program for the extended device queue
|
|
//
|
|
|
|
#include "raidport.h"
|
|
#include "exqueue.h"
|
|
|
|
#define SECONDS (1000)
|
|
|
|
typedef struct _TQ_ITEM {
|
|
ULONG Thread;
|
|
ULONG64 Data;
|
|
ULONG64 Sequence;
|
|
KDEVICE_QUEUE_ENTRY DeviceEntry;
|
|
LIST_ENTRY DpcEntry;
|
|
} TQ_ITEM, *PTQ_ITEM;
|
|
|
|
|
|
typedef struct _DPC_QUEUE {
|
|
HANDLE Available;
|
|
HANDLE Mutex;
|
|
LIST_ENTRY Queue;
|
|
ULONG Count;
|
|
} DPC_QUEUE, *PDPC_QUEUE;
|
|
|
|
|
|
BOOL Verbose = FALSE;
|
|
BOOL Cerbose = TRUE;
|
|
DPC_QUEUE DpcQueue;
|
|
EXTENDED_DEVICE_QUEUE DeviceQueue;
|
|
BOOLEAN Pause = FALSE;
|
|
BOOLEAN PauseProducer = FALSE;
|
|
ULONG64 GlobalSequence = 0;
|
|
DWORD Owner = 0;
|
|
BOOL Available = FALSE;
|
|
|
|
|
|
VOID
|
|
GetNext(
|
|
OUT PULONG64 Sequence,
|
|
OUT PULONG Data
|
|
)
|
|
{
|
|
*Sequence = ++GlobalSequence;
|
|
*Data = (ULONG)rand();
|
|
}
|
|
|
|
#pragma warning (disable:4715)
|
|
|
|
BOOL
|
|
WINAPI
|
|
UpdateThread(
|
|
PVOID Unused
|
|
)
|
|
{
|
|
for (;;) {
|
|
|
|
while (Pause) {
|
|
Sleep (500);
|
|
}
|
|
|
|
printf ("Seq: %I64d, dev %d bypass %d dpc %d \r",
|
|
GlobalSequence,
|
|
DeviceQueue.DeviceRequests,
|
|
DeviceQueue.ByPassRequests,
|
|
DpcQueue.Count);
|
|
Sleep (2000);
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
VOID
|
|
CreateDpcQueue(
|
|
PDPC_QUEUE DpcQueue
|
|
)
|
|
{
|
|
DpcQueue->Available = CreateEvent (NULL, FALSE, FALSE, NULL);
|
|
DpcQueue->Mutex = CreateMutex (NULL, FALSE, NULL);
|
|
InitializeListHead (&DpcQueue->Queue);
|
|
DpcQueue->Count = 0;
|
|
}
|
|
|
|
|
|
VOID
|
|
InsertDpcItem(
|
|
IN PDPC_QUEUE DpcQueue,
|
|
IN PLIST_ENTRY Entry
|
|
)
|
|
{
|
|
WaitForSingleObject (DpcQueue->Mutex, INFINITE);
|
|
InsertTailList (&DpcQueue->Queue, Entry);
|
|
DpcQueue->Count++;
|
|
Owner = GetCurrentThreadId ();
|
|
Available = TRUE;
|
|
SetEvent (DpcQueue->Available);
|
|
ReleaseMutex (DpcQueue->Mutex);
|
|
}
|
|
|
|
PLIST_ENTRY
|
|
RemoveDpcItem(
|
|
IN PDPC_QUEUE DpcQueue
|
|
)
|
|
{
|
|
PLIST_ENTRY Entry;
|
|
HANDLE Tuple[2];
|
|
|
|
Tuple[0] = DpcQueue->Mutex;
|
|
Tuple[1] = DpcQueue->Available;
|
|
|
|
WaitForMultipleObjects (2, Tuple, TRUE, INFINITE);
|
|
Available = FALSE;
|
|
Owner = GetCurrentThreadId ();
|
|
ASSERT (!IsListEmpty (&DpcQueue->Queue));
|
|
Entry = RemoveHeadList (&DpcQueue->Queue);
|
|
DpcQueue->Count--;
|
|
if (!IsListEmpty (&DpcQueue->Queue)) {
|
|
Available = TRUE;
|
|
SetEvent (DpcQueue->Available);
|
|
}
|
|
ReleaseMutex (DpcQueue->Mutex);
|
|
|
|
return Entry;
|
|
}
|
|
|
|
|
|
BOOL
|
|
WINAPI
|
|
ProducerThread(
|
|
PVOID Unused
|
|
)
|
|
{
|
|
BOOLEAN Inserted;
|
|
PTQ_ITEM Item;
|
|
ULONG Data;
|
|
ULONG64 Sequence;
|
|
|
|
srand (GetCurrentThreadId());
|
|
|
|
for (;;) {
|
|
|
|
Sleep (10);
|
|
|
|
while (Pause) {
|
|
Sleep (100);
|
|
}
|
|
|
|
while (PauseProducer) {
|
|
Sleep (100);
|
|
}
|
|
|
|
Item = malloc (sizeof (TQ_ITEM));
|
|
ZeroMemory (Item, sizeof (*Item));
|
|
|
|
GetNext (&Sequence, &Data);
|
|
Item->Thread = GetCurrentThreadId ();
|
|
Item->Sequence = Sequence;
|
|
Item->Data = Data;
|
|
|
|
Inserted = RaidInsertExDeviceQueue (&DeviceQueue,
|
|
&Item->DeviceEntry,
|
|
FALSE,
|
|
(ULONG)Item->Data);
|
|
|
|
if (!Inserted) {
|
|
|
|
if (Verbose) {
|
|
printf ("started %x.%I64d\n", Item->Thread, Item->Data);
|
|
}
|
|
|
|
InsertDpcItem (&DpcQueue, &Item->DpcEntry);
|
|
|
|
} else {
|
|
if (Verbose) {
|
|
printf ("queued %x.%I64d\n", Item->Thread, Item->Data);
|
|
}
|
|
}
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
BOOL
|
|
WINAPI
|
|
ProducerThread100(
|
|
PVOID Unused
|
|
)
|
|
{
|
|
BOOLEAN Inserted;
|
|
PTQ_ITEM Item;
|
|
ULONG Data;
|
|
ULONG64 Sequence;
|
|
|
|
srand (GetCurrentThreadId());
|
|
|
|
for (;;) {
|
|
|
|
Sleep (0);
|
|
|
|
while (Pause) {
|
|
Sleep (100);
|
|
}
|
|
|
|
while (PauseProducer) {
|
|
Sleep (100);
|
|
}
|
|
|
|
Item = malloc (sizeof (TQ_ITEM));
|
|
ZeroMemory (Item, sizeof (*Item));
|
|
|
|
GetNext (&Sequence, &Data);
|
|
Item->Thread = GetCurrentThreadId ();
|
|
Item->Sequence = Sequence;
|
|
Item->Data = 100;
|
|
|
|
Inserted = RaidInsertExDeviceQueue (&DeviceQueue,
|
|
&Item->DeviceEntry,
|
|
FALSE,
|
|
(ULONG)Item->Data);
|
|
|
|
if (!Inserted) {
|
|
|
|
if (Verbose) {
|
|
printf ("started %x.%I64d\n", Item->Thread, Item->Data);
|
|
}
|
|
|
|
InsertDpcItem (&DpcQueue, &Item->DpcEntry);
|
|
|
|
} else {
|
|
if (Verbose) {
|
|
printf ("queued %x.%I64d\n", Item->Thread, Item->Data);
|
|
}
|
|
}
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
BOOL
|
|
WINAPI
|
|
ErrorProducerThread(
|
|
PVOID Unused
|
|
)
|
|
{
|
|
BOOLEAN Inserted;
|
|
PTQ_ITEM Item;
|
|
ULONG64 Sequence;
|
|
ULONG Data;
|
|
|
|
for (;;) {
|
|
|
|
Sleep (500);
|
|
|
|
while (Pause) {
|
|
Sleep (10);
|
|
}
|
|
|
|
Item = malloc (sizeof (TQ_ITEM));
|
|
ZeroMemory (Item, sizeof (*Item));
|
|
|
|
GetNext (&Sequence, &Data);
|
|
Item->Thread = GetCurrentThreadId ();
|
|
Item->Sequence = Sequence;
|
|
Item->Data = Data;
|
|
|
|
Inserted = RaidInsertExDeviceQueue (&DeviceQueue,
|
|
&Item->DeviceEntry,
|
|
TRUE,
|
|
(ULONG)Item->Data);
|
|
|
|
if (!Inserted) {
|
|
|
|
if (Verbose) {
|
|
printf ("started %x.%I64d\n", Item->Thread, Item->Data);
|
|
}
|
|
|
|
InsertDpcItem (&DpcQueue, &Item->DpcEntry);
|
|
|
|
} else {
|
|
if (Verbose) {
|
|
printf ("queued %x.%I64d\n", Item->Thread, Item->Data);
|
|
}
|
|
}
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
VOID
|
|
DpcRoutine(
|
|
IN PTQ_ITEM Item
|
|
)
|
|
{
|
|
BOOLEAN RestartQueue;
|
|
PKDEVICE_QUEUE_ENTRY Entry;
|
|
|
|
if (Verbose || Cerbose) {
|
|
printf ("completed %x.%I64d\n", Item->Thread, Item->Data);
|
|
}
|
|
|
|
free (Item);
|
|
|
|
Entry = RaidRemoveExDeviceQueue (&DeviceQueue, &RestartQueue);
|
|
|
|
if (Entry) {
|
|
Item = CONTAINING_RECORD (Entry, TQ_ITEM, DeviceEntry);
|
|
InsertDpcItem (&DpcQueue, &Item->DpcEntry);
|
|
if (Verbose) {
|
|
printf ("dpc started %x.%I64d\n", Item->Thread, Item->Data);
|
|
}
|
|
|
|
//
|
|
// NB: in the port driver, we actually only do this when necessary.
|
|
// The only problem with doing this always is a speed issue, and
|
|
// we're not measuring speed in the simulation program.
|
|
//
|
|
|
|
for (Entry = RaidNormalizeExDeviceQueue (&DeviceQueue);
|
|
Entry != NULL;
|
|
Entry = RaidNormalizeExDeviceQueue (&DeviceQueue)) {
|
|
|
|
Item = CONTAINING_RECORD (Entry, TQ_ITEM, DeviceEntry);
|
|
InsertDpcItem (&DpcQueue, &Item->DpcEntry);
|
|
}
|
|
|
|
|
|
}
|
|
}
|
|
|
|
|
|
BOOL
|
|
WINAPI
|
|
DpcThread(
|
|
PVOID Unused
|
|
)
|
|
{
|
|
PLIST_ENTRY Entry;
|
|
PTQ_ITEM Item;
|
|
|
|
#if 1
|
|
SetThreadPriority (GetCurrentThread (),
|
|
THREAD_PRIORITY_ABOVE_NORMAL);
|
|
#endif
|
|
|
|
for (;;) {
|
|
|
|
while (Pause) {
|
|
Sleep (10);
|
|
}
|
|
|
|
//
|
|
// Wait for DPC queue to have an item in it
|
|
//
|
|
|
|
Entry = RemoveDpcItem (&DpcQueue);
|
|
Item = CONTAINING_RECORD (Entry, TQ_ITEM, DpcEntry);
|
|
|
|
DpcRoutine (Item);
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
enum {
|
|
NoControl = 0,
|
|
FreezeQueue,
|
|
ResumeQueue
|
|
};
|
|
|
|
volatile ULONG Control = NoControl;
|
|
|
|
BOOL
|
|
WINAPI
|
|
ControlThread(
|
|
PVOID Unused
|
|
)
|
|
{
|
|
for (;;) {
|
|
|
|
Sleep (500);
|
|
|
|
switch (Control) {
|
|
case FreezeQueue:
|
|
RaidFreezeExDeviceQueue (&DeviceQueue);
|
|
Control = NoControl;
|
|
break;
|
|
|
|
case ResumeQueue:
|
|
RaidResumeExDeviceQueue (&DeviceQueue);
|
|
Control = NoControl;
|
|
break;
|
|
|
|
case NoControl:
|
|
break;
|
|
|
|
default:
|
|
ASSERT (FALSE);
|
|
}
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
BOOL
|
|
WINAPI
|
|
ControlHandler(
|
|
DWORD Val
|
|
)
|
|
{
|
|
PLIST_ENTRY NextEntry;
|
|
PTQ_ITEM Item;
|
|
int ch;
|
|
|
|
if (Val != CTRL_C_EVENT) {
|
|
return FALSE;
|
|
}
|
|
|
|
Pause = TRUE;
|
|
Sleep (1000);
|
|
printf ("\n");
|
|
|
|
do {
|
|
printf ("tq> ");
|
|
ch = getchar ();
|
|
printf (" %c\n", ch);
|
|
|
|
switch (tolower (ch)) {
|
|
|
|
case 'd':
|
|
|
|
printf ("Dump of DeviceQueue: %p\n", &DeviceQueue);
|
|
printf (" Depth %d\n", DeviceQueue.Depth);
|
|
printf (" Outstanding %d\n", DeviceQueue.OutstandingRequests);
|
|
printf (" Device %d\n", DeviceQueue.DeviceRequests);
|
|
printf (" ByPass %d\n", DeviceQueue.ByPassRequests);
|
|
printf (" Frozen %d\n", DeviceQueue.Frozen);
|
|
break;
|
|
|
|
case 'l':
|
|
__try {
|
|
|
|
ULONG Count;
|
|
|
|
Count = 0;
|
|
for ( NextEntry = DeviceQueue.DeviceListHead.Flink;
|
|
NextEntry != &DeviceQueue.DeviceListHead;
|
|
NextEntry = NextEntry->Flink ) {
|
|
Count++;
|
|
|
|
Item = CONTAINING_RECORD (NextEntry, TQ_ITEM, DeviceEntry);
|
|
printf (" item %d.%I64d [seq=%d]\n",
|
|
Item->Thread,
|
|
Item->Data,
|
|
Item->Sequence);
|
|
}
|
|
|
|
printf ("DeviceList: %d entries\n", Count);
|
|
}
|
|
|
|
__except (EXCEPTION_EXECUTE_HANDLER) {
|
|
printf ("ERROR: Inconsistent device list!\n");
|
|
}
|
|
break;
|
|
|
|
case 'r':
|
|
Control = ResumeQueue;
|
|
break;
|
|
|
|
case 'f':
|
|
Control = FreezeQueue;
|
|
break;
|
|
|
|
case 'q':
|
|
exit (0);
|
|
break;
|
|
|
|
case 'c':
|
|
case 'g':
|
|
break;
|
|
|
|
case 'p':
|
|
if (PauseProducer) {
|
|
printf ("unpausing producers\n");
|
|
PauseProducer = FALSE;
|
|
} else {
|
|
printf ("unpausing producers\n");
|
|
PauseProducer = TRUE;
|
|
}
|
|
break;
|
|
|
|
case 'v':
|
|
if (Verbose) {
|
|
Verbose = FALSE;
|
|
printf ("verbose mode off\n");
|
|
} else {
|
|
Verbose = TRUE;
|
|
printf ("verbose mode enabled\n");
|
|
}
|
|
|
|
case 'x': {
|
|
ULONG NewDepth;
|
|
printf ("depth> ");
|
|
scanf ("%d", &NewDepth);
|
|
RaidSetExDeviceQueueDepth (&DeviceQueue, NewDepth);
|
|
printf ("stack depth set to %d\n", NewDepth);
|
|
break;
|
|
}
|
|
|
|
case '?':
|
|
printf (" d - dump queue\n");
|
|
printf (" f - freeze queue\n");
|
|
printf (" r - resume queue\n");
|
|
printf (" p - toggle pause of producer threads\n");
|
|
printf (" u - resume producer threads\n");
|
|
printf (" g - go\n");
|
|
printf (" x - set stack depth to new value\n");
|
|
printf (" v - toggle verbose mode\n");
|
|
printf (" q - stop\n");
|
|
break;
|
|
|
|
default:
|
|
printf ("unrecognized operation '%c'\n", ch);
|
|
}
|
|
|
|
} while (ch != 'c' && ch != 'g');
|
|
|
|
|
|
Pause = FALSE;
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
VOID
|
|
__cdecl
|
|
main(
|
|
)
|
|
{
|
|
DWORD ThreadId;
|
|
ULONG Depth;
|
|
ULONG ProducerThreads;
|
|
ULONG DpcThreads;
|
|
ULONG i;
|
|
SCHEDULING_ALGORITHM SchedulingAlgorithm;
|
|
RAID_ADAPTER_QUEUE AdapterQueue;
|
|
QUEUING_MODEL QueuingModel;
|
|
|
|
//
|
|
// Generic adapter queue.
|
|
//
|
|
|
|
QueuingModel.Algorithm = BackOffFullQueue;
|
|
QueuingModel.BackOff.HighWaterPercent = 120;
|
|
QueuingModel.BackOff.LowWaterPercent = 40;
|
|
|
|
RaidCreateAdapterQueue (&AdapterQueue, &QueuingModel);
|
|
|
|
//
|
|
// NB: these should all be parameters
|
|
//
|
|
|
|
Depth = 1;
|
|
ProducerThreads = 8;
|
|
DpcThreads = 1;
|
|
SchedulingAlgorithm = CScanScheduling;
|
|
|
|
printf ("DeviceQueue Test: Depth %d Producers %d DPC Threads %d\n",
|
|
Depth,
|
|
ProducerThreads,
|
|
DpcThreads);
|
|
|
|
if (Verbose) {
|
|
printf ("Verbose\n");
|
|
}
|
|
printf ("\n");
|
|
|
|
CreateDpcQueue (&DpcQueue);
|
|
RaidInitializeExDeviceQueue (&DeviceQueue,
|
|
&AdapterQueue,
|
|
Depth,
|
|
SchedulingAlgorithm);
|
|
|
|
SetConsoleCtrlHandler (ControlHandler, TRUE);
|
|
|
|
for (i = 0; i < DpcThreads; i++) {
|
|
CreateThread (NULL,
|
|
0,
|
|
DpcThread,
|
|
0,
|
|
0,
|
|
&ThreadId);
|
|
}
|
|
|
|
for (i = 0; i < ProducerThreads; i++) {
|
|
|
|
CreateThread (NULL,
|
|
0,
|
|
ProducerThread,
|
|
0,
|
|
0,
|
|
&ThreadId);
|
|
}
|
|
|
|
CreateThread (NULL,
|
|
0,
|
|
ControlThread,
|
|
0,
|
|
0,
|
|
&ThreadId);
|
|
|
|
|
|
CreateThread (NULL,
|
|
0,
|
|
ProducerThread100,
|
|
0,
|
|
0,
|
|
&ThreadId);
|
|
#if 0
|
|
CreateThread (NULL,
|
|
0,
|
|
ErrorProducerThread,
|
|
0,
|
|
0,
|
|
&ThreadId);
|
|
|
|
CreateThread (NULL,
|
|
0,
|
|
UpdateThread,
|
|
0,
|
|
0,
|
|
&ThreadId);
|
|
|
|
#endif
|
|
Sleep (INFINITE);
|
|
}
|
|
|
|
|