1 /*- 2 * Copyright (c) 1990 The Regents of the University of California. 3 * All rights reserved. 4 * 5 * %sccs.include.redist.c% 6 */ 7 8 #if defined(LIBC_SCCS) && !defined(lint) 9 static char sccsid[] = "@(#)mpool.c 5.3 (Berkeley) 11/24/91"; 10 #endif /* LIBC_SCCS and not lint */ 11 12 #include <sys/param.h> 13 #include <sys/stat.h> 14 #include <errno.h> 15 #include <db.h> 16 #include <unistd.h> 17 #include <stdio.h> 18 #include <stdlib.h> 19 #include <string.h> 20 #define __MPOOLINTERFACE_PRIVATE 21 #include "mpool.h" 22 23 static BKT *mpool_bkt __P((MPOOL *)); 24 static BKT *mpool_look __P((MPOOL *, pgno_t)); 25 static int mpool_write __P((MPOOL *, BKT *)); 26 #ifdef DEBUG 27 static void err __P((const char *fmt, ...)); 28 #endif 29 30 /* 31 * MPOOL_OPEN -- initialize a memory pool. 32 * 33 * Parameters: 34 * key: Shared buffer key. 35 * fd: File descriptor. 36 * pagesize: File page size. 37 * maxcache: Max number of cached pages. 38 * 39 * Returns: 40 * MPOOL pointer, NULL on error. 41 */ 42 MPOOL * 43 mpool_open(key, fd, pagesize, maxcache) 44 DBT *key; 45 int fd; 46 pgno_t pagesize, maxcache; 47 { 48 struct stat sb; 49 MPOOL *mp; 50 int entry; 51 52 if (fstat(fd, &sb)) 53 return (NULL); 54 /* XXX 55 * We should only set st_size to 0 for pipes -- 4.4BSD has the fix so 56 * that stat(2) returns true for ISSOCK on pipes. Until then, this is 57 * fairly close. 58 */ 59 if (!S_ISREG(sb.st_mode)) { 60 errno = ESPIPE; 61 return (NULL); 62 } 63 64 if ((mp = malloc(sizeof(MPOOL))) == NULL) 65 return (NULL); 66 mp->free.cnext = mp->free.cprev = (BKT *)&mp->free; 67 mp->lru.cnext = mp->lru.cprev = (BKT *)&mp->lru; 68 for (entry = 0; entry < HASHSIZE; ++entry) 69 mp->hashtable[entry].hnext = mp->hashtable[entry].hprev = 70 mp->hashtable[entry].cnext = mp->hashtable[entry].cprev = 71 (BKT *)&mp->hashtable[entry]; 72 mp->curcache = 0; 73 mp->maxcache = maxcache; 74 mp->pagesize = pagesize; 75 mp->npages = sb.st_size / pagesize; 76 mp->fd = fd; 77 78 #ifdef STATISTICS 79 mp->cachehit = mp->cachemiss = mp->pagealloc = mp->pageflush = 80 mp->pageget = mp->pagenew = mp->pageput = mp->pageread = 81 mp->pagewrite = 0; 82 #endif 83 return (mp); 84 } 85 86 /* 87 * MPOOL_FILTER -- initialize input/output filters. 88 * 89 * Parameters: 90 * pgin: Page in conversion routine. 91 * pgout: Page out conversion routine. 92 * pgcookie: Cookie for page in/out routines. 93 */ 94 void 95 mpool_filter(mp, pgin, pgout, pgcookie) 96 MPOOL *mp; 97 void (*pgin) __P((void *, pgno_t, void *)); 98 void (*pgout) __P((void *, pgno_t, void *)); 99 void *pgcookie; 100 { 101 mp->pgin = pgin; 102 mp->pgout = pgout; 103 mp->pgcookie = pgcookie; 104 } 105 106 /* 107 * MPOOL_NEW -- get a new page 108 * 109 * Parameters: 110 * mp: mpool cookie 111 * pgnoadddr: place to store new page number 112 * Returns: 113 * RET_ERROR, RET_SUCCESS 114 */ 115 void * 116 mpool_new(mp, pgnoaddr) 117 MPOOL *mp; 118 pgno_t *pgnoaddr; 119 { 120 BKT *b; 121 BKTHDR *hp; 122 123 #ifdef STATISTICS 124 ++mp->pagenew; 125 #endif 126 /* 127 * Get a BKT from the cache. Assign a new page number, attach it to 128 * the hash and lru chains and return. 129 */ 130 if ((b = mpool_bkt(mp)) == NULL) 131 return (NULL); 132 *pgnoaddr = b->pgno = mp->npages++; 133 b->flags = MPOOL_PINNED; 134 inshash(b, b->pgno); 135 inschain(b, &mp->lru); 136 return (b->page); 137 } 138 139 /* 140 * MPOOL_GET -- get a page from the pool 141 * 142 * Parameters: 143 * mp: mpool cookie 144 * pgno: page number 145 * flags: not used 146 * 147 * Returns: 148 * RET_ERROR, RET_SUCCESS 149 */ 150 void * 151 mpool_get(mp, pgno, flags) 152 MPOOL *mp; 153 pgno_t pgno; 154 u_int flags; /* XXX not used? */ 155 { 156 BKT *b; 157 BKTHDR *hp; 158 off_t off; 159 int nr; 160 161 /* 162 * If asking for a specific page that is already in the cache, find 163 * it and return it. 164 */ 165 if (b = mpool_look(mp, pgno)) { 166 #ifdef STATISTICS 167 ++mp->pageget; 168 #endif 169 #ifdef DEBUG 170 if (b->flags & MPOOL_PINNED) 171 err("mpool_get: page %d already pinned", b->pgno); 172 #endif 173 rmchain(b); 174 inschain(b, &mp->lru); 175 b->flags |= MPOOL_PINNED; 176 return (b->page); 177 } 178 179 /* Not allowed to retrieve a non-existent page. */ 180 if (pgno >= mp->npages) { 181 errno = EINVAL; 182 return (NULL); 183 } 184 185 /* Get a page from the cache. */ 186 if ((b = mpool_bkt(mp)) == NULL) 187 return (NULL); 188 b->pgno = pgno; 189 b->flags = MPOOL_PINNED; 190 191 #ifdef STATISTICS 192 ++mp->pageread; 193 #endif 194 /* Read in the contents. */ 195 off = mp->pagesize * pgno; 196 if (lseek(mp->fd, off, SEEK_SET) != off) 197 return (NULL); 198 if ((nr = read(mp->fd, b->page, mp->pagesize)) != mp->pagesize) { 199 if (nr >= 0) 200 errno = EFTYPE; 201 return (NULL); 202 } 203 if (mp->pgin) 204 (mp->pgin)(mp->pgcookie, b->pgno, b->page); 205 206 inshash(b, b->pgno); 207 inschain(b, &mp->lru); 208 #ifdef STATISTICS 209 ++mp->pageget; 210 #endif 211 return (b->page); 212 } 213 214 /* 215 * MPOOL_PUT -- return a page to the pool 216 * 217 * Parameters: 218 * mp: mpool cookie 219 * page: page pointer 220 * pgno: page number 221 * 222 * Returns: 223 * RET_ERROR, RET_SUCCESS 224 */ 225 int 226 mpool_put(mp, page, flags) 227 MPOOL *mp; 228 void *page; 229 u_int flags; 230 { 231 BKT *baddr; 232 #ifdef DEBUG 233 BKT *b; 234 #endif 235 236 #ifdef STATISTICS 237 ++mp->pageput; 238 #endif 239 baddr = (BKT *)((char *)page - sizeof(BKT)); 240 #ifdef DEBUG 241 if (!(baddr->flags & MPOOL_PINNED)) 242 err("mpool_put: page %d not pinned", b->pgno); 243 for (b = mp->lru.cnext; b != (BKT *)&mp->lru; b = b->cnext) { 244 if (b == (BKT *)&mp->lru) 245 err("mpool_put: %0x: bad address", baddr); 246 if (b == baddr) 247 break; 248 } 249 #endif 250 baddr->flags &= ~MPOOL_PINNED; 251 baddr->flags |= flags & MPOOL_DIRTY; 252 return (RET_SUCCESS); 253 } 254 255 /* 256 * MPOOL_CLOSE -- close the buffer pool 257 * 258 * Parameters: 259 * mp: mpool cookie 260 * 261 * Returns: 262 * RET_ERROR, RET_SUCCESS 263 */ 264 int 265 mpool_close(mp) 266 MPOOL *mp; 267 { 268 BKT *b, *next; 269 270 /* Free up any space allocated to the lru pages. */ 271 for (b = mp->lru.cprev; b != (BKT *)&mp->lru; b = next) { 272 next = b->cprev; 273 free(b); 274 } 275 free(mp); 276 return (RET_SUCCESS); 277 } 278 279 /* 280 * MPOOL_SYNC -- sync the file to disk. 281 * 282 * Parameters: 283 * mp: mpool cookie 284 * 285 * Returns: 286 * RET_ERROR, RET_SUCCESS 287 */ 288 int 289 mpool_sync(mp) 290 MPOOL *mp; 291 { 292 BKT *b; 293 294 for (b = mp->lru.cprev; b != (BKT *)&mp->lru; b = b->cprev) 295 if (b->flags & MPOOL_DIRTY && mpool_write(mp, b) == RET_ERROR) 296 return (RET_ERROR); 297 return (fsync(mp->fd) ? RET_ERROR : RET_SUCCESS); 298 } 299 300 /* 301 * MPOOL_BKT -- get/create a BKT from the cache 302 * 303 * Parameters: 304 * mp: mpool cookie 305 * 306 * Returns: 307 * NULL on failure and a pointer to the BKT on success 308 */ 309 static BKT * 310 mpool_bkt(mp) 311 MPOOL *mp; 312 { 313 BKT *b; 314 315 if (mp->curcache < mp->maxcache) 316 goto new; 317 318 /* 319 * If the cache is maxxed out, search the lru list for a buffer we 320 * can flush. If we find one, write it if necessary and take it off 321 * any lists. If we don't find anything we grow the cache anyway. 322 * The cache never shrinks. 323 */ 324 for (b = mp->lru.cprev; b != (BKT *)&mp->lru; b = b->cprev) 325 if (!(b->flags & MPOOL_PINNED)) { 326 if (b->flags & MPOOL_DIRTY && 327 mpool_write(mp, b) == RET_ERROR) 328 return (NULL); 329 rmhash(b); 330 rmchain(b); 331 #ifdef STATISTICS 332 ++mp->pageflush; 333 #endif 334 #ifdef DEBUG 335 { 336 void *spage; 337 spage = b->page; 338 memset(b, 0xff, sizeof(BKT) + mp->pagesize); 339 b->page = spage; 340 } 341 #endif 342 return (b); 343 } 344 345 new: if ((b = malloc(sizeof(BKT) + mp->pagesize)) == NULL) 346 return (NULL); 347 #ifdef STATISTICS 348 ++mp->pagealloc; 349 #endif 350 #ifdef DEBUG 351 memset(b, 0xff, sizeof(BKT) + mp->pagesize); 352 #endif 353 b->page = (char *)b + sizeof(BKT); 354 ++mp->curcache; 355 return (b); 356 } 357 358 /* 359 * MPOOL_WRITE -- sync a page to disk 360 * 361 * Parameters: 362 * mp: mpool cookie 363 * 364 * Returns: 365 * RET_ERROR, RET_SUCCESS 366 */ 367 static int 368 mpool_write(mp, b) 369 MPOOL *mp; 370 BKT *b; 371 { 372 off_t off; 373 374 if (mp->pgout) 375 (mp->pgout)(mp->pgcookie, b->pgno, b->page); 376 377 #ifdef STATISTICS 378 ++mp->pagewrite; 379 #endif 380 off = mp->pagesize * b->pgno; 381 if (lseek(mp->fd, off, SEEK_SET) != off) 382 return (RET_ERROR); 383 if (write(mp->fd, b->page, mp->pagesize) != mp->pagesize) 384 return (RET_ERROR); 385 b->flags &= ~MPOOL_DIRTY; 386 return (RET_SUCCESS); 387 } 388 389 /* 390 * MPOOL_LOOK -- lookup a page 391 * 392 * Parameters: 393 * mp: mpool cookie 394 * pgno: page number 395 * 396 * Returns: 397 * NULL on failure and a pointer to the BKT on success 398 */ 399 static BKT * 400 mpool_look(mp, pgno) 401 MPOOL *mp; 402 pgno_t pgno; 403 { 404 register BKT *b; 405 register BKTHDR *tb; 406 407 /* XXX 408 * If find the buffer, put it first on the hash chain so can 409 * find it again quickly. 410 */ 411 tb = &mp->hashtable[HASHKEY(pgno)]; 412 for (b = tb->hnext; b != (BKT *)tb; b = b->hnext) 413 if (b->pgno == pgno) { 414 #ifdef STATISTICS 415 ++mp->cachehit; 416 #endif 417 return (b); 418 } 419 #ifdef STATISTICS 420 ++mp->cachemiss; 421 #endif 422 return (NULL); 423 } 424 425 #ifdef STATISTICS 426 /* 427 * MPOOL_STAT -- cache statistics 428 * 429 * Parameters: 430 * mp: mpool cookie 431 */ 432 void 433 mpool_stat(mp) 434 MPOOL *mp; 435 { 436 BKT *b; 437 int cnt; 438 char *sep; 439 440 (void)fprintf(stderr, "%lu pages in the file\n", mp->npages); 441 (void)fprintf(stderr, 442 "page size %lu, cacheing %lu pages of %lu page max cache\n", 443 mp->pagesize, mp->curcache, mp->maxcache); 444 (void)fprintf(stderr, "%lu page puts, %lu page gets, %lu page new\n", 445 mp->pageput, mp->pageget, mp->pagenew); 446 (void)fprintf(stderr, "%lu page allocs, %lu page flushes\n", 447 mp->pagealloc, mp->pageflush); 448 if (mp->cachehit + mp->cachemiss) 449 (void)fprintf(stderr, 450 "%.0f%% cache hit rate (%lu hits, %lu misses)\n", 451 ((double)mp->cachehit / (mp->cachehit + mp->cachemiss)) 452 * 100, mp->cachehit, mp->cachemiss); 453 (void)fprintf(stderr, "%lu page reads, %lu page writes\n", 454 mp->pageread, mp->pagewrite); 455 456 sep = ""; 457 cnt = 0; 458 for (b = mp->lru.cnext; b != (BKT *)&mp->lru; b = b->cnext) { 459 (void)fprintf(stderr, "%s%d", sep, b->pgno); 460 if (b->flags & MPOOL_DIRTY) 461 (void)fprintf(stderr, "d"); 462 if (b->flags & MPOOL_PINNED) 463 (void)fprintf(stderr, "P"); 464 if (++cnt == 10) { 465 sep = "\n"; 466 cnt = 0; 467 } else 468 sep = ", "; 469 470 } 471 (void)fprintf(stderr, "\n"); 472 } 473 #endif 474 475 #ifdef DEBUG 476 #if __STDC__ 477 #include <stdarg.h> 478 #else 479 #include <varargs.h> 480 #endif 481 482 static void 483 #if __STDC__ 484 err(const char *fmt, ...) 485 #else 486 err(fmt, va_alist) 487 char *fmt; 488 va_dcl 489 #endif 490 { 491 va_list ap; 492 #if __STDC__ 493 va_start(ap, fmt); 494 #else 495 va_start(ap); 496 #endif 497 (void)vfprintf(stderr, fmt, ap); 498 va_end(ap); 499 (void)fprintf(stderr, "\n"); 500 abort(); 501 /* NOTREACHED */ 502 } 503 #endif 504