Line data Source code
1 : /*-------------------------------------------------------------------------
2 : *
3 : * buf_table.c
4 : * routines for mapping BufferTags to buffer indexes.
5 : *
6 : * Note: the routines in this file do no locking of their own. The caller
7 : * must hold a suitable lock on the appropriate BufMappingLock, as specified
8 : * in the comments. We can't do the locking inside these functions because
9 : * in most cases the caller needs to adjust the buffer header contents
10 : * before the lock is released (see notes in README).
11 : *
12 : *
13 : * Portions Copyright (c) 1996-2026, PostgreSQL Global Development Group
14 : * Portions Copyright (c) 1994, Regents of the University of California
15 : *
16 : *
17 : * IDENTIFICATION
18 : * src/backend/storage/buffer/buf_table.c
19 : *
20 : *-------------------------------------------------------------------------
21 : */
22 : #include "postgres.h"
23 :
24 : #include "storage/buf_internals.h"
25 : #include "storage/subsystems.h"
26 :
27 : /* entry for buffer lookup hashtable */
28 : typedef struct
29 : {
30 : BufferTag key; /* Tag of a disk page */
31 : int id; /* Associated buffer ID */
32 : } BufferLookupEnt;
33 :
34 : static HTAB *SharedBufHash;
35 :
36 : static void BufTableShmemRequest(void *arg);
37 :
38 : const ShmemCallbacks BufTableShmemCallbacks = {
39 : .request_fn = BufTableShmemRequest,
40 : /* no special initialization needed, the hash table will start empty */
41 : };
42 :
43 : /*
44 : * Register shmem hash table for mapping buffers.
45 : * size is the desired hash table size (possibly more than NBuffers)
46 : */
47 : void
48 1234 : BufTableShmemRequest(void *arg)
49 : {
50 : int size;
51 :
52 : /*
53 : * Request the shared buffer lookup hashtable.
54 : *
55 : * Since we can't tolerate running out of lookup table entries, we must be
56 : * sure to specify an adequate table size here. The maximum steady-state
57 : * usage is of course NBuffers entries, but BufferAlloc() tries to insert
58 : * a new entry before deleting the old. In principle this could be
59 : * happening in each partition concurrently, so we could need as many as
60 : * NBuffers + NUM_BUFFER_PARTITIONS entries.
61 : */
62 1234 : size = NBuffers + NUM_BUFFER_PARTITIONS;
63 :
64 1234 : ShmemRequestHash(.name = "Shared Buffer Lookup Table",
65 : .nelems = size,
66 : .ptr = &SharedBufHash,
67 : .hash_info.keysize = sizeof(BufferTag),
68 : .hash_info.entrysize = sizeof(BufferLookupEnt),
69 : .hash_info.num_partitions = NUM_BUFFER_PARTITIONS,
70 : .hash_flags = HASH_ELEM | HASH_BLOBS | HASH_PARTITION | HASH_FIXED_SIZE,
71 : );
72 1234 : }
73 :
74 : /*
75 : * BufTableHashCode
76 : * Compute the hash code associated with a BufferTag
77 : *
78 : * This must be passed to the lookup/insert/delete routines along with the
79 : * tag. We do it like this because the callers need to know the hash code
80 : * in order to determine which buffer partition to lock, and we don't want
81 : * to do the hash computation twice (hash_any is a bit slow).
82 : */
83 : uint32
84 84975002 : BufTableHashCode(BufferTag *tagPtr)
85 : {
86 84975002 : return get_hash_value(SharedBufHash, tagPtr);
87 : }
88 :
89 : /*
90 : * BufTableLookup
91 : * Lookup the given BufferTag; return buffer ID, or -1 if not found
92 : *
93 : * Caller must hold at least share lock on BufMappingLock for tag's partition
94 : */
95 : int
96 83148310 : BufTableLookup(BufferTag *tagPtr, uint32 hashcode)
97 : {
98 : BufferLookupEnt *result;
99 :
100 : result = (BufferLookupEnt *)
101 83148310 : hash_search_with_hash_value(SharedBufHash,
102 : tagPtr,
103 : hashcode,
104 : HASH_FIND,
105 : NULL);
106 :
107 83148310 : if (!result)
108 1987646 : return -1;
109 :
110 81160664 : return result->id;
111 : }
112 :
113 : /*
114 : * BufTableInsert
115 : * Insert a hashtable entry for given tag and buffer ID,
116 : * unless an entry already exists for that tag
117 : *
118 : * Returns -1 on successful insertion. If a conflicting entry exists
119 : * already, returns the buffer ID in that entry.
120 : *
121 : * Caller must hold exclusive lock on BufMappingLock for tag's partition
122 : */
123 : int
124 2259669 : BufTableInsert(BufferTag *tagPtr, uint32 hashcode, int buf_id)
125 : {
126 : BufferLookupEnt *result;
127 : bool found;
128 :
129 : Assert(buf_id >= 0); /* -1 is reserved for not-in-table */
130 : Assert(tagPtr->blockNum != P_NEW); /* invalid tag */
131 :
132 : result = (BufferLookupEnt *)
133 2259669 : hash_search_with_hash_value(SharedBufHash,
134 : tagPtr,
135 : hashcode,
136 : HASH_ENTER,
137 : &found);
138 :
139 2259669 : if (found) /* found something already in the table */
140 1024 : return result->id;
141 :
142 2258645 : result->id = buf_id;
143 :
144 2258645 : return -1;
145 : }
146 :
147 : /*
148 : * BufTableDelete
149 : * Delete the hashtable entry for given tag (which must exist)
150 : *
151 : * Caller must hold exclusive lock on BufMappingLock for tag's partition
152 : */
153 : void
154 1544502 : BufTableDelete(BufferTag *tagPtr, uint32 hashcode)
155 : {
156 : BufferLookupEnt *result;
157 :
158 : result = (BufferLookupEnt *)
159 1544502 : hash_search_with_hash_value(SharedBufHash,
160 : tagPtr,
161 : hashcode,
162 : HASH_REMOVE,
163 : NULL);
164 :
165 1544502 : if (!result) /* shouldn't happen */
166 0 : elog(ERROR, "shared buffer hash table corrupted");
167 1544502 : }
|