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-2019, 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/bufmgr.h" |
25 | #include "storage/buf_internals.h" |
26 | |
27 | |
28 | /* entry for buffer lookup hashtable */ |
29 | typedef struct |
30 | { |
31 | BufferTag key; /* Tag of a disk page */ |
32 | int id; /* Associated buffer ID */ |
33 | } BufferLookupEnt; |
34 | |
35 | static HTAB *SharedBufHash; |
36 | |
37 | |
38 | /* |
39 | * Estimate space needed for mapping hashtable |
40 | * size is the desired hash table size (possibly more than NBuffers) |
41 | */ |
42 | Size |
43 | BufTableShmemSize(int size) |
44 | { |
45 | return hash_estimate_size(size, sizeof(BufferLookupEnt)); |
46 | } |
47 | |
48 | /* |
49 | * Initialize shmem hash table for mapping buffers |
50 | * size is the desired hash table size (possibly more than NBuffers) |
51 | */ |
52 | void |
53 | InitBufTable(int size) |
54 | { |
55 | HASHCTL info; |
56 | |
57 | /* assume no locking is needed yet */ |
58 | |
59 | /* BufferTag maps to Buffer */ |
60 | info.keysize = sizeof(BufferTag); |
61 | info.entrysize = sizeof(BufferLookupEnt); |
62 | info.num_partitions = NUM_BUFFER_PARTITIONS; |
63 | |
64 | SharedBufHash = ShmemInitHash("Shared Buffer Lookup Table" , |
65 | size, size, |
66 | &info, |
67 | HASH_ELEM | HASH_BLOBS | HASH_PARTITION); |
68 | } |
69 | |
70 | /* |
71 | * BufTableHashCode |
72 | * Compute the hash code associated with a BufferTag |
73 | * |
74 | * This must be passed to the lookup/insert/delete routines along with the |
75 | * tag. We do it like this because the callers need to know the hash code |
76 | * in order to determine which buffer partition to lock, and we don't want |
77 | * to do the hash computation twice (hash_any is a bit slow). |
78 | */ |
79 | uint32 |
80 | BufTableHashCode(BufferTag *tagPtr) |
81 | { |
82 | return get_hash_value(SharedBufHash, (void *) tagPtr); |
83 | } |
84 | |
85 | /* |
86 | * BufTableLookup |
87 | * Lookup the given BufferTag; return buffer ID, or -1 if not found |
88 | * |
89 | * Caller must hold at least share lock on BufMappingLock for tag's partition |
90 | */ |
91 | int |
92 | BufTableLookup(BufferTag *tagPtr, uint32 hashcode) |
93 | { |
94 | BufferLookupEnt *result; |
95 | |
96 | result = (BufferLookupEnt *) |
97 | hash_search_with_hash_value(SharedBufHash, |
98 | (void *) tagPtr, |
99 | hashcode, |
100 | HASH_FIND, |
101 | NULL); |
102 | |
103 | if (!result) |
104 | return -1; |
105 | |
106 | return result->id; |
107 | } |
108 | |
109 | /* |
110 | * BufTableInsert |
111 | * Insert a hashtable entry for given tag and buffer ID, |
112 | * unless an entry already exists for that tag |
113 | * |
114 | * Returns -1 on successful insertion. If a conflicting entry exists |
115 | * already, returns the buffer ID in that entry. |
116 | * |
117 | * Caller must hold exclusive lock on BufMappingLock for tag's partition |
118 | */ |
119 | int |
120 | BufTableInsert(BufferTag *tagPtr, uint32 hashcode, int buf_id) |
121 | { |
122 | BufferLookupEnt *result; |
123 | bool found; |
124 | |
125 | Assert(buf_id >= 0); /* -1 is reserved for not-in-table */ |
126 | Assert(tagPtr->blockNum != P_NEW); /* invalid tag */ |
127 | |
128 | result = (BufferLookupEnt *) |
129 | hash_search_with_hash_value(SharedBufHash, |
130 | (void *) tagPtr, |
131 | hashcode, |
132 | HASH_ENTER, |
133 | &found); |
134 | |
135 | if (found) /* found something already in the table */ |
136 | return result->id; |
137 | |
138 | result->id = buf_id; |
139 | |
140 | return -1; |
141 | } |
142 | |
143 | /* |
144 | * BufTableDelete |
145 | * Delete the hashtable entry for given tag (which must exist) |
146 | * |
147 | * Caller must hold exclusive lock on BufMappingLock for tag's partition |
148 | */ |
149 | void |
150 | BufTableDelete(BufferTag *tagPtr, uint32 hashcode) |
151 | { |
152 | BufferLookupEnt *result; |
153 | |
154 | result = (BufferLookupEnt *) |
155 | hash_search_with_hash_value(SharedBufHash, |
156 | (void *) tagPtr, |
157 | hashcode, |
158 | HASH_REMOVE, |
159 | NULL); |
160 | |
161 | if (!result) /* shouldn't happen */ |
162 | elog(ERROR, "shared buffer hash table corrupted" ); |
163 | } |
164 | |