]>
Commit | Line | Data |
---|---|---|
e9ce8d39 | 1 | /* |
9385eb3d | 2 | * Copyright (c) 2003 Apple Computer, Inc. All rights reserved. |
e9ce8d39 A |
3 | * |
4 | * @APPLE_LICENSE_HEADER_START@ | |
5 | * | |
734aad71 | 6 | * Copyright (c) 1999-2003 Apple Computer, Inc. All Rights Reserved. |
e9ce8d39 | 7 | * |
734aad71 A |
8 | * This file contains Original Code and/or Modifications of Original Code |
9 | * as defined in and that are subject to the Apple Public Source License | |
10 | * Version 2.0 (the 'License'). You may not use this file except in | |
11 | * compliance with the License. Please obtain a copy of the License at | |
12 | * http://www.opensource.apple.com/apsl/ and read it before using this | |
13 | * file. | |
14 | * | |
15 | * The Original Code and all software distributed under the License are | |
16 | * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER | |
e9ce8d39 A |
17 | * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, |
18 | * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, | |
734aad71 A |
19 | * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. |
20 | * Please see the License for the specific language governing rights and | |
21 | * limitations under the License. | |
e9ce8d39 A |
22 | * |
23 | * @APPLE_LICENSE_HEADER_END@ | |
24 | */ | |
9385eb3d A |
25 | /*- |
26 | * Copyright (c) 1990, 1993, 1994 | |
e9ce8d39 A |
27 | * The Regents of the University of California. All rights reserved. |
28 | * | |
29 | * Redistribution and use in source and binary forms, with or without | |
30 | * modification, are permitted provided that the following conditions | |
31 | * are met: | |
32 | * 1. Redistributions of source code must retain the above copyright | |
33 | * notice, this list of conditions and the following disclaimer. | |
34 | * 2. Redistributions in binary form must reproduce the above copyright | |
35 | * notice, this list of conditions and the following disclaimer in the | |
36 | * documentation and/or other materials provided with the distribution. | |
37 | * 3. All advertising materials mentioning features or use of this software | |
38 | * must display the following acknowledgement: | |
39 | * This product includes software developed by the University of | |
40 | * California, Berkeley and its contributors. | |
41 | * 4. Neither the name of the University nor the names of its contributors | |
42 | * may be used to endorse or promote products derived from this software | |
43 | * without specific prior written permission. | |
44 | * | |
45 | * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND | |
46 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | |
47 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | |
48 | * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE | |
49 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | |
50 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS | |
51 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) | |
52 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT | |
53 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY | |
54 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF | |
55 | * SUCH DAMAGE. | |
56 | */ | |
57 | ||
9385eb3d A |
58 | #if defined(LIBC_SCCS) && !defined(lint) |
59 | static char sccsid[] = "@(#)mpool.c 8.5 (Berkeley) 7/26/94"; | |
60 | #endif /* LIBC_SCCS and not lint */ | |
61 | #include <sys/cdefs.h> | |
e9ce8d39 A |
62 | |
63 | #include <sys/param.h> | |
9385eb3d | 64 | #include <sys/queue.h> |
e9ce8d39 A |
65 | #include <sys/stat.h> |
66 | ||
67 | #include <errno.h> | |
68 | #include <stdio.h> | |
69 | #include <stdlib.h> | |
70 | #include <string.h> | |
71 | #include <unistd.h> | |
72 | ||
73 | #include <db.h> | |
9385eb3d | 74 | |
e9ce8d39 | 75 | #define __MPOOLINTERFACE_PRIVATE |
9385eb3d | 76 | #include <mpool.h> |
e9ce8d39 | 77 | |
9385eb3d A |
78 | static BKT *mpool_bkt(MPOOL *); |
79 | static BKT *mpool_look(MPOOL *, pgno_t); | |
80 | static int mpool_write(MPOOL *, BKT *); | |
e9ce8d39 A |
81 | |
82 | /* | |
9385eb3d A |
83 | * mpool_open -- |
84 | * Initialize a memory pool. | |
e9ce8d39 A |
85 | */ |
86 | MPOOL * | |
87 | mpool_open(key, fd, pagesize, maxcache) | |
9385eb3d | 88 | void *key; |
e9ce8d39 A |
89 | int fd; |
90 | pgno_t pagesize, maxcache; | |
91 | { | |
92 | struct stat sb; | |
93 | MPOOL *mp; | |
94 | int entry; | |
95 | ||
9385eb3d A |
96 | /* |
97 | * Get information about the file. | |
98 | * | |
99 | * XXX | |
100 | * We don't currently handle pipes, although we should. | |
101 | */ | |
e9ce8d39 A |
102 | if (fstat(fd, &sb)) |
103 | return (NULL); | |
e9ce8d39 A |
104 | if (!S_ISREG(sb.st_mode)) { |
105 | errno = ESPIPE; | |
106 | return (NULL); | |
107 | } | |
108 | ||
9385eb3d A |
109 | /* Allocate and initialize the MPOOL cookie. */ |
110 | if ((mp = (MPOOL *)calloc(1, sizeof(MPOOL))) == NULL) | |
e9ce8d39 | 111 | return (NULL); |
9385eb3d | 112 | TAILQ_INIT(&mp->lqh); |
e9ce8d39 | 113 | for (entry = 0; entry < HASHSIZE; ++entry) |
9385eb3d | 114 | TAILQ_INIT(&mp->hqh[entry]); |
e9ce8d39 | 115 | mp->maxcache = maxcache; |
e9ce8d39 | 116 | mp->npages = sb.st_size / pagesize; |
9385eb3d | 117 | mp->pagesize = pagesize; |
e9ce8d39 | 118 | mp->fd = fd; |
e9ce8d39 A |
119 | return (mp); |
120 | } | |
121 | ||
122 | /* | |
9385eb3d A |
123 | * mpool_filter -- |
124 | * Initialize input/output filters. | |
e9ce8d39 A |
125 | */ |
126 | void | |
127 | mpool_filter(mp, pgin, pgout, pgcookie) | |
128 | MPOOL *mp; | |
9385eb3d A |
129 | void (*pgin)(void *, pgno_t, void *); |
130 | void (*pgout)(void *, pgno_t, void *); | |
e9ce8d39 A |
131 | void *pgcookie; |
132 | { | |
133 | mp->pgin = pgin; | |
134 | mp->pgout = pgout; | |
135 | mp->pgcookie = pgcookie; | |
136 | } | |
137 | ||
138 | /* | |
9385eb3d A |
139 | * mpool_new -- |
140 | * Get a new page of memory. | |
e9ce8d39 A |
141 | */ |
142 | void * | |
143 | mpool_new(mp, pgnoaddr) | |
144 | MPOOL *mp; | |
145 | pgno_t *pgnoaddr; | |
146 | { | |
9385eb3d A |
147 | struct _hqh *head; |
148 | BKT *bp; | |
e9ce8d39 | 149 | |
9385eb3d A |
150 | if (mp->npages == MAX_PAGE_NUMBER) { |
151 | (void)fprintf(stderr, "mpool_new: page allocation overflow.\n"); | |
152 | abort(); | |
153 | } | |
e9ce8d39 A |
154 | #ifdef STATISTICS |
155 | ++mp->pagenew; | |
156 | #endif | |
157 | /* | |
9385eb3d A |
158 | * Get a BKT from the cache. Assign a new page number, attach |
159 | * it to the head of the hash chain, the tail of the lru chain, | |
160 | * and return. | |
e9ce8d39 | 161 | */ |
9385eb3d | 162 | if ((bp = mpool_bkt(mp)) == NULL) |
e9ce8d39 | 163 | return (NULL); |
9385eb3d A |
164 | *pgnoaddr = bp->pgno = mp->npages++; |
165 | bp->flags = MPOOL_PINNED; | |
166 | ||
167 | head = &mp->hqh[HASHKEY(bp->pgno)]; | |
168 | TAILQ_INSERT_HEAD(head, bp, hq); | |
169 | TAILQ_INSERT_TAIL(&mp->lqh, bp, q); | |
170 | return (bp->page); | |
e9ce8d39 A |
171 | } |
172 | ||
173 | /* | |
9385eb3d A |
174 | * mpool_get |
175 | * Get a page. | |
e9ce8d39 A |
176 | */ |
177 | void * | |
178 | mpool_get(mp, pgno, flags) | |
179 | MPOOL *mp; | |
180 | pgno_t pgno; | |
9385eb3d | 181 | u_int flags; /* XXX not used? */ |
e9ce8d39 | 182 | { |
9385eb3d A |
183 | struct _hqh *head; |
184 | BKT *bp; | |
e9ce8d39 A |
185 | off_t off; |
186 | int nr; | |
187 | ||
9385eb3d A |
188 | /* Check for attempt to retrieve a non-existent page. */ |
189 | if (pgno >= mp->npages) { | |
190 | errno = EINVAL; | |
191 | return (NULL); | |
192 | } | |
193 | ||
e9ce8d39 | 194 | #ifdef STATISTICS |
9385eb3d | 195 | ++mp->pageget; |
e9ce8d39 | 196 | #endif |
9385eb3d A |
197 | |
198 | /* Check for a page that is cached. */ | |
199 | if ((bp = mpool_look(mp, pgno)) != NULL) { | |
e9ce8d39 | 200 | #ifdef DEBUG |
9385eb3d A |
201 | if (bp->flags & MPOOL_PINNED) { |
202 | (void)fprintf(stderr, | |
203 | "mpool_get: page %d already pinned\n", bp->pgno); | |
204 | abort(); | |
205 | } | |
e9ce8d39 | 206 | #endif |
9385eb3d A |
207 | /* |
208 | * Move the page to the head of the hash chain and the tail | |
209 | * of the lru chain. | |
210 | */ | |
211 | head = &mp->hqh[HASHKEY(bp->pgno)]; | |
212 | TAILQ_REMOVE(head, bp, hq); | |
213 | TAILQ_INSERT_HEAD(head, bp, hq); | |
214 | TAILQ_REMOVE(&mp->lqh, bp, q); | |
215 | TAILQ_INSERT_TAIL(&mp->lqh, bp, q); | |
216 | ||
217 | /* Return a pinned page. */ | |
218 | bp->flags |= MPOOL_PINNED; | |
219 | return (bp->page); | |
e9ce8d39 A |
220 | } |
221 | ||
222 | /* Get a page from the cache. */ | |
9385eb3d | 223 | if ((bp = mpool_bkt(mp)) == NULL) |
e9ce8d39 | 224 | return (NULL); |
e9ce8d39 | 225 | |
9385eb3d | 226 | /* Read in the contents. */ |
e9ce8d39 A |
227 | #ifdef STATISTICS |
228 | ++mp->pageread; | |
229 | #endif | |
e9ce8d39 A |
230 | off = mp->pagesize * pgno; |
231 | if (lseek(mp->fd, off, SEEK_SET) != off) | |
232 | return (NULL); | |
9385eb3d | 233 | if ((nr = read(mp->fd, bp->page, mp->pagesize)) != mp->pagesize) { |
e9ce8d39 A |
234 | if (nr >= 0) |
235 | errno = EFTYPE; | |
236 | return (NULL); | |
237 | } | |
e9ce8d39 | 238 | |
9385eb3d A |
239 | /* Set the page number, pin the page. */ |
240 | bp->pgno = pgno; | |
241 | bp->flags = MPOOL_PINNED; | |
242 | ||
243 | /* | |
244 | * Add the page to the head of the hash chain and the tail | |
245 | * of the lru chain. | |
246 | */ | |
247 | head = &mp->hqh[HASHKEY(bp->pgno)]; | |
248 | TAILQ_INSERT_HEAD(head, bp, hq); | |
249 | TAILQ_INSERT_TAIL(&mp->lqh, bp, q); | |
250 | ||
251 | /* Run through the user's filter. */ | |
252 | if (mp->pgin != NULL) | |
253 | (mp->pgin)(mp->pgcookie, bp->pgno, bp->page); | |
254 | ||
255 | return (bp->page); | |
e9ce8d39 A |
256 | } |
257 | ||
258 | /* | |
9385eb3d A |
259 | * mpool_put |
260 | * Return a page. | |
e9ce8d39 A |
261 | */ |
262 | int | |
263 | mpool_put(mp, page, flags) | |
264 | MPOOL *mp; | |
265 | void *page; | |
266 | u_int flags; | |
267 | { | |
9385eb3d | 268 | BKT *bp; |
e9ce8d39 A |
269 | |
270 | #ifdef STATISTICS | |
271 | ++mp->pageput; | |
272 | #endif | |
9385eb3d | 273 | bp = (BKT *)((char *)page - sizeof(BKT)); |
e9ce8d39 | 274 | #ifdef DEBUG |
9385eb3d A |
275 | if (!(bp->flags & MPOOL_PINNED)) { |
276 | (void)fprintf(stderr, | |
277 | "mpool_put: page %d not pinned\n", bp->pgno); | |
278 | abort(); | |
e9ce8d39 A |
279 | } |
280 | #endif | |
9385eb3d A |
281 | bp->flags &= ~MPOOL_PINNED; |
282 | bp->flags |= flags & MPOOL_DIRTY; | |
e9ce8d39 A |
283 | return (RET_SUCCESS); |
284 | } | |
285 | ||
286 | /* | |
9385eb3d A |
287 | * mpool_close |
288 | * Close the buffer pool. | |
e9ce8d39 A |
289 | */ |
290 | int | |
291 | mpool_close(mp) | |
292 | MPOOL *mp; | |
293 | { | |
9385eb3d | 294 | BKT *bp; |
e9ce8d39 A |
295 | |
296 | /* Free up any space allocated to the lru pages. */ | |
9385eb3d A |
297 | while (!TAILQ_EMPTY(&mp->lqh)) { |
298 | bp = TAILQ_FIRST(&mp->lqh); | |
299 | TAILQ_REMOVE(&mp->lqh, bp, q); | |
300 | free(bp); | |
e9ce8d39 | 301 | } |
9385eb3d A |
302 | |
303 | /* Free the MPOOL cookie. */ | |
e9ce8d39 A |
304 | free(mp); |
305 | return (RET_SUCCESS); | |
306 | } | |
307 | ||
308 | /* | |
9385eb3d A |
309 | * mpool_sync |
310 | * Sync the pool to disk. | |
e9ce8d39 A |
311 | */ |
312 | int | |
313 | mpool_sync(mp) | |
314 | MPOOL *mp; | |
315 | { | |
9385eb3d | 316 | BKT *bp; |
e9ce8d39 | 317 | |
9385eb3d A |
318 | /* Walk the lru chain, flushing any dirty pages to disk. */ |
319 | TAILQ_FOREACH(bp, &mp->lqh, q) | |
320 | if (bp->flags & MPOOL_DIRTY && | |
321 | mpool_write(mp, bp) == RET_ERROR) | |
e9ce8d39 | 322 | return (RET_ERROR); |
9385eb3d A |
323 | |
324 | /* Sync the file descriptor. */ | |
e9ce8d39 A |
325 | return (fsync(mp->fd) ? RET_ERROR : RET_SUCCESS); |
326 | } | |
327 | ||
328 | /* | |
9385eb3d A |
329 | * mpool_bkt |
330 | * Get a page from the cache (or create one). | |
e9ce8d39 A |
331 | */ |
332 | static BKT * | |
333 | mpool_bkt(mp) | |
334 | MPOOL *mp; | |
335 | { | |
9385eb3d A |
336 | struct _hqh *head; |
337 | BKT *bp; | |
e9ce8d39 | 338 | |
9385eb3d | 339 | /* If under the max cached, always create a new page. */ |
e9ce8d39 A |
340 | if (mp->curcache < mp->maxcache) |
341 | goto new; | |
342 | ||
343 | /* | |
9385eb3d A |
344 | * If the cache is max'd out, walk the lru list for a buffer we |
345 | * can flush. If we find one, write it (if necessary) and take it | |
346 | * off any lists. If we don't find anything we grow the cache anyway. | |
e9ce8d39 A |
347 | * The cache never shrinks. |
348 | */ | |
9385eb3d A |
349 | TAILQ_FOREACH(bp, &mp->lqh, q) |
350 | if (!(bp->flags & MPOOL_PINNED)) { | |
351 | /* Flush if dirty. */ | |
352 | if (bp->flags & MPOOL_DIRTY && | |
353 | mpool_write(mp, bp) == RET_ERROR) | |
e9ce8d39 | 354 | return (NULL); |
e9ce8d39 A |
355 | #ifdef STATISTICS |
356 | ++mp->pageflush; | |
357 | #endif | |
9385eb3d A |
358 | /* Remove from the hash and lru queues. */ |
359 | head = &mp->hqh[HASHKEY(bp->pgno)]; | |
360 | TAILQ_REMOVE(head, bp, hq); | |
361 | TAILQ_REMOVE(&mp->lqh, bp, q); | |
e9ce8d39 | 362 | #ifdef DEBUG |
9385eb3d A |
363 | { void *spage; |
364 | spage = bp->page; | |
365 | memset(bp, 0xff, sizeof(BKT) + mp->pagesize); | |
366 | bp->page = spage; | |
e9ce8d39 A |
367 | } |
368 | #endif | |
9385eb3d | 369 | return (bp); |
e9ce8d39 A |
370 | } |
371 | ||
9385eb3d | 372 | new: if ((bp = (BKT *)malloc(sizeof(BKT) + mp->pagesize)) == NULL) |
e9ce8d39 A |
373 | return (NULL); |
374 | #ifdef STATISTICS | |
375 | ++mp->pagealloc; | |
376 | #endif | |
9385eb3d A |
377 | #if defined(DEBUG) || defined(PURIFY) |
378 | memset(bp, 0xff, sizeof(BKT) + mp->pagesize); | |
e9ce8d39 | 379 | #endif |
9385eb3d | 380 | bp->page = (char *)bp + sizeof(BKT); |
e9ce8d39 | 381 | ++mp->curcache; |
9385eb3d | 382 | return (bp); |
e9ce8d39 A |
383 | } |
384 | ||
385 | /* | |
9385eb3d A |
386 | * mpool_write |
387 | * Write a page to disk. | |
e9ce8d39 A |
388 | */ |
389 | static int | |
9385eb3d | 390 | mpool_write(mp, bp) |
e9ce8d39 | 391 | MPOOL *mp; |
9385eb3d | 392 | BKT *bp; |
e9ce8d39 A |
393 | { |
394 | off_t off; | |
395 | ||
e9ce8d39 A |
396 | #ifdef STATISTICS |
397 | ++mp->pagewrite; | |
398 | #endif | |
9385eb3d A |
399 | |
400 | /* Run through the user's filter. */ | |
401 | if (mp->pgout) | |
402 | (mp->pgout)(mp->pgcookie, bp->pgno, bp->page); | |
403 | ||
404 | off = mp->pagesize * bp->pgno; | |
e9ce8d39 A |
405 | if (lseek(mp->fd, off, SEEK_SET) != off) |
406 | return (RET_ERROR); | |
9385eb3d | 407 | if (write(mp->fd, bp->page, mp->pagesize) != mp->pagesize) |
e9ce8d39 | 408 | return (RET_ERROR); |
9385eb3d A |
409 | |
410 | bp->flags &= ~MPOOL_DIRTY; | |
e9ce8d39 A |
411 | return (RET_SUCCESS); |
412 | } | |
413 | ||
414 | /* | |
9385eb3d A |
415 | * mpool_look |
416 | * Lookup a page in the cache. | |
e9ce8d39 A |
417 | */ |
418 | static BKT * | |
419 | mpool_look(mp, pgno) | |
420 | MPOOL *mp; | |
421 | pgno_t pgno; | |
422 | { | |
9385eb3d A |
423 | struct _hqh *head; |
424 | BKT *bp; | |
e9ce8d39 | 425 | |
9385eb3d A |
426 | head = &mp->hqh[HASHKEY(pgno)]; |
427 | TAILQ_FOREACH(bp, head, hq) | |
428 | if (bp->pgno == pgno) { | |
e9ce8d39 A |
429 | #ifdef STATISTICS |
430 | ++mp->cachehit; | |
431 | #endif | |
9385eb3d | 432 | return (bp); |
e9ce8d39 A |
433 | } |
434 | #ifdef STATISTICS | |
435 | ++mp->cachemiss; | |
436 | #endif | |
437 | return (NULL); | |
438 | } | |
439 | ||
440 | #ifdef STATISTICS | |
441 | /* | |
9385eb3d A |
442 | * mpool_stat |
443 | * Print out cache statistics. | |
e9ce8d39 A |
444 | */ |
445 | void | |
446 | mpool_stat(mp) | |
447 | MPOOL *mp; | |
448 | { | |
9385eb3d | 449 | BKT *bp; |
e9ce8d39 A |
450 | int cnt; |
451 | char *sep; | |
452 | ||
453 | (void)fprintf(stderr, "%lu pages in the file\n", mp->npages); | |
454 | (void)fprintf(stderr, | |
455 | "page size %lu, cacheing %lu pages of %lu page max cache\n", | |
456 | mp->pagesize, mp->curcache, mp->maxcache); | |
457 | (void)fprintf(stderr, "%lu page puts, %lu page gets, %lu page new\n", | |
458 | mp->pageput, mp->pageget, mp->pagenew); | |
459 | (void)fprintf(stderr, "%lu page allocs, %lu page flushes\n", | |
460 | mp->pagealloc, mp->pageflush); | |
461 | if (mp->cachehit + mp->cachemiss) | |
462 | (void)fprintf(stderr, | |
463 | "%.0f%% cache hit rate (%lu hits, %lu misses)\n", | |
464 | ((double)mp->cachehit / (mp->cachehit + mp->cachemiss)) | |
465 | * 100, mp->cachehit, mp->cachemiss); | |
466 | (void)fprintf(stderr, "%lu page reads, %lu page writes\n", | |
467 | mp->pageread, mp->pagewrite); | |
468 | ||
469 | sep = ""; | |
470 | cnt = 0; | |
9385eb3d A |
471 | TAILQ_FOREACH(bp, &mp->lqh, q) { |
472 | (void)fprintf(stderr, "%s%d", sep, bp->pgno); | |
473 | if (bp->flags & MPOOL_DIRTY) | |
e9ce8d39 | 474 | (void)fprintf(stderr, "d"); |
9385eb3d | 475 | if (bp->flags & MPOOL_PINNED) |
e9ce8d39 A |
476 | (void)fprintf(stderr, "P"); |
477 | if (++cnt == 10) { | |
478 | sep = "\n"; | |
479 | cnt = 0; | |
480 | } else | |
481 | sep = ", "; | |
482 | ||
483 | } | |
484 | (void)fprintf(stderr, "\n"); | |
485 | } | |
486 | #endif |