|
|
//
// 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); }
|