Windows: consoldiate NextExtent() implementations
[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 //
42 // Called in the paging or non cached read and write paths to get the
43 // first and last extent in a span.  We also the count of how many
44 // discontiguous extents the are in the cache file.
45 //
46
47 NTSTATUS
48 AFSGetExtents( IN AFSFcb *Fcb,
49                IN PLARGE_INTEGER Offset,
50                IN ULONG Length,
51                OUT AFSExtent *From,
52                OUT ULONG *ExtentCount,
53                OUT ULONG *RunCount)
54 {
55     NTSTATUS           ntStatus = STATUS_SUCCESS;
56     ULONG              extentsCount = 0, runCount = 0;
57     AFSExtent         *pEndExtent = NULL;
58     LARGE_INTEGER      liLastCacheOffset;
59
60     *ExtentCount = 0;
61
62     __Enter
63     {
64         ASSERT(AFSExtentContains(From, Offset));
65
66         pEndExtent = From;
67         extentsCount = 1;
68         runCount = 1;
69         liLastCacheOffset.QuadPart = pEndExtent->CacheOffset.QuadPart + pEndExtent->Size;
70
71         while ((Offset->QuadPart + Length) >
72                pEndExtent->FileOffset.QuadPart + pEndExtent->Size) {
73
74             pEndExtent = NextExtent(pEndExtent, AFS_EXTENTS_LIST);
75
76             if (liLastCacheOffset.QuadPart != pEndExtent->CacheOffset.QuadPart) {
77                 //
78                 // Discontiguous (in the cache)
79                 //
80                 runCount += 1;
81
82             }
83
84             extentsCount+= 1;
85
86             liLastCacheOffset.QuadPart = pEndExtent->CacheOffset.QuadPart + pEndExtent->Size;
87         }
88
89         *ExtentCount = extentsCount;
90         *RunCount = runCount;
91         ntStatus = STATUS_SUCCESS;
92     }
93     return ntStatus;
94 }
95
96
97 NTSTATUS
98 AFSSetupIoRun( IN PDEVICE_OBJECT CacheDevice,
99                IN PIRP           MasterIrp,
100                IN PVOID          SystemBuffer,
101                IN OUT AFSIoRun  *IoRuns,
102                IN PLARGE_INTEGER Start,
103                IN ULONG          Length,
104                IN AFSExtent     *From,
105                IN OUT ULONG     *RunCount)
106 {
107     //
108     // Do all the work which we can prior to firing off the IRP
109     // (allocate them, calculate offsets and so on)
110     //
111     LARGE_INTEGER  liCacheOffset;
112     LARGE_INTEGER  liFileOffset = *Start;
113     ULONG          ulCurrLength;
114     ULONG          ulReadOffset;
115     ULONG          ulCurrRun = 0;
116     AFSExtent     *pExtent = From;
117     AFSExtent     *pNextExtent;
118     PMDL          *pMDL;
119     BOOLEAN        done = FALSE;
120     NTSTATUS       ntStatus = STATUS_SUCCESS;
121
122     __Enter
123     {
124
125         while( !done)
126         {
127             ASSERT( liFileOffset.QuadPart >= pExtent->FileOffset.QuadPart );
128
129             liCacheOffset = pExtent->CacheOffset;
130             liCacheOffset.QuadPart += (liFileOffset.QuadPart -
131                                        pExtent->FileOffset.QuadPart);
132
133             ulCurrLength = 0;
134
135             //
136             // While there is still data to process, loop
137             //
138             while ( (pExtent->FileOffset.QuadPart + pExtent->Size) <
139                     (Start->QuadPart + Length) )
140             {
141                 //
142                 // Collapse the read if we can
143                 //
144                 pNextExtent = NextExtent( pExtent, AFS_EXTENTS_LIST);
145
146                 if (pNextExtent->CacheOffset.QuadPart !=
147                     (pExtent->CacheOffset.QuadPart + pExtent->Size))
148                 {
149                     //
150                     // This extent and the next extent are not contiguous
151                     //
152                     break;
153                 }
154                 pExtent = pNextExtent;
155             }
156
157             //
158             // So, this run goes from liCacheOffset to the end of pExtent
159             //
160
161             if ((pExtent->FileOffset.QuadPart + pExtent->Size) >= (Start->QuadPart + Length))
162             {
163                 //
164                 // The last extent overruns the area we care
165                 // about, so trim it back.
166                 //
167                 ulCurrLength = (ULONG) (Start->QuadPart + Length -
168                                         liFileOffset.QuadPart);
169                 //
170                 // But we are done
171                 //
172                 done = TRUE;
173             }
174             else
175             {
176                 //
177                 // Length is from the LiFileOffset to the end of this
178                 // extent.
179                 //
180                 ulCurrLength = (ULONG) (pExtent->FileOffset.QuadPart + pExtent->Size -
181                                         liFileOffset.QuadPart);
182                 done = FALSE;
183             }
184
185             //
186             // Calculate the offset into the buffer
187             //
188             ulReadOffset = (ULONG) (liFileOffset.QuadPart - Start->QuadPart);
189
190             IoRuns[ulCurrRun].CacheOffset = liCacheOffset;
191             IoRuns[ulCurrRun].ByteCount = ulCurrLength;
192
193             IoRuns[ulCurrRun].ChildIrp = IoAllocateIrp( CacheDevice->StackSize + 1,
194                                                         FALSE);
195
196             if (NULL == IoRuns[ulCurrRun].ChildIrp)
197             {
198                 try_return( ntStatus = STATUS_INSUFFICIENT_RESOURCES );
199             }
200
201             IoRuns[ulCurrRun].ChildIrp->UserBuffer = ((PCHAR) SystemBuffer) + ulReadOffset;
202             IoRuns[ulCurrRun].ChildIrp->Tail.Overlay.Thread = PsGetCurrentThread();
203             IoRuns[ulCurrRun].ChildIrp->RequestorMode = KernelMode;
204             IoRuns[ulCurrRun].ChildIrp->MdlAddress = NULL;
205
206             ulCurrRun ++;
207             if (!done)
208             {
209                 ASSERT(pNextExtent != NULL && pNextExtent != pExtent);
210                 //
211                 // Move cursors forward
212                 //
213                 pExtent = pNextExtent;
214                 liFileOffset = pExtent->FileOffset;
215             }
216         }
217         ASSERT(ulCurrRun <= *RunCount);
218 try_exit:
219         if (!NT_SUCCESS(ntStatus))
220         {
221             for (ulCurrRun = 0 ; ulCurrRun < *RunCount; ulCurrRun++)
222             {
223                 if (IoRuns[ulCurrRun].ChildIrp) {
224                     IoFreeIrp( IoRuns[ulCurrRun].ChildIrp );
225                     IoRuns[ulCurrRun].ChildIrp = NULL;
226                 }
227             }
228         }
229         else
230         {
231             *RunCount = ulCurrRun;
232         }
233     }
234     return ntStatus;
235 }
236
237 //
238 // We manage our own scatter / gather.  This completion
239 // function calls into our generic function.
240 //
241
242 static
243 NTSTATUS
244 CompletionFunction(IN PDEVICE_OBJECT DeviceObject,
245                    PIRP Irp,
246                    PVOID Context)
247 {
248     AFSGatherIo *pGather = (AFSGatherIo *) Context;
249
250     AFSCompleteIo( pGather, Irp->IoStatus.Status);
251
252     if (Irp->MdlAddress) {
253         if (Irp->MdlAddress->MdlFlags & MDL_PAGES_LOCKED) {
254             MmUnlockPages(Irp->MdlAddress);
255         }
256         IoFreeMdl(Irp->MdlAddress);
257         Irp->MdlAddress = NULL;
258     }
259     IoFreeIrp(Irp);
260
261     //
262     // Tell io manager that we own the Irp
263     //
264     return STATUS_MORE_PROCESSING_REQUIRED;
265 }
266
267 NTSTATUS
268 AFSStartIos( IN PFILE_OBJECT     CacheFileObject,
269              IN UCHAR            FunctionCode,
270              IN ULONG            IrpFlags,
271              IN AFSIoRun        *IoRun,
272              IN ULONG            Count,
273              IN OUT AFSGatherIo *Gather)
274 {
275
276     AFSDeviceExt       *pRdrDevExt = (AFSDeviceExt *)AFSRDRDeviceObject->DeviceExtension;
277     PIO_STACK_LOCATION  pIoStackLocation = NULL;
278     PIRP                pIrp;
279     NTSTATUS            ntStatus = STATUS_SUCCESS;
280     PDEVICE_OBJECT      pDeviceObject = NULL;
281     LONG                lCount;
282
283     __Enter
284     {
285
286         pDeviceObject = IoGetRelatedDeviceObject( CacheFileObject);
287
288         for (ULONG i = 0; i < Count; i++)
289         {
290
291             pIrp = IoRun[i].ChildIrp;
292
293             pIoStackLocation = IoGetNextIrpStackLocation( pIrp);
294
295             pIrp->Flags = IrpFlags;
296             pIoStackLocation->MajorFunction = FunctionCode;
297             pIoStackLocation->DeviceObject = pDeviceObject;
298             pIoStackLocation->FileObject = CacheFileObject;
299             pIoStackLocation->Parameters.Write.Length = IoRun[i].ByteCount;
300             pIoStackLocation->Parameters.Write.ByteOffset = IoRun[i].CacheOffset;
301
302             //
303             // Bump the count *before* we start the IO, that way if it
304             // completes real fast it will still not set the event
305             //
306             lCount = InterlockedIncrement(&Gather->Count);
307
308             //
309             // Set the completion routine.
310             //
311
312             IoSetCompletionRoutine( pIrp,
313                                     CompletionFunction,
314                                     Gather,
315                                     TRUE,
316                                     TRUE,
317                                     TRUE);
318
319             //
320             // Send it to the Cache
321             //
322
323             ntStatus = IoCallDriver( pDeviceObject,
324                                      pIrp);
325
326             if( !NT_SUCCESS( ntStatus))
327             {
328                 break;
329             }
330         }
331     }
332
333     return ntStatus;
334 }
335
336 VOID
337 AFSCompleteIo( IN AFSGatherIo *Gather,
338                IN NTSTATUS Status)
339 {
340     LONG lCount;
341
342     if (!NT_SUCCESS(Status)) {
343
344         Gather->Status = Status;
345     }
346
347     lCount = InterlockedDecrement( &Gather->Count );
348
349     if (0 == lCount) {
350         //
351         // Last outstanding IO.  Complete the parent and
352         // do whatever it takes to clean up
353         //
354         if (!NT_SUCCESS(Gather->Status))
355         {
356             Gather->MasterIrp->IoStatus.Status = Gather->Status;
357             Gather->MasterIrp->IoStatus.Information = 0;
358         }
359
360         if( Gather->CompleteMasterIrp)
361         {
362
363             AFSDbgLogMsg( AFS_SUBSYSTEM_IO_PROCESSING,
364                           AFS_TRACE_LEVEL_VERBOSE,
365                           "AFSCompleteIo Completing Irp %08lX Status %08lX\n",
366                           Gather->MasterIrp,
367                           Gather->Status);
368
369             AFSCompleteRequest(Gather->MasterIrp, Gather->Status);
370         }
371
372         if (Gather->Synchronous)
373         {
374             //
375             // Someone was waiting for us tell them.  They also own
376             // the data structure so we don't free it
377             //
378             KeSetEvent( &Gather->Event,
379                         0,
380                         FALSE);
381         }
382         else
383         {
384
385             AFSExFreePool( Gather);
386         }
387     }
388 }
389
390 NTSTATUS
391 AFSProcessExtentRun( IN PVOID          SystemBuffer,
392                      IN PLARGE_INTEGER Start,
393                      IN ULONG          Length,
394                      IN AFSExtent     *From,
395                      IN BOOLEAN        WriteRequest)
396 {
397
398     LARGE_INTEGER  liCacheOffset;
399     LARGE_INTEGER  liFileOffset = *Start;
400     ULONG          ulCurrLength;
401     ULONG          ulOffset = 0;
402     ULONG          ulCurrRun = 0;
403     AFSExtent     *pExtent = From;
404     AFSExtent     *pNextExtent;
405     PMDL          *pMDL;
406     BOOLEAN        done = FALSE;
407     NTSTATUS       ntStatus = STATUS_SUCCESS;
408
409     __Enter
410     {
411
412         while( !done)
413         {
414             ASSERT( liFileOffset.QuadPart >= pExtent->FileOffset.QuadPart );
415
416             liCacheOffset = pExtent->CacheOffset;
417             liCacheOffset.QuadPart += (liFileOffset.QuadPart -
418                                        pExtent->FileOffset.QuadPart);
419
420             ulCurrLength = 0;
421
422             //
423             // While there is still data to process, loop
424             //
425             while ( (pExtent->FileOffset.QuadPart + pExtent->Size) <
426                     (Start->QuadPart + Length) )
427             {
428                 //
429                 // Collapse the read if we can
430                 //
431                 pNextExtent = NextExtent( pExtent, AFS_EXTENTS_LIST);
432
433                 if (pNextExtent->CacheOffset.QuadPart !=
434                     (pExtent->CacheOffset.QuadPart + pExtent->Size))
435                 {
436                     //
437                     // This extent and the next extent are not contiguous
438                     //
439                     break;
440                 }
441                 pExtent = pNextExtent;
442             }
443
444             //
445             // So, this run goes from liCacheOffset to the end of pExtent
446             //
447
448             if ((pExtent->FileOffset.QuadPart + pExtent->Size) >= (Start->QuadPart + Length))
449             {
450                 //
451                 // The last extent overruns the area we care
452                 // about, so trim it back.
453                 //
454                 ulCurrLength = (ULONG) (Start->QuadPart + Length -
455                                         liFileOffset.QuadPart);
456                 //
457                 // But we are done
458                 //
459                 done = TRUE;
460             }
461             else
462             {
463                 //
464                 // Length is from the LiFileOffset to the end of this
465                 // extent.
466                 //
467                 ulCurrLength = (ULONG) (pExtent->FileOffset.QuadPart + pExtent->Size -
468                                         liFileOffset.QuadPart);
469                 done = FALSE;
470             }
471
472             //
473             // Calculate the offset into the buffer
474             //
475             ulOffset = (ULONG) (liFileOffset.QuadPart - Start->QuadPart);
476
477             if( WriteRequest)
478             {
479
480 #ifdef AMD64
481                 RtlCopyMemory( ((char *)AFSLibCacheBaseAddress + liCacheOffset.QuadPart),
482                                ((char *)SystemBuffer + ulOffset),
483                                ulCurrLength);
484 #else
485                 ASSERT( liCacheOffset.HighPart == 0);
486                 RtlCopyMemory( ((char *)AFSLibCacheBaseAddress + liCacheOffset.LowPart),
487                                ((char *)SystemBuffer + ulOffset),
488                                ulCurrLength);
489 #endif
490             }
491             else
492             {
493
494 #ifdef AMD64
495                 RtlCopyMemory( ((char *)SystemBuffer + ulOffset),
496                                ((char *)AFSLibCacheBaseAddress + liCacheOffset.QuadPart),
497                                ulCurrLength);
498 #else
499                 ASSERT( liCacheOffset.HighPart == 0);
500                 RtlCopyMemory( ((char *)SystemBuffer + ulOffset),
501                                ((char *)AFSLibCacheBaseAddress + liCacheOffset.LowPart),
502                                ulCurrLength);
503 #endif
504             }
505
506             ulCurrRun ++;
507             if (!done)
508             {
509                 ASSERT(pNextExtent != NULL && pNextExtent != pExtent);
510                 //
511                 // Move cursors forward
512                 //
513                 pExtent = pNextExtent;
514                 liFileOffset = pExtent->FileOffset;
515             }
516         }
517     }
518
519     return ntStatus;
520 }