1 /* 2 * Copyright (c) 2005 The DragonFly Project. All rights reserved. 3 * 4 * This code is derived from software contributed to The DragonFly Project 5 * by Matthew Dillon <dillon@backplane.com> 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 11 * 1. Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in 15 * the documentation and/or other materials provided with the 16 * distribution. 17 * 3. Neither the name of The DragonFly Project nor the names of its 18 * contributors may be used to endorse or promote products derived 19 * from this software without specific, prior written permission. 20 * 21 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 22 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 23 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 24 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 25 * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 26 * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING, 27 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 28 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 29 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 30 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT 31 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 32 * SUCH DAMAGE. 33 * 34 * $FreeBSD: src/sbin/dump/cache.c,v 1.1.2.1 2003/01/25 18:54:59 dillon Exp $ 35 * $DragonFly: src/sbin/dump/cache.c,v 1.5 2006/10/21 04:10:02 pavalos Exp $ 36 */ 37 /* 38 * Block cache for dump 39 */ 40 41 #include <sys/param.h> 42 #include <sys/stat.h> 43 #include <sys/mman.h> 44 45 #ifdef sunos 46 #include <sys/vnode.h> 47 48 #include <ufs/fs.h> 49 #include <ufs/fsdir.h> 50 #include <ufs/inode.h> 51 #else 52 #include <vfs/ufs/dir.h> 53 #include <vfs/ufs/dinode.h> 54 #include <vfs/ufs/fs.h> 55 #endif 56 57 #include <protocols/dumprestore.h> 58 59 #include <ctype.h> 60 #include <stdio.h> 61 #ifdef __STDC__ 62 #include <errno.h> 63 #include <string.h> 64 #include <stdlib.h> 65 #include <unistd.h> 66 #endif 67 #include "dump.h" 68 69 typedef struct Block { 70 struct Block *b_HNext; /* must be first field */ 71 off_t b_Offset; 72 char *b_Data; 73 } Block; 74 75 #define HFACTOR 4 76 #define BLKFACTOR 4 77 78 static char *DataBase; 79 static Block **BlockHash; 80 static int BlockSize; 81 static int HSize; 82 static int NBlocks; 83 84 static void 85 cinit(void) 86 { 87 int i; 88 int hi; 89 Block *base; 90 91 if ((BlockSize = sblock->fs_bsize * BLKFACTOR) > MAXBSIZE) 92 BlockSize = MAXBSIZE; 93 NBlocks = cachesize / BlockSize; 94 HSize = NBlocks / HFACTOR; 95 96 msg("Cache %d MB, blocksize = %d\n", 97 NBlocks * BlockSize / (1024 * 1024), BlockSize); 98 99 base = calloc(sizeof(Block), NBlocks); 100 BlockHash = calloc(sizeof(Block *), HSize); 101 DataBase = mmap(NULL, NBlocks * BlockSize, 102 PROT_READ|PROT_WRITE, MAP_ANON, -1, 0); 103 for (i = 0; i < NBlocks; ++i) { 104 base[i].b_Data = DataBase + i * BlockSize; 105 base[i].b_Offset = (off_t)-1; 106 hi = i / HFACTOR; 107 base[i].b_HNext = BlockHash[hi]; 108 BlockHash[hi] = &base[i]; 109 } 110 } 111 112 ssize_t 113 cread(int fd, void *buf, size_t nbytes, off_t offset) 114 { 115 Block *blk; 116 Block **pblk; 117 Block **ppblk; 118 int hi; 119 int n; 120 off_t mask; 121 122 /* 123 * If the cache is disabled, or we do not yet know the filesystem 124 * block size, then revert to pread. Otherwise initialize the 125 * cache as necessary and continue. 126 */ 127 if (cachesize <= 0 || sblock->fs_bsize == 0) 128 return(pread(fd, buf, nbytes, offset)); 129 if (DataBase == NULL) 130 cinit(); 131 132 /* 133 * If the request crosses a cache block boundary, or the 134 * request is larger or equal to the cache block size, 135 * revert to pread(). Full-block-reads are typically 136 * one-time calls and caching would be detrimental. 137 */ 138 mask = ~(off_t)(BlockSize - 1); 139 if (nbytes >= (unsigned)BlockSize || 140 ((offset ^ (offset + nbytes - 1)) & mask) != 0) { 141 return(pread(fd, buf, nbytes, offset)); 142 } 143 144 /* 145 * Obtain and access the cache block. Cache a successful 146 * result. If an error occurs, revert to pread() (this might 147 * occur near the end of the media). 148 */ 149 hi = (offset / BlockSize) % HSize; 150 pblk = &BlockHash[hi]; 151 ppblk = NULL; 152 while ((blk = *pblk) != NULL) { 153 if (((blk->b_Offset ^ offset) & mask) == 0) 154 break; 155 ppblk = pblk; 156 pblk = &blk->b_HNext; 157 } 158 if (blk == NULL) { 159 blk = *ppblk; 160 pblk = ppblk; 161 blk->b_Offset = offset & mask; 162 n = pread(fd, blk->b_Data, BlockSize, blk->b_Offset); 163 if (n != BlockSize) { 164 blk->b_Offset = (off_t)-1; 165 blk = NULL; 166 } 167 } 168 if (blk) { 169 bcopy(blk->b_Data + (offset - blk->b_Offset), buf, nbytes); 170 *pblk = blk->b_HNext; 171 blk->b_HNext = BlockHash[hi]; 172 BlockHash[hi] = blk; 173 return(nbytes); 174 } else { 175 return(pread(fd, buf, nbytes, offset)); 176 } 177 } 178 179