~ubuntu-branches/ubuntu/hardy/postgresql-8.4/hardy-backports

« back to all changes in this revision

Viewing changes to src/backend/storage/buffer/localbuf.c

  • Committer: Bazaar Package Importer
  • Author(s): Martin Pitt
  • Date: 2009-03-20 12:00:13 UTC
  • Revision ID: james.westby@ubuntu.com-20090320120013-hogj7egc5mjncc5g
Tags: upstream-8.4~0cvs20090328
ImportĀ upstreamĀ versionĀ 8.4~0cvs20090328

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*-------------------------------------------------------------------------
 
2
 *
 
3
 * localbuf.c
 
4
 *        local buffer manager. Fast buffer manager for temporary tables,
 
5
 *        which never need to be WAL-logged or checkpointed, etc.
 
6
 *
 
7
 * Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
 
8
 * Portions Copyright (c) 1994-5, Regents of the University of California
 
9
 *
 
10
 *
 
11
 * IDENTIFICATION
 
12
 *        $PostgreSQL$
 
13
 *
 
14
 *-------------------------------------------------------------------------
 
15
 */
 
16
#include "postgres.h"
 
17
 
 
18
#include "catalog/catalog.h"
 
19
#include "storage/buf_internals.h"
 
20
#include "storage/bufmgr.h"
 
21
#include "storage/smgr.h"
 
22
#include "utils/guc.h"
 
23
#include "utils/memutils.h"
 
24
#include "utils/resowner.h"
 
25
 
 
26
 
 
27
/*#define LBDEBUG*/
 
28
 
 
29
/* entry for buffer lookup hashtable */
 
30
typedef struct
 
31
{
 
32
        BufferTag       key;                    /* Tag of a disk page */
 
33
        int                     id;                             /* Associated local buffer's index */
 
34
} LocalBufferLookupEnt;
 
35
 
 
36
/* Note: this macro only works on local buffers, not shared ones! */
 
37
#define LocalBufHdrGetBlock(bufHdr) \
 
38
        LocalBufferBlockPointers[-((bufHdr)->buf_id + 2)]
 
39
 
 
40
int                     NLocBuffer = 0;         /* until buffers are initialized */
 
41
 
 
42
BufferDesc *LocalBufferDescriptors = NULL;
 
43
Block      *LocalBufferBlockPointers = NULL;
 
44
int32      *LocalRefCount = NULL;
 
45
 
 
46
static int      nextFreeLocalBuf = 0;
 
47
 
 
48
static HTAB *LocalBufHash = NULL;
 
49
 
 
50
 
 
51
static void InitLocalBuffers(void);
 
52
static Block GetLocalBufferStorage(void);
 
53
 
 
54
 
 
55
/*
 
56
 * LocalPrefetchBuffer -
 
57
 *        initiate asynchronous read of a block of a relation
 
58
 *
 
59
 * Do PrefetchBuffer's work for temporary relations.
 
60
 * No-op if prefetching isn't compiled in.
 
61
 */
 
62
void
 
63
LocalPrefetchBuffer(SMgrRelation smgr, ForkNumber forkNum,
 
64
                                        BlockNumber blockNum)
 
65
{
 
66
#ifdef USE_PREFETCH
 
67
        BufferTag       newTag;                 /* identity of requested block */
 
68
        LocalBufferLookupEnt *hresult;
 
69
 
 
70
        INIT_BUFFERTAG(newTag, smgr->smgr_rnode, forkNum, blockNum);
 
71
 
 
72
        /* Initialize local buffers if first request in this session */
 
73
        if (LocalBufHash == NULL)
 
74
                InitLocalBuffers();
 
75
 
 
76
        /* See if the desired buffer already exists */
 
77
        hresult = (LocalBufferLookupEnt *)
 
78
                hash_search(LocalBufHash, (void *) &newTag, HASH_FIND, NULL);
 
79
 
 
80
        if (hresult)
 
81
        {
 
82
                /* Yes, so nothing to do */
 
83
                return;
 
84
        }
 
85
 
 
86
        /* Not in buffers, so initiate prefetch */
 
87
        smgrprefetch(smgr, forkNum, blockNum);
 
88
#endif /* USE_PREFETCH */
 
89
}
 
90
 
 
91
 
 
92
/*
 
93
 * LocalBufferAlloc -
 
94
 *        Find or create a local buffer for the given page of the given relation.
 
95
 *
 
96
 * API is similar to bufmgr.c's BufferAlloc, except that we do not need
 
97
 * to do any locking since this is all local.   Also, IO_IN_PROGRESS
 
98
 * does not get set.  Lastly, we support only default access strategy
 
99
 * (hence, usage_count is always advanced).
 
100
 */
 
101
BufferDesc *
 
102
LocalBufferAlloc(SMgrRelation smgr, ForkNumber forkNum, BlockNumber blockNum,
 
103
                                 bool *foundPtr)
 
104
{
 
105
        BufferTag       newTag;                 /* identity of requested block */
 
106
        LocalBufferLookupEnt *hresult;
 
107
        BufferDesc *bufHdr;
 
108
        int                     b;
 
109
        int                     trycounter;
 
110
        bool            found;
 
111
 
 
112
        INIT_BUFFERTAG(newTag, smgr->smgr_rnode, forkNum, blockNum);
 
113
 
 
114
        /* Initialize local buffers if first request in this session */
 
115
        if (LocalBufHash == NULL)
 
116
                InitLocalBuffers();
 
117
 
 
118
        /* See if the desired buffer already exists */
 
119
        hresult = (LocalBufferLookupEnt *)
 
120
                hash_search(LocalBufHash, (void *) &newTag, HASH_FIND, NULL);
 
121
 
 
122
        if (hresult)
 
123
        {
 
124
                b = hresult->id;
 
125
                bufHdr = &LocalBufferDescriptors[b];
 
126
                Assert(BUFFERTAGS_EQUAL(bufHdr->tag, newTag));
 
127
#ifdef LBDEBUG
 
128
                fprintf(stderr, "LB ALLOC (%u,%d,%d) %d\n",
 
129
                                smgr->smgr_rnode.relNode, forkNum, blockNum, -b - 1);
 
130
#endif
 
131
                /* this part is equivalent to PinBuffer for a shared buffer */
 
132
                if (LocalRefCount[b] == 0)
 
133
                {
 
134
                        if (bufHdr->usage_count < BM_MAX_USAGE_COUNT)
 
135
                                bufHdr->usage_count++;
 
136
                }
 
137
                LocalRefCount[b]++;
 
138
                ResourceOwnerRememberBuffer(CurrentResourceOwner,
 
139
                                                                        BufferDescriptorGetBuffer(bufHdr));
 
140
                if (bufHdr->flags & BM_VALID)
 
141
                        *foundPtr = TRUE;
 
142
                else
 
143
                {
 
144
                        /* Previous read attempt must have failed; try again */
 
145
                        *foundPtr = FALSE;
 
146
                }
 
147
                return bufHdr;
 
148
        }
 
149
 
 
150
#ifdef LBDEBUG
 
151
        fprintf(stderr, "LB ALLOC (%u,%d,%d) %d\n",
 
152
                smgr->smgr_rnode.relNode, forkNum, blockNum, -nextFreeLocalBuf - 1);
 
153
#endif
 
154
 
 
155
        /*
 
156
         * Need to get a new buffer.  We use a clock sweep algorithm (essentially
 
157
         * the same as what freelist.c does now...)
 
158
         */
 
159
        trycounter = NLocBuffer;
 
160
        for (;;)
 
161
        {
 
162
                b = nextFreeLocalBuf;
 
163
 
 
164
                if (++nextFreeLocalBuf >= NLocBuffer)
 
165
                        nextFreeLocalBuf = 0;
 
166
 
 
167
                bufHdr = &LocalBufferDescriptors[b];
 
168
 
 
169
                if (LocalRefCount[b] == 0)
 
170
                {
 
171
                        if (bufHdr->usage_count > 0)
 
172
                        {
 
173
                                bufHdr->usage_count--;
 
174
                                trycounter = NLocBuffer;
 
175
                        }
 
176
                        else
 
177
                        {
 
178
                                /* Found a usable buffer */
 
179
                                LocalRefCount[b]++;
 
180
                                ResourceOwnerRememberBuffer(CurrentResourceOwner,
 
181
                                                                                  BufferDescriptorGetBuffer(bufHdr));
 
182
                                break;
 
183
                        }
 
184
                }
 
185
                else if (--trycounter == 0)
 
186
                        ereport(ERROR,
 
187
                                        (errcode(ERRCODE_INSUFFICIENT_RESOURCES),
 
188
                                         errmsg("no empty local buffer available")));
 
189
        }
 
190
 
 
191
        /*
 
192
         * this buffer is not referenced but it might still be dirty. if that's
 
193
         * the case, write it out before reusing it!
 
194
         */
 
195
        if (bufHdr->flags & BM_DIRTY)
 
196
        {
 
197
                SMgrRelation oreln;
 
198
 
 
199
                /* Find smgr relation for buffer */
 
200
                oreln = smgropen(bufHdr->tag.rnode);
 
201
 
 
202
                /* And write... */
 
203
                smgrwrite(oreln,
 
204
                                  bufHdr->tag.forkNum,
 
205
                                  bufHdr->tag.blockNum,
 
206
                                  (char *) LocalBufHdrGetBlock(bufHdr),
 
207
                                  true);
 
208
 
 
209
                /* Mark not-dirty now in case we error out below */
 
210
                bufHdr->flags &= ~BM_DIRTY;
 
211
 
 
212
                LocalBufferFlushCount++;
 
213
        }
 
214
 
 
215
        /*
 
216
         * lazy memory allocation: allocate space on first use of a buffer.
 
217
         */
 
218
        if (LocalBufHdrGetBlock(bufHdr) == NULL)
 
219
        {
 
220
                /* Set pointer for use by BufferGetBlock() macro */
 
221
                LocalBufHdrGetBlock(bufHdr) = GetLocalBufferStorage();
 
222
        }
 
223
 
 
224
        /*
 
225
         * Update the hash table: remove old entry, if any, and make new one.
 
226
         */
 
227
        if (bufHdr->flags & BM_TAG_VALID)
 
228
        {
 
229
                hresult = (LocalBufferLookupEnt *)
 
230
                        hash_search(LocalBufHash, (void *) &bufHdr->tag,
 
231
                                                HASH_REMOVE, NULL);
 
232
                if (!hresult)                   /* shouldn't happen */
 
233
                        elog(ERROR, "local buffer hash table corrupted");
 
234
                /* mark buffer invalid just in case hash insert fails */
 
235
                CLEAR_BUFFERTAG(bufHdr->tag);
 
236
                bufHdr->flags &= ~(BM_VALID | BM_TAG_VALID);
 
237
        }
 
238
 
 
239
        hresult = (LocalBufferLookupEnt *)
 
240
                hash_search(LocalBufHash, (void *) &newTag, HASH_ENTER, &found);
 
241
        if (found)                                      /* shouldn't happen */
 
242
                elog(ERROR, "local buffer hash table corrupted");
 
243
        hresult->id = b;
 
244
 
 
245
        /*
 
246
         * it's all ours now.
 
247
         */
 
248
        bufHdr->tag = newTag;
 
249
        bufHdr->flags &= ~(BM_VALID | BM_DIRTY | BM_JUST_DIRTIED | BM_IO_ERROR);
 
250
        bufHdr->flags |= BM_TAG_VALID;
 
251
        bufHdr->usage_count = 1;
 
252
 
 
253
        *foundPtr = FALSE;
 
254
        return bufHdr;
 
255
}
 
256
 
 
257
/*
 
258
 * MarkLocalBufferDirty -
 
259
 *        mark a local buffer dirty
 
260
 */
 
261
void
 
262
MarkLocalBufferDirty(Buffer buffer)
 
263
{
 
264
        int                     bufid;
 
265
        BufferDesc *bufHdr;
 
266
 
 
267
        Assert(BufferIsLocal(buffer));
 
268
 
 
269
#ifdef LBDEBUG
 
270
        fprintf(stderr, "LB DIRTY %d\n", buffer);
 
271
#endif
 
272
 
 
273
        bufid = -(buffer + 1);
 
274
 
 
275
        Assert(LocalRefCount[bufid] > 0);
 
276
 
 
277
        bufHdr = &LocalBufferDescriptors[bufid];
 
278
        bufHdr->flags |= BM_DIRTY;
 
279
}
 
280
 
 
281
/*
 
282
 * DropRelFileNodeLocalBuffers
 
283
 *              This function removes from the buffer pool all the pages of the
 
284
 *              specified relation that have block numbers >= firstDelBlock.
 
285
 *              (In particular, with firstDelBlock = 0, all pages are removed.)
 
286
 *              Dirty pages are simply dropped, without bothering to write them
 
287
 *              out first.      Therefore, this is NOT rollback-able, and so should be
 
288
 *              used only with extreme caution!
 
289
 *
 
290
 *              See DropRelFileNodeBuffers in bufmgr.c for more notes.
 
291
 */
 
292
void
 
293
DropRelFileNodeLocalBuffers(RelFileNode rnode, ForkNumber forkNum,
 
294
                                                        BlockNumber firstDelBlock)
 
295
{
 
296
        int                     i;
 
297
 
 
298
        for (i = 0; i < NLocBuffer; i++)
 
299
        {
 
300
                BufferDesc *bufHdr = &LocalBufferDescriptors[i];
 
301
                LocalBufferLookupEnt *hresult;
 
302
 
 
303
                if ((bufHdr->flags & BM_TAG_VALID) &&
 
304
                        RelFileNodeEquals(bufHdr->tag.rnode, rnode) &&
 
305
                        bufHdr->tag.forkNum == forkNum &&
 
306
                        bufHdr->tag.blockNum >= firstDelBlock)
 
307
                {
 
308
                        if (LocalRefCount[i] != 0)
 
309
                                elog(ERROR, "block %u of %s is still referenced (local %u)",
 
310
                                         bufHdr->tag.blockNum,
 
311
                                         relpath(bufHdr->tag.rnode, bufHdr->tag.forkNum),
 
312
                                         LocalRefCount[i]);
 
313
                        /* Remove entry from hashtable */
 
314
                        hresult = (LocalBufferLookupEnt *)
 
315
                                hash_search(LocalBufHash, (void *) &bufHdr->tag,
 
316
                                                        HASH_REMOVE, NULL);
 
317
                        if (!hresult)           /* shouldn't happen */
 
318
                                elog(ERROR, "local buffer hash table corrupted");
 
319
                        /* Mark buffer invalid */
 
320
                        CLEAR_BUFFERTAG(bufHdr->tag);
 
321
                        bufHdr->flags = 0;
 
322
                        bufHdr->usage_count = 0;
 
323
                }
 
324
        }
 
325
}
 
326
 
 
327
/*
 
328
 * InitLocalBuffers -
 
329
 *        init the local buffer cache. Since most queries (esp. multi-user ones)
 
330
 *        don't involve local buffers, we delay allocating actual memory for the
 
331
 *        buffers until we need them; just make the buffer headers here.
 
332
 */
 
333
static void
 
334
InitLocalBuffers(void)
 
335
{
 
336
        int                     nbufs = num_temp_buffers;
 
337
        HASHCTL         info;
 
338
        int                     i;
 
339
 
 
340
        /* Allocate and zero buffer headers and auxiliary arrays */
 
341
        LocalBufferDescriptors = (BufferDesc *) calloc(nbufs, sizeof(BufferDesc));
 
342
        LocalBufferBlockPointers = (Block *) calloc(nbufs, sizeof(Block));
 
343
        LocalRefCount = (int32 *) calloc(nbufs, sizeof(int32));
 
344
        if (!LocalBufferDescriptors || !LocalBufferBlockPointers || !LocalRefCount)
 
345
                ereport(FATAL,
 
346
                                (errcode(ERRCODE_OUT_OF_MEMORY),
 
347
                                 errmsg("out of memory")));
 
348
 
 
349
        nextFreeLocalBuf = 0;
 
350
 
 
351
        /* initialize fields that need to start off nonzero */
 
352
        for (i = 0; i < nbufs; i++)
 
353
        {
 
354
                BufferDesc *buf = &LocalBufferDescriptors[i];
 
355
 
 
356
                /*
 
357
                 * negative to indicate local buffer. This is tricky: shared buffers
 
358
                 * start with 0. We have to start with -2. (Note that the routine
 
359
                 * BufferDescriptorGetBuffer adds 1 to buf_id so our first buffer id
 
360
                 * is -1.)
 
361
                 */
 
362
                buf->buf_id = -i - 2;
 
363
        }
 
364
 
 
365
        /* Create the lookup hash table */
 
366
        MemSet(&info, 0, sizeof(info));
 
367
        info.keysize = sizeof(BufferTag);
 
368
        info.entrysize = sizeof(LocalBufferLookupEnt);
 
369
        info.hash = tag_hash;
 
370
 
 
371
        LocalBufHash = hash_create("Local Buffer Lookup Table",
 
372
                                                           nbufs,
 
373
                                                           &info,
 
374
                                                           HASH_ELEM | HASH_FUNCTION);
 
375
 
 
376
        if (!LocalBufHash)
 
377
                elog(ERROR, "could not initialize local buffer hash table");
 
378
 
 
379
        /* Initialization done, mark buffers allocated */
 
380
        NLocBuffer = nbufs;
 
381
}
 
382
 
 
383
/*
 
384
 * GetLocalBufferStorage - allocate memory for a local buffer
 
385
 *
 
386
 * The idea of this function is to aggregate our requests for storage
 
387
 * so that the memory manager doesn't see a whole lot of relatively small
 
388
 * requests.  Since we'll never give back a local buffer once it's created
 
389
 * within a particular process, no point in burdening memmgr with separately
 
390
 * managed chunks.
 
391
 */
 
392
static Block
 
393
GetLocalBufferStorage(void)
 
394
{
 
395
        static char *cur_block = NULL;
 
396
        static int      next_buf_in_block = 0;
 
397
        static int      num_bufs_in_block = 0;
 
398
        static int      total_bufs_allocated = 0;
 
399
 
 
400
        char       *this_buf;
 
401
 
 
402
        Assert(total_bufs_allocated < NLocBuffer);
 
403
 
 
404
        if (next_buf_in_block >= num_bufs_in_block)
 
405
        {
 
406
                /* Need to make a new request to memmgr */
 
407
                int                     num_bufs;
 
408
 
 
409
                /* Start with a 16-buffer request; subsequent ones double each time */
 
410
                num_bufs = Max(num_bufs_in_block * 2, 16);
 
411
                /* But not more than what we need for all remaining local bufs */
 
412
                num_bufs = Min(num_bufs, NLocBuffer - total_bufs_allocated);
 
413
                /* And don't overflow MaxAllocSize, either */
 
414
                num_bufs = Min(num_bufs, MaxAllocSize / BLCKSZ);
 
415
 
 
416
                /* Allocate space from TopMemoryContext so it never goes away */
 
417
                cur_block = (char *) MemoryContextAlloc(TopMemoryContext,
 
418
                                                                                                num_bufs * BLCKSZ);
 
419
                next_buf_in_block = 0;
 
420
                num_bufs_in_block = num_bufs;
 
421
        }
 
422
 
 
423
        /* Allocate next buffer in current memory block */
 
424
        this_buf = cur_block + next_buf_in_block * BLCKSZ;
 
425
        next_buf_in_block++;
 
426
        total_bufs_allocated++;
 
427
 
 
428
        return (Block) this_buf;
 
429
}
 
430
 
 
431
/*
 
432
 * AtEOXact_LocalBuffers - clean up at end of transaction.
 
433
 *
 
434
 * This is just like AtEOXact_Buffers, but for local buffers.
 
435
 */
 
436
void
 
437
AtEOXact_LocalBuffers(bool isCommit)
 
438
{
 
439
#ifdef USE_ASSERT_CHECKING
 
440
        if (assert_enabled)
 
441
        {
 
442
                int                     i;
 
443
 
 
444
                for (i = 0; i < NLocBuffer; i++)
 
445
                {
 
446
                        Assert(LocalRefCount[i] == 0);
 
447
                }
 
448
        }
 
449
#endif
 
450
}
 
451
 
 
452
/*
 
453
 * AtProcExit_LocalBuffers - ensure we have dropped pins during backend exit.
 
454
 *
 
455
 * This is just like AtProcExit_Buffers, but for local buffers.  We have
 
456
 * to drop pins to ensure that any attempt to drop temp files doesn't
 
457
 * fail in DropRelFileNodeBuffers.
 
458
 */
 
459
void
 
460
AtProcExit_LocalBuffers(void)
 
461
{
 
462
        /* just zero the refcounts ... */
 
463
        if (LocalRefCount)
 
464
                MemSet(LocalRefCount, 0, NLocBuffer * sizeof(*LocalRefCount));
 
465
}