[aoe] Use floating FDO strategy
[people/sha0/winvblock.git] / src / aoe / driver.c
1 /**
2  * Copyright (C) 2009-2010, Shao Miller <shao.miller@yrdsb.edu.on.ca>.
3  * Copyright 2006-2008, V.
4  * For WinAoE contact information, see http://winaoe.org/
5  *
6  * This file is part of WinVBlock, derived from WinAoE.
7  *
8  * WinVBlock is free software: you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License as published by
10  * the Free Software Foundation, either version 3 of the License, or
11  * (at your option) any later version.
12  *
13  * WinVBlock is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  * GNU General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License
19  * along with WinVBlock.  If not, see <http://www.gnu.org/licenses/>.
20  */
21
22 /**
23  * @file
24  *
25  * AoE specifics.
26  */
27
28 #include <stdio.h>
29 #include <ntddk.h>
30 #include <scsi.h>
31
32 #include "winvblock.h"
33 #include "wv_stdlib.h"
34 #include "wv_string.h"
35 #include "portable.h"
36 #include "driver.h"
37 #include "bus.h"
38 #include "device.h"
39 #include "disk.h"
40 #include "mount.h"
41 #include "aoe.h"
42 #include "registry.h"
43 #include "protocol.h"
44 #include "debug.h"
45
46 #define AOEPROTOCOLVER 1
47
48 extern NTSTATUS STDCALL ZwWaitForSingleObject(
49     IN HANDLE Handle,
50     IN winvblock__bool Alertable,
51     IN PLARGE_INTEGER Timeout OPTIONAL
52   );
53
54 /* From aoe/bus.c */
55 extern WV_S_BUS_T AoeBusMain;
56 extern winvblock__bool AoeBusCreate(IN PDRIVER_OBJECT);
57 extern void AoeBusFree(void);
58 extern NTSTATUS STDCALL AoeBusDevCtl(IN PIRP, IN ULONG POINTER_ALIGNMENT);
59 extern NTSTATUS STDCALL AoeBusAttachFdo(
60     IN PDRIVER_OBJECT,
61     IN PDEVICE_OBJECT
62   );
63 extern const WV_S_DRIVER_DUMMY_IDS * AoeBusDummyIds;
64 /* From aoe/registry.c */
65 extern winvblock__bool STDCALL AoeRegSetup(OUT PNTSTATUS);
66
67 /* Forward declarations. */
68 struct AOE_DISK_;
69 static void STDCALL AoeThread_(IN void *);
70 static void AoeProcessAbft_(void);
71 static void STDCALL AoeUnload_(IN PDRIVER_OBJECT);
72 static struct AOE_DISK_ * AoeDiskCreate_(void);
73 static WV_F_DEV_FREE AoeDiskFree_;
74 static WV_F_DISK_IO AoeDiskIo_;
75 static WV_F_DISK_MAX_XFER_LEN AoeDiskMaxXferLen_;
76 static WV_F_DISK_INIT AoeDiskInit_;
77 static WV_F_DISK_CLOSE AoeDiskClose_;
78 static driver__dispatch_func AoeDriverIrpNotSupported_;
79 static driver__dispatch_func AoeDriverIrpPower_;
80 static driver__dispatch_func AoeDriverIrpCreateClose_;
81 static driver__dispatch_func AoeDriverIrpSysCtl_;
82 static driver__dispatch_func AoeDriverIrpDevCtl_;
83 static driver__dispatch_func AoeDriverIrpScsi_;
84 static driver__dispatch_func AoeDriverIrpPnp_;
85 static void STDCALL AoeDriverUnload_(IN PDRIVER_OBJECT);
86
87 /** Tag types. */
88 typedef enum AOE_TAG_TYPE_ {
89     AoeTagTypeIo_,
90     AoeTagTypeSearchDrive_,
91     AoeTagTypes_
92   } AOE_E_TAG_TYPE_, * AOE_EP_TAG_TYPE_;
93
94 #ifdef _MSC_VER
95 #  pragma pack(1)
96 #endif
97 /** AoE packet. */
98 struct AOE_PACKET_ {
99     winvblock__uint8 ReservedFlag:2;
100     winvblock__uint8 ErrorFlag:1;
101     winvblock__uint8 ResponseFlag:1;
102     winvblock__uint8 Ver:4;
103     winvblock__uint8 Error;
104     winvblock__uint16 Major;
105     winvblock__uint8 Minor;
106     winvblock__uint8 Command;
107     winvblock__uint32 Tag;
108
109     winvblock__uint8 WriteAFlag:1;
110     winvblock__uint8 AsyncAFlag:1;
111     winvblock__uint8 Reserved1AFlag:2;
112     winvblock__uint8 DeviceHeadAFlag:1;
113     winvblock__uint8 Reserved2AFlag:1;
114     winvblock__uint8 ExtendedAFlag:1;
115     winvblock__uint8 Reserved3AFlag:1;
116     union {
117         winvblock__uint8 Err;
118         winvblock__uint8 Feature;
119       };
120     winvblock__uint8 Count;
121     union {
122         winvblock__uint8 Cmd;
123         winvblock__uint8 Status;
124       };
125
126     winvblock__uint8 Lba0;
127     winvblock__uint8 Lba1;
128     winvblock__uint8 Lba2;
129     winvblock__uint8 Lba3;
130     winvblock__uint8 Lba4;
131     winvblock__uint8 Lba5;
132     winvblock__uint16 Reserved;
133
134     winvblock__uint8 Data[];
135   } __attribute__((__packed__));
136 typedef struct AOE_PACKET_ AOE_S_PACKET_, * AOE_SP_PACKET_;
137 #ifdef _MSC_VER
138 #  pragma pack()
139 #endif
140
141 /** An I/O request. */
142 typedef struct AOE_IO_REQ_ {
143     WV_E_DISK_IO_MODE Mode;
144     winvblock__uint32 SectorCount;
145     winvblock__uint8_ptr Buffer;
146     PIRP Irp;
147     winvblock__uint32 TagCount;
148     winvblock__uint32 TotalTags;
149   } AOE_S_IO_REQ_, * AOE_SP_IO_REQ_;
150
151 /** A work item "tag". */
152 typedef struct AOE_WORK_TAG_ {
153     AOE_E_TAG_TYPE_ type;
154     WV_SP_DEV_T device;
155     AOE_SP_IO_REQ_ request_ptr;
156     winvblock__uint32 Id;
157     AOE_SP_PACKET_ packet_data;
158     winvblock__uint32 PacketSize;
159     LARGE_INTEGER FirstSendTime;
160     LARGE_INTEGER SendTime;
161     winvblock__uint32 BufferOffset;
162     winvblock__uint32 SectorCount;
163     struct AOE_WORK_TAG_ * next;
164     struct AOE_WORK_TAG_ * previous;
165   } AOE_S_WORK_TAG_, * AOE_SP_WORK_TAG_;
166
167 /** A disk search. */
168 typedef struct AOE_DISK_SEARCH_ {
169     WV_SP_DEV_T device;
170     AOE_SP_WORK_TAG_ tag;
171     struct AOE_DISK_SEARCH_ * next;
172   } AOE_S_DISK_SEARCH_, * AOE_SP_DISK_SEARCH_;
173
174 typedef enum AOE_SEARCH_STATE_ {
175     AoeSearchStateSearchNic_,
176     AoeSearchStateGetSize_,
177     AoeSearchStateGettingSize_,
178     AoeSearchStateGetGeometry_,
179     AoeSearchStateGettingGeometry_,
180     AoeSearchStateGetMaxSectsPerPacket_,
181     AoeSearchStateGettingMaxSectsPerPacket_,
182     AoeSearchStateDone_,
183     AoeSearchStates
184   } AOE_E_SEARCH_STATE_, * AOE_EP_SEARCH_STATE_;
185
186 /** The AoE disk type. */
187 typedef struct AOE_DISK_ {
188     WV_SP_DISK_T disk;
189     winvblock__uint32 MTU;
190     winvblock__uint8 ClientMac[6];
191     winvblock__uint8 ServerMac[6];
192     winvblock__uint32 Major;
193     winvblock__uint32 Minor;
194     winvblock__uint32 MaxSectorsPerPacket;
195     winvblock__uint32 Timeout;
196     AOE_E_SEARCH_STATE_ search_state;
197     WV_FP_DEV_FREE prev_free;
198     LIST_ENTRY tracking;
199   } AOE_S_DISK_, * AOE_SP_DISK_;
200
201 typedef struct AOE_TARGET_LIST_ {
202     aoe__mount_target Target;
203     struct AOE_TARGET_LIST_ * next;
204   } AOE_S_TARGET_LIST_, * AOE_SP_TARGET_LIST_;
205
206 /** Private globals. */
207 static AOE_SP_TARGET_LIST_ AoeTargetList_ = NULL;
208 static KSPIN_LOCK AoeTargetListLock_;
209 static winvblock__bool AoeStop_ = FALSE;
210 static KSPIN_LOCK AoeLock_;
211 static KEVENT AoeSignal_;
212 static AOE_SP_WORK_TAG_ AoeTagListFirst_ = NULL;
213 static AOE_SP_WORK_TAG_ AoeTagListLast_ = NULL;
214 static AOE_SP_WORK_TAG_ AoeProbeTag_ = NULL;
215 static AOE_SP_DISK_SEARCH_ AoeDiskSearchList_ = NULL;
216 static LONG AoePendingTags_ = 0;
217 static HANDLE AoeThreadHandle_;
218 static winvblock__bool AoeStarted_ = FALSE;
219 static LIST_ENTRY AoeDiskList_;
220 static KSPIN_LOCK AoeDiskListLock_;
221
222 /* Yield a pointer to the AoE disk. */
223 static AOE_SP_DISK_ AoeDiskFromDev_(WV_SP_DEV_T dev_ptr) {
224     return disk__get_ptr(dev_ptr)->ext;
225   }
226
227 /**
228  * Start AoE operations.
229  *
230  * @ret Status          Return status code.
231  */
232 NTSTATUS STDCALL DriverEntry(
233     IN PDRIVER_OBJECT DriverObject,
234     IN PUNICODE_STRING RegistryPath
235   ) {
236     NTSTATUS Status;
237     OBJECT_ATTRIBUTES ObjectAttributes;
238     void * ThreadObject;
239     WV_SP_BUS_T bus_ptr;
240     int i;
241
242     DBG("Entry\n");
243
244     if (AoeStarted_)
245       return STATUS_SUCCESS;
246     /* Initialize the global list of AoE disks. */
247     InitializeListHead(&AoeDiskList_);
248     KeInitializeSpinLock(&AoeDiskListLock_);
249     /* Setup the Registry. */
250     if (!NT_SUCCESS(AoeRegSetup(&Status))) {
251         DBG("Could not update Registry!\n");
252         return Status;
253       } else {
254         DBG("Registry updated\n");
255       }
256     /* Start up the protocol. */
257     if (!NT_SUCCESS(Status = Protocol_Start())) {
258         DBG("Protocol startup failure!\n");
259         return Status;
260       }
261     /* Allocate and zero-fill the global probe tag. */
262     AoeProbeTag_ = wv_mallocz(sizeof *AoeProbeTag_);
263     if (AoeProbeTag_ == NULL) {
264         DBG("Couldn't allocate probe tag; bye!\n");
265         return STATUS_INSUFFICIENT_RESOURCES;
266       }
267
268     /* Set up the probe tag's AoE packet reference. */
269     AoeProbeTag_->PacketSize = sizeof (AOE_S_PACKET_);
270     /* Allocate and zero-fill the probe tag's packet reference. */
271     AoeProbeTag_->packet_data = wv_mallocz(AoeProbeTag_->PacketSize);
272     if (AoeProbeTag_->packet_data == NULL) {
273         DBG("Couldn't allocate AoeProbeTag_->packet_data\n");
274         wv_free(AoeProbeTag_);
275         return STATUS_INSUFFICIENT_RESOURCES;
276       }
277     AoeProbeTag_->SendTime.QuadPart = 0LL;
278
279     /* Initialize the probe tag's AoE packet. */
280     AoeProbeTag_->packet_data->Ver = AOEPROTOCOLVER;
281     AoeProbeTag_->packet_data->Major =
282       htons((winvblock__uint16) -1);
283     AoeProbeTag_->packet_data->Minor = (winvblock__uint8) -1;
284     AoeProbeTag_->packet_data->Cmd = 0xec;           /* IDENTIFY DEVICE */
285     AoeProbeTag_->packet_data->Count = 1;
286
287     /* Initialize global target-list spinlock. */
288     KeInitializeSpinLock(&AoeTargetListLock_);
289
290     /* Initialize global spin-lock and global thread signal event. */
291     KeInitializeSpinLock(&AoeLock_);
292     KeInitializeEvent(&AoeSignal_, SynchronizationEvent, FALSE);
293
294     /* Initialize object attributes for thread. */
295     InitializeObjectAttributes(
296         &ObjectAttributes,
297         NULL,
298         OBJ_KERNEL_HANDLE,
299         NULL,
300         NULL
301       );
302
303     /* Create global thread. */
304     if (!NT_SUCCESS(Status = PsCreateSystemThread(
305         &AoeThreadHandle_,
306         THREAD_ALL_ACCESS,
307         &ObjectAttributes,
308         NULL,
309         NULL,
310         AoeThread_,
311         NULL
312       )))
313       return Error("PsCreateSystemThread", Status);
314
315     if (!NT_SUCCESS(Status = ObReferenceObjectByHandle(
316         AoeThreadHandle_,
317         THREAD_ALL_ACCESS,
318         NULL,
319         KernelMode,
320         &ThreadObject,
321         NULL
322       ))) {
323         ZwClose(AoeThreadHandle_);
324         Error("ObReferenceObjectByHandle", Status);
325         AoeStop_ = TRUE;
326         KeSetEvent(&AoeSignal_, 0, FALSE);
327       }
328
329     for (i = 0; i <= IRP_MJ_MAXIMUM_FUNCTION; i++)
330       DriverObject->MajorFunction[i] = AoeDriverIrpNotSupported_;
331     DriverObject->MajorFunction[IRP_MJ_PNP] = AoeDriverIrpPnp_;
332     DriverObject->MajorFunction[IRP_MJ_POWER] = AoeDriverIrpPower_;
333     DriverObject->MajorFunction[IRP_MJ_CREATE] = AoeDriverIrpCreateClose_;
334     DriverObject->MajorFunction[IRP_MJ_CLOSE] = AoeDriverIrpCreateClose_;
335     DriverObject->MajorFunction[IRP_MJ_SYSTEM_CONTROL] = AoeDriverIrpSysCtl_;
336     DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = AoeDriverIrpDevCtl_;
337     DriverObject->MajorFunction[IRP_MJ_SCSI] = AoeDriverIrpScsi_;
338     /* Set the driver Unload callback. */
339     DriverObject->DriverUnload = AoeUnload_;
340     /* Set the driver AddDevice callback. */
341     DriverObject->DriverExtension->AddDevice = AoeBusAttachFdo;
342     AoeStarted_ = TRUE;
343     if (!AoeBusCreate(DriverObject)) {
344         DBG("Unable to create AoE bus!\n");
345         AoeUnload_(DriverObject);
346         return STATUS_INSUFFICIENT_RESOURCES;
347       }
348     AoeProcessAbft_();
349     DBG("Exit\n");
350     return Status;
351   }
352
353 /**
354  * Stop AoE operations.
355  */
356 static void STDCALL AoeUnload_(IN PDRIVER_OBJECT DriverObject) {
357     NTSTATUS Status;
358     AOE_SP_DISK_SEARCH_ disk_searcher, previous_disk_searcher;
359     AOE_SP_WORK_TAG_ tag;
360     KIRQL Irql, Irql2;
361     AOE_SP_TARGET_LIST_ Walker, Next;
362
363     DBG("Entry\n");
364     /* If we're not already started, there's nothing to do. */
365     if (!AoeStarted_)
366       return;
367     /* Destroy the AoE bus. */
368     AoeBusFree();
369     /* Stop the AoE protocol. */
370     Protocol_Stop();
371     /* If we're not already shutting down, signal the event. */
372     if (!AoeStop_) {
373         AoeStop_ = TRUE;
374         KeSetEvent(&AoeSignal_, 0, FALSE);
375         /* Wait until the event has been signalled. */
376         if (!NT_SUCCESS(Status = ZwWaitForSingleObject(
377             AoeThreadHandle_,
378             FALSE,
379             NULL
380           )))
381           Error("AoE_Stop ZwWaitForSingleObject", Status);
382         ZwClose(AoeThreadHandle_);
383       }
384
385     /* Free the target list. */
386     KeAcquireSpinLock(&AoeTargetListLock_, &Irql2);
387     Walker = AoeTargetList_;
388     while (Walker != NULL) {
389         Next = Walker->next;
390         wv_free(Walker);
391         Walker = Next;
392       }
393     KeReleaseSpinLock(&AoeTargetListLock_, Irql2);
394
395     /* Wait until we have the global spin-lock. */
396     KeAcquireSpinLock(&AoeLock_, &Irql);
397
398     /* Free disk searches in the global disk search list. */
399     disk_searcher = AoeDiskSearchList_;
400     while (disk_searcher != NULL) {
401         KeSetEvent(
402             &(disk__get_ptr(disk_searcher->device)->SearchEvent),
403             0,
404             FALSE
405           );
406         previous_disk_searcher = disk_searcher;
407         disk_searcher = disk_searcher->next;
408         wv_free(previous_disk_searcher);
409       }
410
411     /* Cancel and free all tags in the global tag list. */
412     tag = AoeTagListFirst_;
413     while (tag != NULL) {
414         if (tag->request_ptr != NULL && --tag->request_ptr->TagCount == 0) {
415             tag->request_ptr->Irp->IoStatus.Information = 0;
416             tag->request_ptr->Irp->IoStatus.Status = STATUS_CANCELLED;
417             IoCompleteRequest(tag->request_ptr->Irp, IO_NO_INCREMENT);
418             wv_free(tag->request_ptr);
419           }
420         if (tag->next == NULL) {
421             wv_free(tag->packet_data);
422             wv_free(tag);
423             tag = NULL;
424           } else {
425             tag = tag->next;
426             wv_free(tag->previous->packet_data);
427             wv_free(tag->previous);
428           }
429       }
430     AoeTagListFirst_ = NULL;
431     AoeTagListLast_ = NULL;
432
433     /* Free the global probe tag and its AoE packet. */
434     wv_free(AoeProbeTag_->packet_data);
435     wv_free(AoeProbeTag_);
436
437     /* Release the global spin-lock. */
438     KeReleaseSpinLock(&AoeLock_, Irql);
439     AoeBusFree();
440     AoeStarted_ = FALSE;
441     DBG("Exit\n");
442   }
443
444 /**
445  * Search for disk parameters.
446  *
447  * @v disk_ptr          The disk to initialize (for AoE, match).
448  * @ret BOOLEAN         See below.
449  *
450  * Returns TRUE if the disk could be matched, FALSE otherwise.
451  */
452 static winvblock__bool STDCALL AoeDiskInit_(IN WV_SP_DISK_T disk_ptr) {
453     AOE_SP_DISK_SEARCH_
454       disk_searcher, disk_search_walker, previous_disk_searcher;
455     LARGE_INTEGER Timeout, CurrentTime;
456     AOE_SP_WORK_TAG_ tag, tag_walker;
457     KIRQL Irql, InnerIrql;
458     LARGE_INTEGER MaxSectorsPerPacketSendTime;
459     winvblock__uint32 MTU;
460     AOE_SP_DISK_ aoe_disk_ptr;
461
462     aoe_disk_ptr = AoeDiskFromDev_(disk_ptr->Dev);
463     /*
464      * Allocate our disk search 
465      */
466     if ((disk_searcher = wv_malloc(sizeof *disk_searcher)) == NULL) {
467         DBG ( "Couldn't allocate for disk_searcher; bye!\n" );
468         return FALSE;
469       }
470
471     /*
472      * Initialize the disk search 
473      */
474     disk_searcher->device = disk_ptr->Dev;
475     disk_searcher->next = NULL;
476     aoe_disk_ptr->search_state = AoeSearchStateSearchNic_;
477     KeResetEvent ( &disk_ptr->SearchEvent );
478
479     /*
480      * Wait until we have the global spin-lock 
481      */
482     KeAcquireSpinLock ( &AoeLock_, &Irql );
483
484     /*
485      * Add our disk search to the global list of disk searches 
486      */
487     if ( AoeDiskSearchList_ == NULL )
488       {
489         AoeDiskSearchList_ = disk_searcher;
490       }
491     else
492       {
493         disk_search_walker = AoeDiskSearchList_;
494         while ( disk_search_walker->next )
495     disk_search_walker = disk_search_walker->next;
496         disk_search_walker->next = disk_searcher;
497       }
498
499     /*
500      * Release the global spin-lock 
501      */
502     KeReleaseSpinLock ( &AoeLock_, Irql );
503
504     /*
505      * We go through all the states until the disk is ready for use 
506      */
507     while ( TRUE )
508       {
509         /*
510          * Wait for our device's extension's search to be signalled 
511          */
512         /*
513          * TODO: Make the below value a #defined constant 
514          */
515         /*
516          * 500.000 * 100ns = 50.000.000 ns = 50ms 
517          */
518         Timeout.QuadPart = -500000LL;
519         KeWaitForSingleObject ( &disk_ptr->SearchEvent, Executive, KernelMode,
520               FALSE, &Timeout );
521         if ( AoeStop_ )
522     {
523       DBG ( "AoE is shutting down; bye!\n" );
524       return FALSE;
525     }
526
527         /*
528          * Wait until we have the device extension's spin-lock 
529          */
530         KeAcquireSpinLock ( &disk_ptr->SpinLock, &Irql );
531
532         if (aoe_disk_ptr->search_state == AoeSearchStateSearchNic_)
533     {
534       if ( !Protocol_SearchNIC ( aoe_disk_ptr->ClientMac ) )
535         {
536           KeReleaseSpinLock ( &disk_ptr->SpinLock, Irql );
537           continue;
538         }
539       else
540         {
541           /*
542            * We found the adapter to use, get MTU next 
543            */
544           aoe_disk_ptr->MTU = Protocol_GetMTU ( aoe_disk_ptr->ClientMac );
545           aoe_disk_ptr->search_state = AoeSearchStateGetSize_;
546         }
547     }
548
549         if (aoe_disk_ptr->search_state == AoeSearchStateGettingSize_)
550     {
551       /*
552        * Still getting the disk's size 
553        */
554       KeReleaseSpinLock ( &disk_ptr->SpinLock, Irql );
555       continue;
556     }
557         if (aoe_disk_ptr->search_state == AoeSearchStateGettingGeometry_)
558     {
559       /*
560        * Still getting the disk's geometry 
561        */
562       KeReleaseSpinLock ( &disk_ptr->SpinLock, Irql );
563       continue;
564     }
565         if (aoe_disk_ptr->search_state ==
566           AoeSearchStateGettingMaxSectsPerPacket_)
567     {
568       KeQuerySystemTime ( &CurrentTime );
569       /*
570        * TODO: Make the below value a #defined constant 
571        */
572       /*
573        * 2.500.000 * 100ns = 250.000.000 ns = 250ms 
574        */
575       if ( CurrentTime.QuadPart >
576            MaxSectorsPerPacketSendTime.QuadPart + 2500000LL )
577         {
578           DBG ( "No reply after 250ms for MaxSectorsPerPacket %d, "
579           "giving up\n", aoe_disk_ptr->MaxSectorsPerPacket );
580           aoe_disk_ptr->MaxSectorsPerPacket--;
581           aoe_disk_ptr->search_state = AoeSearchStateDone_;
582         }
583       else
584         {
585           /*
586            * Still getting the maximum sectors per packet count 
587            */
588           KeReleaseSpinLock ( &disk_ptr->SpinLock, Irql );
589           continue;
590         }
591     }
592
593         if (aoe_disk_ptr->search_state == AoeSearchStateDone_)
594     {
595       /*
596        * We've finished the disk search; perform clean-up 
597        */
598       KeAcquireSpinLock ( &AoeLock_, &InnerIrql );
599
600       /*
601        * Tag clean-up: Find out if our tag is in the global tag list 
602        */
603       tag_walker = AoeTagListFirst_;
604       while ( tag_walker != NULL && tag_walker != tag )
605         tag_walker = tag_walker->next;
606       if ( tag_walker != NULL )
607         {
608           /*
609            * We found it.  If it's at the beginning of the list, adjust
610            * the list to point the the next tag
611            */
612           if ( tag->previous == NULL )
613       AoeTagListFirst_ = tag->next;
614           else
615       /*
616        * Remove our tag from the list 
617        */
618       tag->previous->next = tag->next;
619           /*
620            * If we 're at the end of the list, adjust the list's end to
621            * point to the penultimate tag
622            */
623           if ( tag->next == NULL )
624       AoeTagListLast_ = tag->previous;
625           else
626       /*
627        * Remove our tag from the list 
628        */
629       tag->next->previous = tag->previous;
630           AoePendingTags_--;
631           if ( AoePendingTags_ < 0 )
632       DBG ( "AoePendingTags_ < 0!!\n" );
633           /*
634            * Free our tag and its AoE packet 
635            */
636           wv_free(tag->packet_data);
637           wv_free(tag);
638         }
639
640       /*
641        * Disk search clean-up 
642        */
643       if ( AoeDiskSearchList_ == NULL )
644         {
645           DBG ( "AoeDiskSearchList_ == NULL!!\n" );
646         }
647       else
648         {
649           /*
650            * Find our disk search in the global list of disk searches 
651            */
652           disk_search_walker = AoeDiskSearchList_;
653           while (
654               disk_search_walker &&
655               disk_search_walker->device != disk_ptr->Dev
656             ) {
657         previous_disk_searcher = disk_search_walker;
658         disk_search_walker = disk_search_walker->next;
659       }
660           if ( disk_search_walker )
661       {
662         /*
663          * We found our disk search.  If it's the first one in
664          * the list, adjust the list and remove it
665          */
666         if ( disk_search_walker == AoeDiskSearchList_ )
667           AoeDiskSearchList_ = disk_search_walker->next;
668         else
669           /*
670            * Just remove it 
671            */
672           previous_disk_searcher->next = disk_search_walker->next;
673         /*
674          * Free our disk search 
675          */
676         wv_free(disk_search_walker);
677       }
678           else
679       {
680         DBG ( "Disk not found in AoeDiskSearchList_!!\n" );
681       }
682         }
683
684       /*
685        * Release global and device extension spin-locks 
686        */
687       KeReleaseSpinLock ( &AoeLock_, InnerIrql );
688       KeReleaseSpinLock ( &disk_ptr->SpinLock, Irql );
689
690       DBG ( "Disk size: %I64uM cylinders: %I64u heads: %u "
691       "sectors: %u sectors per packet: %u\n",
692       disk_ptr->LBADiskSize / 2048, disk_ptr->Cylinders,
693       disk_ptr->Heads, disk_ptr->Sectors,
694       aoe_disk_ptr->MaxSectorsPerPacket );
695       return TRUE;
696     }
697
698         #if 0
699         if ( aoe_disk_ptr->search_state == AoeSearchStateDone_)
700         #endif
701         /*
702          * Establish our tag 
703          */
704         if ((tag = wv_mallocz(sizeof *tag)) == NULL) {
705       DBG ( "Couldn't allocate tag\n" );
706       KeReleaseSpinLock ( &disk_ptr->SpinLock, Irql );
707       /*
708        * Maybe next time around 
709        */
710       continue;
711     }
712         tag->type = AoeTagTypeSearchDrive_;
713         tag->device = disk_ptr->Dev;
714
715         /*
716          * Establish our tag's AoE packet 
717          */
718         tag->PacketSize = sizeof (AOE_S_PACKET_);
719         if ((tag->packet_data = wv_mallocz(tag->PacketSize)) == NULL) {
720       DBG ( "Couldn't allocate tag->packet_data\n" );
721       wv_free(tag);
722       tag = NULL;
723       KeReleaseSpinLock ( &disk_ptr->SpinLock, Irql );
724       /*
725        * Maybe next time around 
726        */
727       continue;
728     }
729         tag->packet_data->Ver = AOEPROTOCOLVER;
730         tag->packet_data->Major =
731     htons ( ( winvblock__uint16 ) aoe_disk_ptr->Major );
732         tag->packet_data->Minor = ( winvblock__uint8 ) aoe_disk_ptr->Minor;
733         tag->packet_data->ExtendedAFlag = TRUE;
734
735         /*
736          * Initialize the packet appropriately based on our current phase 
737          */
738         switch ( aoe_disk_ptr->search_state )
739     {
740       case AoeSearchStateGetSize_:
741         /*
742          * TODO: Make the below value into a #defined constant 
743          */
744         tag->packet_data->Cmd = 0xec;  /* IDENTIFY DEVICE */
745         tag->packet_data->Count = 1;
746         aoe_disk_ptr->search_state = AoeSearchStateGettingSize_;
747         break;
748       case AoeSearchStateGetGeometry_:
749         /*
750          * TODO: Make the below value into a #defined constant 
751          */
752         tag->packet_data->Cmd = 0x24;  /* READ SECTOR */
753         tag->packet_data->Count = 1;
754         aoe_disk_ptr->search_state = AoeSearchStateGettingGeometry_;
755         break;
756       case AoeSearchStateGetMaxSectsPerPacket_:
757         /*
758          * TODO: Make the below value into a #defined constant 
759          */
760         tag->packet_data->Cmd = 0x24;  /* READ SECTOR */
761         tag->packet_data->Count =
762           ( winvblock__uint8 ) ( ++aoe_disk_ptr->MaxSectorsPerPacket );
763         KeQuerySystemTime ( &MaxSectorsPerPacketSendTime );
764         aoe_disk_ptr->search_state =
765           AoeSearchStateGettingMaxSectsPerPacket_;
766         /*
767          * TODO: Make the below value into a #defined constant 
768          */
769         aoe_disk_ptr->Timeout = 200000;
770         break;
771       default:
772         DBG ( "Undefined search_state!!\n" );
773         wv_free(tag->packet_data);
774         wv_free(tag);
775         /*
776          * TODO: Do we need to nullify tag here? 
777          */
778         KeReleaseSpinLock ( &disk_ptr->SpinLock, Irql );
779         continue;
780         break;
781     }
782
783         /*
784          * Enqueue our tag 
785          */
786         tag->next = NULL;
787         KeAcquireSpinLock ( &AoeLock_, &InnerIrql );
788         if ( AoeTagListFirst_ == NULL )
789     {
790       AoeTagListFirst_ = tag;
791       tag->previous = NULL;
792     }
793         else
794     {
795       AoeTagListLast_->next = tag;
796       tag->previous = AoeTagListLast_;
797     }
798         AoeTagListLast_ = tag;
799         KeReleaseSpinLock ( &AoeLock_, InnerIrql );
800         KeReleaseSpinLock ( &disk_ptr->SpinLock, Irql );
801       }
802   }
803
804 static NTSTATUS STDCALL AoeDiskIo_(
805     IN WV_SP_DEV_T dev_ptr,
806     IN WV_E_DISK_IO_MODE mode,
807     IN LONGLONG start_sector,
808     IN winvblock__uint32 sector_count,
809     IN winvblock__uint8_ptr buffer,
810     IN PIRP irp
811   ) {
812     AOE_SP_IO_REQ_ request_ptr;
813     AOE_SP_WORK_TAG_ tag, new_tag_list = NULL, previous_tag = NULL;
814     KIRQL Irql;
815     winvblock__uint32 i;
816     PHYSICAL_ADDRESS PhysicalAddress;
817     winvblock__uint8_ptr PhysicalMemory;
818     WV_SP_DISK_T disk_ptr;
819     AOE_SP_DISK_ aoe_disk_ptr;
820
821     /*
822      * Establish pointers to the disk and AoE disk
823      */
824     disk_ptr = disk__get_ptr ( dev_ptr );
825     aoe_disk_ptr = AoeDiskFromDev_(dev_ptr);
826
827     if ( AoeStop_ )
828       {
829         /*
830          * Shutting down AoE; we can't service this request 
831          */
832         irp->IoStatus.Information = 0;
833         irp->IoStatus.Status = STATUS_CANCELLED;
834         IoCompleteRequest ( irp, IO_NO_INCREMENT );
835         return STATUS_CANCELLED;
836       }
837
838     if ( sector_count < 1 )
839       {
840         /*
841          * A silly request 
842          */
843         DBG ( "sector_count < 1; cancelling\n" );
844         irp->IoStatus.Information = 0;
845         irp->IoStatus.Status = STATUS_CANCELLED;
846         IoCompleteRequest ( irp, IO_NO_INCREMENT );
847         return STATUS_CANCELLED;
848       }
849
850     /*
851      * Allocate and zero-fill our request 
852      */
853     if ((request_ptr = wv_mallocz(sizeof *request_ptr)) == NULL) {
854         DBG ( "Couldn't allocate for reques_ptr; bye!\n" );
855         irp->IoStatus.Information = 0;
856         irp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES;
857         IoCompleteRequest ( irp, IO_NO_INCREMENT );
858         return STATUS_INSUFFICIENT_RESOURCES;
859       }
860
861     /*
862      * Initialize the request 
863      */
864     request_ptr->Mode = mode;
865     request_ptr->SectorCount = sector_count;
866     request_ptr->Buffer = buffer;
867     request_ptr->Irp = irp;
868     request_ptr->TagCount = 0;
869
870     /*
871      * Split the requested sectors into packets in tags
872      */
873     for ( i = 0; i < sector_count; i += aoe_disk_ptr->MaxSectorsPerPacket )
874       {
875         /*
876          * Allocate each tag 
877          */
878         if ((tag = wv_mallocz(sizeof *tag)) == NULL) {
879       DBG ( "Couldn't allocate tag; bye!\n" );
880       /*
881        * We failed while allocating tags; free the ones we built 
882        */
883       tag = new_tag_list;
884       while ( tag != NULL )
885         {
886           previous_tag = tag;
887           tag = tag->next;
888           wv_free(previous_tag->packet_data);
889           wv_free(previous_tag);
890         }
891       wv_free(request_ptr);
892       irp->IoStatus.Information = 0;
893       irp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES;
894       IoCompleteRequest ( irp, IO_NO_INCREMENT );
895       return STATUS_INSUFFICIENT_RESOURCES;
896     }
897
898         /*
899          * Initialize each tag 
900          */
901         tag->type = AoeTagTypeIo_;
902         tag->request_ptr = request_ptr;
903         tag->device = dev_ptr;
904         request_ptr->TagCount++;
905         tag->Id = 0;
906         tag->BufferOffset = i * disk_ptr->SectorSize;
907         tag->SectorCount =
908     ( ( sector_count - i ) <
909       aoe_disk_ptr->MaxSectorsPerPacket ? sector_count -
910       i : aoe_disk_ptr->MaxSectorsPerPacket );
911
912         /*
913          * Allocate and initialize each tag's AoE packet 
914          */
915         tag->PacketSize = sizeof (AOE_S_PACKET_);
916         if (mode == WvDiskIoModeWrite)
917     tag->PacketSize += tag->SectorCount * disk_ptr->SectorSize;
918         if ((tag->packet_data = wv_mallocz(tag->PacketSize)) == NULL) {
919       DBG ( "Couldn't allocate tag->packet_data; bye!\n" );
920       /*
921        * We failed while allocating an AoE packet; free
922        * the tags we built
923        */
924       wv_free(tag);
925       tag = new_tag_list;
926       while ( tag != NULL )
927         {
928           previous_tag = tag;
929           tag = tag->next;
930           wv_free(previous_tag->packet_data);
931           wv_free(previous_tag);
932         }
933       wv_free(request_ptr);
934       irp->IoStatus.Information = 0;
935       irp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES;
936       IoCompleteRequest ( irp, IO_NO_INCREMENT );
937       return STATUS_INSUFFICIENT_RESOURCES;
938     }
939         tag->packet_data->Ver = AOEPROTOCOLVER;
940         tag->packet_data->Major =
941     htons ( ( winvblock__uint16 ) aoe_disk_ptr->Major );
942         tag->packet_data->Minor = ( winvblock__uint8 ) aoe_disk_ptr->Minor;
943         tag->packet_data->Tag = 0;
944         tag->packet_data->Command = 0;
945         tag->packet_data->ExtendedAFlag = TRUE;
946         if (mode == WvDiskIoModeRead)
947     {
948       tag->packet_data->Cmd = 0x24;  /* READ SECTOR */
949     }
950         else
951     {
952       tag->packet_data->Cmd = 0x34;  /* WRITE SECTOR */
953       tag->packet_data->WriteAFlag = 1;
954     }
955         tag->packet_data->Count = ( winvblock__uint8 ) tag->SectorCount;
956         tag->packet_data->Lba0 =
957     ( winvblock__uint8 ) ( ( ( start_sector + i ) >> 0 ) & 255 );
958         tag->packet_data->Lba1 =
959     ( winvblock__uint8 ) ( ( ( start_sector + i ) >> 8 ) & 255 );
960         tag->packet_data->Lba2 =
961     ( winvblock__uint8 ) ( ( ( start_sector + i ) >> 16 ) & 255 );
962         tag->packet_data->Lba3 =
963     ( winvblock__uint8 ) ( ( ( start_sector + i ) >> 24 ) & 255 );
964         tag->packet_data->Lba4 =
965     ( winvblock__uint8 ) ( ( ( start_sector + i ) >> 32 ) & 255 );
966         tag->packet_data->Lba5 =
967     ( winvblock__uint8 ) ( ( ( start_sector + i ) >> 40 ) & 255 );
968
969         /* For a write request, copy from the buffer into the AoE packet. */
970         if (mode == WvDiskIoModeWrite)
971     RtlCopyMemory ( tag->packet_data->Data, &buffer[tag->BufferOffset],
972         tag->SectorCount * disk_ptr->SectorSize );
973
974         /*
975          * Add this tag to the request's tag list 
976          */
977         tag->previous = previous_tag;
978         tag->next = NULL;
979         if ( new_tag_list == NULL )
980     {
981       new_tag_list = tag;
982     }
983         else
984     {
985       previous_tag->next = tag;
986     }
987         previous_tag = tag;
988       }
989     /*
990      * Split the requested sectors into packets in tags
991      */
992     request_ptr->TotalTags = request_ptr->TagCount;
993
994     /*
995      * Wait until we have the global spin-lock 
996      */
997     KeAcquireSpinLock ( &AoeLock_, &Irql );
998
999     /*
1000      * Enqueue our request's tag list to the global tag list 
1001      */
1002     if ( AoeTagListLast_ == NULL )
1003       {
1004         AoeTagListFirst_ = new_tag_list;
1005       }
1006     else
1007       {
1008         AoeTagListLast_->next = new_tag_list;
1009         new_tag_list->previous = AoeTagListLast_;
1010       }
1011     /*
1012      * Adjust the global list to reflect our last tag 
1013      */
1014     AoeTagListLast_ = tag;
1015
1016     irp->IoStatus.Information = 0;
1017     irp->IoStatus.Status = STATUS_PENDING;
1018     IoMarkIrpPending ( irp );
1019
1020     KeReleaseSpinLock ( &AoeLock_, Irql );
1021     KeSetEvent ( &AoeSignal_, 0, FALSE );
1022     return STATUS_PENDING;
1023   }
1024
1025 static void STDCALL add_target(
1026     IN winvblock__uint8_ptr ClientMac,
1027     IN winvblock__uint8_ptr ServerMac,
1028     winvblock__uint16 Major,
1029     winvblock__uint8 Minor,
1030     LONGLONG LBASize
1031   )
1032   {
1033     AOE_SP_TARGET_LIST_ Walker, Last;
1034     KIRQL Irql;
1035
1036     KeAcquireSpinLock ( &AoeTargetListLock_, &Irql );
1037     Walker = Last = AoeTargetList_;
1038     while ( Walker != NULL )
1039       {
1040         if (wv_memcmpeq(&Walker->Target.ClientMac, ClientMac, 6) &&
1041           wv_memcmpeq(&Walker->Target.ServerMac, ServerMac, 6) &&
1042           Walker->Target.Major == Major
1043           && Walker->Target.Minor == Minor) {
1044       if ( Walker->Target.LBASize != LBASize )
1045         {
1046           DBG ( "LBASize changed for e%d.%d " "(%I64u->%I64u)\n", Major,
1047           Minor, Walker->Target.LBASize, LBASize );
1048           Walker->Target.LBASize = LBASize;
1049         }
1050       KeQuerySystemTime ( &Walker->Target.ProbeTime );
1051       KeReleaseSpinLock ( &AoeTargetListLock_, Irql );
1052       return;
1053     }
1054         Last = Walker;
1055         Walker = Walker->next;
1056       }
1057
1058     if ((Walker = wv_malloc(sizeof *Walker)) == NULL) {
1059         DBG("wv_malloc Walker\n");
1060         KeReleaseSpinLock ( &AoeTargetListLock_, Irql );
1061         return;
1062       }
1063     Walker->next = NULL;
1064     RtlCopyMemory ( Walker->Target.ClientMac, ClientMac, 6 );
1065     RtlCopyMemory ( Walker->Target.ServerMac, ServerMac, 6 );
1066     Walker->Target.Major = Major;
1067     Walker->Target.Minor = Minor;
1068     Walker->Target.LBASize = LBASize;
1069     KeQuerySystemTime ( &Walker->Target.ProbeTime );
1070
1071     if ( Last == NULL )
1072       {
1073         AoeTargetList_ = Walker;
1074       }
1075     else
1076       {
1077         Last->next = Walker;
1078       }
1079     KeReleaseSpinLock ( &AoeTargetListLock_, Irql );
1080   }
1081
1082 /**
1083  * Process an AoE reply.
1084  *
1085  * @v SourceMac         The AoE server's MAC address.
1086  * @v DestinationMac    The AoE client's MAC address.
1087  * @v Data              The AoE packet.
1088  * @v DataSize          The AoE packet's size.
1089  */
1090 NTSTATUS STDCALL aoe__reply(
1091     IN winvblock__uint8_ptr SourceMac,
1092     IN winvblock__uint8_ptr DestinationMac,
1093     IN winvblock__uint8_ptr Data,
1094     IN winvblock__uint32 DataSize
1095   )
1096   {
1097     AOE_SP_PACKET_ reply = (AOE_SP_PACKET_) Data;
1098     LONGLONG LBASize;
1099     AOE_SP_WORK_TAG_ tag;
1100     KIRQL Irql;
1101     winvblock__bool Found = FALSE;
1102     LARGE_INTEGER CurrentTime;
1103     WV_SP_DISK_T disk_ptr;
1104     AOE_SP_DISK_ aoe_disk_ptr;
1105
1106     /*
1107      * Discard non-responses 
1108      */
1109     if ( !reply->ResponseFlag )
1110       return STATUS_SUCCESS;
1111
1112     /*
1113      * If the response matches our probe, add the AoE disk device 
1114      */
1115     if ( AoeProbeTag_->Id == reply->Tag )
1116       {
1117         RtlCopyMemory ( &LBASize, &reply->Data[200], sizeof ( LONGLONG ) );
1118         add_target ( DestinationMac, SourceMac, ntohs ( reply->Major ),
1119          reply->Minor, LBASize );
1120         return STATUS_SUCCESS;
1121       }
1122
1123     /*
1124      * Wait until we have the global spin-lock 
1125      */
1126     KeAcquireSpinLock ( &AoeLock_, &Irql );
1127
1128     /*
1129      * Search for request tag 
1130      */
1131     if ( AoeTagListFirst_ == NULL )
1132       {
1133         KeReleaseSpinLock ( &AoeLock_, Irql );
1134         return STATUS_SUCCESS;
1135       }
1136     tag = AoeTagListFirst_;
1137     while ( tag != NULL )
1138       {
1139         if ( ( tag->Id == reply->Tag )
1140        && ( tag->packet_data->Major == reply->Major )
1141        && ( tag->packet_data->Minor == reply->Minor ) )
1142     {
1143       Found = TRUE;
1144       break;
1145     }
1146         tag = tag->next;
1147       }
1148     if ( !Found )
1149       {
1150         KeReleaseSpinLock ( &AoeLock_, Irql );
1151         return STATUS_SUCCESS;
1152       }
1153     else
1154       {
1155         /*
1156          * Remove the tag from the global tag list 
1157          */
1158         if ( tag->previous == NULL )
1159     AoeTagListFirst_ = tag->next;
1160         else
1161     tag->previous->next = tag->next;
1162         if ( tag->next == NULL )
1163     AoeTagListLast_ = tag->previous;
1164         else
1165     tag->next->previous = tag->previous;
1166         AoePendingTags_--;
1167         if ( AoePendingTags_ < 0 )
1168     DBG ( "AoePendingTags_ < 0!!\n" );
1169         KeSetEvent ( &AoeSignal_, 0, FALSE );
1170       }
1171     KeReleaseSpinLock ( &AoeLock_, Irql );
1172
1173     /*
1174      * Establish pointers to the disk device and AoE disk
1175      */
1176     disk_ptr = disk__get_ptr ( tag->device );
1177     aoe_disk_ptr = AoeDiskFromDev_(tag->device);
1178
1179     /*
1180      * If our tag was a discovery request, note the server 
1181      */
1182     if (wv_memcmpeq(aoe_disk_ptr->ServerMac, "\xff\xff\xff\xff\xff\xff", 6)) {
1183         RtlCopyMemory ( aoe_disk_ptr->ServerMac, SourceMac, 6 );
1184         DBG ( "Major: %d minor: %d found on server "
1185         "%02x:%02x:%02x:%02x:%02x:%02x\n", aoe_disk_ptr->Major,
1186         aoe_disk_ptr->Minor, SourceMac[0], SourceMac[1], SourceMac[2],
1187         SourceMac[3], SourceMac[4], SourceMac[5] );
1188       }
1189
1190     KeQuerySystemTime ( &CurrentTime );
1191     aoe_disk_ptr->Timeout -=
1192       ( winvblock__uint32 ) ( ( aoe_disk_ptr->Timeout -
1193               ( CurrentTime.QuadPart -
1194           tag->FirstSendTime.QuadPart ) ) / 1024 );
1195     /*
1196      * TODO: Replace the values below with #defined constants 
1197      */
1198     if ( aoe_disk_ptr->Timeout > 100000000 )
1199       aoe_disk_ptr->Timeout = 100000000;
1200
1201     switch ( tag->type )
1202       {
1203         case AoeTagTypeSearchDrive_:
1204     KeAcquireSpinLock ( &disk_ptr->SpinLock, &Irql );
1205     switch ( aoe_disk_ptr->search_state )
1206       {
1207         case AoeSearchStateGettingSize_:
1208           /*
1209            * The reply tells us the disk size
1210            */
1211           RtlCopyMemory ( &disk_ptr->LBADiskSize, &reply->Data[200],
1212               sizeof ( LONGLONG ) );
1213           /*
1214            * Next we are concerned with the disk geometry
1215            */
1216           aoe_disk_ptr->search_state = AoeSearchStateGetGeometry_;
1217           break;
1218         case AoeSearchStateGettingGeometry_:
1219           /*
1220            * FIXME: use real values from partition table.
1221            * We used to truncate a fractional end cylinder, but
1222            * now leave it be in the hopes everyone uses LBA
1223            */
1224           disk_ptr->SectorSize = 512;
1225           disk_ptr->Heads = 255;
1226           disk_ptr->Sectors = 63;
1227           disk_ptr->Cylinders =
1228       disk_ptr->LBADiskSize / ( disk_ptr->Heads *
1229               disk_ptr->Sectors );
1230           /*
1231            * Next we are concerned with the maximum sectors per packet
1232            */
1233           aoe_disk_ptr->search_state =
1234             AoeSearchStateGetMaxSectsPerPacket_;
1235           break;
1236         case AoeSearchStateGettingMaxSectsPerPacket_:
1237           DataSize -= sizeof (AOE_S_PACKET_);
1238           if ( DataSize <
1239          ( aoe_disk_ptr->MaxSectorsPerPacket *
1240            disk_ptr->SectorSize ) )
1241       {
1242         DBG ( "Packet size too low while getting "
1243         "MaxSectorsPerPacket (tried %d, got size of %d)\n",
1244         aoe_disk_ptr->MaxSectorsPerPacket, DataSize );
1245         aoe_disk_ptr->MaxSectorsPerPacket--;
1246         aoe_disk_ptr->search_state = AoeSearchStateDone_;
1247       }
1248           else if ( aoe_disk_ptr->MTU <
1249         ( sizeof (AOE_S_PACKET_) +
1250           ( ( aoe_disk_ptr->MaxSectorsPerPacket +
1251               1 ) * disk_ptr->SectorSize ) ) )
1252       {
1253         DBG ( "Got MaxSectorsPerPacket %d at size of %d. "
1254         "MTU of %d reached\n",
1255         aoe_disk_ptr->MaxSectorsPerPacket, DataSize,
1256         aoe_disk_ptr->MTU );
1257         aoe_disk_ptr->search_state = AoeSearchStateDone_;
1258       }
1259           else
1260       {
1261         DBG ( "Got MaxSectorsPerPacket %d at size of %d, "
1262         "trying next...\n", aoe_disk_ptr->MaxSectorsPerPacket,
1263         DataSize );
1264         aoe_disk_ptr->search_state =
1265           AoeSearchStateGetMaxSectsPerPacket_;
1266       }
1267           break;
1268         default:
1269           DBG ( "Undefined search_state!\n" );
1270           break;
1271       }
1272     KeReleaseSpinLock ( &disk_ptr->SpinLock, Irql );
1273     KeSetEvent ( &disk_ptr->SearchEvent, 0, FALSE );
1274     break;
1275         case AoeTagTypeIo_:
1276     /* If the reply is in response to a read request, get our data! */
1277     if (tag->request_ptr->Mode == WvDiskIoModeRead)
1278       RtlCopyMemory ( &tag->request_ptr->Buffer[tag->BufferOffset],
1279           reply->Data,
1280           tag->SectorCount * disk_ptr->SectorSize );
1281     /*
1282      * If this is the last reply expected for the read request,
1283      * complete the IRP and free the request
1284      */
1285     if ( InterlockedDecrement ( &tag->request_ptr->TagCount ) == 0 )
1286       {
1287         tag->request_ptr->Irp->IoStatus.Information =
1288           tag->request_ptr->SectorCount * disk_ptr->SectorSize;
1289         tag->request_ptr->Irp->IoStatus.Status = STATUS_SUCCESS;
1290         WvDriverCompletePendingIrp(tag->request_ptr->Irp);
1291         wv_free(tag->request_ptr);
1292       }
1293     break;
1294         default:
1295     DBG ( "Unknown tag type!!\n" );
1296     break;
1297       }
1298
1299     KeSetEvent ( &AoeSignal_, 0, FALSE );
1300     wv_free(tag->packet_data);
1301     wv_free(tag);
1302     return STATUS_SUCCESS;
1303   }
1304
1305 void aoe__reset_probe(void)
1306   {
1307     AoeProbeTag_->SendTime.QuadPart = 0LL;
1308   }
1309
1310 static void STDCALL AoeThread_(IN void *StartContext)
1311   {
1312     NTSTATUS status;
1313     LARGE_INTEGER Timeout, CurrentTime, ProbeTime, ReportTime;
1314     winvblock__uint32 NextTagId = 1;
1315     AOE_SP_WORK_TAG_ tag;
1316     KIRQL Irql;
1317     winvblock__uint32 Sends = 0;
1318     winvblock__uint32 Resends = 0;
1319     winvblock__uint32 ResendFails = 0;
1320     winvblock__uint32 Fails = 0;
1321     winvblock__uint32 RequestTimeout = 0;
1322     WV_SP_DISK_T disk_ptr;
1323     AOE_SP_DISK_ aoe_disk_ptr;
1324
1325     DBG ( "Entry\n" );
1326
1327     /* Create the PDO for the sub-bus on the WinVBlock bus. */
1328     status = WvDriverAddDummy(
1329         AoeBusDummyIds,
1330         FILE_DEVICE_CONTROLLER,
1331         FILE_DEVICE_SECURE_OPEN
1332       );
1333     if (!NT_SUCCESS(status)) {
1334         DBG("Couldn't add AoE bus to WinVBlock bus!\n");
1335         status = PsTerminateSystemThread(status);
1336         return;
1337       }
1338
1339     ReportTime.QuadPart = 0LL;
1340     ProbeTime.QuadPart = 0LL;
1341
1342     while ( TRUE )
1343       {
1344         /*
1345          * TODO: Make the below value a #defined constant 
1346          */
1347         /*
1348          * 100.000 * 100ns = 10.000.000 ns = 10ms
1349          */
1350         Timeout.QuadPart = -100000LL;
1351         KeWaitForSingleObject ( &AoeSignal_, Executive,
1352               KernelMode, FALSE, &Timeout );
1353         KeResetEvent ( &AoeSignal_ );
1354         if ( AoeStop_ )
1355     {
1356       DBG ( "Stopping...\n" );
1357       PsTerminateSystemThread ( STATUS_SUCCESS );
1358     }
1359
1360         KeQuerySystemTime ( &CurrentTime );
1361         /*
1362          * TODO: Make the below value a #defined constant 
1363          */
1364         if ( CurrentTime.QuadPart > ( ReportTime.QuadPart + 10000000LL ) )
1365     {
1366       DBG ( "Sends: %d  Resends: %d  ResendFails: %d  Fails: %d  "
1367       "AoePendingTags_: %d  RequestTimeout: %d\n", Sends,
1368       Resends, ResendFails, Fails, AoePendingTags_,
1369       RequestTimeout );
1370       Sends = 0;
1371       Resends = 0;
1372       ResendFails = 0;
1373       Fails = 0;
1374       KeQuerySystemTime ( &ReportTime );
1375     }
1376
1377         /*
1378          * TODO: Make the below value a #defined constant 
1379          */
1380         if ( CurrentTime.QuadPart >
1381        ( AoeProbeTag_->SendTime.QuadPart + 100000000LL ) )
1382     {
1383       AoeProbeTag_->Id = NextTagId++;
1384       if ( NextTagId == 0 )
1385         NextTagId++;
1386       AoeProbeTag_->packet_data->Tag = AoeProbeTag_->Id;
1387       Protocol_Send ( "\xff\xff\xff\xff\xff\xff",
1388           "\xff\xff\xff\xff\xff\xff",
1389           ( winvblock__uint8_ptr ) AoeProbeTag_->
1390           packet_data, AoeProbeTag_->PacketSize,
1391           NULL );
1392       KeQuerySystemTime ( &AoeProbeTag_->SendTime );
1393     }
1394
1395         KeAcquireSpinLock ( &AoeLock_, &Irql );
1396         if ( AoeTagListFirst_ == NULL )
1397     {
1398       KeReleaseSpinLock ( &AoeLock_, Irql );
1399       continue;
1400     }
1401         tag = AoeTagListFirst_;
1402         while ( tag != NULL )
1403     {
1404       /*
1405        * Establish pointers to the disk and AoE disk
1406        */
1407       disk_ptr = disk__get_ptr ( tag->device );
1408       aoe_disk_ptr = AoeDiskFromDev_(tag->device);
1409
1410       RequestTimeout = aoe_disk_ptr->Timeout;
1411       if ( tag->Id == 0 )
1412         {
1413           if ( AoePendingTags_ <= 64 )
1414       {
1415         /*
1416          * if ( AoePendingTags_ <= 102400 ) { 
1417          */
1418         if ( AoePendingTags_ < 0 )
1419           DBG ( "AoePendingTags_ < 0!!\n" );
1420         tag->Id = NextTagId++;
1421         if ( NextTagId == 0 )
1422           NextTagId++;
1423         tag->packet_data->Tag = tag->Id;
1424         if ( Protocol_Send
1425              ( aoe_disk_ptr->ClientMac, aoe_disk_ptr->ServerMac,
1426          ( winvblock__uint8_ptr ) tag->packet_data,
1427          tag->PacketSize, tag ) )
1428           {
1429             KeQuerySystemTime ( &tag->FirstSendTime );
1430             KeQuerySystemTime ( &tag->SendTime );
1431             AoePendingTags_++;
1432             Sends++;
1433           }
1434         else
1435           {
1436             Fails++;
1437             tag->Id = 0;
1438             break;
1439           }
1440       }
1441         }
1442       else
1443         {
1444           KeQuerySystemTime ( &CurrentTime );
1445           if ( CurrentTime.QuadPart >
1446          ( tag->SendTime.QuadPart +
1447            ( LONGLONG ) ( aoe_disk_ptr->Timeout * 2 ) ) )
1448       {
1449         if ( Protocol_Send
1450              ( aoe_disk_ptr->ClientMac, aoe_disk_ptr->ServerMac,
1451          ( winvblock__uint8_ptr ) tag->packet_data,
1452          tag->PacketSize, tag ) )
1453           {
1454             KeQuerySystemTime ( &tag->SendTime );
1455             aoe_disk_ptr->Timeout += aoe_disk_ptr->Timeout / 1000;
1456             if ( aoe_disk_ptr->Timeout > 100000000 )
1457         aoe_disk_ptr->Timeout = 100000000;
1458             Resends++;
1459           }
1460         else
1461           {
1462             ResendFails++;
1463             break;
1464           }
1465       }
1466         }
1467       tag = tag->next;
1468       if ( tag == AoeTagListFirst_ )
1469         {
1470           DBG ( "Taglist Cyclic!!\n" );
1471           break;
1472         }
1473     }
1474         KeReleaseSpinLock ( &AoeLock_, Irql );
1475       }
1476     DBG ( "Exit\n" );
1477   }
1478
1479 static winvblock__uint32 AoeDiskMaxXferLen_(IN WV_SP_DISK_T disk_ptr) {
1480     AOE_SP_DISK_ aoe_disk_ptr = AoeDiskFromDev_(disk_ptr->Dev);
1481
1482     return disk_ptr->SectorSize * aoe_disk_ptr->MaxSectorsPerPacket;
1483   }
1484
1485 static winvblock__uint32 STDCALL query_id(
1486     IN WV_SP_DEV_T dev,
1487     IN BUS_QUERY_ID_TYPE query_type,
1488     IN OUT WCHAR (*buf)[512]
1489   ) {
1490     AOE_SP_DISK_ aoe_disk = AoeDiskFromDev_(dev);
1491
1492     switch (query_type) {
1493         case BusQueryDeviceID:
1494           return swprintf(*buf, winvblock__literal_w L"\\AoEHardDisk") + 1;
1495
1496         case BusQueryInstanceID:
1497           return swprintf(
1498               *buf,
1499               L"AoE_at_Shelf_%d.Slot_%d",
1500               aoe_disk->Major,
1501               aoe_disk->Minor
1502             ) + 1;
1503
1504         case BusQueryHardwareIDs: {
1505             winvblock__uint32 tmp;
1506
1507             tmp = swprintf(
1508                 *buf,
1509                 winvblock__literal_w L"\\AoEHardDisk"
1510               ) + 1;
1511             tmp += swprintf(*buf + tmp, L"GenDisk") + 4;
1512             return tmp;
1513           }
1514
1515         case BusQueryCompatibleIDs:
1516           return swprintf(*buf, L"GenDisk") + 4;
1517
1518         default:
1519           return 0;
1520       }
1521   }
1522
1523 #ifdef _MSC_VER
1524 #  pragma pack(1)
1525 #endif
1526 winvblock__def_struct(abft) {
1527     winvblock__uint32 Signature;  /* 0x54464261 (aBFT) */
1528     winvblock__uint32 Length;
1529     winvblock__uint8 Revision;
1530     winvblock__uint8 Checksum;
1531     winvblock__uint8 OEMID[6];
1532     winvblock__uint8 OEMTableID[8];
1533     winvblock__uint8 Reserved1[12];
1534     winvblock__uint16 Major;
1535     winvblock__uint8 Minor;
1536     winvblock__uint8 Reserved2;
1537     winvblock__uint8 ClientMac[6];
1538   } __attribute__((__packed__));
1539 #ifdef _MSC_VER
1540 #  pragma pack()
1541 #endif
1542
1543 static void STDCALL AoeDiskClose_(IN WV_SP_DISK_T disk_ptr) {
1544     return;
1545   }
1546
1547 static void AoeProcessAbft_(void) {
1548     PHYSICAL_ADDRESS PhysicalAddress;
1549     winvblock__uint8_ptr PhysicalMemory;
1550     winvblock__uint32 Offset, Checksum, i;
1551     winvblock__bool FoundAbft = FALSE;
1552     abft AoEBootRecord;
1553     AOE_SP_DISK_ aoe_disk;
1554
1555     /* Find aBFT. */
1556     PhysicalAddress.QuadPart = 0LL;
1557     PhysicalMemory = MmMapIoSpace(PhysicalAddress, 0xa0000, MmNonCached);
1558     if (!PhysicalMemory) {
1559         DBG("Could not map low memory\n");
1560         goto err_map_mem;
1561       }
1562     for (Offset = 0; Offset < 0xa0000; Offset += 0x10) {
1563         if (!(
1564             ((abft_ptr) (PhysicalMemory + Offset))->Signature ==
1565             0x54464261
1566           ))
1567           continue;
1568         Checksum = 0;
1569         for (
1570             i = 0;
1571             i < ((abft_ptr) (PhysicalMemory + Offset))->Length;
1572             i++
1573           )
1574           Checksum += PhysicalMemory[Offset + i];
1575         if (Checksum & 0xff)
1576           continue;
1577         if (((abft_ptr) (PhysicalMemory + Offset))->Revision != 1) {
1578             DBG(
1579                 "Found aBFT with mismatched revision v%d at "
1580                   "segment 0x%4x. want v1.\n",
1581                 ((abft_ptr) (PhysicalMemory + Offset))->Revision,
1582                 (Offset / 0x10)
1583               );
1584             continue;
1585           }
1586         DBG("Found aBFT at segment: 0x%04x\n", (Offset / 0x10));
1587         RtlCopyMemory(
1588             &AoEBootRecord,
1589             PhysicalMemory + Offset,
1590             sizeof (abft)
1591           );
1592         FoundAbft = TRUE;
1593         break;
1594       }
1595     MmUnmapIoSpace(PhysicalMemory, 0xa0000);
1596
1597     #ifdef RIS
1598     FoundAbft = TRUE;
1599     RtlCopyMemory(AoEBootRecord.ClientMac, "\x00\x0c\x29\x34\x69\x34", 6);
1600     AoEBootRecord.Major = 0;
1601     AoEBootRecord.Minor = 10;
1602     #endif
1603
1604     if (!FoundAbft)
1605       goto out_no_abft;
1606     aoe_disk = AoeDiskCreate_();
1607     if(aoe_disk == NULL) {
1608         DBG("Could not create AoE disk from aBFT!\n");
1609         return;
1610       }
1611     DBG(
1612         "Attaching AoE disk from client NIC "
1613           "%02x:%02x:%02x:%02x:%02x:%02x to major: %d minor: %d\n",
1614         AoEBootRecord.ClientMac[0],
1615         AoEBootRecord.ClientMac[1],
1616         AoEBootRecord.ClientMac[2],
1617         AoEBootRecord.ClientMac[3],
1618         AoEBootRecord.ClientMac[4],
1619         AoEBootRecord.ClientMac[5],
1620         AoEBootRecord.Major,
1621         AoEBootRecord.Minor
1622       );
1623     RtlCopyMemory(aoe_disk->ClientMac, AoEBootRecord.ClientMac, 6);
1624     RtlFillMemory(aoe_disk->ServerMac, 6, 0xff);
1625     aoe_disk->Major = AoEBootRecord.Major;
1626     aoe_disk->Minor = AoEBootRecord.Minor;
1627     aoe_disk->MaxSectorsPerPacket = 1;
1628     aoe_disk->Timeout = 200000;          /* 20 ms. */
1629     aoe_disk->disk->Dev->Boot = TRUE;
1630     aoe_disk->disk->Media = WvDiskMediaTypeHard;
1631     WvDriverBusAddDev(aoe_disk->disk->Dev);
1632     return;
1633
1634     out_no_abft:
1635     DBG("No aBFT found\n");
1636
1637     err_map_mem:
1638
1639     return;
1640   }
1641
1642 NTSTATUS STDCALL AoeBusDevCtlScan(IN PIRP irp) {
1643     KIRQL irql;
1644     winvblock__uint32 count;
1645     AOE_SP_TARGET_LIST_ target_walker;
1646     aoe__mount_targets_ptr targets;
1647     PIO_STACK_LOCATION io_stack_loc = IoGetCurrentIrpStackLocation(irp);
1648
1649     DBG("Got IOCTL_AOE_SCAN...\n");
1650     KeAcquireSpinLock(&AoeTargetListLock_, &irql);
1651
1652     count = 0;
1653     target_walker = AoeTargetList_;
1654     while (target_walker != NULL) {
1655         count++;
1656         target_walker = target_walker->next;
1657       }
1658
1659     targets = wv_malloc(sizeof *targets + (count * sizeof targets->Target[0]));
1660     if (targets == NULL) {
1661         DBG("wv_malloc targets\n");
1662         return driver__complete_irp(
1663             irp,
1664             0,
1665             STATUS_INSUFFICIENT_RESOURCES
1666           );
1667       }
1668     irp->IoStatus.Information =
1669       sizeof (aoe__mount_targets) + (count * sizeof (aoe__mount_target));
1670     targets->Count = count;
1671
1672     count = 0;
1673     target_walker = AoeTargetList_;
1674     while (target_walker != NULL) {
1675         RtlCopyMemory(
1676             &targets->Target[count],
1677             &target_walker->Target,
1678             sizeof (aoe__mount_target)
1679           );
1680         count++;
1681         target_walker = target_walker->next;
1682       }
1683     RtlCopyMemory(
1684         irp->AssociatedIrp.SystemBuffer,
1685         targets,
1686         (io_stack_loc->Parameters.DeviceIoControl.OutputBufferLength <
1687           (sizeof (aoe__mount_targets) + (count * sizeof (aoe__mount_target))) ?
1688           io_stack_loc->Parameters.DeviceIoControl.OutputBufferLength :
1689           (sizeof (aoe__mount_targets) + (count * sizeof (aoe__mount_target)))
1690         )
1691       );
1692     wv_free(targets);
1693
1694     KeReleaseSpinLock(&AoeTargetListLock_, irql);
1695     return driver__complete_irp(irp, irp->IoStatus.Information, STATUS_SUCCESS);
1696   }
1697
1698 NTSTATUS STDCALL AoeBusDevCtlShow(IN PIRP irp) {
1699     winvblock__uint32 count;
1700     WV_SP_BUS_NODE walker;
1701     aoe__mount_disks_ptr disks;
1702     wv_size_t size;
1703     PIO_STACK_LOCATION io_stack_loc = IoGetCurrentIrpStackLocation(irp);
1704
1705     if (!(io_stack_loc->Control & SL_PENDING_RETURNED)) {
1706         NTSTATUS status;
1707
1708         /* Enqueue the IRP. */
1709         status = WvBusEnqueueIrp(&AoeBusMain, irp);
1710         if (status != STATUS_PENDING)
1711           /* Problem. */
1712           return driver__complete_irp(irp, 0, status);
1713         /* Ok. */
1714         return status;
1715       }
1716     /* If we get here, we should be called by WvBusProcessWorkItems() */
1717     DBG("Got IOCTL_AOE_SHOW...\n");
1718
1719     count = WvBusGetNodeCount(&AoeBusMain);
1720     size = sizeof *disks + (count * sizeof disks->Disk[0]);
1721
1722     disks = wv_malloc(size);
1723     if (disks == NULL) {
1724         DBG("wv_malloc disks\n");
1725         return driver__complete_irp(
1726             irp,
1727             0,
1728             STATUS_INSUFFICIENT_RESOURCES
1729           );
1730       }
1731     irp->IoStatus.Information = size;
1732     disks->Count = count;
1733
1734     count = 0;
1735     walker = NULL;
1736     /* For each node on the bus... */
1737     while (walker = WvBusGetNextNode(&AoeBusMain, walker)) {
1738         WV_SP_DEV_T dev = WvDevFromDevObj(WvBusGetNodePdo(walker));
1739         WV_SP_DISK_T disk = disk__get_ptr(dev);
1740         AOE_SP_DISK_ aoe_disk = AoeDiskFromDev_(dev);
1741
1742         disks->Disk[count].Disk = dev->DevNum;
1743         RtlCopyMemory(
1744             &disks->Disk[count].ClientMac,
1745             &aoe_disk->ClientMac,
1746             6
1747           );
1748         RtlCopyMemory(
1749             &disks->Disk[count].ServerMac,
1750             &aoe_disk->ServerMac,
1751             6
1752           );
1753         disks->Disk[count].Major = aoe_disk->Major;
1754         disks->Disk[count].Minor = aoe_disk->Minor;
1755         disks->Disk[count].LBASize = disk->LBADiskSize;
1756         count++;
1757       }
1758     RtlCopyMemory(
1759         irp->AssociatedIrp.SystemBuffer,
1760         disks,
1761         (io_stack_loc->Parameters.DeviceIoControl.OutputBufferLength <
1762           (sizeof (aoe__mount_disks) + (count * sizeof (aoe__mount_disk))) ?
1763           io_stack_loc->Parameters.DeviceIoControl.OutputBufferLength :
1764           (sizeof (aoe__mount_disks) + (count * sizeof (aoe__mount_disk)))
1765         )
1766       );
1767     wv_free(disks);
1768     return driver__complete_irp(irp, irp->IoStatus.Information, STATUS_SUCCESS);
1769   }
1770
1771 NTSTATUS STDCALL AoeBusDevCtlMount(IN PIRP irp) {
1772     winvblock__uint8_ptr buffer = irp->AssociatedIrp.SystemBuffer;
1773     AOE_SP_DISK_ aoe_disk;
1774
1775     DBG(
1776         "Got IOCTL_AOE_MOUNT for client: %02x:%02x:%02x:%02x:%02x:%02x "
1777           "Major:%d Minor:%d\n",
1778         buffer[0],
1779         buffer[1],
1780         buffer[2],
1781         buffer[3],
1782         buffer[4],
1783         buffer[5],
1784         *(winvblock__uint16_ptr) (buffer + 6),
1785         (winvblock__uint8) buffer[8]
1786       );
1787     aoe_disk = AoeDiskCreate_();
1788     if (aoe_disk == NULL) {
1789         DBG("Could not create AoE disk!\n");
1790         return driver__complete_irp(
1791             irp,
1792             0,
1793             STATUS_INSUFFICIENT_RESOURCES
1794           );
1795       }
1796     RtlCopyMemory(aoe_disk->ClientMac, buffer, 6);
1797     RtlFillMemory(aoe_disk->ServerMac, 6, 0xff);
1798     aoe_disk->Major = *(winvblock__uint16_ptr) (buffer + 6);
1799     aoe_disk->Minor = (winvblock__uint8) buffer[8];
1800     aoe_disk->MaxSectorsPerPacket = 1;
1801     aoe_disk->Timeout = 200000;             /* 20 ms. */
1802     aoe_disk->disk->Dev->Boot = FALSE;
1803     aoe_disk->disk->Media = WvDiskMediaTypeHard;
1804     WvDriverBusAddDev(aoe_disk->disk->Dev);
1805
1806     return driver__complete_irp(irp, 0, STATUS_SUCCESS);
1807   }
1808
1809 /**
1810  * Create a new AoE disk.
1811  *
1812  * @ret aoe_disk        The address of a new AoE disk, or NULL for failure.
1813  *
1814  * This function should not be confused with a PDO creation routine, which is
1815  * actually implemented for each device type.  This routine will allocate an
1816  * AOE_S_DISK_, track it in a global list, as well as populate the disk
1817  * with default values.
1818  */
1819 static AOE_SP_DISK_ AoeDiskCreate_(void) {
1820     WV_SP_DISK_T disk;
1821     AOE_SP_DISK_ aoe_disk;
1822
1823     /* Try to create a disk. */
1824     disk = disk__create();
1825     if (disk == NULL)
1826       goto err_nodisk;
1827     /*
1828      * AoE disk devices might be used for booting and should
1829      * not be allocated from a paged memory pool.
1830      */
1831     aoe_disk = wv_mallocz(sizeof *aoe_disk);
1832     if (aoe_disk == NULL)
1833       goto err_noaoedisk;
1834     /* Track the new AoE disk in our global list. */
1835     ExInterlockedInsertTailList(
1836         &AoeDiskList_,
1837         &aoe_disk->tracking,
1838         &AoeDiskListLock_
1839       );
1840     /* Populate non-zero device defaults. */
1841     aoe_disk->disk = disk;
1842     aoe_disk->prev_free = disk->Dev->Ops.Free;
1843     disk->Dev->Ops.Free = AoeDiskFree_;
1844     disk->Dev->Ops.PnpId = query_id;
1845     disk->disk_ops.Io = AoeDiskIo_;
1846     disk->disk_ops.MaxXferLen = AoeDiskMaxXferLen_;
1847     disk->disk_ops.Init = AoeDiskInit_;
1848     disk->disk_ops.Close = AoeDiskClose_;
1849     disk->ext = aoe_disk;
1850
1851     return aoe_disk;
1852
1853     err_noaoedisk:
1854
1855     WvDevFree(disk->Dev);
1856     err_nodisk:
1857
1858     return NULL;
1859   }
1860
1861 /**
1862  * Default AoE disk deletion operation.
1863  *
1864  * @v dev               Points to the AoE disk device to delete.
1865  */
1866 static void STDCALL AoeDiskFree_(IN WV_SP_DEV_T dev) {
1867     AOE_SP_DISK_ aoe_disk = AoeDiskFromDev_(dev);
1868     /* Free the "inherited class". */
1869     aoe_disk->prev_free(dev);
1870     /*
1871      * Track the AoE disk deletion in our global list.  Unfortunately,
1872      * for now we have faith that an AoE disk won't be deleted twice and
1873      * result in a race condition.  Something to keep in mind...
1874      */
1875     ExInterlockedRemoveHeadList(
1876         aoe_disk->tracking.Blink,
1877         &AoeDiskListLock_
1878       );
1879
1880     wv_free(aoe_disk);
1881   }
1882
1883 static NTSTATUS STDCALL AoeDriverIrpNotSupported_(
1884     IN PDEVICE_OBJECT dev_obj,
1885     IN PIRP irp
1886   ) {
1887     return driver__complete_irp(irp, 0, STATUS_NOT_SUPPORTED);
1888   }
1889
1890 /* Handle a power IRP. */
1891 static NTSTATUS AoeDriverIrpPower_(
1892     IN PDEVICE_OBJECT dev_obj,
1893     IN PIRP irp
1894   ) {
1895     WV_SP_DEV_T dev;
1896
1897     #ifdef DEBUGIRPS
1898     Debug_IrpStart(dev_obj, irp);
1899     #endif
1900     /* Check for a bus IRP. */
1901     if (dev_obj == AoeBusMain.Fdo)
1902       return WvBusPower(&AoeBusMain, irp);
1903     /* WvDevFromDevObj() checks for a NULL dev_obj */
1904     dev = WvDevFromDevObj(dev_obj);
1905     /* Check that the device exists. */
1906     if (!dev || dev->State == WvDevStateDeleted) {
1907         /* Even if it doesn't, a power IRP is important! */
1908         PoStartNextPowerIrp(irp);
1909         return driver__complete_irp(irp, 0, STATUS_NO_SUCH_DEVICE);
1910       }
1911     /* Call the particular device's power handler. */
1912     if (dev->IrpMj && dev->IrpMj->Power)
1913       return dev->IrpMj->Power(dev, irp);
1914     /* Otherwise, we don't support the IRP. */
1915     return driver__complete_irp(irp, 0, STATUS_NOT_SUPPORTED);
1916   }
1917
1918 /* Handle an IRP_MJ_CREATE or IRP_MJ_CLOSE IRP. */
1919 static NTSTATUS AoeDriverIrpCreateClose_(
1920     IN PDEVICE_OBJECT dev_obj,
1921     IN PIRP irp
1922   ) {
1923     WV_SP_DEV_T dev;
1924
1925     #ifdef DEBUGIRPS
1926     Debug_IrpStart(dev_obj, irp);
1927     #endif
1928     /* Check for a bus IRP. */
1929     if (dev_obj == AoeBusMain.Fdo)
1930       return driver__complete_irp(irp, 0, STATUS_SUCCESS);
1931     /* WvDevFromDevObj() checks for a NULL dev_obj */
1932     dev = WvDevFromDevObj(dev_obj);
1933     /* Check that the device exists. */
1934     if (!dev || dev->State == WvDevStateDeleted)
1935       return driver__complete_irp(irp, 0, STATUS_NO_SUCH_DEVICE);
1936     /* Always succeed with nothing to do. */
1937     return driver__complete_irp(irp, 0, STATUS_SUCCESS);
1938   }
1939
1940 /* Handle an IRP_MJ_SYSTEM_CONTROL IRP. */
1941 static NTSTATUS AoeDriverIrpSysCtl_(
1942     IN PDEVICE_OBJECT dev_obj,
1943     IN PIRP irp
1944   ) {
1945     WV_SP_DEV_T dev;
1946
1947     #ifdef DEBUGIRPS
1948     Debug_IrpStart(dev_obj, irp);
1949     #endif
1950     /* Check for a bus IRP. */
1951     if (dev_obj == AoeBusMain.Fdo)
1952       return WvBusSysCtl(&AoeBusMain, irp);
1953     /* WvDevFromDevObj() checks for a NULL dev_obj */
1954     dev = WvDevFromDevObj(dev_obj);
1955     /* Check that the device exists. */
1956     if (!dev || dev->State == WvDevStateDeleted)
1957       return driver__complete_irp(irp, 0, STATUS_NO_SUCH_DEVICE);
1958     /* Call the particular device's power handler. */
1959     if (dev->IrpMj && dev->IrpMj->SysCtl)
1960       return dev->IrpMj->SysCtl(dev, irp);
1961     /* Otherwise, we don't support the IRP. */
1962     return driver__complete_irp(irp, 0, STATUS_NOT_SUPPORTED);
1963   }
1964
1965 /* Handle an IRP_MJ_DEVICE_CONTROL IRP. */
1966 static NTSTATUS AoeDriverIrpDevCtl_(
1967     IN PDEVICE_OBJECT dev_obj,
1968     IN PIRP irp
1969   ) {
1970     WV_SP_DEV_T dev;
1971     PIO_STACK_LOCATION io_stack_loc = IoGetCurrentIrpStackLocation(irp);
1972
1973     #ifdef DEBUGIRPS
1974     Debug_IrpStart(dev_obj, irp);
1975     #endif
1976     /* Check for a bus IRP. */
1977     if (dev_obj == AoeBusMain.Fdo) {
1978         return AoeBusDevCtl(
1979             irp,
1980             io_stack_loc->Parameters.DeviceIoControl.IoControlCode
1981           );
1982       }
1983     /* WvDevFromDevObj() checks for a NULL dev_obj */
1984     dev = WvDevFromDevObj(dev_obj);
1985     /* Check that the device exists. */
1986     if (!dev || dev->State == WvDevStateDeleted)
1987       return driver__complete_irp(irp, 0, STATUS_NO_SUCH_DEVICE);
1988     /* Call the particular device's power handler. */
1989     if (dev->IrpMj && dev->IrpMj->DevCtl) {
1990         return dev->IrpMj->DevCtl(
1991             dev,
1992             irp,
1993             io_stack_loc->Parameters.DeviceIoControl.IoControlCode
1994           );
1995       }
1996     /* Otherwise, we don't support the IRP. */
1997     return driver__complete_irp(irp, 0, STATUS_NOT_SUPPORTED);
1998   }
1999
2000 /* Handle an IRP_MJ_SCSI IRP. */
2001 static NTSTATUS AoeDriverIrpScsi_(
2002     IN PDEVICE_OBJECT dev_obj,
2003     IN PIRP irp
2004   ) {
2005     WV_SP_DEV_T dev;
2006     PIO_STACK_LOCATION io_stack_loc;
2007
2008     #ifdef DEBUGIRPS
2009     Debug_IrpStart(dev_obj, irp);
2010     #endif
2011     /* Check for a bus IRP. */
2012     if (dev_obj == AoeBusMain.Fdo)
2013       return driver__complete_irp(irp, 0, STATUS_NOT_SUPPORTED);
2014     /* WvDevFromDevObj() checks for a NULL dev_obj */
2015     dev = WvDevFromDevObj(dev_obj);
2016     io_stack_loc = IoGetCurrentIrpStackLocation(irp);
2017     /* Check that the device exists. */
2018     if (!dev || dev->State == WvDevStateDeleted)
2019       return driver__complete_irp(irp, 0, STATUS_NO_SUCH_DEVICE);
2020     /* Call the particular device's power handler. */
2021     if (dev->IrpMj && dev->IrpMj->Scsi) {
2022         return dev->IrpMj->Scsi(
2023             dev,
2024             irp,
2025             io_stack_loc->Parameters.Scsi.Srb->Function
2026           );
2027       }
2028     /* Otherwise, we don't support the IRP. */
2029     return driver__complete_irp(irp, 0, STATUS_NOT_SUPPORTED);
2030   }
2031
2032 /* Handle an IRP_MJ_PNP IRP. */
2033 static NTSTATUS AoeDriverIrpPnp_(
2034     IN PDEVICE_OBJECT dev_obj,
2035     IN PIRP irp
2036   ) {
2037     WV_SP_DEV_T dev;
2038     PIO_STACK_LOCATION io_stack_loc = IoGetCurrentIrpStackLocation(irp);
2039
2040     #ifdef DEBUGIRPS
2041     Debug_IrpStart(dev_obj, irp);
2042     #endif
2043     /* Check for a bus IRP. */
2044     if (dev_obj == AoeBusMain.Fdo)
2045       return WvBusPnp(&AoeBusMain, irp, io_stack_loc->MinorFunction);
2046     /* WvDevFromDevObj() checks for a NULL dev_obj */
2047     dev = WvDevFromDevObj(dev_obj);
2048     /* Check that the device exists. */
2049     if (!dev || dev->State == WvDevStateDeleted)
2050       return driver__complete_irp(irp, 0, STATUS_NO_SUCH_DEVICE);
2051     /* Call the particular device's power handler. */
2052     if (dev->IrpMj && dev->IrpMj->Pnp) {
2053         return dev->IrpMj->Pnp(
2054             dev,
2055             irp,
2056             io_stack_loc->MinorFunction
2057           );
2058       }
2059     /* Otherwise, we don't support the IRP. */
2060     return driver__complete_irp(irp, 0, STATUS_NOT_SUPPORTED);
2061   }