summaryrefslogblamecommitdiffstats
path: root/private/ntos/lfs/lbcbsup.c
blob: 71e60173081f50c20d3828fcbcfc52a67c0589d2 (plain) (tree)
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















































































































































































































































































































































































































































































                                                                                                  
/*++

Copyright (c) 1990  Microsoft Corporation

Module Name:

    LbcbSup.c

Abstract:

    This module provides support for manipulating log buffer control blocks.

Author:

    Brian Andrew    [BrianAn]   20-June-1991

Revision History:

--*/

#include "lfsprocs.h"

//
//  The debug trace level
//

#define Dbg                              (DEBUG_TRACE_LBCB_SUP)

#ifdef ALLOC_PRAGMA
#pragma alloc_text(PAGE, LfsFlushLbcb)
#pragma alloc_text(PAGE, LfsFlushToLsnPriv)
#pragma alloc_text(PAGE, LfsGetLbcb)
#endif


VOID
LfsFlushLbcb (
    IN PLFCB Lfcb,
    IN PLBCB Lbcb
    )

/*++

Routine Description:

    This routine is called to make sure the data within an Lbcb makes it out
    to disk.  The Lbcb must either already be in the workque or it must be
    a restart Lbcb.

Arguments:

    Lfcb - This is the file control block for the log file.

    Lbcb - This is the Lbcb to flush.

Return Value:

    None.

--*/

{
    LSN LastLsn;
    PLSN FlushedLsn;

    PAGED_CODE();

    DebugTrace( +1, Dbg, "LfsFlushLbcb:  Entered\n", 0 );
    DebugTrace(  0, Dbg, "Lfcb      -> %08lx\n", Lfcb );
    DebugTrace(  0, Dbg, "Lbcb      -> %08lx\n", Lbcb );

    LastLsn = Lbcb->LastEndLsn;

    //
    //  If this is a restart area we use the restart counter in the
    //  Lfcb.  Otherwise we can use the LastFlushedLsn value in the
    //  Lfcb.  This way we can determine that the Lbcb that interests
    //  us has made it out to disk.
    //

    if (LfsLbcbIsRestart( Lbcb )) {

        FlushedLsn = &Lfcb->LastFlushedRestartLsn;

    } else {

        FlushedLsn = &Lfcb->LastFlushedLsn;
    }

    //
    //  We loop here until the desired Lsn has made it to disk.
    //  If we are able to do the I/O, we will perform it.
    //

    do {

        //
        //
        //  If we can do the Io, call down to flush the Lfcb.
        //

        if (Lfcb->LfsIoState == LfsNoIoInProgress) {

            LfsFlushLfcb( Lfcb, Lbcb );

            break;
        }

        //
        //  Otherwise we release the Lfcb and immediately wait on the event.
        //

        LfsReleaseLfcb( Lfcb );

        KeWaitForSingleObject( &Lfcb->Sync->Event,
                               Executive,
                               KernelMode,
                               FALSE,
                               NULL );

        LfsAcquireLfcb( Lfcb );

    } while ( LastLsn.QuadPart > FlushedLsn->QuadPart );

    DebugTrace( -1, Dbg, "LfsFlushLbcb:  Exit\n", 0 );
    return;
}


VOID
LfsFlushToLsnPriv (
    IN PLFCB Lfcb,
    IN LSN Lsn
    )

/*++

Routine Description:

    This routine is the worker routine which performs the work of flushing
    a particular Lsn to disk.  This routine is always called with the
    Lfcb acquired.  This routines makes no guarantee about whether the Lfcb
    is acquired on exit.

Arguments:

    Lfcb - This is the file control block for the log file.

    Lsn - This is the Lsn to flush to disk.

Return Value:

    None.

--*/

{
    PAGED_CODE();

    DebugTrace( +1, Dbg, "LfsFlushToLsnPriv:  Entered\n", 0 );
    DebugTrace(  0, Dbg, "Lfcb          -> %08lx\n", Lfcb );
    DebugTrace(  0, Dbg, "Lsn (Low)     -> %08lx\n", Lsn.LowPart );
    DebugTrace(  0, Dbg, "Lsn (High)    -> %08lx\n", Lsn.HighPart );

    //
    //  We check if the Lsn is in the valid range.  Raising an
    //  exception if not.
    //

    if ( Lsn.QuadPart > Lfcb->RestartArea->CurrentLsn.QuadPart ) {

        DebugTrace( 0, Dbg, "Lsn is not in the file\n", 0 );
        ExRaiseStatus( STATUS_INVALID_PARAMETER );
    }

    //
    //  If the Lsn has already been flushed we are done.
    //  Otherwise we need to look through the workqueues and the
    //  active queue.
    //

    if ( Lsn.QuadPart > Lfcb->LastFlushedLsn.QuadPart ) {

        PLIST_ENTRY ThisEntry;
        PLBCB ThisLbcb;

        //
        //  Check the workqueue first.  We are looking for the last
        //  buffer block of a log page block which contains this
        //  Lsn.
        //

        ThisEntry = Lfcb->LbcbWorkque.Flink;

        //
        //  We keep looping.
        //

        while (TRUE) {

            ThisLbcb = CONTAINING_RECORD( ThisEntry,
                                          LBCB,
                                          WorkqueLinks );

            //
            //  We pass over any restart areas.  We also skip any
            //  Lbcb's which do not contain the end of a log record.
            //

            if (!LfsLbcbIsRestart( ThisLbcb )
                && FlagOn( ThisLbcb->Flags, LOG_PAGE_LOG_RECORD_END )) {

                //
                //  If the last complete Lsn in this Lbcb is greater or equal
                //  to the desired Lsn, we exit the loop.
                //

                if ( ThisLbcb->LastEndLsn.QuadPart >= Lsn.QuadPart ) {

                    break;
                }
            }

            //
            //  Otherwise move to the next Lbcb.
            //

            ThisEntry = ThisEntry->Flink;

            ASSERT( ThisEntry != &Lfcb->LbcbWorkque );
        }

        //
        //  If we are not supporting a packed log file and this Lbcb is from
        //  the active queue, we need to check that losing the tail of the
        //  will not swallow up any of our reserved space.
        //

        if (!FlagOn( Lfcb->Flags, LFCB_PACK_LOG )
            && FlagOn( ThisLbcb->LbcbFlags, LBCB_ON_ACTIVE_QUEUE )) {

            LONGLONG CurrentAvail;
            LONGLONG UnusedBytes;

            //
            //  Find the unused bytes.
            //

            UnusedBytes = 0;

            LfsCurrentAvailSpace( Lfcb,
                                  &CurrentAvail,
                                  (PULONG)&UnusedBytes );

            CurrentAvail = CurrentAvail - Lfcb->TotalUndoCommitment;

            if ( UnusedBytes > CurrentAvail ) {

                DebugTrace( -1, Dbg, "Have to preserve these bytes for possible aborts\n", 0 );

                ExRaiseStatus( STATUS_LOG_FILE_FULL );
            }

            //
            //  We want to make sure we don't write any more data into this
            //  page.  Remove this from the active queue.
            //

            RemoveEntryList( &ThisLbcb->ActiveLinks );
            ClearFlag( ThisLbcb->LbcbFlags, LBCB_ON_ACTIVE_QUEUE );
        }

        //
        //  We now have the Lbcb we want to flush to disk.
        //

        LfsFlushLbcb( Lfcb, ThisLbcb );
    }

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

    return;
}


PLBCB
LfsGetLbcb (
    IN PLFCB Lfcb
    )

/*++

Routine Description:

    This routine is called to add a Lbcb to the active queue.

Arguments:

    Lfcb - This is the file control block for the log file.

Return Value:

    PLBCB - Pointer to the Lbcb allocated.

--*/

{
    PLBCB Lbcb = NULL;
    PVOID PageHeader;
    PBCB PageHeaderBcb = NULL;

    BOOLEAN WrappedOrUsaError;

    PAGED_CODE();

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

    //
    //  Use a try-finally to facilitate cleanup.
    //

    try {

        //
        //  Pin the desired record page.
        //

        LfsPreparePinWriteData( Lfcb,
                                Lfcb->NextLogPage,
                                (ULONG)Lfcb->LogPageSize,
                                &PageHeader,
                                &PageHeaderBcb );

        //
        //  Put our signature into the page so we won't fail if we
        //  see a previous 'BAAD' signature.
        //

        *((PULONG) PageHeader) = LFS_SIGNATURE_RECORD_PAGE_ULONG;

        //
        //  Now allocate an Lbcb.
        //

        LfsAllocateLbcb( Lfcb, &Lbcb );

        //
        //  If we are at the beginning of the file we test that the
        //  sequence number won't wrap to 0.
        //

        if (!FlagOn( Lfcb->Flags, LFCB_NO_LAST_LSN | LFCB_REUSE_TAIL )
            && ( Lfcb->NextLogPage == Lfcb->FirstLogPage )) {

            Lfcb->SeqNumber = Lfcb->SeqNumber + 1;

            //
            //  If the sequence number is going from 0 to 1, then
            //  this is the first time the log file has wrapped.  We want
            //  to remember this because it means that we can now do
            //  large spiral writes.
            //

            if (Int64ShllMod32( Lfcb->SeqNumber, Lfcb->FileDataBits ) == 0) {

                DebugTrace( 0, Dbg, "Log sequence number about to wrap:  Lfcb -> %08lx\n", Lfcb );
                KeBugCheck( FILE_SYSTEM );
            }

            //
            //  If this number is greater or equal to  the wrap sequence number in
            //  the Lfcb, set the wrap flag in the Lbcb.
            //

            if (!FlagOn( Lfcb->Flags, LFCB_LOG_WRAPPED )
                && ( Lfcb->SeqNumber >= Lfcb->SeqNumberForWrap )) {

                SetFlag( Lbcb->LbcbFlags, LBCB_LOG_WRAPPED );
                SetFlag( Lfcb->Flags, LFCB_LOG_WRAPPED );
            }
        }

        //
        //  Now initialize the rest of the Lbcb fields.
        //

        Lbcb->FileOffset = Lfcb->NextLogPage;
        Lbcb->SeqNumber = Lfcb->SeqNumber;
        Lbcb->BufferOffset = Lfcb->LogPageDataOffset;

        //
        //  Store the next page in the Lfcb.
        //

        LfsNextLogPageOffset( Lfcb,
                              Lfcb->NextLogPage,
                              &Lfcb->NextLogPage,
                              &WrappedOrUsaError );

        Lbcb->Length = Lfcb->LogPageSize;
        Lbcb->PageHeader = PageHeader;
        Lbcb->LogPageBcb = PageHeaderBcb;

        Lbcb->ResourceThread = ExGetCurrentResourceThread();

        //
        //  If we are reusing a previous page then set a flag in
        //  the Lbcb to indicate that we should flush a copy
        //  first.
        //

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

            SetFlag( Lbcb->LbcbFlags, LBCB_FLUSH_COPY );
            ClearFlag( Lfcb->Flags, LFCB_REUSE_TAIL );

            (ULONG)Lbcb->BufferOffset = Lfcb->ReusePageOffset;

            Lbcb->Flags = ((PLFS_RECORD_PAGE_HEADER) PageHeader)->Flags;
            Lbcb->LastLsn = ((PLFS_RECORD_PAGE_HEADER) PageHeader)->Copy.LastLsn;
            Lbcb->LastEndLsn = ((PLFS_RECORD_PAGE_HEADER) PageHeader)->Header.Packed.LastEndLsn;
        }

        //
        //  Put the Lbcb on the active queue
        //

        InsertTailList( &Lfcb->LbcbActive, &Lbcb->ActiveLinks );

        SetFlag( Lbcb->LbcbFlags, LBCB_ON_ACTIVE_QUEUE );

    } finally {

        DebugUnwind( LfsGetLbcb );

        //
        //  If an error occurred, we need to clean up any blocks which
        //  have not been added to the active queue.
        //

        if (AbnormalTermination()) {

            if (Lbcb != NULL) {

                LfsDeallocateLbcb( Lfcb, Lbcb );
                Lbcb = NULL;
            }

            //
            //  Unpin the system page if pinned.
            //

            if (PageHeaderBcb != NULL) {

                CcUnpinData( PageHeaderBcb );
            }
        }

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

    return Lbcb;
}