fb23f0cc0905e866b8c7a761ac3c4e13a0e26579
[openafs.git] / src / WINNT / afsrdr / kernel / lib / AFSIoSupport.cpp
1 /*
2  * Copyright (c) 2008, 2009, 2010, 2011 Kernel Drivers, LLC.
3  * Copyright (c) 2009, 2010, 2011 Your File System, Inc.
4  * All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  *
10  * - Redistributions of source code must retain the above copyright notice,
11  *   this list of conditions and the following disclaimer.
12  * - Redistributions in binary form must reproduce the above copyright
13  *   notice,
14  *   this list of conditions and the following disclaimer in the
15  *   documentation
16  *   and/or other materials provided with the distribution.
17  * - Neither the names of Kernel Drivers, LLC and Your File System, Inc.
18  *   nor the names of their contributors may be used to endorse or promote
19  *   products derived from this software without specific prior written
20  *   permission from Kernel Drivers, LLC and Your File System, Inc.
21  *
22  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
23  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
24  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
25  * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
26  * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
27  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
28  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
29  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
30  * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
31  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
32  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
33  */
34
35 //
36 // File: AFSIoSupport.cpp
37 //
38
39 #include "AFSCommon.h"
40
41 static AFSExtent *NextExtent(AFSExtent *Extent);
42
43 //
44 // Called in the paging or non cached read and write paths to get the
45 // first and last extent in a span.  We also the count of how many
46 // discontiguous extents the are in the cache file.
47 //
48
49 NTSTATUS
50 AFSGetExtents( IN AFSFcb *Fcb,
51                IN PLARGE_INTEGER Offset,
52                IN ULONG Length,
53                OUT AFSExtent *From,
54                OUT ULONG *ExtentCount,
55                OUT ULONG *RunCount)
56 {
57     NTSTATUS           ntStatus = STATUS_SUCCESS;
58     ULONG              extentsCount = 0, runCount = 0;
59     AFSExtent         *pEndExtent = NULL;
60     LARGE_INTEGER      liLastCacheOffset;
61
62     *ExtentCount = 0;
63
64     __Enter
65     {
66         ASSERT(AFSExtentContains(From, Offset));
67
68         pEndExtent = From;
69         extentsCount = 1;
70         runCount = 1;
71         liLastCacheOffset.QuadPart = pEndExtent->CacheOffset.QuadPart + pEndExtent->Size;
72
73         while ((Offset->QuadPart + Length) >
74                pEndExtent->FileOffset.QuadPart + pEndExtent->Size) {
75
76             pEndExtent = NextExtent(pEndExtent);
77
78             if (liLastCacheOffset.QuadPart != pEndExtent->CacheOffset.QuadPart) {
79                 //
80                 // Discontiguous (in the cache)
81                 //
82                 runCount += 1;
83
84             }
85
86             extentsCount+= 1;
87
88             liLastCacheOffset.QuadPart = pEndExtent->CacheOffset.QuadPart + pEndExtent->Size;
89         }
90
91         *ExtentCount = extentsCount;
92         *RunCount = runCount;
93         ntStatus = STATUS_SUCCESS;
94     }
95     return ntStatus;
96 }
97
98
99 NTSTATUS
100 AFSSetupIoRun( IN PDEVICE_OBJECT CacheDevice,
101                IN PIRP           MasterIrp,
102                IN PVOID          SystemBuffer,
103                IN OUT AFSIoRun  *IoRuns,
104                IN PLARGE_INTEGER Start,
105                IN ULONG          Length,
106                IN AFSExtent     *From,
107                IN OUT ULONG     *RunCount)
108 {
109     //
110     // Do all the work which we can prior to firing off the IRP
111     // (allocate them, calculate offsets and so on)
112     //
113     LARGE_INTEGER  liCacheOffset;
114     LARGE_INTEGER  liFileOffset = *Start;
115     ULONG          ulCurrLength;
116     ULONG          ulReadOffset;
117     ULONG          ulCurrRun = 0;
118     AFSExtent     *pExtent = From;
119     AFSExtent     *pNextExtent;
120     PMDL          *pMDL;
121     BOOLEAN        done = FALSE;
122     NTSTATUS       ntStatus = STATUS_SUCCESS;
123
124     __Enter
125     {
126
127         while( !done)
128         {
129             ASSERT( liFileOffset.QuadPart >= pExtent->FileOffset.QuadPart );
130
131             liCacheOffset = pExtent->CacheOffset;
132             liCacheOffset.QuadPart += (liFileOffset.QuadPart -
133                                        pExtent->FileOffset.QuadPart);
134
135             ulCurrLength = 0;
136
137             //
138             // While there is still data to process, loop
139             //
140             while ( (pExtent->FileOffset.QuadPart + pExtent->Size) <
141                     (Start->QuadPart + Length) )
142             {
143                 //
144                 // Collapse the read if we can
145                 //
146                 pNextExtent = NextExtent( pExtent );
147
148                 if (pNextExtent->CacheOffset.QuadPart !=
149                     (pExtent->CacheOffset.QuadPart + pExtent->Size))
150                 {
151                     //
152                     // This extent and the next extent are not contiguous
153                     //
154                     break;
155                 }
156                 pExtent = pNextExtent;
157             }
158
159             //
160             // So, this run goes from liCacheOffset to the end of pExtent
161             //
162
163             if ((pExtent->FileOffset.QuadPart + pExtent->Size) >= (Start->QuadPart + Length))
164             {
165                 //
166                 // The last extent overruns the area we care
167                 // about, so trim it back.
168                 //
169                 ulCurrLength = (ULONG) (Start->QuadPart + Length -
170                                         liFileOffset.QuadPart);
171                 //
172                 // But we are done
173                 //
174                 done = TRUE;
175             }
176             else
177             {
178                 //
179                 // Length is from the LiFileOffset to the end of this
180                 // extent.
181                 //
182                 ulCurrLength = (ULONG) (pExtent->FileOffset.QuadPart + pExtent->Size -
183                                         liFileOffset.QuadPart);
184                 done = FALSE;
185             }
186
187             //
188             // Calculate the offset into the buffer
189             //
190             ulReadOffset = (ULONG) (liFileOffset.QuadPart - Start->QuadPart);
191
192             IoRuns[ulCurrRun].CacheOffset = liCacheOffset;
193             IoRuns[ulCurrRun].ByteCount = ulCurrLength;
194
195             IoRuns[ulCurrRun].ChildIrp = IoAllocateIrp( CacheDevice->StackSize + 1,
196                                                         FALSE);
197
198             if (NULL == IoRuns[ulCurrRun].ChildIrp)
199             {
200                 try_return( ntStatus = STATUS_INSUFFICIENT_RESOURCES );
201             }
202
203             IoRuns[ulCurrRun].ChildIrp->UserBuffer = ((PCHAR) SystemBuffer) + ulReadOffset;
204             IoRuns[ulCurrRun].ChildIrp->Tail.Overlay.Thread = PsGetCurrentThread();
205             IoRuns[ulCurrRun].ChildIrp->RequestorMode = KernelMode;
206             IoRuns[ulCurrRun].ChildIrp->MdlAddress = NULL;
207
208             ulCurrRun ++;
209             if (!done)
210             {
211                 ASSERT(pNextExtent != NULL && pNextExtent != pExtent);
212                 //
213                 // Move cursors forward
214                 //
215                 pExtent = pNextExtent;
216                 liFileOffset = pExtent->FileOffset;
217             }
218         }
219         ASSERT(ulCurrRun <= *RunCount);
220 try_exit:
221         if (!NT_SUCCESS(ntStatus))
222         {
223             for (ulCurrRun = 0 ; ulCurrRun < *RunCount; ulCurrRun++)
224             {
225                 if (IoRuns[ulCurrRun].ChildIrp) {
226                     IoFreeIrp( IoRuns[ulCurrRun].ChildIrp );
227                     IoRuns[ulCurrRun].ChildIrp = NULL;
228                 }
229             }
230         }
231         else
232         {
233             *RunCount = ulCurrRun;
234         }
235     }
236     return ntStatus;
237 }
238
239 //
240 // We manage our own scatter / gather.  This completion
241 // function calls into our generic function.
242 //
243
244 static
245 NTSTATUS
246 CompletionFunction(IN PDEVICE_OBJECT DeviceObject,
247                    PIRP Irp,
248                    PVOID Context)
249 {
250     AFSGatherIo *pGather = (AFSGatherIo *) Context;
251
252     AFSCompleteIo( pGather, Irp->IoStatus.Status);
253
254     if (Irp->MdlAddress) {
255         if (Irp->MdlAddress->MdlFlags & MDL_PAGES_LOCKED) {
256             MmUnlockPages(Irp->MdlAddress);
257         }
258         IoFreeMdl(Irp->MdlAddress);
259         Irp->MdlAddress = NULL;
260     }
261     IoFreeIrp(Irp);
262
263     //
264     // Tell io manager that we own the Irp
265     //
266     return STATUS_MORE_PROCESSING_REQUIRED;
267 }
268
269 NTSTATUS
270 AFSStartIos( IN PFILE_OBJECT     CacheFileObject,
271              IN UCHAR            FunctionCode,
272              IN ULONG            IrpFlags,
273              IN AFSIoRun        *IoRun,
274              IN ULONG            Count,
275              IN OUT AFSGatherIo *Gather)
276 {
277
278     AFSDeviceExt       *pRdrDevExt = (AFSDeviceExt *)AFSRDRDeviceObject->DeviceExtension;
279     PIO_STACK_LOCATION  pIoStackLocation = NULL;
280     PIRP                pIrp;
281     NTSTATUS            ntStatus = STATUS_SUCCESS;
282     PDEVICE_OBJECT      pDeviceObject = NULL;
283     LONG                lCount;
284
285     __Enter
286     {
287
288         pDeviceObject = IoGetRelatedDeviceObject( CacheFileObject);
289
290         for (ULONG i = 0; i < Count; i++)
291         {
292
293             pIrp = IoRun[i].ChildIrp;
294
295             pIoStackLocation = IoGetNextIrpStackLocation( pIrp);
296
297             pIrp->Flags = IrpFlags;
298             pIoStackLocation->MajorFunction = FunctionCode;
299             pIoStackLocation->DeviceObject = pDeviceObject;
300             pIoStackLocation->FileObject = CacheFileObject;
301             pIoStackLocation->Parameters.Write.Length = IoRun[i].ByteCount;
302             pIoStackLocation->Parameters.Write.ByteOffset = IoRun[i].CacheOffset;
303
304             //
305             // Bump the count *before* we start the IO, that way if it
306             // completes real fast it will still not set the event
307             //
308             lCount = InterlockedIncrement(&Gather->Count);
309
310             //
311             // Set the completion routine.
312             //
313
314             IoSetCompletionRoutine( pIrp,
315                                     CompletionFunction,
316                                     Gather,
317                                     TRUE,
318                                     TRUE,
319                                     TRUE);
320
321             //
322             // Send it to the Cache
323             //
324
325             ntStatus = IoCallDriver( pDeviceObject,
326                                      pIrp);
327
328             if( !NT_SUCCESS( ntStatus))
329             {
330                 break;
331             }
332         }
333     }
334
335     return ntStatus;
336 }
337
338 VOID
339 AFSCompleteIo( IN AFSGatherIo *Gather,
340                IN NTSTATUS Status)
341 {
342     LONG lCount;
343
344     if (!NT_SUCCESS(Status)) {
345
346         Gather->Status = Status;
347     }
348
349     lCount = InterlockedDecrement( &Gather->Count );
350
351     if (0 == lCount) {
352         //
353         // Last outstanding IO.  Complete the parent and
354         // do whatever it takes to clean up
355         //
356         if (!NT_SUCCESS(Gather->Status))
357         {
358             Gather->MasterIrp->IoStatus.Status = Gather->Status;
359             Gather->MasterIrp->IoStatus.Information = 0;
360         }
361
362         if( Gather->CompleteMasterIrp)
363         {
364
365             AFSDbgLogMsg( AFS_SUBSYSTEM_IO_PROCESSING,
366                           AFS_TRACE_LEVEL_VERBOSE,
367                           "AFSCompleteIo Completing Irp %08lX Status %08lX\n",
368                           Gather->MasterIrp,
369                           Gather->Status);
370
371             AFSCompleteRequest(Gather->MasterIrp, Gather->Status);
372         }
373
374         if (Gather->Synchronous)
375         {
376             //
377             // Someone was waiting for us tell them.  They also own
378             // the data structure so we don't free it
379             //
380             KeSetEvent( &Gather->Event,
381                         0,
382                         FALSE);
383         }
384         else
385         {
386
387             AFSExFreePool( Gather);
388         }
389     }
390 }
391
392 static AFSExtent *ExtentFor(PLIST_ENTRY le)
393 {
394     return CONTAINING_RECORD( le, AFSExtent, Lists[AFS_EXTENTS_LIST] );
395 }
396
397 static AFSExtent *NextExtent(AFSExtent *Extent)
398 {
399     return ExtentFor(Extent->Lists[AFS_EXTENTS_LIST].Flink);
400 }
401
402 NTSTATUS
403 AFSProcessExtentRun( IN PVOID          SystemBuffer,
404                      IN PLARGE_INTEGER Start,
405                      IN ULONG          Length,
406                      IN AFSExtent     *From,
407                      IN BOOLEAN        WriteRequest)
408 {
409
410     LARGE_INTEGER  liCacheOffset;
411     LARGE_INTEGER  liFileOffset = *Start;
412     ULONG          ulCurrLength;
413     ULONG          ulOffset = 0;
414     ULONG          ulCurrRun = 0;
415     AFSExtent     *pExtent = From;
416     AFSExtent     *pNextExtent;
417     PMDL          *pMDL;
418     BOOLEAN        done = FALSE;
419     NTSTATUS       ntStatus = STATUS_SUCCESS;
420
421     __Enter
422     {
423
424         while( !done)
425         {
426             ASSERT( liFileOffset.QuadPart >= pExtent->FileOffset.QuadPart );
427
428             liCacheOffset = pExtent->CacheOffset;
429             liCacheOffset.QuadPart += (liFileOffset.QuadPart -
430                                        pExtent->FileOffset.QuadPart);
431
432             ulCurrLength = 0;
433
434             //
435             // While there is still data to process, loop
436             //
437             while ( (pExtent->FileOffset.QuadPart + pExtent->Size) <
438                     (Start->QuadPart + Length) )
439             {
440                 //
441                 // Collapse the read if we can
442                 //
443                 pNextExtent = NextExtent( pExtent );
444
445                 if (pNextExtent->CacheOffset.QuadPart !=
446                     (pExtent->CacheOffset.QuadPart + pExtent->Size))
447                 {
448                     //
449                     // This extent and the next extent are not contiguous
450                     //
451                     break;
452                 }
453                 pExtent = pNextExtent;
454             }
455
456             //
457             // So, this run goes from liCacheOffset to the end of pExtent
458             //
459
460             if ((pExtent->FileOffset.QuadPart + pExtent->Size) >= (Start->QuadPart + Length))
461             {
462                 //
463                 // The last extent overruns the area we care
464                 // about, so trim it back.
465                 //
466                 ulCurrLength = (ULONG) (Start->QuadPart + Length -
467                                         liFileOffset.QuadPart);
468                 //
469                 // But we are done
470                 //
471                 done = TRUE;
472             }
473             else
474             {
475                 //
476                 // Length is from the LiFileOffset to the end of this
477                 // extent.
478                 //
479                 ulCurrLength = (ULONG) (pExtent->FileOffset.QuadPart + pExtent->Size -
480                                         liFileOffset.QuadPart);
481                 done = FALSE;
482             }
483
484             //
485             // Calculate the offset into the buffer
486             //
487             ulOffset = (ULONG) (liFileOffset.QuadPart - Start->QuadPart);
488
489             if( WriteRequest)
490             {
491
492 #ifdef AMD64
493                 RtlCopyMemory( ((char *)AFSLibCacheBaseAddress + liCacheOffset.QuadPart),
494                                ((char *)SystemBuffer + ulOffset),
495                                ulCurrLength);
496 #else
497                 ASSERT( liCacheOffset.HighPart == 0);
498                 RtlCopyMemory( ((char *)AFSLibCacheBaseAddress + liCacheOffset.LowPart),
499                                ((char *)SystemBuffer + ulOffset),
500                                ulCurrLength);
501 #endif
502             }
503             else
504             {
505
506 #ifdef AMD64
507                 RtlCopyMemory( ((char *)SystemBuffer + ulOffset),
508                                ((char *)AFSLibCacheBaseAddress + liCacheOffset.QuadPart),
509                                ulCurrLength);
510 #else
511                 ASSERT( liCacheOffset.HighPart == 0);
512                 RtlCopyMemory( ((char *)SystemBuffer + ulOffset),
513                                ((char *)AFSLibCacheBaseAddress + liCacheOffset.LowPart),
514                                ulCurrLength);
515 #endif
516             }
517
518             ulCurrRun ++;
519             if (!done)
520             {
521                 ASSERT(pNextExtent != NULL && pNextExtent != pExtent);
522                 //
523                 // Move cursors forward
524                 //
525                 pExtent = pNextExtent;
526                 liFileOffset = pExtent->FileOffset;
527             }
528         }
529     }
530
531     return ntStatus;
532 }