1 /* 2 * CACHE.C 3 * 4 * Block cache for dump 5 */ 6 7 #include <sys/param.h> 8 #include <sys/stat.h> 9 #include <sys/mman.h> 10 11 #ifdef sunos 12 #include <sys/vnode.h> 13 14 #include <ufs/fs.h> 15 #include <ufs/fsdir.h> 16 #include <ufs/inode.h> 17 #else 18 #include <ufs/ufs/dir.h> 19 #include <ufs/ufs/dinode.h> 20 #include <ufs/ffs/fs.h> 21 #endif 22 23 #include <protocols/dumprestore.h> 24 25 #include <ctype.h> 26 #include <stdio.h> 27 #ifdef __STDC__ 28 #include <errno.h> 29 #include <string.h> 30 #include <stdlib.h> 31 #include <unistd.h> 32 #endif 33 #include "dump.h" 34 35 typedef struct Block { 36 struct Block *b_HNext; /* must be first field */ 37 off_t b_Offset; 38 char *b_Data; 39 } Block; 40 41 #define HFACTOR 4 42 #define BLKFACTOR 4 43 44 static char *DataBase; 45 static Block **BlockHash; 46 static int BlockSize; 47 static int HSize; 48 static int NBlocks; 49 50 static void 51 cinit(void) 52 { 53 int i; 54 int hi; 55 Block *base; 56 57 if ((BlockSize = sblock->fs_bsize * BLKFACTOR) > MAXBSIZE) 58 BlockSize = MAXBSIZE; 59 NBlocks = cachesize / BlockSize; 60 HSize = NBlocks / HFACTOR; 61 62 msg("Cache %d MB, blocksize = %d\n", 63 NBlocks * BlockSize / (1024 * 1024), BlockSize); 64 65 base = calloc(sizeof(Block), NBlocks); 66 BlockHash = calloc(sizeof(Block *), HSize); 67 DataBase = mmap(NULL, NBlocks * BlockSize, 68 PROT_READ|PROT_WRITE, MAP_ANON, -1, 0); 69 for (i = 0; i < NBlocks; ++i) { 70 base[i].b_Data = DataBase + i * BlockSize; 71 base[i].b_Offset = (off_t)-1; 72 hi = i / HFACTOR; 73 base[i].b_HNext = BlockHash[hi]; 74 BlockHash[hi] = &base[i]; 75 } 76 } 77 78 ssize_t 79 cread(int fd, void *buf, size_t nbytes, off_t offset) 80 { 81 Block *blk; 82 Block **pblk; 83 Block **ppblk; 84 int hi; 85 int n; 86 off_t mask; 87 88 /* 89 * If the cache is disabled, or we do not yet know the filesystem 90 * block size, then revert to pread. Otherwise initialize the 91 * cache as necessary and continue. 92 */ 93 if (cachesize <= 0 || sblock->fs_bsize == 0) 94 return(pread(fd, buf, nbytes, offset)); 95 if (DataBase == NULL) 96 cinit(); 97 98 /* 99 * If the request crosses a cache block boundary, or the 100 * request is larger or equal to the cache block size, 101 * revert to pread(). Full-block-reads are typically 102 * one-time calls and caching would be detrimental. 103 */ 104 mask = ~(off_t)(BlockSize - 1); 105 if (nbytes >= BlockSize || 106 ((offset ^ (offset + nbytes - 1)) & mask) != 0) { 107 return(pread(fd, buf, nbytes, offset)); 108 } 109 110 /* 111 * Obtain and access the cache block. Cache a successful 112 * result. If an error occurs, revert to pread() (this might 113 * occur near the end of the media). 114 */ 115 hi = (offset / BlockSize) % HSize; 116 pblk = &BlockHash[hi]; 117 ppblk = NULL; 118 while ((blk = *pblk) != NULL) { 119 if (((blk->b_Offset ^ offset) & mask) == 0) 120 break; 121 ppblk = pblk; 122 pblk = &blk->b_HNext; 123 } 124 if (blk == NULL) { 125 blk = *ppblk; 126 pblk = ppblk; 127 blk->b_Offset = offset & mask; 128 n = pread(fd, blk->b_Data, BlockSize, blk->b_Offset); 129 if (n != BlockSize) { 130 blk->b_Offset = (off_t)-1; 131 blk = NULL; 132 } 133 } 134 if (blk) { 135 bcopy(blk->b_Data + (offset - blk->b_Offset), buf, nbytes); 136 *pblk = blk->b_HNext; 137 blk->b_HNext = BlockHash[hi]; 138 BlockHash[hi] = blk; 139 return(nbytes); 140 } else { 141 return(pread(fd, buf, nbytes, offset)); 142 } 143 } 144 145