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