summaryrefslogtreecommitdiffstats
path: root/private/ntos/lfs/verfysup.c
blob: 68b1aa7234f32b301fa4939acfe169ebda1d631e (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
/*++

Copyright (c) 1990  Microsoft Corporation

Module Name:

    VerfySup.c

Abstract:

    This module implements consistency checking and structure comparisions
    on Lfs structures.

Author:

    Brian Andrew    [BrianAn]   20-June-1991

Revision History:

--*/

#include "lfsprocs.h"

#ifdef BRIANDBG
BOOLEAN LfsRaiseFull = FALSE;
#endif

#ifdef ALLOC_PRAGMA
#pragma alloc_text(PAGE, LfsCurrentAvailSpace)
#pragma alloc_text(PAGE, LfsFindCurrentAvail)
#pragma alloc_text(PAGE, LfsVerifyLogSpaceAvail)
#endif


VOID
LfsCurrentAvailSpace (
    IN PLFCB Lfcb,
    OUT PLONGLONG CurrentAvailSpace,
    OUT PULONG CurrentPageBytes
    )

/*++

Routine Description:

    This routine is called to determine the available log space in the log file.
    It returns the total number of free bytes and the number available on the
    active page if present.  The total free bytes will reflect all of the empty
    pages as well as the number in the active page.

Arguments:

    Lfcb - Lfcb for this log file.

    CurrentAvailSpace - This is the number of bytes available for log
                        records.

    CurrentPageBytes - This is the number of bytes remaining on the
                       current log page.

Return Value:

    None.

--*/

{
    *CurrentPageBytes = 0;

    PAGED_CODE();

    DebugTrace( +1, Dbg, "LfsCurrentAvailSpace:  Entered\n", 0 );

    //
    //  Get the total number from the Lfcb.
    //

    *CurrentAvailSpace = Lfcb->CurrentAvailable;

    //
    //  We now look to see if there are any bytes available on the Lbcb in
    //  the active queue.  We can add this to the bytes available in the
    //  log pages and also give this back to the caller.
    //

    if (!IsListEmpty( &Lfcb->LbcbActive )) {

        PLBCB ThisLbcb;

        ThisLbcb = CONTAINING_RECORD( Lfcb->LbcbActive.Flink,
                                      LBCB,
                                      ActiveLinks );

        //
        //  If the page is not empty or the page is empty but this is a
        //  restart page then add the remaining bytes on this page.
        //

        if (FlagOn( ThisLbcb->LbcbFlags, LBCB_NOT_EMPTY | LBCB_FLUSH_COPY )) {

            *CurrentPageBytes = (ULONG)Lfcb->LogPageSize - (ULONG)ThisLbcb->BufferOffset;

            *CurrentAvailSpace = *CurrentAvailSpace + *CurrentPageBytes;                                               //**** xxAdd( *CurrentAvailSpace, xxFromUlong( *CurrentPageBytes ));
        }
    }

    DebugTrace( +1, Dbg, "LfsCurrentAvailSpace:  Exit\n", 0 );

    return;
}


BOOLEAN
LfsVerifyLogSpaceAvail (
    IN PLFCB Lfcb,
    IN PLCH Lch,
    IN ULONG RemainingLogBytes,
    IN LONG UndoRequirement,
    IN BOOLEAN ForceToDisk
    )

/*++

Routine Description:

    This routine is called to verify that we may write this log record into the
    log file.  We want to always leave room for each transaction to abort.

    We determine how much space the current log record will take and the
    worst case for its undo operation.  If this space is available we
    update the corresponding values in the Lfcb and Lch for bookkeeping.
    Otherwise we raise a status indicating that the log file is full.

    The disk usage is different for the packed and unpacked cases.  Make the
    following adjustments after finding the total available and amount still
    remaining on the last active page,

    Packed Case:

        Size needed for log record is data size plus header size.

        Undo requirement is the undo data size plus the header size.
            We have already taken into account the end of the pages
            except for the current page.

        Add the log record size to the undo requirement to get the
            log file usage.  Compare this number with the actual available
            space (Available - CommittedUndo).  If the space is not
            available, then raise LOG_FILE_FULL.  Must take into account
            any unused bytes at the end of the current page.

    Unpacked Case:

        Size needed is initially header size plus data size.

        If the log record can't begin on the current page then
            add the bytes being thrown away to the log record size.

        If the page is being forced to disk then add any remaining
            bytes on the last page.  To the bytes being used.

        Undo requirement is twice the sum of the header size and
            undo size.  We double the requested size since the log
            record will always fit on a page.  This can be a
            positive or negative number.

        Add the log record usage to the undo usage to get the log file
            usage.  Compare this number with the actual available
            space (Available - CommittedUndo).  If the space is not
            available, then raise LOG_FILE_FULL.

Arguments:

    Lfcb - Lfcb for this log file.

    Lch - Client handle

    RemainingLogBytes - Number of bytes for the current log record

    UndoRequirement - User's requirement for the undo record.

    ForceToDisk - Indicates if this log record will be flushed to disk.

Return Value:

    BOOLEAN - Advisory, indicates that there is less than 1/4 of the log file available.

--*/

{
    ULONG CurrentLogRecordSize;
    ULONG LogRecordStart;
    ULONG TailBytes;

    LONGLONG CurrentAvailSpace;
    ULONG CurrentPageBytes;

    LONGLONG LogFileUsage;

    PAGED_CODE();

    DebugTrace( +1, Dbg, "LfsVerifyLogSpaceAvail:  Entered\n", 0 );
    DebugTrace(  0, Dbg, "Lfcb              -> %08x\n", Lfcb );
    DebugTrace(  0, Dbg, "Lch               -> %08lx\n", Lch );
    DebugTrace(  0, Dbg, "RemainingLogBytes -> %08lx\n", RemainingLogBytes );
    DebugTrace(  0, Dbg, "UndoRequirement   -> %08lx\n", UndoRequirement );
    DebugTrace(  0, Dbg, "ForceToDisk       -> %04x\n", ForceToDisk );

    //
    //  Start by collecting the current data on the file.
    //

    LfsCurrentAvailSpace( Lfcb,
                          &CurrentAvailSpace,
                          &CurrentPageBytes );

    //
    //  We compute the amount of space needed for the current log record by
    //  adding up the following:
    //
    //      Space at end of current log page which won't be used.
    //      Size of header for log record.
    //      Size of client data in log record.
    //      Size of wasted portion of log page if this is forced to disk.
    //

    //
    //  Start with the size of the header and the client data.
    //

    CurrentLogRecordSize = RemainingLogBytes + Lfcb->RecordHeaderLength;

    //
    //  If the log is packed and there are bytes on the current page we need
    //  to take into account any bytes at the end of the page which won't
    //  be used.  This will happen if the log record spills into the end of
    //  the log page but doesn't use up the page.  If the remaining bytes are
    //  less than a record header size we must throw them away.
    //

    if (FlagOn( Lfcb->Flags, LFCB_PACK_LOG )) {

        if (CurrentPageBytes != 0
            && CurrentLogRecordSize < CurrentPageBytes
            && (CurrentPageBytes - CurrentLogRecordSize) < Lfcb->RecordHeaderLength) {

            CurrentLogRecordSize += (CurrentPageBytes - CurrentLogRecordSize);
        }

    //
    //  If this is the unpacked case we need to check for bytes being thrown away
    //  on the current page or the last page.
    //

    } else {

        //
        //  If there is an active Lbcb, we need to add any bytes that
        //  would be thrown away at the end.
        //

        if (CurrentPageBytes != 0) {

            //
            //  We won't use this log page unless the new log record will fit or
            //  unless this is the first log record in the page.
            //

            if ((CurrentPageBytes != (ULONG)Lfcb->LogPageDataSize)
                && (CurrentLogRecordSize > CurrentPageBytes)) {

                CurrentLogRecordSize += CurrentPageBytes;

                //
                //  Remember that we will start this log record at the first
                //  byte in the data portion of a page.
                //

                LogRecordStart = 0;

            //
            //  Otherwise this will start at the current offset into the
            //  data portion of the log page.
            //

            } else {

                LogRecordStart = (ULONG)Lfcb->LogPageDataSize - CurrentPageBytes;
            }

        //
        //  If there was no Lbcb, then we know that we will start at the first
        //  byte of the data portion.
        //

        } else {

            LogRecordStart = 0;
        }

        //
        //  We always assume that we will use up the rest of the bytes on the last page
        //  in computing whether the log record will fit in the available space.  We
        //  only subtract that space from the available space if this is a force write.
        //

        if (ForceToDisk) {

            //
            //  We take into account where we start on a log page and continue
            //  to subtract log pages until we know the amount on the last
            //  page.
            //

            TailBytes = RemainingLogBytes + Lfcb->RecordHeaderLength + LogRecordStart;

            while (TailBytes > (ULONG)Lfcb->LogPageDataSize) {

                TailBytes -= (ULONG)Lfcb->LogPageDataSize;
            }

            TailBytes = (ULONG)Lfcb->LogPageDataSize - TailBytes;

            CurrentLogRecordSize += TailBytes;
        }
    }

    //
    //  We now know the number of bytes needed for the current log page.
    //  Next we compute the number of bytes being reserved by UndoRequirement.
    //  If the UndoRequirement is positive, we will add to the amount reserved
    //  in the log file.  If it is negative, we will subtract from the amount
    //  reserved in the log file.
    //

    //
    //  When we have an actual reserve amount, we convert it to positive
    //  and then reserve twice the space required to hold the data and
    //  its header (up to the maximum of a single page.
    //

    if (UndoRequirement != 0) {

        if (!FlagOn( Lfcb->Flags, LFCB_PACK_LOG )) {

            UndoRequirement *= 2;
        }

        if (UndoRequirement < 0) {

            UndoRequirement -= (2 * Lfcb->RecordHeaderLength);
        } else {

            UndoRequirement += (2 * Lfcb->RecordHeaderLength);
        }
    }

    //
    //  Now compute the net log file usage.  The result may be positive or
    //  negative.
    //

    LogFileUsage = ((LONG) CurrentLogRecordSize)  + UndoRequirement;                                                   //**** xxFromLong( ((LONG) CurrentLogRecordSize)  + UndoRequirement );

    //
    //  The actual available space is the CurrentAvail minus the reserved
    //  undo value in the Lfcb.
    //

    CurrentAvailSpace = CurrentAvailSpace - Lfcb->TotalUndoCommitment;                                                 //**** xxSub( CurrentAvailSpace, Lfcb->TotalUndoCommitment );

    //
    //  If this log file usage is greater than the available log file space
    //  then we raise a status code.
    //

#ifdef BRIANDBG
    if (LfsRaiseFull) {

        LfsRaiseFull = FALSE;
        DebugTrace( -1, Dbg, "LfsVerifyLogSpaceAvail:  About to raise\n", 0 );
        ExRaiseStatus( STATUS_LOG_FILE_FULL );
    }
#endif

    if (LogFileUsage > CurrentAvailSpace) {

        DebugTrace( -1, Dbg, "LfsVerifyLogSpaceAvail:  About to raise\n", 0 );
        ExRaiseStatus( STATUS_LOG_FILE_FULL );
    }

    Lfcb->TotalUndoCommitment = Lfcb->TotalUndoCommitment + UndoRequirement;                                           //**** xxAdd( Lfcb->TotalUndoCommitment, xxFromLong( UndoRequirement ));

    Lch->ClientUndoCommitment = Lch->ClientUndoCommitment + UndoRequirement;                                           //**** xxAdd( Lch->ClientUndoCommitment, xxFromLong( UndoRequirement ));

    DebugTrace( -1, Dbg, "LfsVerifyLogSpaceAvail:  Exit\n", 0 );

    //
    //  Now check if the log file is almost used up.
    //

    if ((CurrentAvailSpace - LogFileUsage) < (Lfcb->TotalAvailable >> 2)) {

        return TRUE;
    }

    return FALSE;
}


VOID
LfsFindCurrentAvail (
    IN PLFCB Lfcb
    )

/*++

Routine Description:

    This routine is called to calculate the number of bytes available for log
    records which are in completely empty log record pages.  It ignores any
    partial pages in the active work queue and ignores any page which is
    going to be reused.

Arguments:

    Lfcb - Lfcb for this log file.

Return Value:

    None.

--*/

{
    LONGLONG OldestPageOffset;
    LONGLONG NextFreePageOffset;
    LONGLONG FreeBytes;

    PAGED_CODE();

    DebugTrace( +1, Dbg, "LfsFindCurrentAvail:  Entered\n", 0 );
    DebugTrace(  0, Dbg, "Lfcb              -> %08x\n", Lfcb );

    //
    //  If there is a last lsn in the restart area then we know
    //  that we will have to compute the free range.
    //

    if (!FlagOn( Lfcb->Flags, LFCB_NO_LAST_LSN )) {

        //
        //  If there is no oldest Lsn then start at the
        //  first page of the file.
        //

        if (FlagOn( Lfcb->Flags, LFCB_NO_OLDEST_LSN )) {

            OldestPageOffset = Lfcb->FirstLogPage;

        } else {

            LfsTruncateOffsetToLogPage( Lfcb,
                                        Lfcb->OldestLsnOffset,
                                        &OldestPageOffset );
        }

        //
        //  We will use the next log page offset to compute the
        //  next free page.  If we are going to reuse this page
        //  go to the next page,  if we are at the first page then
        //  use the end of the file.
        //

        if (FlagOn( Lfcb->Flags, LFCB_REUSE_TAIL )) {

            NextFreePageOffset = Lfcb->NextLogPage + Lfcb->LogPageSize;                                                //**** xxAdd( Lfcb->NextLogPage, Lfcb->LogPageSize );

        } else if ( Lfcb->NextLogPage == Lfcb->FirstLogPage ) {                                                        //**** xxEql( Lfcb->NextLogPage, Lfcb->FirstLogPage )

            NextFreePageOffset = Lfcb->FileSize;

        } else {

            NextFreePageOffset = Lfcb->NextLogPage;
        }

        //
        //  If the two offsets are the same then there is no available space.
        //

        if ( OldestPageOffset == NextFreePageOffset ) {                                                                //**** xxEql( OldestPageOffset, NextFreePageOffset )

            Lfcb->CurrentAvailable = 0;

        } else {

            //
            //  If the free offset follows the oldest offset then subtract
            //  this range from the total available pages.
            //

            if ( OldestPageOffset < NextFreePageOffset ) {                                                             //**** xxLtr( OldestPageOffset, NextFreePageOffset )

                FreeBytes = Lfcb->TotalAvailInPages - ( NextFreePageOffset - OldestPageOffset );                       //**** xxSub( Lfcb->TotalAvailInPages, xxSub( NextFreePageOffset, OldestPageOffset ));

            } else {

                FreeBytes = OldestPageOffset - NextFreePageOffset;                                                     //**** xxSub( OldestPageOffset, NextFreePageOffset );
            }

            //
            //  We now have the total bytes in the pages available.  We
            //  now have to subtract the size of the page header to get
            //  the total available bytes.
            //
            //  We will convert the bytes to pages and then multiple
            //  by the data size of each page.
            //

            FreeBytes = Int64ShrlMod32(((ULONGLONG)(FreeBytes)), Lfcb->LogPageShift);

            Lfcb->CurrentAvailable = FreeBytes * (ULONG)Lfcb->ReservedLogPageSize;                                     //**** xxXMul( FreeBytes, Lfcb->ReservedLogPageSize.LowPart );
        }

    //
    //  Otherwise the entire file is available.
    //

    } else {

        Lfcb->CurrentAvailable = Lfcb->MaxCurrentAvail;
    }

    DebugTrace( -1, Dbg, "LfsFindCurrentAvail:  Exit\n", 0 );

    return;
}