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 */
29typedef struct
30{
31 BufferTag key; /* Tag of a disk page */
32 int id; /* Associated buffer ID */
33} BufferLookupEnt;
34
35static 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 */
42Size
43BufTableShmemSize(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 */
52void
53InitBufTable(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 */
79uint32
80BufTableHashCode(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 */
91int
92BufTableLookup(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 */
119int
120BufTableInsert(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 */
149void
150BufTableDelete(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