Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/lib/libc/db/hash/hash_buf.c
39562 views
1
/*-
2
* SPDX-License-Identifier: BSD-3-Clause
3
*
4
* Copyright (c) 1990, 1993, 1994
5
* The Regents of the University of California. All rights reserved.
6
*
7
* This code is derived from software contributed to Berkeley by
8
* Margo Seltzer.
9
*
10
* Redistribution and use in source and binary forms, with or without
11
* modification, are permitted provided that the following conditions
12
* are met:
13
* 1. Redistributions of source code must retain the above copyright
14
* notice, this list of conditions and the following disclaimer.
15
* 2. Redistributions in binary form must reproduce the above copyright
16
* notice, this list of conditions and the following disclaimer in the
17
* documentation and/or other materials provided with the distribution.
18
* 3. Neither the name of the University nor the names of its contributors
19
* may be used to endorse or promote products derived from this software
20
* without specific prior written permission.
21
*
22
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
23
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
26
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
27
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
28
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
29
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
31
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32
* SUCH DAMAGE.
33
*/
34
35
/*
36
* PACKAGE: hash
37
*
38
* DESCRIPTION:
39
* Contains buffer management
40
*
41
* ROUTINES:
42
* External
43
* __buf_init
44
* __get_buf
45
* __buf_free
46
* __reclaim_buf
47
* Internal
48
* newbuf
49
*/
50
51
#include <sys/param.h>
52
53
#include <stddef.h>
54
#include <stdio.h>
55
#include <stdlib.h>
56
#include <string.h>
57
58
#ifdef DEBUG
59
#include <assert.h>
60
#endif
61
62
#include <db.h>
63
#include "hash.h"
64
#include "page.h"
65
#include "extern.h"
66
67
static BUFHEAD *newbuf(HTAB *, u_int32_t, BUFHEAD *);
68
69
/* Unlink B from its place in the lru */
70
#define BUF_REMOVE(B) { \
71
(B)->prev->next = (B)->next; \
72
(B)->next->prev = (B)->prev; \
73
}
74
75
/* Insert B after P */
76
#define BUF_INSERT(B, P) { \
77
(B)->next = (P)->next; \
78
(B)->prev = (P); \
79
(P)->next = (B); \
80
(B)->next->prev = (B); \
81
}
82
83
#define MRU hashp->bufhead.next
84
#define LRU hashp->bufhead.prev
85
86
#define MRU_INSERT(B) BUF_INSERT((B), &hashp->bufhead)
87
#define LRU_INSERT(B) BUF_INSERT((B), LRU)
88
89
/*
90
* We are looking for a buffer with address "addr". If prev_bp is NULL, then
91
* address is a bucket index. If prev_bp is not NULL, then it points to the
92
* page previous to an overflow page that we are trying to find.
93
*
94
* CAVEAT: The buffer header accessed via prev_bp's ovfl field may no longer
95
* be valid. Therefore, you must always verify that its address matches the
96
* address you are seeking.
97
*/
98
BUFHEAD *
99
__get_buf(HTAB *hashp, u_int32_t addr,
100
BUFHEAD *prev_bp, /* If prev_bp set, indicates a new overflow page. */
101
int newpage)
102
{
103
BUFHEAD *bp;
104
u_int32_t is_disk_mask;
105
int is_disk, segment_ndx;
106
SEGMENT segp;
107
108
is_disk = 0;
109
is_disk_mask = 0;
110
if (prev_bp) {
111
bp = prev_bp->ovfl;
112
if (!bp || (bp->addr != addr))
113
bp = NULL;
114
if (!newpage)
115
is_disk = BUF_DISK;
116
} else {
117
/* Grab buffer out of directory */
118
segment_ndx = addr & (hashp->SGSIZE - 1);
119
120
/* valid segment ensured by __call_hash() */
121
segp = hashp->dir[addr >> hashp->SSHIFT];
122
#ifdef DEBUG
123
assert(segp != NULL);
124
#endif
125
bp = PTROF(segp[segment_ndx]);
126
is_disk_mask = ISDISK(segp[segment_ndx]);
127
is_disk = is_disk_mask || !hashp->new_file;
128
}
129
130
if (!bp) {
131
bp = newbuf(hashp, addr, prev_bp);
132
if (!bp ||
133
__get_page(hashp, bp->page, addr, !prev_bp, is_disk, 0))
134
return (NULL);
135
if (!prev_bp)
136
segp[segment_ndx] =
137
(BUFHEAD *)((intptr_t)bp | is_disk_mask);
138
} else {
139
BUF_REMOVE(bp);
140
MRU_INSERT(bp);
141
}
142
return (bp);
143
}
144
145
/*
146
* We need a buffer for this page. Either allocate one, or evict a resident
147
* one (if we have as many buffers as we're allowed) and put this one in.
148
*
149
* If newbuf finds an error (returning NULL), it also sets errno.
150
*/
151
static BUFHEAD *
152
newbuf(HTAB *hashp, u_int32_t addr, BUFHEAD *prev_bp)
153
{
154
BUFHEAD *bp; /* The buffer we're going to use */
155
BUFHEAD *xbp; /* Temp pointer */
156
BUFHEAD *next_xbp;
157
SEGMENT segp;
158
int segment_ndx;
159
u_int16_t oaddr, *shortp;
160
161
oaddr = 0;
162
bp = LRU;
163
164
/* It is bad to overwrite the page under the cursor. */
165
if (bp == hashp->cpage) {
166
BUF_REMOVE(bp);
167
MRU_INSERT(bp);
168
bp = LRU;
169
}
170
171
/* If prev_bp is part of bp overflow, create a new buffer. */
172
if (hashp->nbufs == 0 && prev_bp && bp->ovfl) {
173
BUFHEAD *ovfl;
174
175
for (ovfl = bp->ovfl; ovfl ; ovfl = ovfl->ovfl) {
176
if (ovfl == prev_bp) {
177
hashp->nbufs++;
178
break;
179
}
180
}
181
}
182
183
/*
184
* If LRU buffer is pinned, the buffer pool is too small. We need to
185
* allocate more buffers.
186
*/
187
if (hashp->nbufs || (bp->flags & BUF_PIN) || bp == hashp->cpage) {
188
/* Allocate a new one */
189
if ((bp = (BUFHEAD *)calloc(1, sizeof(BUFHEAD))) == NULL)
190
return (NULL);
191
if ((bp->page = (char *)calloc(1, hashp->BSIZE)) == NULL) {
192
free(bp);
193
return (NULL);
194
}
195
if (hashp->nbufs)
196
hashp->nbufs--;
197
} else {
198
/* Kick someone out */
199
BUF_REMOVE(bp);
200
/*
201
* If this is an overflow page with addr 0, it's already been
202
* flushed back in an overflow chain and initialized.
203
*/
204
if ((bp->addr != 0) || (bp->flags & BUF_BUCKET)) {
205
/*
206
* Set oaddr before __put_page so that you get it
207
* before bytes are swapped.
208
*/
209
shortp = (u_int16_t *)bp->page;
210
if (shortp[0])
211
oaddr = shortp[shortp[0] - 1];
212
if ((bp->flags & BUF_MOD) && __put_page(hashp, bp->page,
213
bp->addr, (int)IS_BUCKET(bp->flags), 0))
214
return (NULL);
215
/*
216
* Update the pointer to this page (i.e. invalidate it).
217
*
218
* If this is a new file (i.e. we created it at open
219
* time), make sure that we mark pages which have been
220
* written to disk so we retrieve them from disk later,
221
* rather than allocating new pages.
222
*/
223
if (IS_BUCKET(bp->flags)) {
224
segment_ndx = bp->addr & (hashp->SGSIZE - 1);
225
segp = hashp->dir[bp->addr >> hashp->SSHIFT];
226
#ifdef DEBUG
227
assert(segp != NULL);
228
#endif
229
230
if (hashp->new_file &&
231
((bp->flags & BUF_MOD) ||
232
ISDISK(segp[segment_ndx])))
233
segp[segment_ndx] = (BUFHEAD *)BUF_DISK;
234
else
235
segp[segment_ndx] = NULL;
236
}
237
/*
238
* Since overflow pages can only be access by means of
239
* their bucket, free overflow pages associated with
240
* this bucket.
241
*/
242
for (xbp = bp; xbp->ovfl;) {
243
next_xbp = xbp->ovfl;
244
xbp->ovfl = NULL;
245
xbp = next_xbp;
246
247
/* Check that ovfl pointer is up date. */
248
if (IS_BUCKET(xbp->flags) ||
249
(oaddr != xbp->addr))
250
break;
251
252
shortp = (u_int16_t *)xbp->page;
253
if (shortp[0])
254
/* set before __put_page */
255
oaddr = shortp[shortp[0] - 1];
256
if ((xbp->flags & BUF_MOD) && __put_page(hashp,
257
xbp->page, xbp->addr, 0, 0))
258
return (NULL);
259
xbp->addr = 0;
260
xbp->flags = 0;
261
BUF_REMOVE(xbp);
262
LRU_INSERT(xbp);
263
}
264
}
265
}
266
267
/* Now assign this buffer */
268
bp->addr = addr;
269
#ifdef DEBUG1
270
(void)fprintf(stderr, "NEWBUF1: %d->ovfl was %d is now %d\n",
271
bp->addr, (bp->ovfl ? bp->ovfl->addr : 0), 0);
272
#endif
273
bp->ovfl = NULL;
274
if (prev_bp) {
275
/*
276
* If prev_bp is set, this is an overflow page, hook it in to
277
* the buffer overflow links.
278
*/
279
#ifdef DEBUG1
280
(void)fprintf(stderr, "NEWBUF2: %d->ovfl was %d is now %d\n",
281
prev_bp->addr, (prev_bp->ovfl ? prev_bp->ovfl->addr : 0),
282
(bp ? bp->addr : 0));
283
#endif
284
prev_bp->ovfl = bp;
285
bp->flags = 0;
286
} else
287
bp->flags = BUF_BUCKET;
288
MRU_INSERT(bp);
289
return (bp);
290
}
291
292
void
293
__buf_init(HTAB *hashp, int nbytes)
294
{
295
BUFHEAD *bfp;
296
int npages;
297
298
bfp = &(hashp->bufhead);
299
npages = (nbytes + hashp->BSIZE - 1) >> hashp->BSHIFT;
300
npages = MAX(npages, MIN_BUFFERS);
301
302
hashp->nbufs = npages;
303
bfp->next = bfp;
304
bfp->prev = bfp;
305
/*
306
* This space is calloc'd so these are already null.
307
*
308
* bfp->ovfl = NULL;
309
* bfp->flags = 0;
310
* bfp->page = NULL;
311
* bfp->addr = 0;
312
*/
313
}
314
315
int
316
__buf_free(HTAB *hashp, int do_free, int to_disk)
317
{
318
BUFHEAD *bp;
319
320
/* Need to make sure that buffer manager has been initialized */
321
if (!LRU)
322
return (0);
323
for (bp = LRU; bp != &hashp->bufhead;) {
324
/* Check that the buffer is valid */
325
if (bp->addr || IS_BUCKET(bp->flags)) {
326
if (to_disk && (bp->flags & BUF_MOD) &&
327
__put_page(hashp, bp->page,
328
bp->addr, IS_BUCKET(bp->flags), 0))
329
return (-1);
330
}
331
/* Check if we are freeing stuff */
332
if (do_free) {
333
if (bp->page) {
334
(void)memset(bp->page, 0, hashp->BSIZE);
335
free(bp->page);
336
}
337
BUF_REMOVE(bp);
338
free(bp);
339
bp = LRU;
340
} else
341
bp = bp->prev;
342
}
343
return (0);
344
}
345
346
void
347
__reclaim_buf(HTAB *hashp, BUFHEAD *bp)
348
{
349
bp->ovfl = NULL;
350
bp->addr = 0;
351
bp->flags = 0;
352
BUF_REMOVE(bp);
353
LRU_INSERT(bp);
354
}
355
356