~ubuntu-branches/ubuntu/precise/postgresql-9.1/precise-security

« 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: 2011-05-11 10:41:53 UTC
  • Revision ID: james.westby@ubuntu.com-20110511104153-psbh2o58553fv1m0
Tags: upstream-9.1~beta1
ImportĀ upstreamĀ versionĀ 9.1~beta1

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